Skip to content

Commit

Permalink
Fix dataloader fiber switching in Mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Aug 9, 2024
1 parent 4e07d3f commit 288359c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 11 deletions.
32 changes: 21 additions & 11 deletions lib/graphql/execution/interpreter/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,22 +207,32 @@ def evaluate_selections(gathered_selections, selections_result, target_result, r
finished_jobs = 0
enqueued_jobs = gathered_selections.size
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
@dataloader.append_job {
evaluate_selection(
result_name, field_ast_nodes_or_ast_node, selections_result
)
finished_jobs += 1
if target_result && finished_jobs == enqueued_jobs
selections_result.merge_into(target_result)
end
}

# Field resolution may pause the fiber,
# so it wouldn't get to the `Resolve` call that happens below.
# So instead trigger a run from this outer context.
if selections_result.graphql_is_eager
@dataloader.clear_cache
@dataloader.run
@dataloader.clear_cache
@dataloader.run_isolated {
evaluate_selection(
result_name, field_ast_nodes_or_ast_node, selections_result
)
finished_jobs += 1
if target_result && finished_jobs == enqueued_jobs
selections_result.merge_into(target_result)
end
@dataloader.clear_cache
}
else
@dataloader.append_job {
evaluate_selection(
result_name, field_ast_nodes_or_ast_node, selections_result
)
finished_jobs += 1
if target_result && finished_jobs == enqueued_jobs
selections_result.merge_into(target_result)
end
}
end
end
selections_result
Expand Down
26 changes: 26 additions & 0 deletions spec/graphql/schema/mutation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,24 @@ def resolve(counter:)
end
end

class ReadyCounter < GraphQL::Schema::Mutation
field :id, ID
argument :counter_id, ID

def ready?(counter_id:)
# Just fill the cache:
dataloader.with(CounterSource).load(counter_id)
true
end

def resolve(counter_id:)
{ id: counter_id }
end
end

class Mutation < GraphQL::Schema::Object
field :increment, mutation: Increment
field :ready_counter, mutation: ReadyCounter
end

mutation(Mutation)
Expand All @@ -328,5 +344,15 @@ def self.resolve_type(abs_type, obj, ctx)
res2 = MutationDataloaderCacheSchema.execute("mutation { increment(counterId: \"4\") { counter { value } } }")
assert_equal 2, res2["data"]["increment"]["counter"]["value"]
end

it "uses a fresh cache for `ready?` calls" do
multiplex = [
{ query: "mutation { r1: readyCounter(counterId: 1) { id } }" },
{ query: "mutation { r2: readyCounter(counterId: 1) { id } }" },
{ query: "mutation { r3: readyCounter(counterId: 1) { id } }" },
]
result = MutationDataloaderCacheSchema.multiplex(multiplex)
assert_equal ["1", "1", "1"], result.map { |r| r["data"].first.last["id"] }
end
end
end

0 comments on commit 288359c

Please sign in to comment.