Skip to content

Commit ba48968

Browse files
committed
refactor(IncrementalGraph): use set of pending deferred grouped field set results to reduce mutation
Replicates graphql/graphql-js@15ab731
1 parent 2efb386 commit ba48968

File tree

4 files changed

+68
-62
lines changed

4 files changed

+68
-62
lines changed

src/graphql/execution/execute.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,15 +1626,19 @@ def execute_deferred_grouped_field_sets(
16261626
defer_usage_set, defer_map
16271627
)
16281628

1629+
deferred_grouped_field_set_record = DeferredGroupedFieldSetRecord(
1630+
deferred_fragment_records, cast("DeferredGroupedFieldSetResult", None)
1631+
)
1632+
16291633
if should_defer(parent_defer_usages, defer_usage_set):
16301634

16311635
async def executor(
1632-
deferred_fragment_records: list[DeferredFragmentRecord],
1636+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord,
16331637
grouped_field_set: GroupedFieldSet,
16341638
defer_usage_set: DeferUsageSet,
16351639
) -> DeferredGroupedFieldSetResult:
16361640
result = self.execute_deferred_grouped_field_set(
1637-
deferred_fragment_records,
1641+
deferred_grouped_field_set_record,
16381642
parent_type,
16391643
source_value,
16401644
path,
@@ -1646,33 +1650,30 @@ async def executor(
16461650
return await result
16471651
return cast("DeferredGroupedFieldSetResult", result)
16481652

1649-
deferred_grouped_field_set_record = DeferredGroupedFieldSetRecord(
1650-
deferred_fragment_records,
1651-
executor(
1652-
deferred_fragment_records, grouped_field_set, defer_usage_set
1653-
),
1653+
deferred_grouped_field_set_record.result = executor(
1654+
deferred_grouped_field_set_record,
1655+
grouped_field_set,
1656+
defer_usage_set,
16541657
)
16551658
else:
16561659
executed = self.execute_deferred_grouped_field_set(
1657-
deferred_fragment_records,
1660+
deferred_grouped_field_set_record,
16581661
parent_type,
16591662
source_value,
16601663
path,
16611664
grouped_field_set,
16621665
IncrementalContext(defer_usage_set),
16631666
defer_map,
16641667
)
1665-
deferred_grouped_field_set_record = DeferredGroupedFieldSetRecord(
1666-
deferred_fragment_records, executed
1667-
)
1668+
deferred_grouped_field_set_record.result = executed
16681669

16691670
append_record(deferred_grouped_field_set_record)
16701671

16711672
return new_deferred_grouped_field_set_records
16721673

16731674
def execute_deferred_grouped_field_set(
16741675
self,
1675-
deferred_fragment_records: list[DeferredFragmentRecord],
1676+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord,
16761677
parent_type: GraphQLObjectType,
16771678
source_value: Any,
16781679
path: Path | None,
@@ -1692,7 +1693,7 @@ def execute_deferred_grouped_field_set(
16921693
)
16931694
except GraphQLError as error:
16941695
return NonReconcilableDeferredGroupedFieldSetResult(
1695-
deferred_fragment_records,
1696+
deferred_grouped_field_set_record,
16961697
path.as_list() if path else [],
16971698
with_error(incremental_context.errors, error),
16981699
)
@@ -1704,13 +1705,13 @@ async def await_result() -> DeferredGroupedFieldSetResult:
17041705
awaited_result = await result
17051706
except GraphQLError as error:
17061707
return NonReconcilableDeferredGroupedFieldSetResult(
1707-
deferred_fragment_records,
1708+
deferred_grouped_field_set_record,
17081709
path.as_list() if path else [],
17091710
with_error(incremental_context.errors, error),
17101711
)
17111712
return build_deferred_grouped_field_set_result(
17121713
incremental_context.errors,
1713-
deferred_fragment_records,
1714+
deferred_grouped_field_set_record,
17141715
path,
17151716
awaited_result,
17161717
)
@@ -1719,7 +1720,7 @@ async def await_result() -> DeferredGroupedFieldSetResult:
17191720

17201721
return build_deferred_grouped_field_set_result(
17211722
incremental_context.errors,
1722-
deferred_fragment_records,
1723+
deferred_grouped_field_set_record,
17231724
path,
17241725
result, # type: ignore
17251726
)
@@ -2330,13 +2331,13 @@ def should_defer(
23302331

23312332
def build_deferred_grouped_field_set_result(
23322333
errors: list[GraphQLError] | None,
2333-
deferred_fragment_records: list[DeferredFragmentRecord],
2334+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord,
23342335
path: Path | None,
23352336
result: GraphQLWrappedResult[dict[str, Any]],
23362337
) -> DeferredGroupedFieldSetResult:
23372338
"""Build a deferred grouped fieldset result."""
23382339
return ReconcilableDeferredGroupedFieldSetResult(
2339-
deferred_fragment_records=deferred_fragment_records,
2340+
deferred_grouped_field_set_record=deferred_grouped_field_set_record,
23402341
path=path.as_list() if path else [],
23412342
result=BareDeferredGroupedFieldSetResult(result.result, errors or None),
23422343
incremental_data_records=result.increments,

src/graphql/execution/incremental_graph.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
if TYPE_CHECKING:
2626
from graphql.execution.types import (
2727
DeferredFragmentRecord,
28+
DeferredGroupedFieldSetRecord,
2829
DeferredGroupedFieldSetResult,
2930
IncrementalDataRecord,
3031
IncrementalDataRecordResult,
@@ -48,23 +49,23 @@ class DeferredFragmentNode:
4849
__slots__ = (
4950
"children",
5051
"deferred_fragment_record",
51-
"expected_reconcilable_results",
52+
"deferred_grouped_field_set_records",
5253
"reconcilable_results",
5354
"results",
5455
)
5556

5657
deferred_fragment_record: DeferredFragmentRecord
57-
expected_reconcilable_results: int
58+
deferred_grouped_field_set_records: dict[DeferredGroupedFieldSetRecord, None]
5859
results: list[DeferredGroupedFieldSetResult]
59-
reconcilable_results: list[ReconcilableDeferredGroupedFieldSetResult]
60+
reconcilable_results: dict[ReconcilableDeferredGroupedFieldSetResult, None]
6061
children: list[DeferredFragmentNode]
6162

6263
def __init__(self, deferred_fragment_record: DeferredFragmentRecord) -> None:
6364
"""Initialize the DeferredFragmentNode."""
6465
self.deferred_fragment_record = deferred_fragment_record
65-
self.expected_reconcilable_results = 0
66+
self.deferred_grouped_field_set_records = {}
6667
self.results = []
67-
self.reconcilable_results = []
68+
self.reconcilable_results = {}
6869
self.children = []
6970

7071

@@ -120,7 +121,9 @@ def add_incremental_data_records(
120121
deferred_fragment_node = self._add_deferred_fragment_node(
121122
deferred_fragment_record
122123
)
123-
deferred_fragment_node.expected_reconcilable_results += 1
124+
deferred_fragment_node.deferred_grouped_field_set_records[
125+
incremental_data_record
126+
] = None
124127

125128
deferred_result = incremental_data_record.result
126129
if is_awaitable(deferred_result):
@@ -160,15 +163,17 @@ def add_completed_reconcilable_deferred_grouped_field_set(
160163
self, reconcilable_result: ReconcilableDeferredGroupedFieldSetResult
161164
) -> None:
162165
"""Add a completed reconcilable deferred grouped field set result."""
166+
record = reconcilable_result.deferred_grouped_field_set_record
163167
deferred_fragment_nodes = filter(
164168
is_deferred_fragment_node,
165169
map(
166170
self._deferred_fragment_nodes.get,
167-
reconcilable_result.deferred_fragment_records,
171+
record.deferred_fragment_records,
168172
),
169173
)
170174
for deferred_fragment_node in deferred_fragment_nodes:
171-
deferred_fragment_node.reconcilable_results.append(reconcilable_result)
175+
del deferred_fragment_node.deferred_grouped_field_set_records[record]
176+
deferred_fragment_node.reconcilable_results[reconcilable_result] = None
172177

173178
def get_new_pending(self) -> list[SubsequentResultRecord]:
174179
"""Get new pending subsequent result records."""
@@ -183,7 +188,7 @@ def get_new_pending(self) -> list[SubsequentResultRecord]:
183188
if is_stream_node(node):
184189
_pending[node] = None
185190
add_result(node)
186-
elif node.expected_reconcilable_results: # type: ignore
191+
elif node.deferred_grouped_field_set_records: # type: ignore
187192
_pending[node] = None
188193
add_result(node.deferred_fragment_record) # type: ignore
189194
else:
@@ -218,17 +223,26 @@ def complete_deferred_fragment(
218223
deferred_fragment_record: DeferredFragmentRecord,
219224
) -> list[ReconcilableDeferredGroupedFieldSetResult] | None:
220225
"""Complete a deferred fragment."""
226+
deferred_fragment_nodes = self._deferred_fragment_nodes
221227
try:
222-
deferred_fragment_node = self._deferred_fragment_nodes[
223-
deferred_fragment_record
224-
]
228+
deferred_fragment_node = deferred_fragment_nodes[deferred_fragment_record]
225229
except KeyError: # pragma: no cover
226230
return None
227-
reconcilable_results = deferred_fragment_node.reconcilable_results
228-
if deferred_fragment_node.expected_reconcilable_results != len(
229-
reconcilable_results
230-
):
231+
if deferred_fragment_node.deferred_grouped_field_set_records:
231232
return None
233+
reconcilable_results = list(deferred_fragment_node.reconcilable_results)
234+
for reconcilable_result in reconcilable_results:
235+
record = reconcilable_result.deferred_grouped_field_set_record
236+
for other_deferred_fragment_record in record.deferred_fragment_records:
237+
try:
238+
other_deferred_fragment_node = deferred_fragment_nodes[
239+
other_deferred_fragment_record
240+
]
241+
except KeyError: # pragma: no cover
242+
continue
243+
del other_deferred_fragment_node.reconcilable_results[
244+
reconcilable_result
245+
]
232246
self._remove_pending(deferred_fragment_node)
233247
new_pending = self._new_pending
234248
for child in deferred_fragment_node.children:
@@ -288,11 +302,11 @@ def _enqueue_completed_deferred_grouped_field_set(
288302
) -> None:
289303
"""Enqueue completed deferred grouped field set result."""
290304
is_pending = False
291-
for deferred_fragment_record in result.deferred_fragment_records:
305+
nodes = self._deferred_fragment_nodes
306+
record = result.deferred_grouped_field_set_record
307+
for deferred_fragment_record in record.deferred_fragment_records:
292308
try:
293-
deferred_fragment_node = self._deferred_fragment_nodes[
294-
deferred_fragment_record
295-
]
309+
deferred_fragment_node = nodes[deferred_fragment_record]
296310
except KeyError: # pragma: no cover
297311
continue
298312
if deferred_fragment_node in self._pending:

src/graphql/execution/incremental_publisher.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,12 @@ def _handle_completed_deferred_grouped_field_set(
215215
"""Handle completed deferred grouped field set result."""
216216
append_completed = context.completed.append
217217
append_incremental = context.incremental.append
218+
record = deferred_grouped_field_set_result.deferred_grouped_field_set_record
218219
if is_non_reconcilable_deferred_grouped_field_set_result(
219220
deferred_grouped_field_set_result
220221
):
221222
remove_deferred = self._incremental_graph.remove_deferred_fragment
222-
for deferred_fragment_record in (
223-
deferred_grouped_field_set_result.deferred_fragment_records
224-
): # pragma: no branch
223+
for deferred_fragment_record in record.deferred_fragment_records:
225224
id_ = deferred_fragment_record.id
226225
if id_ is not None: # pragma: no branch
227226
append_completed(
@@ -244,20 +243,16 @@ def _handle_completed_deferred_grouped_field_set(
244243
self._incremental_graph.add_incremental_data_records(
245244
incremental_data_records
246245
)
246+
247247
complete_deferred = self._incremental_graph.complete_deferred_fragment
248-
for deferred_fragment_record in (
249-
deferred_grouped_field_set_result.deferred_fragment_records
250-
): # pragma: no branch
248+
for deferred_fragment_record in record.deferred_fragment_records:
251249
id_ = deferred_fragment_record.id
252250
if id_ is None:
253251
continue # pragma: no cover
254252
reconcilable_results = complete_deferred(deferred_fragment_record)
255253
if reconcilable_results is None:
256254
continue
257255
for reconcilable_result in reconcilable_results:
258-
if reconcilable_result.sent:
259-
continue
260-
reconcilable_result.sent = True
261256
best_id, sub_path = self._get_best_id_and_sub_path(
262257
id_, deferred_fragment_record, reconcilable_result
263258
)
@@ -320,9 +315,8 @@ def _get_best_id_and_sub_path(
320315
path = initial_deferred_fragment_record.path
321316
max_length = len(path.as_list()) if path else 0
322317
best_id = initial_id
323-
for deferred_fragment_record in (
324-
deferred_grouped_field_set_result.deferred_fragment_records
325-
): # pragma: no branch
318+
record = deferred_grouped_field_set_result.deferred_grouped_field_set_record
319+
for deferred_fragment_record in record.deferred_fragment_records:
326320
if deferred_fragment_record is initial_deferred_fragment_record:
327321
continue
328322
id_ = deferred_fragment_record.id

src/graphql/execution/types.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -704,52 +704,49 @@ def is_deferred_grouped_field_set_record(
704704
class ReconcilableDeferredGroupedFieldSetResult:
705705
"""Reconcilable deferred grouped field set result"""
706706

707-
deferred_fragment_records: list[DeferredFragmentRecord]
707+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord
708708
path: list[str | int]
709709
result: BareDeferredGroupedFieldSetResult
710710
incremental_data_records: list[IncrementalDataRecord] | None
711-
sent: bool
712711
errors: None = None
713712

714713
__slots__ = (
715-
"deferred_fragment_records",
714+
"deferred_grouped_field_set_record",
716715
"incremental_data_records",
717716
"path",
718717
"result",
719-
"sent",
720718
)
721719

722720
def __init__(
723721
self,
724-
deferred_fragment_records: list[DeferredFragmentRecord],
722+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord,
725723
path: list[str | int],
726724
result: BareDeferredGroupedFieldSetResult,
727725
incremental_data_records: list[IncrementalDataRecord] | None = None,
728726
) -> None:
729-
self.deferred_fragment_records = deferred_fragment_records
727+
self.deferred_grouped_field_set_record = deferred_grouped_field_set_record
730728
self.path = path
731729
self.result = result
732730
self.incremental_data_records = incremental_data_records
733-
self.sent = False
734731

735732

736733
class NonReconcilableDeferredGroupedFieldSetResult:
737734
"""Non-reconcilable deferred grouped field set result"""
738735

739-
errors: list[GraphQLError]
740-
deferred_fragment_records: list[DeferredFragmentRecord]
736+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord
741737
path: list[str | int]
738+
errors: list[GraphQLError]
742739
result: None = None
743740

744-
__slots__ = "deferred_fragment_records", "errors", "path"
741+
__slots__ = "deferred_grouped_field_set_record", "errors", "path"
745742

746743
def __init__(
747744
self,
748-
deferred_fragment_records: list[DeferredFragmentRecord],
745+
deferred_grouped_field_set_record: DeferredGroupedFieldSetRecord,
749746
path: list[str | int],
750747
errors: list[GraphQLError],
751748
) -> None:
752-
self.deferred_fragment_records = deferred_fragment_records
749+
self.deferred_grouped_field_set_record = deferred_grouped_field_set_record
753750
self.path = path
754751
self.errors = errors
755752

0 commit comments

Comments
 (0)