Skip to content

Commit c21715d

Browse files
authored
fix: call resolve_references once per __typename (#244)
The previous implementation called `resolve_references` once per contiguous `__typename` to ensure the correct ordering of the response. This could have resulted in wildly varying runtime efficiencies. A request calling for N unique types across M entities could have as little as N queries or as many as M queries, depending on if the entities happened to be in large groups of the same type or very interleaved. By keeping track of the requested representations and their original indices, we can rebuild the response in the correct order even when things get shuffled during computation.
1 parent 04e67ca commit c21715d

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

lib/apollo-federation/entities_field.rb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ def define_entities_field(possible_entities)
2727
end
2828

2929
def _entities(representations:)
30-
chunked_references = representations.chunk { |r| r[:__typename] }
30+
final_result = Array.new(representations.size)
31+
grouped_references_with_indices =
32+
representations
33+
.map
34+
.with_index { |r, i| [r, i] }
35+
.group_by { |(r, _i)| r[:__typename] }
36+
37+
grouped_references_with_indices.each do |typename, references_with_indices|
38+
references = references_with_indices.map(&:first)
39+
indices = references_with_indices.map(&:last)
3140

32-
chunked_references.flat_map do |typename, references|
3341
# TODO: Use warden or schema?
3442
type = context.warden.get_type(typename)
3543
if type.nil? || type.kind != GraphQL::TypeKinds::OBJECT
@@ -49,8 +57,10 @@ def _entities(representations:)
4957
results = references
5058
end
5159

52-
results.map do |result|
53-
context.schema.after_lazy(result) do |resolved_value|
60+
results_with_indices = results.zip(indices)
61+
62+
results_with_indices.each do |result, i|
63+
final_result[i] = context.schema.after_lazy(result) do |resolved_value|
5464
# TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference
5565
# calls return the same object, it might not have the right type
5666
# Right now, apollo-federation just adds a __typename property to the result,
@@ -60,6 +70,8 @@ def _entities(representations:)
6070
end
6171
end
6272
end
73+
74+
final_result
6375
end
6476

6577
private

spec/apollo-federation/entities_field_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ def self.resolve_references(references, _context)
327327
],
328328
)
329329
end
330+
331+
it 'calls resolve_references once per __typename' do
332+
allow(type_with_key).to receive(:resolve_references).and_call_original
333+
allow(another_type_with_key).to receive(:resolve_references).and_call_original
334+
subject
335+
expect([type_with_key, another_type_with_key]).to all have_received(:resolve_references).once
336+
end
330337
end
331338
end
332339

0 commit comments

Comments
 (0)