在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):DmitryTsepelev/graphql-ruby-fragment_cache开源软件地址(OpenSource Url):https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache开源编程语言(OpenSource Language):Ruby 99.7%开源软件介绍(OpenSource Introduction):GraphQL::FragmentCache
class PostType < BaseObject
field :id, ID, null: false
field :title, String, null: false, cache_fragment: true
end Getting startedAdd the gem to your Gemfile class GraphqSchema < GraphQL::Schema
use GraphQL::FragmentCache
query QueryType
end Include class BaseType < GraphQL::Schema::Object
include GraphQL::FragmentCache::Object
end If you're using resolvers — include the module into the base resolver as well: class Resolvers::BaseResolver < GraphQL::Schema::Resolver
include GraphQL::FragmentCache::ObjectHelpers
end Now you can add class PostType < BaseObject
field :id, ID, null: false
field :title, String, null: false, cache_fragment: true
end Alternatively, you can use class QueryType < BaseObject
field :post, PostType, null: true do
argument :id, ID, required: true
end
def post(id:)
cache_fragment { Post.find(id) }
end
end Cache key generationCache keys consist of the following parts: namespace, implicit key, and explicit key. Cache namespaceYou can optionally define a namespace that will be prefixed to every cache key: GraphQL::FragmentCache.namespace = "my-prefix" Implicit cache keyImplicit part of a cache key contains the information about the schema and the current query. It includes:
Let's take a look at the example: query = <<~GQL
query {
post(id: 1) {
id
title
cachedAuthor {
id
name
}
}
}
GQL
schema_cache_key = GraphqSchema.schema_cache_key
path_cache_key = "post(id:1)/cachedAuthor"
selections_cache_key = "[#{%w[id name].join(".")}]"
query_cache_key = Digest::SHA1.hexdigest("#{path_cache_key}#{selections_cache_key}")
cache_key = "#{schema_cache_key}/#{query_cache_key}/#{object_cache_key}" You can override class QueryType < BaseObject
field :post, PostType, null: true do
argument :id, ID, required: true
end
def post(id:)
cache_fragment(query_cache_key: "post(#{id})") { Post.find(id) }
end
end Overriding Same for the option: class PostType < BaseObject
field :id, ID, null: false
field :title, String, null: false, cache_fragment: {query_cache_key: "post_title"}
end Overriding class QueryType < BaseObject
field :post, PostType, null: true do
argument :id, ID, required: true
end
def post(id:)
query = Post.where("updated_at < ?", Time.now - 1.day)
cache_fragment(object_cache_key: query.cache_key) { query.some_process }
end
end User-provided cache key (custom key)In most cases you want your cache key to depend on the resolved object (say, def post(id:)
post = Post.find(id)
cache_fragment(post) { post }
end You can pass arrays as well to build a compound cache key: def post(id:)
post = Post.find(id)
cache_fragment([post, current_account]) { post }
end You can omit the block if its return value is the same as the cached object: # the following line
cache_fragment(post)
# is the same as
cache_fragment(post) { post } Using literals: Even when using a same string for all queries, the cache changes per argument and per selection set (because of the query_key). def post(id:)
cache_fragment("find_post") { Post.find(id) }
end Combining with options: def post(id:)
cache_fragment("find_post", expires_in: 5.minutes) { Post.find(id) }
end Dynamic cache key: def post(id:)
last_updated_at = Post.select(:updated_at).find_by(id: id)&.updated_at
cache_fragment(last_updated_at, expires_in: 5.minutes) { Post.find(id) }
end Note the usage of You can also add touch options for the belongs_to association e.g author's When using field :post, PostType, null: true, cache_fragment: {cache_key: :object} do
argument :id, ID, required: true
end
# this is equal to
def post(id:)
cache_fragment(Post.find(id))
end Also, you can pass field :post, PostType, null: true, cache_fragment: {cache_key: :value} do
argument :id, ID, required: true
end
# this is equal to
def post(id:)
post = Post.find(id)
cache_fragment(post) { post }
end The way cache key part is generated for the passed argument is the following:
Context cache keyBy default, we do not take context into account when calculating cache keys. That's because caching is more efficient when it's context-free. However, if you want some fields to be cached per context, you can do that either by passing context objects directly to the For instance, imagine a query that allows the current user's social profiles: query {
socialProfiles {
provider
id
}
} You can cache the result using the context ( class QueryType < BaseObject
field :social_profiles, [SocialProfileType], null: false, cache_fragment: {context_key: :user}
def social_profiles
context[:user].social_profiles
end
end This is equal to using class QueryType < BaseObject
field :social_profiles, [SocialProfileType], null: false
def social_profiles
cache_fragment(context[:user]) { context[:user].social_profiles }
end
end Conditional cachingUse the def post(id:)
cache_fragment(if: current_user.nil?) { Post.find(id) }
end
# or
field :post, PostType, cache_fragment: {if: -> { current_user.nil? }} do
argument :id, ID, required: true
end Default optionsYou can configure default options that will be passed to all GraphQL::FragmentCache.configure do |config|
config.default_options = {
expires_in: 1.hour, # Expire cache keys after 1 hour
schema_cache_key: nil # Do not clear the cache on each schema change
}
end Renewing the cacheYou can force the cache to renew during query execution by adding
MyAppSchema.execute("query { posts { title } }", context: {renew_cache: true}) This will treat any cached value as missing even if it's present, and store fresh new computed values in the cache. This can be useful for cache warmers. Cache storage and optionsIt's up to your to decide which caching engine to use, all you need is to configure the cache store: GraphQL::FragmentCache.configure do |config|
config.cache_store = MyCacheStore.new
end Or, in Rails: # config/application.rb (or config/environments/<environment>.rb)
Rails.application.configure do |config|
# arguments and options are the same as for `config.cache_store`
config.graphql_fragment_cache.store = :redis_cache_store
end
The gem provides only in-memory store out-of-the-box ( You can pass store-specific options to class PostType < BaseObject
field :id, ID, null: false
field :title, String, null: false, cache_fragment: {expires_in: 5.minutes}
end
class QueryType < BaseObject
field :post, PostType, null: true do
argument :id, ID, required: true
end
def post(id:)
cache_fragment(expires_in: 5.minutes) { Post.find(id) }
end
end
How to use |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论