Skip to content

Commit 15ab731

Browse files
committed
refactor(IncrementalGraph): use set of pending deferred grouped field set results to reduce mutation
1 parent eceeb4c commit 15ab731

File tree

4 files changed

+63
-40
lines changed

4 files changed

+63
-40
lines changed

src/execution/IncrementalGraph.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js';
33

44
import type {
55
DeferredFragmentRecord,
6+
DeferredGroupedFieldSetRecord,
67
DeferredGroupedFieldSetResult,
78
IncrementalDataRecord,
89
IncrementalDataRecordResult,
@@ -14,9 +15,9 @@ import { isDeferredGroupedFieldSetRecord } from './types.js';
1415

1516
interface DeferredFragmentNode {
1617
deferredFragmentRecord: DeferredFragmentRecord;
17-
expectedReconcilableResults: number;
18+
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
1819
results: Array<DeferredGroupedFieldSetResult>;
19-
reconcilableResults: Array<ReconcilableDeferredGroupedFieldSetResult>;
20+
reconcilableResults: Set<ReconcilableDeferredGroupedFieldSetResult>;
2021
children: Array<DeferredFragmentNode>;
2122
}
2223

@@ -67,7 +68,9 @@ export class IncrementalGraph {
6768
const deferredFragmentNode = this._addDeferredFragmentNode(
6869
deferredFragmentRecord,
6970
);
70-
deferredFragmentNode.expectedReconcilableResults++;
71+
deferredFragmentNode.deferredGroupedFieldSetRecords.add(
72+
incrementalDataRecord,
73+
);
7174
}
7275

7376
const result = incrementalDataRecord.result;
@@ -104,13 +107,16 @@ export class IncrementalGraph {
104107
reconcilableResult: ReconcilableDeferredGroupedFieldSetResult,
105108
): void {
106109
const deferredFragmentNodes: Array<DeferredFragmentNode> =
107-
reconcilableResult.deferredFragmentRecords
110+
reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords
108111
.map((deferredFragmentRecord) =>
109112
this._deferredFragmentNodes.get(deferredFragmentRecord),
110113
)
111114
.filter<DeferredFragmentNode>(isDeferredFragmentNode);
112115
for (const deferredFragmentNode of deferredFragmentNodes) {
113-
deferredFragmentNode.reconcilableResults.push(reconcilableResult);
116+
deferredFragmentNode.deferredGroupedFieldSetRecords.delete(
117+
reconcilableResult.deferredGroupedFieldSetRecord,
118+
);
119+
deferredFragmentNode.reconcilableResults.add(reconcilableResult);
114120
}
115121
}
116122

@@ -120,7 +126,7 @@ export class IncrementalGraph {
120126
if (isStreamNode(node)) {
121127
this._pending.add(node);
122128
newPending.push(node);
123-
} else if (node.expectedReconcilableResults) {
129+
} else if (node.deferredGroupedFieldSetRecords.size > 0) {
124130
this._pending.add(node);
125131
newPending.push(node.deferredFragmentRecord);
126132
} else {
@@ -181,13 +187,26 @@ export class IncrementalGraph {
181187
if (deferredFragmentNode === undefined) {
182188
return undefined;
183189
}
184-
const reconcilableResults = deferredFragmentNode.reconcilableResults;
185-
if (
186-
deferredFragmentNode.expectedReconcilableResults !==
187-
reconcilableResults.length
188-
) {
190+
if (deferredFragmentNode.deferredGroupedFieldSetRecords.size > 0) {
189191
return;
190192
}
193+
const reconcilableResults = Array.from(
194+
deferredFragmentNode.reconcilableResults,
195+
);
196+
for (const reconcilableResult of reconcilableResults) {
197+
for (const otherDeferredFragmentRecord of reconcilableResult
198+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
199+
const otherDeferredFragmentNode = this._deferredFragmentNodes.get(
200+
otherDeferredFragmentRecord,
201+
);
202+
if (otherDeferredFragmentNode === undefined) {
203+
continue;
204+
}
205+
otherDeferredFragmentNode.reconcilableResults.delete(
206+
reconcilableResult,
207+
);
208+
}
209+
}
191210
this._removePending(deferredFragmentNode);
192211
for (const child of deferredFragmentNode.children) {
193212
this._newPending.add(child);
@@ -240,9 +259,9 @@ export class IncrementalGraph {
240259
}
241260
deferredFragmentNode = {
242261
deferredFragmentRecord,
243-
expectedReconcilableResults: 0,
262+
deferredGroupedFieldSetRecords: new Set(),
244263
results: [],
245-
reconcilableResults: [],
264+
reconcilableResults: new Set(),
246265
children: [],
247266
};
248267
this._deferredFragmentNodes.set(
@@ -263,7 +282,8 @@ export class IncrementalGraph {
263282
result: DeferredGroupedFieldSetResult,
264283
): void {
265284
let isPending = false;
266-
for (const deferredFragmentRecord of result.deferredFragmentRecords) {
285+
for (const deferredFragmentRecord of result.deferredGroupedFieldSetRecord
286+
.deferredFragmentRecords) {
267287
const deferredFragmentNode = this._deferredFragmentNodes.get(
268288
deferredFragmentRecord,
269289
);

src/execution/IncrementalPublisher.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ class IncrementalPublisher {
230230
deferredGroupedFieldSetResult,
231231
)
232232
) {
233-
for (const deferredFragmentRecord of deferredGroupedFieldSetResult.deferredFragmentRecords) {
233+
for (const deferredFragmentRecord of deferredGroupedFieldSetResult
234+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
234235
const id = deferredFragmentRecord.id;
235236
if (id !== undefined) {
236237
context.completed.push({
@@ -253,7 +254,8 @@ class IncrementalPublisher {
253254
this._incrementalGraph.addIncrementalDataRecords(incrementalDataRecords);
254255
}
255256

256-
for (const deferredFragmentRecord of deferredGroupedFieldSetResult.deferredFragmentRecords) {
257+
for (const deferredFragmentRecord of deferredGroupedFieldSetResult
258+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
257259
const id = deferredFragmentRecord.id;
258260
// TODO: add test case for this.
259261
// Presumably, this can occur if an error causes a fragment to be completed early,
@@ -269,10 +271,6 @@ class IncrementalPublisher {
269271
}
270272
const incremental = context.incremental;
271273
for (const reconcilableResult of reconcilableResults) {
272-
if (reconcilableResult.sent) {
273-
continue;
274-
}
275-
reconcilableResult.sent = true;
276274
const { bestId, subPath } = this._getBestIdAndSubPath(
277275
id,
278276
deferredFragmentRecord,
@@ -343,7 +341,8 @@ class IncrementalPublisher {
343341
let maxLength = pathToArray(initialDeferredFragmentRecord.path).length;
344342
let bestId = initialId;
345343

346-
for (const deferredFragmentRecord of deferredGroupedFieldSetResult.deferredFragmentRecords) {
344+
for (const deferredFragmentRecord of deferredGroupedFieldSetResult
345+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
347346
if (deferredFragmentRecord === initialDeferredFragmentRecord) {
348347
continue;
349348
}

src/execution/execute.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,9 +2089,14 @@ function executeDeferredGroupedFieldSets(
20892089
deferMap,
20902090
);
20912091

2092+
const deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord = {
2093+
deferredFragmentRecords,
2094+
result: undefined as unknown as DeferredGroupedFieldSetResult,
2095+
};
2096+
20922097
const executor = () =>
20932098
executeDeferredGroupedFieldSet(
2094-
deferredFragmentRecords,
2099+
deferredGroupedFieldSetRecord,
20952100
exeContext,
20962101
parentType,
20972102
sourceValue,
@@ -2104,12 +2109,12 @@ function executeDeferredGroupedFieldSets(
21042109
deferMap,
21052110
);
21062111

2107-
const deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord = {
2108-
deferredFragmentRecords,
2109-
result: shouldDefer(parentDeferUsages, deferUsageSet)
2110-
? Promise.resolve().then(executor)
2111-
: executor(),
2112-
};
2112+
deferredGroupedFieldSetRecord.result = shouldDefer(
2113+
parentDeferUsages,
2114+
deferUsageSet,
2115+
)
2116+
? Promise.resolve().then(executor)
2117+
: executor();
21132118

21142119
newDeferredGroupedFieldSetRecords.push(deferredGroupedFieldSetRecord);
21152120
}
@@ -2134,7 +2139,7 @@ function shouldDefer(
21342139
}
21352140

21362141
function executeDeferredGroupedFieldSet(
2137-
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>,
2142+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
21382143
exeContext: ExecutionContext,
21392144
parentType: GraphQLObjectType,
21402145
sourceValue: unknown,
@@ -2156,7 +2161,7 @@ function executeDeferredGroupedFieldSet(
21562161
);
21572162
} catch (error) {
21582163
return {
2159-
deferredFragmentRecords,
2164+
deferredGroupedFieldSetRecord,
21602165
path: pathToArray(path),
21612166
errors: withError(incrementalContext.errors, error),
21622167
};
@@ -2167,12 +2172,12 @@ function executeDeferredGroupedFieldSet(
21672172
(resolved) =>
21682173
buildDeferredGroupedFieldSetResult(
21692174
incrementalContext.errors,
2170-
deferredFragmentRecords,
2175+
deferredGroupedFieldSetRecord,
21712176
path,
21722177
resolved,
21732178
),
21742179
(error) => ({
2175-
deferredFragmentRecords,
2180+
deferredGroupedFieldSetRecord,
21762181
path: pathToArray(path),
21772182
errors: withError(incrementalContext.errors, error),
21782183
}),
@@ -2181,20 +2186,20 @@ function executeDeferredGroupedFieldSet(
21812186

21822187
return buildDeferredGroupedFieldSetResult(
21832188
incrementalContext.errors,
2184-
deferredFragmentRecords,
2189+
deferredGroupedFieldSetRecord,
21852190
path,
21862191
result,
21872192
);
21882193
}
21892194

21902195
function buildDeferredGroupedFieldSetResult(
21912196
errors: ReadonlyArray<GraphQLError> | undefined,
2192-
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>,
2197+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
21932198
path: Path | undefined,
21942199
result: GraphQLWrappedResult<ObjMap<unknown>>,
21952200
): DeferredGroupedFieldSetResult {
21962201
return {
2197-
deferredFragmentRecords,
2202+
deferredGroupedFieldSetRecord,
21982203
path: pathToArray(path),
21992204
result:
22002205
errors === undefined ? { data: result[0] } : { data: result[0], errors },

src/execution/types.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,22 +179,21 @@ export type DeferredGroupedFieldSetResult =
179179
export function isDeferredGroupedFieldSetResult(
180180
subsequentResult: DeferredGroupedFieldSetResult | StreamItemsResult,
181181
): subsequentResult is DeferredGroupedFieldSetResult {
182-
return 'deferredFragmentRecords' in subsequentResult;
182+
return 'deferredGroupedFieldSetRecord' in subsequentResult;
183183
}
184184

185185
export interface ReconcilableDeferredGroupedFieldSetResult {
186-
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
186+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord;
187187
path: Array<string | number>;
188188
result: BareDeferredGroupedFieldSetResult;
189189
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined;
190-
sent?: true | undefined;
191190
errors?: never;
192191
}
193192

194193
interface NonReconcilableDeferredGroupedFieldSetResult {
195-
errors: ReadonlyArray<GraphQLError>;
196-
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
194+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord;
197195
path: Array<string | number>;
196+
errors: ReadonlyArray<GraphQLError>;
198197
result?: never;
199198
}
200199

0 commit comments

Comments
 (0)