Skip to content

Commit 049eae4

Browse files
committed
polish(incremental): refactor getNewPending
return newPending alongside incremental results
1 parent e160b6f commit 049eae4

File tree

2 files changed

+157
-130
lines changed

2 files changed

+157
-130
lines changed

src/execution/IncrementalGraph.ts

Lines changed: 143 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BoxedPromiseOrValue } from '../jsutils/BoxedPromiseOrValue.js';
2+
import { invariant } from '../jsutils/invariant.js';
23
import { isPromise } from '../jsutils/isPromise.js';
34
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js';
45

@@ -20,19 +21,13 @@ interface DeferredFragmentNode {
2021
deferredFragmentRecord: DeferredFragmentRecord;
2122
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
2223
reconcilableResults: Set<ReconcilableDeferredGroupedFieldSetResult>;
23-
children: Array<DeferredFragmentNode>;
24+
children: Set<SubsequentResultNode>;
2425
}
2526

2627
function isDeferredFragmentNode(
27-
node: DeferredFragmentNode | undefined,
28+
node: SubsequentResultNode | undefined,
2829
): node is DeferredFragmentNode {
29-
return node !== undefined;
30-
}
31-
32-
function isStreamNode(
33-
record: SubsequentResultNode | IncrementalDataRecord,
34-
): record is StreamRecord {
35-
return 'streamItemQueue' in record;
30+
return node !== undefined && 'deferredFragmentRecord' in node;
3631
}
3732

3833
type SubsequentResultNode = DeferredFragmentNode | StreamRecord;
@@ -47,8 +42,6 @@ export class IncrementalGraph {
4742
DeferredFragmentNode
4843
>;
4944

50-
private _newPending: Set<SubsequentResultNode>;
51-
private _newIncrementalDataRecords: Set<IncrementalDataRecord>;
5245
private _completedQueue: Array<IncrementalDataRecordResult>;
5346
private _nextQueue: Array<
5447
(iterable: IteratorResult<Iterable<IncrementalDataRecordResult>>) => void
@@ -57,87 +50,42 @@ export class IncrementalGraph {
5750
constructor() {
5851
this._pending = new Set();
5952
this._deferredFragmentNodes = new Map();
60-
this._newIncrementalDataRecords = new Set();
61-
this._newPending = new Set();
6253
this._completedQueue = [];
6354
this._nextQueue = [];
6455
}
6556

66-
addIncrementalDataRecords(
57+
getNewPending(
6758
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
68-
): void {
69-
for (const incrementalDataRecord of incrementalDataRecords) {
70-
if (isDeferredGroupedFieldSetRecord(incrementalDataRecord)) {
71-
this._addDeferredGroupedFieldSetRecord(incrementalDataRecord);
72-
} else {
73-
this._addStreamRecord(incrementalDataRecord);
74-
}
75-
}
59+
): ReadonlyArray<SubsequentResultRecord> {
60+
const newPending = new Set<SubsequentResultNode>();
61+
this._addIncrementalDataRecords(
62+
incrementalDataRecords,
63+
undefined,
64+
newPending,
65+
);
66+
return this._pendingNodesToResults(newPending);
7667
}
7768

7869
addCompletedReconcilableDeferredGroupedFieldSet(
7970
reconcilableResult: ReconcilableDeferredGroupedFieldSetResult,
8071
): void {
81-
const deferredFragmentNodes: Array<DeferredFragmentNode> =
82-
reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords
83-
.map((deferredFragmentRecord) =>
84-
this._deferredFragmentNodes.get(deferredFragmentRecord),
85-
)
86-
.filter<DeferredFragmentNode>(isDeferredFragmentNode);
87-
for (const deferredFragmentNode of deferredFragmentNodes) {
72+
for (const deferredFragmentNode of this._fragmentsToNodes(
73+
reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords,
74+
)) {
8875
deferredFragmentNode.deferredGroupedFieldSetRecords.delete(
8976
reconcilableResult.deferredGroupedFieldSetRecord,
9077
);
9178
deferredFragmentNode.reconcilableResults.add(reconcilableResult);
9279
}
93-
}
94-
95-
getNewPending(): ReadonlyArray<SubsequentResultRecord> {
96-
const newPending: Array<SubsequentResultRecord> = [];
97-
for (const node of this._newPending) {
98-
if (isStreamNode(node)) {
99-
this._pending.add(node);
100-
newPending.push(node);
101-
this._newIncrementalDataRecords.add(node);
102-
} else if (node.deferredGroupedFieldSetRecords.size > 0) {
103-
for (const deferredGroupedFieldSetNode of node.deferredGroupedFieldSetRecords) {
104-
this._newIncrementalDataRecords.add(deferredGroupedFieldSetNode);
105-
}
106-
this._pending.add(node);
107-
newPending.push(node.deferredFragmentRecord);
108-
} else {
109-
for (const child of node.children) {
110-
this._newPending.add(child);
111-
}
112-
}
113-
}
114-
this._newPending.clear();
115-
116-
for (const incrementalDataRecord of this._newIncrementalDataRecords) {
117-
if (isStreamNode(incrementalDataRecord)) {
118-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
119-
this._onStreamItems(
120-
incrementalDataRecord,
121-
incrementalDataRecord.streamItemQueue,
122-
);
123-
} else {
124-
const deferredGroupedFieldSetResult = incrementalDataRecord.result;
125-
const result =
126-
deferredGroupedFieldSetResult instanceof BoxedPromiseOrValue
127-
? deferredGroupedFieldSetResult.value
128-
: deferredGroupedFieldSetResult().value;
12980

130-
if (isPromise(result)) {
131-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
132-
result.then((resolved) => this._enqueue(resolved));
133-
} else {
134-
this._enqueue(result);
135-
}
136-
}
81+
const incrementalDataRecords = reconcilableResult.incrementalDataRecords;
82+
if (incrementalDataRecords !== undefined) {
83+
this._addIncrementalDataRecords(
84+
incrementalDataRecords,
85+
reconcilableResult.deferredGroupedFieldSetRecord
86+
.deferredFragmentRecords,
87+
);
13788
}
138-
this._newIncrementalDataRecords.clear();
139-
140-
return newPending;
14189
}
14290

14391
completedIncrementalData() {
@@ -177,9 +125,12 @@ export class IncrementalGraph {
177125
return this._pending.size > 0;
178126
}
179127

180-
completeDeferredFragment(
181-
deferredFragmentRecord: DeferredFragmentRecord,
182-
): Array<ReconcilableDeferredGroupedFieldSetResult> | undefined {
128+
completeDeferredFragment(deferredFragmentRecord: DeferredFragmentRecord):
129+
| {
130+
newPending: ReadonlyArray<SubsequentResultRecord>;
131+
reconcilableResults: ReadonlyArray<ReconcilableDeferredGroupedFieldSetResult>;
132+
}
133+
| undefined {
183134
const deferredFragmentNode = this._deferredFragmentNodes.get(
184135
deferredFragmentRecord,
185136
);
@@ -194,25 +145,21 @@ export class IncrementalGraph {
194145
const reconcilableResults = Array.from(
195146
deferredFragmentNode.reconcilableResults,
196147
);
148+
this._removePending(deferredFragmentNode);
197149
for (const reconcilableResult of reconcilableResults) {
198-
for (const otherDeferredFragmentRecord of reconcilableResult
199-
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
200-
const otherDeferredFragmentNode = this._deferredFragmentNodes.get(
201-
otherDeferredFragmentRecord,
202-
);
203-
if (otherDeferredFragmentNode === undefined) {
204-
continue;
205-
}
150+
for (const otherDeferredFragmentNode of this._fragmentsToNodes(
151+
reconcilableResult.deferredGroupedFieldSetRecord
152+
.deferredFragmentRecords,
153+
)) {
206154
otherDeferredFragmentNode.reconcilableResults.delete(
207155
reconcilableResult,
208156
);
209157
}
210158
}
211-
this._removePending(deferredFragmentNode);
212-
for (const child of deferredFragmentNode.children) {
213-
this._newPending.add(child);
214-
}
215-
return reconcilableResults;
159+
const newPending = this._pendingNodesToResults(
160+
deferredFragmentNode.children,
161+
);
162+
return { newPending, reconcilableResults };
216163
}
217164

218165
removeDeferredFragment(
@@ -227,9 +174,11 @@ export class IncrementalGraph {
227174
this._removePending(deferredFragmentNode);
228175
this._deferredFragmentNodes.delete(deferredFragmentRecord);
229176
// TODO: add test case for an erroring deferred fragment with child defers
230-
/* c8 ignore next 3 */
177+
/* c8 ignore next 5 */
231178
for (const child of deferredFragmentNode.children) {
232-
this.removeDeferredFragment(child.deferredFragmentRecord);
179+
if (isDeferredFragmentNode(child)) {
180+
this.removeDeferredFragment(child.deferredFragmentRecord);
181+
}
233182
}
234183
return true;
235184
}
@@ -247,28 +196,92 @@ export class IncrementalGraph {
247196
}
248197
}
249198

250-
private _addDeferredGroupedFieldSetRecord(
251-
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
199+
private _addIncrementalDataRecords(
200+
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
201+
parents: ReadonlyArray<DeferredFragmentRecord> | undefined,
202+
newPending?: Set<SubsequentResultNode> | undefined,
252203
): void {
253-
for (const deferredFragmentRecord of deferredGroupedFieldSetRecord.deferredFragmentRecords) {
254-
const deferredFragmentNode = this._addDeferredFragmentNode(
255-
deferredFragmentRecord,
256-
);
257-
if (this._pending.has(deferredFragmentNode)) {
258-
this._newIncrementalDataRecords.add(deferredGroupedFieldSetRecord);
204+
for (const incrementalDataRecord of incrementalDataRecords) {
205+
if (isDeferredGroupedFieldSetRecord(incrementalDataRecord)) {
206+
for (const deferredFragmentRecord of incrementalDataRecord.deferredFragmentRecords) {
207+
const deferredFragmentNode = this._addDeferredFragmentNode(
208+
deferredFragmentRecord,
209+
newPending,
210+
);
211+
deferredFragmentNode.deferredGroupedFieldSetRecords.add(
212+
incrementalDataRecord,
213+
);
214+
}
215+
if (this._hasPendingFragment(incrementalDataRecord)) {
216+
this._onDeferredGroupedFieldSet(incrementalDataRecord);
217+
}
218+
} else if (parents === undefined) {
219+
invariant(newPending !== undefined);
220+
newPending.add(incrementalDataRecord);
221+
} else {
222+
for (const parent of parents) {
223+
const deferredFragmentNode = this._addDeferredFragmentNode(
224+
parent,
225+
newPending,
226+
);
227+
deferredFragmentNode.children.add(incrementalDataRecord);
228+
}
259229
}
260-
deferredFragmentNode.deferredGroupedFieldSetRecords.add(
261-
deferredGroupedFieldSetRecord,
262-
);
263230
}
264231
}
265232

266-
private _addStreamRecord(streamRecord: StreamRecord): void {
267-
this._newPending.add(streamRecord);
233+
private _pendingNodesToResults(
234+
newPendingNodes: Set<SubsequentResultNode>,
235+
): ReadonlyArray<SubsequentResultRecord> {
236+
const newPendingResults: Array<SubsequentResultRecord> = [];
237+
for (const node of newPendingNodes) {
238+
if (isDeferredFragmentNode(node)) {
239+
if (node.deferredGroupedFieldSetRecords.size > 0) {
240+
for (const deferredGroupedFieldSetRecord of node.deferredGroupedFieldSetRecords) {
241+
if (!this._hasPendingFragment(deferredGroupedFieldSetRecord)) {
242+
this._onDeferredGroupedFieldSet(deferredGroupedFieldSetRecord);
243+
}
244+
}
245+
this._pending.add(node);
246+
newPendingResults.push(node.deferredFragmentRecord);
247+
continue;
248+
}
249+
this._deferredFragmentNodes.delete(node.deferredFragmentRecord);
250+
for (const child of node.children) {
251+
newPendingNodes.add(child);
252+
}
253+
} else {
254+
this._pending.add(node);
255+
newPendingResults.push(node);
256+
257+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
258+
this._onStreamItems(node);
259+
}
260+
}
261+
return newPendingResults;
262+
}
263+
264+
private _hasPendingFragment(
265+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
266+
): boolean {
267+
return this._fragmentsToNodes(
268+
deferredGroupedFieldSetRecord.deferredFragmentRecords,
269+
).some((node) => this._pending.has(node));
270+
}
271+
272+
private _fragmentsToNodes(
273+
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>,
274+
): Array<DeferredFragmentNode> {
275+
return deferredFragmentRecords
276+
.map((deferredFragmentRecord) =>
277+
this._deferredFragmentNodes.get(deferredFragmentRecord),
278+
)
279+
.filter<DeferredFragmentNode>(isDeferredFragmentNode);
268280
}
269281

270282
private _addDeferredFragmentNode(
271283
deferredFragmentRecord: DeferredFragmentRecord,
284+
newPending: Set<SubsequentResultNode> | undefined,
272285
): DeferredFragmentNode {
273286
let deferredFragmentNode = this._deferredFragmentNodes.get(
274287
deferredFragmentRecord,
@@ -280,29 +293,45 @@ export class IncrementalGraph {
280293
deferredFragmentRecord,
281294
deferredGroupedFieldSetRecords: new Set(),
282295
reconcilableResults: new Set(),
283-
children: [],
296+
children: new Set(),
284297
};
285298
this._deferredFragmentNodes.set(
286299
deferredFragmentRecord,
287300
deferredFragmentNode,
288301
);
289302
const parent = deferredFragmentRecord.parent;
290303
if (parent === undefined) {
291-
this._newPending.add(deferredFragmentNode);
304+
invariant(newPending !== undefined);
305+
newPending.add(deferredFragmentNode);
292306
return deferredFragmentNode;
293307
}
294-
const parentNode = this._addDeferredFragmentNode(parent);
295-
parentNode.children.push(deferredFragmentNode);
308+
const parentNode = this._addDeferredFragmentNode(parent, newPending);
309+
parentNode.children.add(deferredFragmentNode);
296310
return deferredFragmentNode;
297311
}
298312

299-
private async _onStreamItems(
300-
streamRecord: StreamRecord,
301-
streamItemQueue: Array<StreamItemRecord>,
302-
): Promise<void> {
313+
private _onDeferredGroupedFieldSet(
314+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
315+
): void {
316+
const deferredGroupedFieldSetResult = deferredGroupedFieldSetRecord.result;
317+
const result =
318+
deferredGroupedFieldSetResult instanceof BoxedPromiseOrValue
319+
? deferredGroupedFieldSetResult.value
320+
: deferredGroupedFieldSetResult().value;
321+
322+
if (isPromise(result)) {
323+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
324+
result.then((resolved) => this._enqueue(resolved));
325+
} else {
326+
this._enqueue(result);
327+
}
328+
}
329+
330+
private async _onStreamItems(streamRecord: StreamRecord): Promise<void> {
303331
let items: Array<unknown> = [];
304332
let errors: Array<GraphQLError> = [];
305333
let incrementalDataRecords: Array<IncrementalDataRecord> = [];
334+
const streamItemQueue = streamRecord.streamItemQueue;
306335
let streamItemRecord: StreamItemRecord | undefined;
307336
while ((streamItemRecord = streamItemQueue.shift()) !== undefined) {
308337
let result =

0 commit comments

Comments
 (0)