graphql-persisted_queries 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff010e4d5d11f8e7028d4152a880de6d4503bda6b6e777fbc0e7b3bc19058ac8
4
- data.tar.gz: 0d213b909ab7668c8248d64ab43c1e80f223d5b46b3d9b3954272728e5b96cec
3
+ metadata.gz: 18dc641307c696315b6978f4f2e57beaa482fa43db63ab01cdb3eead99ec827c
4
+ data.tar.gz: a746e4c611a9478c3ced4adc2319db49d4d40e5d56dbefcb345ff871e16b8684
5
5
  SHA512:
6
- metadata.gz: fc0ec99c3950301f1d23779b7706f24f77b5bbffd4f6df9d167379fc25eff9e6eebeb12296331c9a50972152b9fd7e457262bf6fa8379e2dce6d9e4668cb6e9b
7
- data.tar.gz: dd09cab2aff3905cd6afca029f16377b963856f160eb9c4730ce64fdc2b8cbce861f402c26f748d38fa1fd466a9af04165d284f9792f33f777ffe5c8e2eaebcb
6
+ metadata.gz: '08b4dac2681ea25d189bfb51d9e0fe6bbcc44033787d534f2cf02a0a1b1cc2a8cdae0a3e7a4ba5a44bc4d22c43bf5275113ad1b16e30ae2e4269db68e16a0b6d'
7
+ data.tar.gz: f34db005a3a4c3297a1c682290faaa94b54ff28dcba05140fb8f83cfb0e330fc4b937663f960fbf687a0bf49d97cd06cb0363094a887cf1857cea1397e4b0623
@@ -23,6 +23,9 @@ jobs:
23
23
  "gemfiles/graphql_1_8.gemfile",
24
24
  "gemfiles/graphql_1_9.gemfile",
25
25
  "gemfiles/graphql_1_10.gemfile",
26
+ "gemfiles/graphql_1_11.gemfile",
27
+ "gemfiles/graphql_1_12_0.gemfile",
28
+ "gemfiles/graphql_1_12_4.gemfile",
26
29
  "gemfiles/graphql_master.gemfile"
27
30
  ]
28
31
 
@@ -49,4 +52,4 @@ jobs:
49
52
  bundle update
50
53
  - name: Run RSpec
51
54
  run: |
52
- bundle exec rake spec
55
+ bundle exec rake ci_specs
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.2.0 (2021-02-24)
6
+
7
+ - [PR#39](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries/pull/39) Implement compiled queries ([@DmitryTsepelev][])
8
+
5
9
  ## 1.1.1 (2020-12-03)
6
10
 
7
11
  - [PR#37](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries/pull/37) Fix deprecation warnings ([@rbviz][])
data/README.md CHANGED
@@ -65,6 +65,20 @@ GraphqlSchema.execute(
65
65
 
66
66
  You're all set!
67
67
 
68
+ ## Compiled queries (increases performance up to 2x!)
69
+
70
+ When query arrives to the backend, GraphQL execution engine needs some time to _parse_ it and build the AST. In case of a huge query it might take [a lot](https://gist.github.com/DmitryTsepelev/36e290cf64b4ec0b18294d0a57fb26ff#file-1_result-md) of time. What if we cache the AST instead of a query text and skip parsing completely? The only thing you need to do is to turn `:compiled_queries` option on:
71
+
72
+ ```ruby
73
+ class GraphqlSchema < GraphQL::Schema
74
+ use GraphQL::PersistedQueries, compiled_queries: true
75
+ end
76
+ ```
77
+
78
+ Using this option might make your endpoint up to 2x faster according to the [benchmark](docs/compiled_queries_benchmark.md).
79
+
80
+ **Heads up!** This feature only works on `graphql-ruby` 1.12.0 or later, but I guess it might be backported.
81
+
68
82
  ## Advanced usage
69
83
 
70
84
  All the queries are stored in memory by default, but you can easily switch to another storage (e.g., _redis_:
data/Rakefile CHANGED
@@ -5,4 +5,32 @@ require "rubocop/rake_task"
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
  RuboCop::RakeTask.new
7
7
 
8
- task default: [:rubocop, :spec]
8
+ desc "Run specs for compiled queries"
9
+ RSpec::Core::RakeTask.new("spec:compiled_queries") do |task|
10
+ task.pattern = "**/compiled_queries/**"
11
+ task.verbose = false
12
+ end
13
+
14
+ RSpec::Core::RakeTask.new("spec:without_compiled_queries") do |task|
15
+ task.exclude_pattern = "**/compiled_queries/**"
16
+ task.verbose = false
17
+ end
18
+
19
+ task ci_specs: ["spec:without_compiled_queries", "spec:compiled_queries"]
20
+
21
+ task :bench_gql do
22
+ cmd = %w[bundle exec ruby benchmark/plain_gql.rb]
23
+ system(*cmd)
24
+ end
25
+
26
+ task :bench_pq do
27
+ cmd = %w[bundle exec ruby benchmark/persisted_queries.rb]
28
+ system(*cmd)
29
+ end
30
+
31
+ task :bench_compiled do
32
+ cmd = %w[bundle exec ruby benchmark/compiled_queries.rb]
33
+ system(*cmd)
34
+ end
35
+
36
+ task bench: [:bench_gql, :bench_pq, :bench_compiled]
@@ -0,0 +1,41 @@
1
+ require "bundler/inline"
2
+
3
+ gemfile do
4
+ source "https://rubygems.org"
5
+ gem "graphql", "1.12.4"
6
+ end
7
+
8
+ $:.push File.expand_path("../lib", __dir__)
9
+
10
+ require "benchmark"
11
+ require "graphql/persisted_queries"
12
+ require_relative "helpers"
13
+
14
+ class GraphqlSchema < GraphQL::Schema
15
+ use GraphQL::PersistedQueries, compiled_queries: true
16
+
17
+ query QueryType
18
+ end
19
+
20
+ GraphqlSchema.to_definition
21
+
22
+ puts
23
+ puts "Schema with compiled queries:"
24
+ puts
25
+
26
+ Benchmark.bm(28) do |x|
27
+ [false, true].each do |with_nested|
28
+ FIELD_COUNTS.each do |field_count|
29
+ query = generate_query(field_count, with_nested)
30
+ sha256 = Digest::SHA256.hexdigest(query)
31
+
32
+ context = { extensions: { "persistedQuery" => { "sha256Hash" => sha256 } } }
33
+ # warmup
34
+ GraphqlSchema.execute(query, context: context)
35
+
36
+ x.report("#{field_count} fields#{" (nested)" if with_nested}") do
37
+ GraphqlSchema.execute(query, context: context)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ FIELD_COUNTS = [10, 50, 100, 200, 300]
2
+
3
+ def generate_fields(field_count, with_nested)
4
+ fields = field_count.times.map do |i|
5
+ field = "field#{i+1}"
6
+ field += "\s{#{generate_fields(field_count, false)}}" if with_nested
7
+ field
8
+ end
9
+
10
+ fields.join("\n")
11
+ end
12
+
13
+ def generate_query(field_count, with_nested)
14
+ <<-gql
15
+ query {
16
+ #{generate_fields(field_count, with_nested)}
17
+ }
18
+ gql
19
+ end
20
+
21
+ class ChildType < GraphQL::Schema::Object
22
+ FIELD_COUNTS.max.times do |i|
23
+ field "field#{i + 1}".to_sym, String, null: false, method: :itself
24
+ end
25
+ end
26
+
27
+ class QueryType < GraphQL::Schema::Object
28
+ FIELD_COUNTS.max.times do |i|
29
+ field "field#{i + 1}".to_sym, ChildType, null: false, method: :itself
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ require "bundler/inline"
2
+
3
+ gemfile do
4
+ source "https://rubygems.org"
5
+ gem "graphql", "1.12.4"
6
+ end
7
+
8
+ $:.push File.expand_path("../lib", __dir__)
9
+
10
+ require "benchmark"
11
+ require "graphql/persisted_queries"
12
+ require_relative "helpers"
13
+
14
+ class GraphqlSchema < GraphQL::Schema
15
+ use GraphQL::PersistedQueries
16
+
17
+ query QueryType
18
+ end
19
+
20
+ GraphqlSchema.to_definition
21
+
22
+ puts
23
+ puts "Schema with persisted queries:"
24
+ puts
25
+
26
+ Benchmark.bm(28) do |x|
27
+ [false, true].each do |with_nested|
28
+ FIELD_COUNTS.each do |field_count|
29
+ query = generate_query(field_count, with_nested)
30
+ sha256 = Digest::SHA256.hexdigest(query)
31
+ context = { extensions: { "persistedQuery" => { "sha256Hash" => sha256 } } }
32
+
33
+ # warmup
34
+ GraphqlSchema.execute(query, context: context)
35
+
36
+ x.report("#{field_count} fields#{" (nested)" if with_nested}") do
37
+ GraphqlSchema.execute(query, context: context)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ require "bundler/inline"
2
+
3
+ gemfile do
4
+ source "https://rubygems.org"
5
+ gem "graphql", "1.12.4"
6
+ end
7
+
8
+ $:.push File.expand_path("../lib", __dir__)
9
+
10
+ require "benchmark"
11
+ require "graphql/persisted_queries"
12
+ require_relative "helpers"
13
+
14
+ class GraphqlSchema < GraphQL::Schema
15
+ query QueryType
16
+ end
17
+
18
+ GraphqlSchema.to_definition
19
+
20
+ puts "Plain schema:"
21
+ puts
22
+
23
+ Benchmark.bm(28) do |x|
24
+ [false, true].each do |with_nested|
25
+ FIELD_COUNTS.each do |field_count|
26
+ query = generate_query(field_count, with_nested)
27
+
28
+ x.report("#{field_count} fields#{" (nested)" if with_nested}") do
29
+ GraphqlSchema.execute(query)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,75 @@
1
+ # Compiled queries benchmarks
2
+
3
+ The name of benchmark consists of a field count and optional "nested" label. In case of non–nested one we just generate a query with that field count, e.g. `2 fields` means:
4
+
5
+ ```gql
6
+ query {
7
+ field1
8
+ field2
9
+ }
10
+ ```
11
+
12
+ In case of "nested" benchmark we also put a list of fields to each top–level field, e.g. `2 fields (nested)` means:
13
+
14
+ ```gql
15
+ query {
16
+ field1 {
17
+ field1
18
+ field2
19
+ }
20
+ field2 {
21
+ field1
22
+ field2
23
+ }
24
+ }
25
+ ```
26
+
27
+ Field resolver just returns a string, so real–world tests might be way slower because of IO.
28
+
29
+ Here are the results:
30
+
31
+ ```
32
+ Plain schema:
33
+
34
+ user system total real
35
+ 10 fields 0.001061 0.000039 0.001100 ( 0.001114)
36
+ 50 fields 0.001658 0.000003 0.001661 ( 0.001661)
37
+ 100 fields 0.004587 0.000026 0.004613 ( 0.004614)
38
+ 200 fields 0.006447 0.000016 0.006463 ( 0.006476)
39
+ 300 fields 0.024493 0.000073 0.024566 ( 0.024614)
40
+ 10 fields (nested) 0.003061 0.000043 0.003104 ( 0.003109)
41
+ 50 fields (nested) 0.056927 0.000995 0.057922 ( 0.057997)
42
+ 100 fields (nested) 0.245235 0.001336 0.246571 ( 0.246727)
43
+ 200 fields (nested) 0.974444 0.006531 0.980975 ( 0.981810)
44
+ 300 fields (nested) 2.175855 0.012773 2.188628 ( 2.190130)
45
+
46
+ Schema with persisted queries:
47
+
48
+ user system total real
49
+ 10 fields 0.000606 0.000007 0.000613 ( 0.000607)
50
+ 50 fields 0.001855 0.000070 0.001925 ( 0.001915)
51
+ 100 fields 0.003239 0.000009 0.003248 ( 0.003239)
52
+ 200 fields 0.007542 0.000009 0.007551 ( 0.007551)
53
+ 300 fields 0.014975 0.000237 0.015212 ( 0.015318)
54
+ 10 fields (nested) 0.002992 0.000068 0.003060 ( 0.003049)
55
+ 50 fields (nested) 0.062314 0.000274 0.062588 ( 0.062662)
56
+ 100 fields (nested) 0.256404 0.000865 0.257269 ( 0.257419)
57
+ 200 fields (nested) 0.978408 0.007437 0.985845 ( 0.986579)
58
+ 300 fields (nested) 2.263338 0.010994 2.274332 ( 2.275967)
59
+
60
+ Schema with compiled queries:
61
+
62
+ user system total real
63
+ 10 fields 0.000526 0.000009 0.000535 ( 0.000530)
64
+ 50 fields 0.001280 0.000012 0.001292 ( 0.001280)
65
+ 100 fields 0.002292 0.000004 0.002296 ( 0.002286)
66
+ 200 fields 0.005462 0.000001 0.005463 ( 0.005463)
67
+ 300 fields 0.014229 0.000121 0.014350 ( 0.014348)
68
+ 10 fields (nested) 0.002027 0.000069 0.002096 ( 0.002104)
69
+ 50 fields (nested) 0.029933 0.000087 0.030020 ( 0.030040)
70
+ 100 fields (nested) 0.133933 0.000502 0.134435 ( 0.134756)
71
+ 200 fields (nested) 0.495052 0.003545 0.498597 ( 0.499452)
72
+ 300 fields (nested) 1.041463 0.005130 1.046593 ( 1.047137)
73
+ ```
74
+
75
+ Results gathered from my MacBook Pro Mid 2014 (2,5 GHz Quad-Core Intel Core i7, 16 GB 1600 MHz DDR3).
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "graphql", "~> 1.11.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "graphql", "~> 1.12.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "graphql", "~> 1.12.4"
4
+
5
+ gemspec path: "../"
@@ -1,17 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/persisted_queries/resolver_helpers"
4
+ require "graphql/persisted_queries/errors"
3
5
  require "graphql/persisted_queries/error_handlers"
4
6
  require "graphql/persisted_queries/schema_patch"
5
7
  require "graphql/persisted_queries/store_adapters"
6
8
  require "graphql/persisted_queries/version"
7
9
  require "graphql/persisted_queries/builder_helpers"
8
10
 
11
+ require "graphql/persisted_queries/compiled_queries/resolver"
12
+ require "graphql/persisted_queries/compiled_queries/multiplex_patch"
13
+ require "graphql/persisted_queries/compiled_queries/query_patch"
14
+
9
15
  module GraphQL
10
16
  # Plugin definition
11
17
  module PersistedQueries
18
+ # rubocop:disable Metrics/MethodLength
12
19
  def self.use(schema_defn, **options)
13
20
  schema = schema_defn.is_a?(Class) ? schema_defn : schema_defn.target
14
- SchemaPatch.patch(schema)
21
+
22
+ compiled_queries = options.delete(:compiled_queries)
23
+ SchemaPatch.patch(schema, compiled_queries)
24
+ configure_compiled_queries if compiled_queries
15
25
 
16
26
  schema.hash_generator = options.delete(:hash_generator) || :sha256
17
27
 
@@ -25,5 +35,18 @@ module GraphQL
25
35
  store = options.delete(:store) || :memory
26
36
  schema.configure_persisted_query_store(store, **options)
27
37
  end
38
+ # rubocop:enable Metrics/MethodLength
39
+
40
+ def self.configure_compiled_queries
41
+ if Gem::Dependency.new("graphql", "< 1.12.0").match?("graphql", GraphQL::VERSION)
42
+ raise ArgumentError, "compiled_queries are not supported for graphql-ruby < 1.12.0"
43
+ end
44
+
45
+ GraphQL::Execution::Multiplex.singleton_class.prepend(
46
+ GraphQL::PersistedQueries::CompiledQueries::MultiplexPatch
47
+ )
48
+
49
+ GraphQL::Query.prepend(GraphQL::PersistedQueries::CompiledQueries::QueryPatch)
50
+ end
28
51
  end
29
52
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module PersistedQueries
5
+ module CompiledQueries
6
+ # Patches GraphQL::Execution::Multiplex to support compiled queries
7
+ module MultiplexPatch
8
+ if Gem::Dependency.new("graphql", ">= 1.12.4").match?("graphql", GraphQL::VERSION)
9
+ def begin_query(results, idx, query, multiplex)
10
+ return super unless query.persisted_query_not_found?
11
+
12
+ results[idx] = add_not_found_error(query)
13
+ end
14
+ else
15
+ def begin_query(query, multiplex)
16
+ return super unless query.persisted_query_not_found?
17
+
18
+ add_not_found_error(query)
19
+ end
20
+ end
21
+
22
+ def add_not_found_error(query)
23
+ query.context.errors.clear
24
+ query.context.errors << GraphQL::ExecutionError.new("PersistedQueryNotFound")
25
+ GraphQL::Execution::Multiplex::NO_OPERATION
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module PersistedQueries
5
+ module CompiledQueries
6
+ # Patches GraphQL::Query to support compiled queries
7
+ module QueryPatch
8
+ def persisted_query_not_found?
9
+ @persisted_query_not_found
10
+ end
11
+
12
+ def prepare_ast
13
+ return super unless @context[:extensions]
14
+
15
+ @document = resolver.fetch
16
+ not_loaded_document = @document.nil?
17
+
18
+ @persisted_query_not_found = not_loaded_document && query_string.nil?
19
+
20
+ super.tap do
21
+ resolver.persist(query_string, @document) if not_loaded_document && query_string
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def resolver
28
+ @resolver ||= Resolver.new(@schema, @context[:extensions])
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module PersistedQueries
5
+ module CompiledQueries
6
+ # Fetches and persists compiled query
7
+ class Resolver
8
+ include GraphQL::PersistedQueries::ResolverHelpers
9
+
10
+ def initialize(schema, extensions)
11
+ @schema = schema
12
+ @extensions = extensions
13
+ end
14
+
15
+ def fetch
16
+ return if hash.nil?
17
+
18
+ with_error_handling do
19
+ compiled_query = @schema.persisted_query_store.fetch_query(hash, compiled_query: true)
20
+ Marshal.load(compiled_query) if compiled_query # rubocop:disable Security/MarshalLoad
21
+ end
22
+ end
23
+
24
+ def persist(query_string, compiled_query)
25
+ return if hash.nil?
26
+
27
+ validate_hash!(query_string)
28
+
29
+ with_error_handling do
30
+ @schema.persisted_query_store.save_query(
31
+ hash, Marshal.dump(compiled_query), compiled_query: true
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module PersistedQueries
5
+ # Raised when persisted query is not found in the storage
6
+ class NotFound < StandardError
7
+ def message
8
+ "PersistedQueryNotFound"
9
+ end
10
+ end
11
+
12
+ # Raised when provided hash is not matched with query
13
+ class WrongHash < StandardError
14
+ def message
15
+ "Wrong hash was passed"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -33,7 +33,7 @@ module GraphQL
33
33
  return unless extensions
34
34
 
35
35
  query_params[:query] = Resolver.new(extensions, @schema).resolve(query_params[:query])
36
- rescue Resolver::NotFound, Resolver::WrongHash => e
36
+ rescue GraphQL::PersistedQueries::NotFound, GraphQL::PersistedQueries::WrongHash => e
37
37
  values = { "errors" => [{ "message" => e.message }] }
38
38
  query = GraphQL::Query.new(@schema, query_params[:query])
39
39
  results[pos] = GraphQL::Query::Result.new(query: query, values: values)
@@ -4,54 +4,32 @@ module GraphQL
4
4
  module PersistedQueries
5
5
  # Fetches or stores query string in the storage
6
6
  class Resolver
7
- # Raised when persisted query is not found in the storage
8
- class NotFound < StandardError
9
- def message
10
- "PersistedQueryNotFound"
11
- end
12
- end
13
-
14
- # Raised when provided hash is not matched with query
15
- class WrongHash < StandardError
16
- def message
17
- "Wrong hash was passed"
18
- end
19
- end
7
+ include GraphQL::PersistedQueries::ResolverHelpers
20
8
 
21
9
  def initialize(extensions, schema)
22
10
  @extensions = extensions
23
11
  @schema = schema
24
12
  end
25
13
 
26
- def resolve(query_str)
27
- return query_str if hash.nil?
14
+ def resolve(query_string)
15
+ return query_string if hash.nil?
28
16
 
29
- if query_str
30
- persist_query(query_str)
17
+ if query_string
18
+ persist_query(query_string)
31
19
  else
32
- query_str = with_error_handling { @schema.persisted_query_store.fetch_query(hash) }
33
- raise NotFound if query_str.nil?
20
+ query_string = with_error_handling { @schema.persisted_query_store.fetch_query(hash) }
21
+ raise GraphQL::PersistedQueries::NotFound if query_string.nil?
34
22
  end
35
23
 
36
- query_str
24
+ query_string
37
25
  end
38
26
 
39
27
  private
40
28
 
41
- def with_error_handling
42
- yield
43
- rescue StandardError => e
44
- @schema.persisted_query_error_handler.call(e)
45
- end
46
-
47
- def persist_query(query_str)
48
- raise WrongHash if @schema.hash_generator_proc.call(query_str) != hash
49
-
50
- with_error_handling { @schema.persisted_query_store.save_query(hash, query_str) }
51
- end
29
+ def persist_query(query_string)
30
+ validate_hash!(query_string)
52
31
 
53
- def hash
54
- @hash ||= @extensions.dig("persistedQuery", "sha256Hash")
32
+ with_error_handling { @schema.persisted_query_store.save_query(hash, query_string) }
55
33
  end
56
34
  end
57
35
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module PersistedQueries
5
+ # Helper functions for resolvers
6
+ module ResolverHelpers
7
+ module_function
8
+
9
+ def with_error_handling
10
+ yield
11
+ rescue StandardError => e
12
+ @schema.persisted_query_error_handler.call(e)
13
+ end
14
+
15
+ def validate_hash!(query_string)
16
+ return if @schema.hash_generator_proc.call(query_string) == hash
17
+
18
+ raise GraphQL::PersistedQueries::WrongHash
19
+ end
20
+
21
+ def hash
22
+ @hash ||= @extensions.dig("persistedQuery", "sha256Hash")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -10,9 +10,20 @@ module GraphQL
10
10
  # Patches GraphQL::Schema to support persisted queries
11
11
  module SchemaPatch
12
12
  class << self
13
- def patch(schema)
14
- schema.singleton_class.class_eval { alias_method :multiplex_original, :multiplex }
13
+ def patch(schema, compiled_queries)
15
14
  schema.singleton_class.prepend(SchemaPatch)
15
+
16
+ return if compiled_queries
17
+
18
+ schema.singleton_class.class_eval { alias_method :multiplex_original, :multiplex }
19
+ schema.singleton_class.prepend(MultiplexPatch)
20
+ end
21
+ end
22
+
23
+ # Patches GraphQL::Schema to override multiplex (not needed for compiled queries)
24
+ module MultiplexPatch
25
+ def multiplex(queries, **kwargs)
26
+ MultiplexResolver.new(self, queries, **kwargs).resolve
16
27
  end
17
28
  end
18
29
 
@@ -47,10 +58,6 @@ module GraphQL
47
58
  @persisted_queries_tracing_enabled
48
59
  end
49
60
 
50
- def multiplex(queries, **kwargs)
51
- MultiplexResolver.new(self, queries, **kwargs).resolve
52
- end
53
-
54
61
  def tracer(name)
55
62
  super.tap do
56
63
  # Since tracers can be set before *and* after our plugin hooks in,
@@ -12,15 +12,18 @@ module GraphQL
12
12
  @name = :base
13
13
  end
14
14
 
15
- def fetch_query(hash)
16
- fetch(hash).tap do |result|
15
+ def fetch_query(hash, compiled_query: false)
16
+ key = build_key(hash, compiled_query)
17
+
18
+ fetch(key).tap do |result|
17
19
  event = result ? "cache_hit" : "cache_miss"
18
20
  trace("fetch_query.#{event}", adapter: @name)
19
21
  end
20
22
  end
21
23
 
22
- def save_query(hash, query)
23
- trace("save_query", adapter: @name) { save(hash, query) }
24
+ def save_query(hash, query, compiled_query: false)
25
+ key = build_key(hash, compiled_query)
26
+ trace("save_query", adapter: @name) { save(key, query) }
24
27
  end
25
28
 
26
29
  protected
@@ -41,6 +44,13 @@ module GraphQL
41
44
  yield
42
45
  end
43
46
  end
47
+
48
+ private
49
+
50
+ def build_key(hash, compiled_query)
51
+ key = "#{RUBY_ENGINE}-#{RUBY_VERSION}:#{GraphQL::VERSION}:#{hash}"
52
+ compiled_query ? "compiled:#{key}" : key
53
+ end
44
54
  end
45
55
  end
46
56
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module PersistedQueries
5
- VERSION = "1.1.1"
5
+ VERSION = "1.2.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: 1.1.1
4
+ version: 1.2.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-12-03 00:00:00.000000000 Z
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -124,14 +124,22 @@ files:
124
124
  - LICENSE.txt
125
125
  - README.md
126
126
  - Rakefile
127
+ - benchmark/compiled_queries.rb
128
+ - benchmark/helpers.rb
129
+ - benchmark/persisted_queries.rb
130
+ - benchmark/plain_gql.rb
127
131
  - bin/console
128
132
  - bin/setup
129
133
  - docs/alternative_stores.md
134
+ - docs/compiled_queries_benchmark.md
130
135
  - docs/error_handling.md
131
136
  - docs/hash.md
132
137
  - docs/http_cache.md
133
138
  - docs/tracing.md
134
139
  - gemfiles/graphql_1_10.gemfile
140
+ - gemfiles/graphql_1_11.gemfile
141
+ - gemfiles/graphql_1_12_0.gemfile
142
+ - gemfiles/graphql_1_12_4.gemfile
135
143
  - gemfiles/graphql_1_8.gemfile
136
144
  - gemfiles/graphql_1_9.gemfile
137
145
  - gemfiles/graphql_master.gemfile
@@ -141,12 +149,17 @@ files:
141
149
  - lib/graphql/persisted_queries/analyzers/http_method_ast_analyzer.rb
142
150
  - lib/graphql/persisted_queries/analyzers/http_method_validator.rb
143
151
  - lib/graphql/persisted_queries/builder_helpers.rb
152
+ - lib/graphql/persisted_queries/compiled_queries/multiplex_patch.rb
153
+ - lib/graphql/persisted_queries/compiled_queries/query_patch.rb
154
+ - lib/graphql/persisted_queries/compiled_queries/resolver.rb
144
155
  - lib/graphql/persisted_queries/error_handlers.rb
145
156
  - lib/graphql/persisted_queries/error_handlers/base_error_handler.rb
146
157
  - lib/graphql/persisted_queries/error_handlers/default_error_handler.rb
158
+ - lib/graphql/persisted_queries/errors.rb
147
159
  - lib/graphql/persisted_queries/hash_generator_builder.rb
148
160
  - lib/graphql/persisted_queries/multiplex_resolver.rb
149
161
  - lib/graphql/persisted_queries/resolver.rb
162
+ - lib/graphql/persisted_queries/resolver_helpers.rb
150
163
  - lib/graphql/persisted_queries/schema_patch.rb
151
164
  - lib/graphql/persisted_queries/store_adapters.rb
152
165
  - lib/graphql/persisted_queries/store_adapters/base_store_adapter.rb