graphql 2.0.32 → 2.5.22
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 +4 -4
- data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +8 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/analyzer.rb +90 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +263 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +280 -0
- data/lib/graphql/analysis.rb +95 -1
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/current.rb +57 -0
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +96 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +112 -0
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +35 -12
- data/lib/graphql/dataloader.rb +224 -149
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +23 -25
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
- data/lib/graphql/execution/interpreter/runtime.rb +363 -434
- data/lib/graphql/execution/interpreter.rb +91 -164
- data/lib/graphql/execution/lookahead.rb +105 -31
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +70 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +20 -6
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/schema_type.rb +8 -11
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +26 -17
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +90 -61
- data/lib/graphql/language/lexer.rb +319 -193
- data/lib/graphql/language/nodes.rb +136 -77
- data/lib/graphql/language/parser.rb +807 -1985
- data/lib/graphql/language/printer.rb +324 -151
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +171 -0
- data/lib/graphql/language/visitor.rb +23 -83
- data/lib/graphql/language.rb +71 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +30 -1
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +82 -144
- data/lib/graphql/query/null_context.rb +15 -18
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +126 -81
- data/lib/graphql/railtie.rb +16 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +26 -13
- data/lib/graphql/schema/always_visible.rb +7 -2
- data/lib/graphql/schema/argument.rb +75 -9
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +123 -60
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +54 -2
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +15 -49
- data/lib/graphql/schema/field/scope_extension.rb +23 -7
- data/lib/graphql/schema/field.rb +245 -118
- data/lib/graphql/schema/field_extension.rb +34 -1
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +116 -60
- data/lib/graphql/schema/interface.rb +34 -16
- data/lib/graphql/schema/introspection_system.rb +8 -17
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +3 -3
- data/lib/graphql/schema/loader.rb +3 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -2
- data/lib/graphql/schema/member/has_arguments.rb +132 -100
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +99 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +115 -15
- data/lib/graphql/schema/member/has_interfaces.rb +26 -12
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +34 -8
- data/lib/graphql/schema/printer.rb +9 -7
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
- data/lib/graphql/schema/resolver.rb +90 -32
- data/lib/graphql/schema/scalar.rb +4 -9
- data/lib/graphql/schema/subscription.rb +63 -10
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +2 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +92 -11
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +445 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +311 -0
- data/lib/graphql/schema/warden.rb +275 -103
- data/lib/graphql/schema.rb +950 -210
- data/lib/graphql/static_validation/all_rules.rb +3 -3
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/literal_validator.rb +6 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +21 -5
- data/lib/graphql/static_validation/validator.rb +9 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +21 -4
- data/lib/graphql/subscriptions/serialize.rb +3 -1
- data/lib/graphql/subscriptions.rb +21 -17
- data/lib/graphql/testing/helpers.rb +161 -0
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +3 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +7 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -59
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -162
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +156 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
- data/lib/graphql/tracing/prometheus_trace.rb +73 -73
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -58
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -45
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +112 -1
- data/lib/graphql/tracing.rb +31 -28
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +44 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -10
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +71 -54
- data/readme.md +12 -2
- metadata +233 -37
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -96
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
def request(value)
|
|
22
22
|
res_key = result_key_for(value)
|
|
23
23
|
if !@results.key?(res_key)
|
|
24
|
-
@pending[res_key] ||= value
|
|
24
|
+
@pending[res_key] ||= normalize_fetch_key(value)
|
|
25
25
|
end
|
|
26
26
|
Dataloader::Request.new(self, value)
|
|
27
27
|
end
|
|
@@ -35,12 +35,24 @@ module GraphQL
|
|
|
35
35
|
value
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# Implement this method if varying values given to {load} (etc) should be consolidated
|
|
39
|
+
# or normalized before being handed off to your {fetch} implementation.
|
|
40
|
+
#
|
|
41
|
+
# This is different than {result_key_for} because _that_ method handles unification inside Dataloader's cache,
|
|
42
|
+
# but this method changes the value passed into {fetch}.
|
|
43
|
+
#
|
|
44
|
+
# @param value [Object] The value passed to {load}, {load_all}, {request}, or {request_all}
|
|
45
|
+
# @return [Object] The value given to {fetch}
|
|
46
|
+
def normalize_fetch_key(value)
|
|
47
|
+
value
|
|
48
|
+
end
|
|
49
|
+
|
|
38
50
|
# @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
|
|
39
51
|
def request_all(values)
|
|
40
52
|
values.each do |v|
|
|
41
53
|
res_key = result_key_for(v)
|
|
42
54
|
if !@results.key?(res_key)
|
|
43
|
-
@pending[res_key] ||= v
|
|
55
|
+
@pending[res_key] ||= normalize_fetch_key(v)
|
|
44
56
|
end
|
|
45
57
|
end
|
|
46
58
|
Dataloader::RequestAll.new(self, values)
|
|
@@ -53,7 +65,7 @@ module GraphQL
|
|
|
53
65
|
if @results.key?(result_key)
|
|
54
66
|
result_for(result_key)
|
|
55
67
|
else
|
|
56
|
-
@pending[result_key] ||= value
|
|
68
|
+
@pending[result_key] ||= normalize_fetch_key(value)
|
|
57
69
|
sync([result_key])
|
|
58
70
|
result_for(result_key)
|
|
59
71
|
end
|
|
@@ -68,12 +80,12 @@ module GraphQL
|
|
|
68
80
|
k = result_key_for(v)
|
|
69
81
|
result_keys << k
|
|
70
82
|
if !@results.key?(k)
|
|
71
|
-
@pending[k] ||= v
|
|
83
|
+
@pending[k] ||= normalize_fetch_key(v)
|
|
72
84
|
pending_keys << k
|
|
73
85
|
end
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
if pending_keys.
|
|
88
|
+
if !pending_keys.empty?
|
|
77
89
|
sync(pending_keys)
|
|
78
90
|
end
|
|
79
91
|
|
|
@@ -88,18 +100,19 @@ module GraphQL
|
|
|
88
100
|
raise "Implement `#{self.class}#fetch(#{keys.inspect}) to return a record for each of the keys"
|
|
89
101
|
end
|
|
90
102
|
|
|
103
|
+
MAX_ITERATIONS = 1000
|
|
91
104
|
# Wait for a batch, if there's anything to batch.
|
|
92
105
|
# Then run the batch and update the cache.
|
|
93
106
|
# @return [void]
|
|
94
107
|
def sync(pending_result_keys)
|
|
95
|
-
@dataloader.yield
|
|
108
|
+
@dataloader.yield(self)
|
|
96
109
|
iterations = 0
|
|
97
110
|
while pending_result_keys.any? { |key| !@results.key?(key) }
|
|
98
111
|
iterations += 1
|
|
99
|
-
if iterations >
|
|
100
|
-
raise "#{self.class}#sync tried
|
|
112
|
+
if iterations > MAX_ITERATIONS
|
|
113
|
+
raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
|
|
101
114
|
end
|
|
102
|
-
@dataloader.yield
|
|
115
|
+
@dataloader.yield(self)
|
|
103
116
|
end
|
|
104
117
|
nil
|
|
105
118
|
end
|
|
@@ -161,7 +174,14 @@ module GraphQL
|
|
|
161
174
|
[*batch_args, **batch_kwargs]
|
|
162
175
|
end
|
|
163
176
|
|
|
164
|
-
|
|
177
|
+
# Clear any already-loaded objects for this source
|
|
178
|
+
# @return [void]
|
|
179
|
+
def clear_cache
|
|
180
|
+
@results.clear
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
attr_reader :pending, :results
|
|
165
185
|
|
|
166
186
|
private
|
|
167
187
|
|
|
@@ -178,8 +198,11 @@ This key should have been loaded already. This is a bug in GraphQL::Dataloader,
|
|
|
178
198
|
ERR
|
|
179
199
|
end
|
|
180
200
|
result = @results[key]
|
|
181
|
-
|
|
182
|
-
|
|
201
|
+
if result.is_a?(StandardError)
|
|
202
|
+
# Dup it because the rescuer may modify it.
|
|
203
|
+
# (This happens for GraphQL::ExecutionErrors, at least)
|
|
204
|
+
raise result.dup
|
|
205
|
+
end
|
|
183
206
|
|
|
184
207
|
result
|
|
185
208
|
end
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "graphql/dataloader/null_dataloader"
|
|
|
4
4
|
require "graphql/dataloader/request"
|
|
5
5
|
require "graphql/dataloader/request_all"
|
|
6
6
|
require "graphql/dataloader/source"
|
|
7
|
+
require "graphql/dataloader/active_record_association_source"
|
|
8
|
+
require "graphql/dataloader/active_record_source"
|
|
7
9
|
|
|
8
10
|
module GraphQL
|
|
9
11
|
# This plugin supports Fiber-based concurrency, along with {GraphQL::Dataloader::Source}.
|
|
@@ -24,17 +26,23 @@ module GraphQL
|
|
|
24
26
|
#
|
|
25
27
|
class Dataloader
|
|
26
28
|
class << self
|
|
27
|
-
attr_accessor :default_nonblocking
|
|
29
|
+
attr_accessor :default_nonblocking, :default_fiber_limit
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
AsyncDataloader
|
|
32
|
+
def self.use(schema, nonblocking: nil, fiber_limit: nil)
|
|
33
|
+
dataloader_class = if nonblocking
|
|
34
|
+
warn("`nonblocking: true` is deprecated from `GraphQL::Dataloader`, please use `GraphQL::Dataloader::AsyncDataloader` instead. Docs: https://graphql-ruby.org/dataloader/async_dataloader.")
|
|
35
|
+
Class.new(self) { self.default_nonblocking = true }
|
|
35
36
|
else
|
|
36
37
|
self
|
|
37
38
|
end
|
|
39
|
+
|
|
40
|
+
if fiber_limit
|
|
41
|
+
dataloader_class = Class.new(dataloader_class)
|
|
42
|
+
dataloader_class.default_fiber_limit = fiber_limit
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
schema.dataloader_class = dataloader_class
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
# Call the block with a Dataloader instance,
|
|
@@ -49,18 +57,51 @@ module GraphQL
|
|
|
49
57
|
result
|
|
50
58
|
end
|
|
51
59
|
|
|
52
|
-
def initialize(nonblocking: self.class.default_nonblocking)
|
|
60
|
+
def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
|
|
53
61
|
@source_cache = Hash.new { |h, k| h[k] = {} }
|
|
54
62
|
@pending_jobs = []
|
|
55
63
|
if !nonblocking.nil?
|
|
56
64
|
@nonblocking = nonblocking
|
|
57
65
|
end
|
|
66
|
+
@fiber_limit = fiber_limit
|
|
67
|
+
@lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
58
68
|
end
|
|
59
69
|
|
|
70
|
+
# @return [Integer, nil]
|
|
71
|
+
attr_reader :fiber_limit
|
|
72
|
+
|
|
60
73
|
def nonblocking?
|
|
61
74
|
@nonblocking
|
|
62
75
|
end
|
|
63
76
|
|
|
77
|
+
# This is called before the fiber is spawned, from the parent context (i.e. from
|
|
78
|
+
# the thread or fiber that it is scheduled from).
|
|
79
|
+
#
|
|
80
|
+
# @return [Hash<Symbol, Object>] Current fiber-local variables
|
|
81
|
+
def get_fiber_variables
|
|
82
|
+
fiber_vars = {}
|
|
83
|
+
Thread.current.keys.each do |fiber_var_key|
|
|
84
|
+
fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
|
|
85
|
+
end
|
|
86
|
+
fiber_vars
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Set up the fiber variables in a new fiber.
|
|
90
|
+
#
|
|
91
|
+
# This is called within the fiber, right after it is spawned.
|
|
92
|
+
#
|
|
93
|
+
# @param vars [Hash<Symbol, Object>] Fiber-local variables from {get_fiber_variables}
|
|
94
|
+
# @return [void]
|
|
95
|
+
def set_fiber_variables(vars)
|
|
96
|
+
vars.each { |k, v| Thread.current[k] = v }
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# This method is called when Dataloader is finished using a fiber.
|
|
101
|
+
# Use it to perform any cleanup, such as releasing database connections (if required manually)
|
|
102
|
+
def cleanup_fiber
|
|
103
|
+
end
|
|
104
|
+
|
|
64
105
|
# Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
|
|
65
106
|
#
|
|
66
107
|
# @param source_class [Class<GraphQL::Dataloader::Source]
|
|
@@ -91,16 +132,28 @@ module GraphQL
|
|
|
91
132
|
# Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
|
|
92
133
|
#
|
|
93
134
|
# @return [void]
|
|
94
|
-
def yield
|
|
135
|
+
def yield(source = Fiber[:__graphql_current_dataloader_source])
|
|
136
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
137
|
+
trace&.dataloader_fiber_yield(source)
|
|
95
138
|
Fiber.yield
|
|
139
|
+
trace&.dataloader_fiber_resume(source)
|
|
96
140
|
nil
|
|
97
141
|
end
|
|
98
142
|
|
|
99
143
|
# @api private Nothing to see here
|
|
100
|
-
def append_job(&job)
|
|
144
|
+
def append_job(callable = nil, &job)
|
|
101
145
|
# Given a block, queue it up to be worked through when `#run` is called.
|
|
102
|
-
# (If the dataloader is already running,
|
|
103
|
-
@pending_jobs.push(job)
|
|
146
|
+
# (If the dataloader is already running, then a Fiber will pick this up later.)
|
|
147
|
+
@pending_jobs.push(callable || job)
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Clear any already-loaded objects from {Source} caches
|
|
152
|
+
# @return [void]
|
|
153
|
+
def clear_cache
|
|
154
|
+
@source_cache.each do |_source_class, batched_sources|
|
|
155
|
+
batched_sources.each_value(&:clear_cache)
|
|
156
|
+
end
|
|
104
157
|
nil
|
|
105
158
|
end
|
|
106
159
|
|
|
@@ -108,6 +161,10 @@ module GraphQL
|
|
|
108
161
|
def run_isolated
|
|
109
162
|
prev_queue = @pending_jobs
|
|
110
163
|
prev_pending_keys = {}
|
|
164
|
+
prev_lazies_at_depth = @lazies_at_depth
|
|
165
|
+
@lazies_at_depth = @lazies_at_depth.dup.clear
|
|
166
|
+
# Clear pending loads but keep already-cached records
|
|
167
|
+
# in case they are useful to the given block.
|
|
111
168
|
@source_cache.each do |source_class, batched_sources|
|
|
112
169
|
batched_sources.each do |batch_args, batched_source_instance|
|
|
113
170
|
if batched_source_instance.pending?
|
|
@@ -127,185 +184,203 @@ module GraphQL
|
|
|
127
184
|
res
|
|
128
185
|
ensure
|
|
129
186
|
@pending_jobs = prev_queue
|
|
187
|
+
@lazies_at_depth = prev_lazies_at_depth
|
|
130
188
|
prev_pending_keys.each do |source_instance, pending|
|
|
131
|
-
|
|
189
|
+
pending.each do |key, value|
|
|
190
|
+
if !source_instance.results.key?(key)
|
|
191
|
+
source_instance.pending[key] = value
|
|
192
|
+
end
|
|
193
|
+
end
|
|
132
194
|
end
|
|
133
195
|
end
|
|
134
196
|
|
|
135
|
-
# @
|
|
136
|
-
def run
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# A) Inside Fibers, run jobs from the queue one-by-one
|
|
143
|
-
# - When one of the jobs yields to the dataloader (`Fiber.yield`), then that fiber will pause
|
|
144
|
-
# - In that case, if there are still pending jobs, a new Fiber will be created to run jobs
|
|
145
|
-
# - Continue until all jobs have been _started_ by a Fiber. (Any number of those Fibers may be waiting to be resumed, after their data is loaded)
|
|
146
|
-
# B) Once all known jobs have been run until they are complete or paused for data, run all pending data sources.
|
|
147
|
-
# - Similarly, create a Fiber to consume pending sources and tell them to load their data.
|
|
148
|
-
# - If one of those Fibers pauses, then create a new Fiber to continue working through remaining pending sources.
|
|
149
|
-
# - When a source causes another source to become pending, run the newly-pending source _first_, since it's a dependency of the previous one.
|
|
150
|
-
# C) After all pending sources have been completely loaded (there are no more pending sources), resume any Fibers that were waiting for data.
|
|
151
|
-
# - Those Fibers assume that source caches will have been populated with the data they were waiting for.
|
|
152
|
-
# - Those Fibers may request data from a source again, in which case they will yeilded and be added to a new pending fiber list.
|
|
153
|
-
# D) Once all pending fibers have been resumed once, return to `A` above.
|
|
154
|
-
#
|
|
155
|
-
# For whatever reason, the best implementation I could find was to order the steps `[D, A, B, C]`, with a special case for skipping `D`
|
|
156
|
-
# on the first pass. I just couldn't find a better way to write the loops in a way that was DRY and easy to read.
|
|
157
|
-
#
|
|
158
|
-
pending_fibers = []
|
|
159
|
-
next_fibers = []
|
|
160
|
-
pending_source_fibers = []
|
|
197
|
+
# @param trace_query_lazy [nil, Execution::Multiplex]
|
|
198
|
+
def run(trace_query_lazy: nil)
|
|
199
|
+
trace = Fiber[:__graphql_current_multiplex]&.current_trace
|
|
200
|
+
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
201
|
+
job_fibers = []
|
|
202
|
+
next_job_fibers = []
|
|
203
|
+
source_fibers = []
|
|
161
204
|
next_source_fibers = []
|
|
162
205
|
first_pass = true
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
206
|
+
manager = spawn_fiber do
|
|
207
|
+
trace&.begin_dataloader(self)
|
|
208
|
+
while first_pass || !job_fibers.empty?
|
|
166
209
|
first_pass = false
|
|
167
|
-
else
|
|
168
|
-
# These fibers were previously waiting for sources to load data,
|
|
169
|
-
# resume them. (They might wait again, in which case, re-enqueue them.)
|
|
170
|
-
resume(f)
|
|
171
|
-
if f.alive?
|
|
172
|
-
next_fibers << f
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
210
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
211
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
212
|
+
|
|
213
|
+
if !@lazies_at_depth.empty?
|
|
214
|
+
with_trace_query_lazy(trace_query_lazy) do
|
|
215
|
+
run_next_pending_lazies(job_fibers, trace)
|
|
216
|
+
run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
182
217
|
end
|
|
183
|
-
}
|
|
184
|
-
resume(f)
|
|
185
|
-
# In this case, the job yielded. Queue it up to run again after
|
|
186
|
-
# we load whatever it's waiting for.
|
|
187
|
-
if f.alive?
|
|
188
|
-
next_fibers << f
|
|
189
218
|
end
|
|
190
219
|
end
|
|
191
220
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
# Sources might queue up other Sources, which is fine -- those will also run before resuming execution.
|
|
195
|
-
#
|
|
196
|
-
# This is where an evented approach would be even better -- can we tell which
|
|
197
|
-
# fibers are ready to continue, and continue execution there?
|
|
198
|
-
#
|
|
199
|
-
if (first_source_fiber = create_source_fiber)
|
|
200
|
-
pending_source_fibers << first_source_fiber
|
|
201
|
-
end
|
|
221
|
+
trace&.end_dataloader(self)
|
|
222
|
+
end
|
|
202
223
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
next_source_fibers << outer_source_fiber
|
|
208
|
-
end
|
|
209
|
-
if (next_source_fiber = create_source_fiber)
|
|
210
|
-
pending_source_fibers << next_source_fiber
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
join_queues(pending_source_fibers, next_source_fibers)
|
|
214
|
-
next_source_fibers.clear
|
|
215
|
-
end
|
|
216
|
-
# Move newly-enqueued Fibers on to the list to be resumed.
|
|
217
|
-
# Clear out the list of next-round Fibers, so that
|
|
218
|
-
# any Fibers that pause can be put on it.
|
|
219
|
-
join_queues(pending_fibers, next_fibers)
|
|
220
|
-
next_fibers.clear
|
|
221
|
-
end
|
|
224
|
+
run_fiber(manager)
|
|
225
|
+
|
|
226
|
+
if manager.alive?
|
|
227
|
+
raise "Invariant: Manager fiber didn't terminate properly."
|
|
222
228
|
end
|
|
223
229
|
|
|
224
|
-
if
|
|
225
|
-
raise "Invariant: #{
|
|
226
|
-
elsif pending_fibers.any?
|
|
227
|
-
raise "Invariant: #{pending_fibers.size} pending fibers"
|
|
228
|
-
elsif next_fibers.any?
|
|
229
|
-
raise "Invariant: #{next_fibers.size} next fibers"
|
|
230
|
+
if !job_fibers.empty?
|
|
231
|
+
raise "Invariant: job fibers should have exited but #{job_fibers.size} remained"
|
|
230
232
|
end
|
|
231
|
-
|
|
233
|
+
if !source_fibers.empty?
|
|
234
|
+
raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
rescue UncaughtThrowError => e
|
|
238
|
+
throw e.tag, e.value
|
|
232
239
|
end
|
|
233
240
|
|
|
234
|
-
def
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
241
|
+
def run_fiber(f)
|
|
242
|
+
f.resume
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# @api private
|
|
246
|
+
def lazy_at_depth(depth, lazy)
|
|
247
|
+
@lazies_at_depth[depth] << lazy
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def spawn_fiber
|
|
251
|
+
fiber_vars = get_fiber_variables
|
|
252
|
+
Fiber.new(blocking: !@nonblocking) {
|
|
253
|
+
set_fiber_variables(fiber_vars)
|
|
254
|
+
yield
|
|
255
|
+
cleanup_fiber
|
|
256
|
+
}
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
|
|
260
|
+
# These will be used by {Dataloader::ActiveRecordSource}, {Dataloader::ActiveRecordAssociationSource} and their helper
|
|
261
|
+
# methods, `dataload_record` and `dataload_association`.
|
|
262
|
+
# @param records [Array<ActiveRecord::Base>] Already-loaded records to warm the cache with
|
|
263
|
+
# @param index_by [Symbol] The attribute to use as the cache key. (Should match `find_by:` when using {ActiveRecordSource})
|
|
264
|
+
# @return [void]
|
|
265
|
+
def merge_records(records, index_by: :id)
|
|
266
|
+
records_by_class = Hash.new { |h, k| h[k] = {} }
|
|
267
|
+
records.each do |r|
|
|
268
|
+
records_by_class[r.class][r.public_send(index_by)] = r
|
|
269
|
+
end
|
|
270
|
+
records_by_class.each do |r_class, records|
|
|
271
|
+
with(ActiveRecordSource, r_class).merge(records)
|
|
238
272
|
end
|
|
239
|
-
previous_queue.concat(next_queue)
|
|
240
273
|
end
|
|
241
274
|
|
|
242
275
|
private
|
|
243
276
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
277
|
+
def run_next_pending_lazies(job_fibers, trace)
|
|
278
|
+
smallest_depth = nil
|
|
279
|
+
@lazies_at_depth.each_key do |depth_key|
|
|
280
|
+
smallest_depth ||= depth_key
|
|
281
|
+
if depth_key < smallest_depth
|
|
282
|
+
smallest_depth = depth_key
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if smallest_depth
|
|
287
|
+
lazies = @lazies_at_depth.delete(smallest_depth)
|
|
288
|
+
if !lazies.empty?
|
|
289
|
+
lazies.each_with_index do |l, idx|
|
|
290
|
+
append_job { l.value }
|
|
255
291
|
end
|
|
292
|
+
job_fibers.unshift(spawn_job_fiber(trace))
|
|
256
293
|
end
|
|
257
294
|
end
|
|
295
|
+
end
|
|
258
296
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# This design could probably be improved by maintaining a `@pending_sources` queue which is shared by the fibers,
|
|
267
|
-
# similar to `@pending_jobs`. That way, when a fiber is resumed, it would never pick up work that was finished by a different fiber.
|
|
268
|
-
source_fiber = spawn_fiber do
|
|
269
|
-
pending_sources.each(&:run_pending_keys)
|
|
297
|
+
def run_pending_steps(trace, job_fibers, next_job_fibers, jobs_fiber_limit, source_fibers, next_source_fibers, total_fiber_limit)
|
|
298
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace))))
|
|
299
|
+
if f.alive?
|
|
300
|
+
finished = run_fiber(f)
|
|
301
|
+
if !finished
|
|
302
|
+
next_job_fibers << f
|
|
303
|
+
end
|
|
270
304
|
end
|
|
271
305
|
end
|
|
306
|
+
join_queues(job_fibers, next_job_fibers)
|
|
272
307
|
|
|
273
|
-
|
|
308
|
+
while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
309
|
+
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace)))
|
|
310
|
+
if f.alive?
|
|
311
|
+
finished = run_fiber(f)
|
|
312
|
+
if !finished
|
|
313
|
+
next_source_fibers << f
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
join_queues(source_fibers, next_source_fibers)
|
|
318
|
+
end
|
|
274
319
|
end
|
|
275
320
|
|
|
276
|
-
def
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
321
|
+
def with_trace_query_lazy(multiplex_or_nil, &block)
|
|
322
|
+
if (multiplex = multiplex_or_nil)
|
|
323
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
324
|
+
multiplex.current_trace.execute_query_lazy(query: query, multiplex: multiplex, &block)
|
|
325
|
+
else
|
|
326
|
+
yield
|
|
327
|
+
end
|
|
280
328
|
end
|
|
281
329
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
330
|
+
def calculate_fiber_limit
|
|
331
|
+
total_fiber_limit = @fiber_limit || Float::INFINITY
|
|
332
|
+
if total_fiber_limit < 4
|
|
333
|
+
raise ArgumentError, "Dataloader fiber limit is too low (#{total_fiber_limit}), it must be at least 4"
|
|
334
|
+
end
|
|
335
|
+
total_fiber_limit -= 1 # deduct one fiber for `manager`
|
|
336
|
+
# Deduct at least one fiber for sources
|
|
337
|
+
jobs_fiber_limit = total_fiber_limit - 2
|
|
338
|
+
return jobs_fiber_limit, total_fiber_limit
|
|
339
|
+
end
|
|
290
340
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
341
|
+
def join_queues(prev_queue, new_queue)
|
|
342
|
+
@nonblocking && Fiber.scheduler.run
|
|
343
|
+
prev_queue.concat(new_queue)
|
|
344
|
+
new_queue.clear
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def spawn_job_fiber(trace)
|
|
348
|
+
if !@pending_jobs.empty?
|
|
349
|
+
spawn_fiber do
|
|
350
|
+
trace&.dataloader_spawn_execution_fiber(@pending_jobs)
|
|
351
|
+
while job = @pending_jobs.shift
|
|
352
|
+
job.call
|
|
353
|
+
end
|
|
354
|
+
trace&.dataloader_fiber_exit
|
|
295
355
|
end
|
|
296
356
|
end
|
|
357
|
+
end
|
|
297
358
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
359
|
+
def spawn_source_fiber(trace)
|
|
360
|
+
pending_sources = nil
|
|
361
|
+
@source_cache.each_value do |source_by_batch_params|
|
|
362
|
+
source_by_batch_params.each_value do |source|
|
|
363
|
+
if source.pending?
|
|
364
|
+
pending_sources ||= []
|
|
365
|
+
pending_sources << source
|
|
366
|
+
end
|
|
302
367
|
end
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
if pending_sources
|
|
371
|
+
spawn_fiber do
|
|
372
|
+
trace&.dataloader_spawn_source_fiber(pending_sources)
|
|
373
|
+
pending_sources.each do |source|
|
|
374
|
+
Fiber[:__graphql_current_dataloader_source] = source
|
|
375
|
+
trace&.begin_dataloader_source(source)
|
|
376
|
+
source.run_pending_keys
|
|
377
|
+
trace&.end_dataloader_source(source)
|
|
378
|
+
end
|
|
379
|
+
trace&.dataloader_fiber_exit
|
|
307
380
|
end
|
|
308
381
|
end
|
|
309
382
|
end
|
|
310
383
|
end
|
|
311
384
|
end
|
|
385
|
+
|
|
386
|
+
require "graphql/dataloader/async_dataloader"
|
|
@@ -10,7 +10,7 @@ module GraphQL
|
|
|
10
10
|
|
|
11
11
|
def initialize(value)
|
|
12
12
|
@date_value = value
|
|
13
|
-
super("Date cannot be parsed: #{value}. \nDate must be
|
|
13
|
+
super("Date cannot be parsed: #{value}. \nDate must be able to be parsed as a Ruby Date object.")
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
data/lib/graphql/dig.rb
CHANGED
|
@@ -5,7 +5,8 @@ module GraphQL
|
|
|
5
5
|
# so we can use some of the magic in Schema::InputObject and Interpreter::Arguments
|
|
6
6
|
# to handle stringified/symbolized keys.
|
|
7
7
|
#
|
|
8
|
-
# @param
|
|
8
|
+
# @param own_key [String, Symbol] A key to retrieve
|
|
9
|
+
# @param rest_keys [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly
|
|
9
10
|
# @return [Object]
|
|
10
11
|
def dig(own_key, *rest_keys)
|
|
11
12
|
val = self[own_key]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
# This error is raised when `Types::ISO8601Duration` is asked to return a value
|
|
4
|
+
# that cannot be parsed as an ISO8601-formatted duration by ActiveSupport::Duration.
|
|
5
|
+
#
|
|
6
|
+
# @see GraphQL::Types::ISO8601Duration which raises this error
|
|
7
|
+
class DurationEncodingError < GraphQL::RuntimeTypeError
|
|
8
|
+
# The value which couldn't be encoded
|
|
9
|
+
attr_reader :duration_value
|
|
10
|
+
|
|
11
|
+
def initialize(value)
|
|
12
|
+
@duration_value = value
|
|
13
|
+
super("Duration cannot be parsed: #{value}. \nDuration must be an ISO8601-formatted duration.")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -6,15 +6,19 @@ module GraphQL
|
|
|
6
6
|
# A container for metadata regarding arguments present in a GraphQL query.
|
|
7
7
|
# @see Interpreter::Arguments#argument_values for a hash of these objects.
|
|
8
8
|
class ArgumentValue
|
|
9
|
-
def initialize(definition:, value:, default_used:)
|
|
9
|
+
def initialize(definition:, value:, original_value:, default_used:)
|
|
10
10
|
@definition = definition
|
|
11
11
|
@value = value
|
|
12
|
+
@original_value = original_value
|
|
12
13
|
@default_used = default_used
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# @return [Object] The Ruby-ready value for this Argument
|
|
16
17
|
attr_reader :value
|
|
17
18
|
|
|
19
|
+
# @return [Object] The value of this argument _before_ `prepare` is applied.
|
|
20
|
+
attr_reader :original_value
|
|
21
|
+
|
|
18
22
|
# @return [GraphQL::Schema::Argument] The definition instance for this argument
|
|
19
23
|
attr_reader :definition
|
|
20
24
|
|
|
@@ -8,22 +8,17 @@ module GraphQL
|
|
|
8
8
|
@query = query
|
|
9
9
|
@dataloader = query.context.dataloader
|
|
10
10
|
@storage = Hash.new do |h, argument_owner|
|
|
11
|
-
|
|
11
|
+
h[argument_owner] = if argument_owner.arguments_statically_coercible?
|
|
12
12
|
shared_values_cache = {}
|
|
13
13
|
Hash.new do |h2, ignored_parent_object|
|
|
14
14
|
h2[ignored_parent_object] = shared_values_cache
|
|
15
|
-
end
|
|
15
|
+
end.compare_by_identity
|
|
16
16
|
else
|
|
17
17
|
Hash.new do |h2, parent_object|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
h2[parent_object] = args_by_node
|
|
21
|
-
end
|
|
18
|
+
h2[parent_object] = {}.compare_by_identity
|
|
19
|
+
end.compare_by_identity
|
|
22
20
|
end
|
|
23
|
-
|
|
24
|
-
h[argument_owner] = args_by_parent
|
|
25
|
-
end
|
|
26
|
-
@storage.compare_by_identity
|
|
21
|
+
end.compare_by_identity
|
|
27
22
|
end
|
|
28
23
|
|
|
29
24
|
def fetch(ast_node, argument_owner, parent_object)
|