Skip to content

Commit

Permalink
Introduce cache lookup instrumentation hook (#125)
Browse files Browse the repository at this point in the history
* Introduce hook to monitor cache hits

* Fixes failing specs when query context is missing

* Support any number of named arguments

* fix: address rubocop violation

* Introduce option to enable cache lookup monitoring

* Adds documentation for cache monitoring

---------

Co-authored-by: Daniel Hartnell <[email protected]>
  • Loading branch information
danielhartnell and Daniel Hartnell authored Jan 3, 2025
1 parent 8796342 commit 2cffc00
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,30 @@ Cache processing can be disabled if needed. For example:
GraphQL::FragmentCache.enabled = false if Rails.env.test?
```

## Cache lookup monitoring

It may be useful to capture cache lookup events. When monitoring is enabled, the `cache_key`, `operation_name`, `path` and a boolean indicating a cache hit or miss will be sent to a `cache_lookup_event` method. This method can be implemented in your application to handle the event.

Example handler defined in a Rails initializer:

```ruby
module GraphQL
module FragmentCache
class Fragment
def self.cache_lookup_event(**args)
# Monitoring such as incrementing a cache hit counter metric
end
end
end
end
```

Like managing caching itself, monitoring can be enabled if needed. It is disabled by default. For example:

```ruby
GraphQL::FragmentCache.monitoring_enabled = true
```

## Limitations

1. `Schema#execute`, [graphql-batch](https://github.com/Shopify/graphql-batch) and _graphql-ruby-fragment_cache_ do not [play well](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache/issues/45) together. The problem appears when `cache_fragment` is _inside_ the `.then` block:
Expand Down
2 changes: 2 additions & 0 deletions lib/graphql/fragment_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module FragmentCache
class << self
attr_reader :cache_store
attr_accessor :enabled
attr_accessor :monitoring_enabled
attr_accessor :namespace
attr_accessor :default_options

Expand Down Expand Up @@ -87,6 +88,7 @@ def verify_interpreter_and_analysis!(schema_defn)

self.cache_store = MemoryStore.new
self.enabled = true
self.monitoring_enabled = false
self.namespace = "graphql"
self.default_options = {}
self.skip_cache_when_query_has_errors = false
Expand Down
20 changes: 20 additions & 0 deletions lib/graphql/fragment_cache/fragment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ def read_multi(fragments)
FragmentCache.cache_store.read_multi(*cache_keys)
end

if GraphQL::FragmentCache.monitoring_enabled
begin
fragments.map do |fragment|
cache_lookup_event(
cache_key: fragment.cache_key,
operation_name: fragment.context.query.operation_name,
path: fragment.path,
cache_hit: cache_keys_to_values.key?(fragment.cache_key)
)
end
rescue
# Allow cache_lookup_event to fail when we do not have all of the requested attributes
end
end

# Fragmenst without values or with renew_cache: true in their context will have nil values like the read method
fragments_to_cache_keys
.map { |fragment, cache_key| [fragment, cache_keys_to_values[cache_key]] }.to_h
Expand Down Expand Up @@ -87,6 +102,11 @@ def interpreter_context
def final_value
@final_value ||= context.query.result["data"]
end

def cache_lookup_event(**args)
# This method can be implemented in your application
# This provides a mechanism to monitor cache hits for a fragment
end
end
end
end

0 comments on commit 2cffc00

Please sign in to comment.