graphql-persisted_queries 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12531a6f0099e3220f5ea0bf400b1ccd23f2e6f3161d91d54c05ae404d3e1532
4
- data.tar.gz: 4660b422f801db343ce2f8e04ea21576ae500d7d9519f87399300e957f555160
3
+ metadata.gz: 47cee014870d4382ee5d140302c25a5942ac2e0b8ba3886577a9299e8e097e24
4
+ data.tar.gz: 1d0406419d6bcce3137863906d72c3d5a670cf56477c166d54b1dc182e016dfa
5
5
  SHA512:
6
- metadata.gz: c2d317723d55e67aebc607624e32e7052c53f378e8dd4584d161102167f5f44c221580518ab912a88db3e8b82381455ba77b20aefce5904602159c33bd23b88c
7
- data.tar.gz: b831aeb69392cdd662fcf285cc8f48051feb3cd3703db862bd4434cfdbb67acd4b85cf70ae64cbcee61f7df969cbd913c152517af4a7da2b7121ae99fd0282a4
6
+ metadata.gz: b5cf4c0e7d3021dff138122c176983edc1ebbf17ffad0327423389d354e19f6fdc1ef541be58adb5aa4d364aa0e21bf17226ede8dee47cb94f283d677dae326a
7
+ data.tar.gz: 10763b571169cdddd8c47c31f4a0350ee8a0a5d3aa6729f6a7669799800b3474810edecd5126c4c6f18c5cf5b52de7ac4f686c5ead9d648eb3032f8cddedbe4c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.5.0 (2020-03-12)
6
+
7
+ - [PR#29](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries/pull/29) Instrumentation via graphql-ruby's tracer feature ([@bmorton][])
8
+
5
9
  ## 0.4.0 (2020-02-26)
6
10
 
7
11
  - [PR#26](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries/pull/26) Add Memcached store ([@JanStevens][])
data/README.md CHANGED
@@ -208,6 +208,10 @@ Using `GET` requests for persisted queries allows you to enable HTTP caching (e.
208
208
 
209
209
  HTTP method verification is important, because when mutations are allowed via `GET` requests, it's easy to perform an attack by sending the link containing mutation to a signed in user.
210
210
 
211
+ ## Tracing and instrumentation
212
+
213
+ An experimental tracing feature can be enabled by setting `tracing: true` when configuring the plugin. Read more about this feature in the [Tracing guide](docs/tracing.md).
214
+
211
215
  ## Contributing
212
216
 
213
217
  Bug reports and pull requests are welcome on GitHub at https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries.
data/docs/tracing.md ADDED
@@ -0,0 +1,51 @@
1
+ # Tracing
2
+
3
+ Tracing is an experimental feature that, when enabled, uses the tracing system defined in `graphql-ruby` to surface these events:
4
+
5
+ * `persisted_queries.fetch_query.cache_hit` - Triggered when a store adapter successfully looks up a hash and finds a query.
6
+ * `persisted_queries.fetch_query.cache_miss` - Triggered when a store adapter attempts to look up a hash but cannot find it.
7
+ * `persisted_queries.save_query` - Triggered when a store adapter persists a query.
8
+
9
+ All events include a metadata hash as their `data` parameter. This hash currently only includes the name of the adapter that triggered the event.
10
+
11
+ ## Usage
12
+
13
+ Tracing must be opted into via the plugin configuration for the events to trigger. Once they are enabled, any tracer that is defined on the schema will get the following events yielded to them. An example configuration will look similar to this:
14
+
15
+ ```ruby
16
+ class GraphqlSchema < GraphQL::Schema
17
+ use GraphQL::PersistedQueries, tracing: true
18
+ tracer MyPersistedQueriesTracer
19
+ end
20
+ ```
21
+
22
+ Tracers in this plugin integrate with the `GraphQL::Tracing` feature in [`graphql-ruby`](https://github.com/rmosolgo/graphql-ruby). The same tracers are used for tracing events directly from `graphql-ruby` and this plugin. The [guide on "Tracing"](https://graphql-ruby.org/queries/tracing.html) in `graphql-ruby` has implementation details, but an example tracer would look similar to this:
23
+
24
+ ```ruby
25
+ class MyPersistedQueriesTracer
26
+ def self.trace(key, data)
27
+ yield.tap do |result|
28
+ # Note: this tracer will get called for these persisted queries events as
29
+ # well as all events traced by the graphql-ruby gem.
30
+ case key
31
+ when "persisted_queries.fetch_query.cache_hit"
32
+ # data = { adapter: :redis }
33
+ # result = nil
34
+ # increment a counter metric to track cache hits
35
+ when "persisted_queries.fetch_query.cache_miss"
36
+ # data = { adapter: :redis }
37
+ # result = nil
38
+ # increment a counter metric to track cache misses
39
+ when "persisted_queries.save_query"
40
+ # data = { adapter: :redis }
41
+ # result = return value from method call
42
+ # increment a counter metric to track saved queries
43
+ end
44
+ end
45
+ end
46
+ end
47
+ ```
48
+
49
+ ## Be aware of tracers-as-notifications
50
+
51
+ A word of caution about the `cache_hit` and `cache_miss` events: they yield an empty block. The `GraphQL::Tracing` feature typically wraps around the code performing the event. The `save_query` event works this way, too -- the block that is yielded is essentially the `StoreAdapter#save` method. This means you can add timing instrumentation for this call. However, the `cache_hit` and `cache_miss` events are simply event notifications and do not wrap any code. This means that they won't yield anything meaningful and they can't be timed.
@@ -21,6 +21,8 @@ module GraphQL
21
21
  error_handler = options.delete(:error_handler) || :default
22
22
  schema.configure_persisted_query_error_handler(error_handler)
23
23
 
24
+ schema.persisted_queries_tracing_enabled = options.delete(:tracing)
25
+
24
26
  store = options.delete(:store) || :memory
25
27
  schema.configure_persisted_query_store(store, options)
26
28
  end
@@ -16,9 +16,12 @@ module GraphQL
16
16
  end
17
17
 
18
18
  attr_reader :persisted_query_store, :hash_generator_proc, :persisted_query_error_handler
19
+ attr_writer :persisted_queries_tracing_enabled
19
20
 
20
21
  def configure_persisted_query_store(store, options)
21
- @persisted_query_store = StoreAdapters.build(store, options)
22
+ @persisted_query_store = StoreAdapters.build(store, options).tap do |adapter|
23
+ adapter.tracers = tracers if persisted_queries_tracing_enabled?
24
+ end
22
25
  end
23
26
 
24
27
  def configure_persisted_query_error_handler(handler)
@@ -41,9 +44,22 @@ module GraphQL
41
44
  end
42
45
  end
43
46
 
47
+ def persisted_queries_tracing_enabled?
48
+ @persisted_queries_tracing_enabled
49
+ end
50
+
44
51
  def multiplex(queries, **kwargs)
45
52
  MultiplexResolver.new(self, queries, kwargs).resolve
46
53
  end
54
+
55
+ def tracer(name)
56
+ super.tap do
57
+ # Since tracers can be set before *and* after our plugin hooks in,
58
+ # we need to set tracers both when this plugin gets initialized
59
+ # and any time a tracer is added after initialization
60
+ persisted_query_store.tracers = tracers if persisted_queries_tracing_enabled?
61
+ end
62
+ end
47
63
  end
48
64
  end
49
65
  end
@@ -5,15 +5,42 @@ module GraphQL
5
5
  module StoreAdapters
6
6
  # Base class for all store adapters
7
7
  class BaseStoreAdapter
8
- def initialize(_options); end
8
+ include GraphQL::Tracing::Traceable
9
+ attr_writer :tracers
9
10
 
10
- def fetch_query(_hash)
11
+ def initialize(_options)
12
+ @name = :base
13
+ end
14
+
15
+ def fetch_query(hash)
16
+ fetch(hash).tap do |result|
17
+ event = result ? "cache_hit" : "cache_miss"
18
+ trace("fetch_query.#{event}", adapter: @name)
19
+ end
20
+ end
21
+
22
+ def save_query(hash, query)
23
+ trace("save_query", adapter: @name) { save(hash, query) }
24
+ end
25
+
26
+ protected
27
+
28
+ def fetch(_hash)
11
29
  raise NotImplementedError
12
30
  end
13
31
 
14
- def save_query(_hash, _query)
32
+ def save(_hash, _query)
15
33
  raise NotImplementedError
16
34
  end
35
+
36
+ def trace(key, metadata)
37
+ if @tracers
38
+ key = "persisted_queries.#{key}"
39
+ block_given? ? super : super {}
40
+ elsif block_given?
41
+ yield
42
+ end
43
+ end
17
44
  end
18
45
  end
19
46
  end
@@ -14,13 +14,16 @@ module GraphQL
14
14
  @dalli_proc = build_dalli_proc(dalli_client)
15
15
  @expiration = expiration || DEFAULT_EXPIRATION
16
16
  @namespace = namespace || DEFAULT_NAMESPACE
17
+ @name = :memcached
17
18
  end
18
19
 
19
- def fetch_query(hash)
20
+ protected
21
+
22
+ def fetch(hash)
20
23
  @dalli_proc.call { |dalli| dalli.get(key_for(hash)) }
21
24
  end
22
25
 
23
- def save_query(hash, query)
26
+ def save(hash, query)
24
27
  @dalli_proc.call { |dalli| dalli.set(key_for(hash), query, @expiration) }
25
28
  end
26
29
 
@@ -7,13 +7,16 @@ module GraphQL
7
7
  class MemoryStoreAdapter < BaseStoreAdapter
8
8
  def initialize(_options)
9
9
  @storage = {}
10
+ @name = :memory
10
11
  end
11
12
 
12
- def fetch_query(hash)
13
+ protected
14
+
15
+ def fetch(hash)
13
16
  @storage[hash]
14
17
  end
15
18
 
16
- def save_query(hash, query)
19
+ def save(hash, query)
17
20
  @storage[hash] = query
18
21
  end
19
22
  end
@@ -14,13 +14,16 @@ module GraphQL
14
14
  @redis_proc = build_redis_proc(redis_client)
15
15
  @expiration = expiration || DEFAULT_EXPIRATION
16
16
  @namespace = namespace || DEFAULT_NAMESPACE
17
+ @name = :redis
17
18
  end
18
19
 
19
- def fetch_query(hash)
20
+ protected
21
+
22
+ def fetch(hash)
20
23
  @redis_proc.call { |redis| redis.get(key_for(hash)) }
21
24
  end
22
25
 
23
- def save_query(hash, query)
26
+ def save(hash, query)
24
27
  @redis_proc.call { |redis| redis.set(key_for(hash), query, ex: @expiration) }
25
28
  end
26
29
 
@@ -19,9 +19,20 @@ module GraphQL
19
19
  namespace: namespace
20
20
  )
21
21
  @memory_adapter = memory_adapter_class.new(nil)
22
+ @name = :redis_with_local_cache
22
23
  end
23
24
 
24
- def fetch_query(hash)
25
+ # We don't need to implement our own traces for this adapter since the
26
+ # underlying adapters will emit the proper events for us. However,
27
+ # since tracers can be defined at any time, we need to pass them through.
28
+ def tracers=(tracers)
29
+ @memory_adapter.tracers = tracers
30
+ @redis_adapter.tracers = tracers
31
+ end
32
+
33
+ protected
34
+
35
+ def fetch(hash)
25
36
  result = @memory_adapter.fetch_query(hash)
26
37
  result ||= begin
27
38
  inner_result = @redis_adapter.fetch_query(hash)
@@ -31,7 +42,7 @@ module GraphQL
31
42
  result
32
43
  end
33
44
 
34
- def save_query(hash, query)
45
+ def save(hash, query)
35
46
  @redis_adapter.save_query(hash, query)
36
47
  @memory_adapter.save_query(hash, query)
37
48
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module PersistedQueries
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-persisted_queries
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-26 00:00:00.000000000 Z
11
+ date: 2020-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -125,6 +125,7 @@ files:
125
125
  - Rakefile
126
126
  - bin/console
127
127
  - bin/setup
128
+ - docs/tracing.md
128
129
  - gemfiles/graphql_1_10.gemfile
129
130
  - gemfiles/graphql_1_8.gemfile
130
131
  - gemfiles/graphql_1_9.gemfile