From 94085cf1edd6c693cb5945d040852d38136d3e9d Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Thu, 25 Jun 2026 10:21:24 -0400 Subject: [PATCH 1/4] Tune some Dataloader::Source methods --- lib/graphql/dataloader/source.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/graphql/dataloader/source.rb b/lib/graphql/dataloader/source.rb index ec3bbc964fc..80582e1d0a9 100644 --- a/lib/graphql/dataloader/source.rb +++ b/lib/graphql/dataloader/source.rb @@ -146,14 +146,18 @@ def run_pending_keys @pending = {} @fetching.merge!(fetch_h) results = fetch(fetch_h.values) - fetch_h.each_with_index do |(key, _value), idx| + idx = 0 + fetch_h.each_key do |key| @results[key] = results[idx] + @fetching.delete(key) + idx += 1 end nil rescue StandardError => error - fetch_h.each_key { |key| @results[key] = error } - ensure - fetch_h && fetch_h.each_key { |k| @fetching.delete(k) } + fetch_h.each_key { |key| + @results[key] = error + @fetching.delete(key) + } end # These arguments are given to `dataloader.with(source_class, ...)`. The object @@ -171,7 +175,11 @@ def run_pending_keys # @param batch_kwargs [Hash] # @return [Object] def self.batch_key_for(*batch_args, **batch_kwargs) - [*batch_args, **batch_kwargs] + if batch_kwargs.any? + [*batch_args, **batch_kwargs] + else + batch_args + end end # Clear any already-loaded objects for this source From 69fe51eba2af465ddf53107e81f3aa4c7e066067 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Tue, 30 Jun 2026 09:56:05 -0400 Subject: [PATCH 2/4] use identity in source cache --- lib/graphql/dataloader.rb | 2 +- lib/graphql/dataloader/source.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/graphql/dataloader.rb b/lib/graphql/dataloader.rb index 61a7b776296..4fe37cabeed 100644 --- a/lib/graphql/dataloader.rb +++ b/lib/graphql/dataloader.rb @@ -58,7 +58,7 @@ def self.with_dataloading(&block) end def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit) - @source_cache = Hash.new { |h, k| h[k] = {} } + @source_cache = Hash.new { |h, k| h[k] = {} }.compare_by_identity @pending_jobs = [] if !nonblocking.nil? @nonblocking = nonblocking diff --git a/lib/graphql/dataloader/source.rb b/lib/graphql/dataloader/source.rb index 80582e1d0a9..d0970ed559d 100644 --- a/lib/graphql/dataloader/source.rb +++ b/lib/graphql/dataloader/source.rb @@ -89,7 +89,8 @@ def load_all(values) sync(pending_keys) end - result_keys.map { |k| result_for(k) } + result_keys.map! { |k| result_for(k) } + result_keys end # Subclasses must implement this method to return a value for each of `keys` @@ -138,15 +139,14 @@ def merge(new_results) # @api private # @return [void] def run_pending_keys - if !@fetching.empty? - @fetching.each_key { |k| @pending.delete(k) } - end + @fetching.each_key { |k| @pending.delete(k) } return if @pending.empty? fetch_h = @pending - @pending = {} @fetching.merge!(fetch_h) + @pending = {} results = fetch(fetch_h.values) idx = 0 + fetch_h.each_key do |key| @results[key] = results[idx] @fetching.delete(key) From 164093aca575c13ec4fa43f416342870a5d6b0b6 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Mon, 6 Jul 2026 13:25:15 -0400 Subject: [PATCH 3/4] Fix lint error, update spec --- lib/graphql/dataloader/source.rb | 2 +- spec/graphql/dataloader/source_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/graphql/dataloader/source.rb b/lib/graphql/dataloader/source.rb index d0970ed559d..44d82d94a52 100644 --- a/lib/graphql/dataloader/source.rb +++ b/lib/graphql/dataloader/source.rb @@ -175,7 +175,7 @@ def run_pending_keys # @param batch_kwargs [Hash] # @return [Object] def self.batch_key_for(*batch_args, **batch_kwargs) - if batch_kwargs.any? + if batch_kwargs.any? # rubocop:disable Development/NoneWithoutBlockCop [*batch_args, **batch_kwargs] else batch_args diff --git a/spec/graphql/dataloader/source_spec.rb b/spec/graphql/dataloader/source_spec.rb index 0a4e9e8d16c..be7b747f7d9 100644 --- a/spec/graphql/dataloader/source_spec.rb +++ b/spec/graphql/dataloader/source_spec.rb @@ -47,7 +47,7 @@ def fetch(keys) # The value of this changed in Ruby 3.3.3, see https://bugs.ruby-lang.org/issues/20180 # In previous versions, it was `[{}]`, but now it's `[]` - empty_execution_next_key = [*[], **{}] + empty_execution_next_key = [*[]] source_inst = source_cache_for_source[empty_execution_next_key] assert_instance_of FailsToLoadSource, source_inst, "The cache includes a pending source (#{source_cache_for_source.inspect})" assert source_inst.pending? From fb3c3ea03d85837e1939334c07843ef8e51adb86 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Mon, 6 Jul 2026 13:32:19 -0400 Subject: [PATCH 4/4] lint fixes --- cop/development/context_is_passed_cop.rb | 6 +++--- lib/graphql/execution/finalize.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cop/development/context_is_passed_cop.rb b/cop/development/context_is_passed_cop.rb index 9d8049fa9c2..f3491d03aa0 100644 --- a/cop/development/context_is_passed_cop.rb +++ b/cop/development/context_is_passed_cop.rb @@ -32,9 +32,9 @@ class ContextIsPassedCop < RuboCop::Cop::Base def on_send(node) if ( - method_doesnt_receive_second_context_argument?(node) || - method_doesnt_receive_first_context_argument?(node) || - is_enum_values_call_without_arguments?(node) + method_doesnt_receive_second_context_argument?(node) || + method_doesnt_receive_first_context_argument?(node) || + is_enum_values_call_without_arguments?(node) ) && !likely_query_specific_receiver?(node.to_a[0]) add_offense(node) end diff --git a/lib/graphql/execution/finalize.rb b/lib/graphql/execution/finalize.rb index 850b000168c..c6500cb67a3 100644 --- a/lib/graphql/execution/finalize.rb +++ b/lib/graphql/execution/finalize.rb @@ -151,8 +151,8 @@ def check_object_result(result_h, parent_type, ast_selections) when Language::Nodes::InlineFragment static_type_at_result = @static_type_at[result_h] if static_type_at_result && ( - (t = ast_selection.type).nil? || - @runner.type_condition_applies?(@query.context, static_type_at_result, t.name) + (t = ast_selection.type).nil? || + @runner.type_condition_applies?(@query.context, static_type_at_result, t.name) ) result_h = check_object_result(result_h, parent_type, ast_selection.selections) return nil if result_h.nil?