Skip to content

Commit 42fdde5

Browse files
authored
Merge pull request #4430 from rmosolgo/skip-args-when-possible
Speed up handling of fields without arguments at runtime
2 parents fb65e75 + f90b66d commit 42fdde5

File tree

1 file changed

+66
-52
lines changed

1 file changed

+66
-52
lines changed

lib/graphql/execution/interpreter/runtime.rb

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,14 @@ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, o
447447
total_args_count = field_defn.arguments(context).size
448448
if total_args_count == 0
449449
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
450-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
450+
if field_defn.extras.size == 0
451+
evaluate_selection_with_resolved_keyword_args(
452+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type,
453+
)
454+
else
455+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
456+
end
451457
else
452-
# TODO remove all arguments(...) usages?
453458
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
454459
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
455460
end
@@ -463,9 +468,13 @@ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_node
463468
next
464469
end
465470

466-
kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
467-
# We can avoid allocating the `{ Symbol => Object }` hash in this case
468-
NO_ARGS
471+
kwarg_arguments = if field_defn.extras.empty?
472+
if resolved_arguments.empty?
473+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
474+
NO_ARGS
475+
else
476+
resolved_arguments.keyword_arguments
477+
end
469478
else
470479
# Bundle up the extras, then make a new arguments instance
471480
# that includes the extras, too.
@@ -504,60 +513,65 @@ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_node
504513
resolved_arguments.keyword_arguments
505514
end
506515

507-
st = get_current_runtime_state
508-
st.current_field = field_defn
509-
st.current_object = object
510-
st.current_arguments = resolved_arguments
511-
st.current_result_name = result_name
512-
st.current_result = selection_result
513-
# Optimize for the case that field is selected only once
514-
if field_ast_nodes.nil? || field_ast_nodes.size == 1
515-
next_selections = ast_node.selections
516-
directives = ast_node.directives
517-
else
518-
next_selections = []
519-
directives = []
520-
field_ast_nodes.each { |f|
521-
next_selections.concat(f.selections)
522-
directives.concat(f.directives)
523-
}
524-
end
525-
526-
field_result = call_method_on_directives(:resolve, object, directives) do
527-
# Actually call the field resolver and capture the result
528-
app_result = begin
529-
query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
530-
field_defn.resolve(object, kwarg_arguments, context)
531-
end
532-
rescue GraphQL::ExecutionError => err
533-
err
534-
rescue StandardError => err
535-
begin
536-
query.handle_or_reraise(err)
537-
rescue GraphQL::ExecutionError => ex_err
538-
ex_err
539-
end
516+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type)
517+
end
518+
end
519+
520+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type) # rubocop:disable Metrics/ParameterLists
521+
st = get_current_runtime_state
522+
st.current_field = field_defn
523+
st.current_object = object
524+
st.current_arguments = resolved_arguments
525+
st.current_result_name = result_name
526+
st.current_result = selection_result
527+
# Optimize for the case that field is selected only once
528+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
529+
next_selections = ast_node.selections
530+
directives = ast_node.directives
531+
else
532+
next_selections = []
533+
directives = []
534+
field_ast_nodes.each { |f|
535+
next_selections.concat(f.selections)
536+
directives.concat(f.directives)
537+
}
538+
end
539+
540+
field_result = call_method_on_directives(:resolve, object, directives) do
541+
# Actually call the field resolver and capture the result
542+
app_result = begin
543+
query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
544+
field_defn.resolve(object, kwarg_arguments, context)
540545
end
541-
after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
542-
continue_value = continue_value(inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
543-
if HALT != continue_value
544-
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
545-
end
546+
rescue GraphQL::ExecutionError => err
547+
err
548+
rescue StandardError => err
549+
begin
550+
query.handle_or_reraise(err)
551+
rescue GraphQL::ExecutionError => ex_err
552+
ex_err
546553
end
547554
end
548-
549-
# If this field is a root mutation field, immediately resolve
550-
# all of its child fields before moving on to the next root mutation field.
551-
# (Subselections of this mutation will still be resolved level-by-level.)
552-
if is_eager_field
553-
Interpreter::Resolve.resolve_all([field_result], @dataloader)
554-
else
555-
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
556-
field_result
555+
after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
556+
continue_value = continue_value(inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
557+
if HALT != continue_value
558+
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
559+
end
557560
end
558561
end
562+
563+
# If this field is a root mutation field, immediately resolve
564+
# all of its child fields before moving on to the next root mutation field.
565+
# (Subselections of this mutation will still be resolved level-by-level.)
566+
if is_eager_field
567+
Interpreter::Resolve.resolve_all([field_result], @dataloader)
568+
else
569+
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
570+
field_result
571+
end
559572
end
560573

574+
561575
def dead_result?(selection_result)
562576
selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
563577
end

0 commit comments

Comments
 (0)