Skip to content

Commit 3654d3f

Browse files
authored
Merge pull request rmosolgo#4456 from rmosolgo/better-argument-caching
Cache resolved arguments better
2 parents a692a75 + 009ea07 commit 3654d3f

File tree

4 files changed

+39
-36
lines changed

4 files changed

+39
-36
lines changed

lib/graphql/execution/interpreter/arguments_cache.rb

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,50 @@ class ArgumentsCache
77
def initialize(query)
88
@query = query
99
@dataloader = query.context.dataloader
10-
@storage = Hash.new do |h, ast_node|
11-
h[ast_node] = Hash.new do |h2, arg_owner|
12-
h2[arg_owner] = Hash.new do |h3, parent_object|
13-
dataload_for(ast_node, arg_owner, parent_object) do |kwarg_arguments|
14-
h3[parent_object] = @query.after_lazy(kwarg_arguments) do |resolved_args|
15-
h3[parent_object] = resolved_args
16-
end
17-
end
18-
19-
if !h3.key?(parent_object)
20-
# TODO should i bother putting anything here?
21-
h3[parent_object] = NO_ARGUMENTS
22-
else
23-
h3[parent_object]
24-
end
10+
@storage = Hash.new do |h, argument_owner|
11+
args_by_parent = if argument_owner.arguments_statically_coercible?
12+
shared_values_cache = {}
13+
Hash.new do |h2, ignored_parent_object|
14+
h2[ignored_parent_object] = shared_values_cache
15+
end
16+
else
17+
Hash.new do |h2, parent_object|
18+
args_by_node = {}
19+
args_by_node.compare_by_identity
20+
h2[parent_object] = args_by_node
2521
end
2622
end
23+
args_by_parent.compare_by_identity
24+
h[argument_owner] = args_by_parent
2725
end
26+
@storage.compare_by_identity
2827
end
2928

3029
def fetch(ast_node, argument_owner, parent_object)
31-
# If any jobs were enqueued, run them now,
32-
# since this might have been called outside of execution.
33-
# (The jobs are responsible for updating `result` in-place.)
34-
if !@storage.key?(ast_node) || !@storage[ast_node].key?(argument_owner)
35-
@dataloader.run_isolated do
36-
@storage[ast_node][argument_owner][parent_object]
30+
# This runs eagerly if no block is given
31+
@storage[argument_owner][parent_object][ast_node] ||= begin
32+
args_hash = self.class.prepare_args_hash(@query, ast_node)
33+
kwarg_arguments = argument_owner.coerce_arguments(parent_object, args_hash, @query.context)
34+
@query.after_lazy(kwarg_arguments) do |resolved_args|
35+
@storage[argument_owner][parent_object][ast_node] = resolved_args
3736
end
3837
end
39-
# Ack, the _hash_ is updated, but the key is eventually
40-
# overridden with an immutable arguments instance.
41-
# The first call queues up the job,
42-
# then this call fetches the result.
43-
# TODO this should be better, find a solution
44-
# that works with merging the runtime.rb code
45-
@storage[ast_node][argument_owner][parent_object]
38+
4639
end
4740

4841
# @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
4942
def dataload_for(ast_node, argument_owner, parent_object, &block)
5043
# First, normalize all AST or Ruby values to a plain Ruby hash
51-
args_hash = self.class.prepare_args_hash(@query, ast_node)
52-
argument_owner.coerce_arguments(parent_object, args_hash, @query.context, &block)
44+
arg_storage = @storage[argument_owner][parent_object]
45+
if (args = arg_storage[ast_node])
46+
yield(args)
47+
else
48+
args_hash = self.class.prepare_args_hash(@query, ast_node)
49+
argument_owner.coerce_arguments(parent_object, args_hash, @query.context) do |resolved_args|
50+
arg_storage[ast_node] = resolved_args
51+
yield(resolved_args)
52+
end
53+
end
5354
nil
5455
end
5556

lib/graphql/schema/argument.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ def type
198198

199199
def statically_coercible?
200200
return @statically_coercible if defined?(@statically_coercible)
201-
202-
@statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
201+
requires_parent_object = @prepare.is_a?(String) || @prepare.is_a?(Symbol) || @own_validators
202+
@statically_coercible = !requires_parent_object
203203
end
204204

205205
# Apply the {prepare} configuration to `value`, using methods from `obj`.

lib/graphql/schema/field.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CON
235235
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236236

237237
@description = description
238-
@type = @owner_type = @own_validators = @own_directives = @own_arguments = nil # these will be prepared later if necessary
238+
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239239

240240
self.deprecation_reason = deprecation_reason
241241

lib/graphql/schema/member/has_arguments.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,11 @@ def validate_directive_argument(arg_defn, value)
320320
end
321321

322322
def arguments_statically_coercible?
323-
return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
324-
325-
@arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
323+
if defined?(@arguments_statically_coercible) && !@arguments_statically_coercible.nil?
324+
@arguments_statically_coercible
325+
else
326+
@arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
327+
end
326328
end
327329

328330
module ArgumentClassAccessor

0 commit comments

Comments
 (0)