Skip to content

Commit 929cdc8

Browse files
committed
emit all deferred fragments as soon as possible
1 parent 2aedf25 commit 929cdc8

File tree

5 files changed

+68
-131
lines changed

5 files changed

+68
-131
lines changed

src/execution/IncrementalPublisher.ts

Lines changed: 46 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,11 @@ export class IncrementalPublisher {
196196
this._reset();
197197
}
198198

199-
reportNewDeferFragmentRecord(
200-
deferredFragmentRecord: DeferredFragmentRecord,
201-
parentIncrementalResultRecord:
202-
| InitialResultRecord
203-
| DeferredFragmentRecord
204-
| StreamItemsRecord,
199+
reportNewSubsequentResultRecord(
200+
subsequentResultRecord: SubsequentResultRecord,
201+
parentIncrementalDataRecord: IncrementalDataRecord,
205202
): void {
206-
parentIncrementalResultRecord.children.add(deferredFragmentRecord);
203+
parentIncrementalDataRecord.children.add(subsequentResultRecord);
207204
}
208205

209206
reportNewDeferredGroupedFieldSetRecord(
@@ -217,19 +214,6 @@ export class IncrementalPublisher {
217214
}
218215
}
219216

220-
reportNewStreamItemsRecord(
221-
streamItemsRecord: StreamItemsRecord,
222-
parentIncrementalDataRecord: IncrementalDataRecord,
223-
): void {
224-
if (isDeferredGroupedFieldSetRecord(parentIncrementalDataRecord)) {
225-
for (const parent of parentIncrementalDataRecord.deferredFragmentRecords) {
226-
parent.children.add(streamItemsRecord);
227-
}
228-
} else {
229-
parentIncrementalDataRecord.children.add(streamItemsRecord);
230-
}
231-
}
232-
233217
completeDeferredGroupedFieldSet(
234218
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
235219
data: ObjMap<unknown>,
@@ -349,8 +333,9 @@ export class IncrementalPublisher {
349333

350334
const streams = new Set<StreamRecord>();
351335

352-
const children = this._getChildren(erroringIncrementalDataRecord);
353-
const descendants = this._getDescendants(children);
336+
const descendants = this._getDescendants(
337+
erroringIncrementalDataRecord.children,
338+
);
354339

355340
for (const child of descendants) {
356341
if (!this._nullsChildSubsequentResultRecord(child, nullPathArray)) {
@@ -538,19 +523,19 @@ export class IncrementalPublisher {
538523
const incrementalResults: Array<IncrementalResult> = [];
539524
const completedResults: Array<CompletedResult> = [];
540525
for (const subsequentResultRecord of completedRecords) {
541-
for (const child of subsequentResultRecord.children) {
542-
if (child.filtered) {
543-
continue;
544-
}
545-
const pendingSource = isStreamItemsRecord(child)
546-
? child.streamRecord
547-
: child;
548-
if (!pendingSource.pendingSent) {
549-
newPendingSources.add(pendingSource);
550-
}
551-
this._publish(child);
552-
}
553526
if (isStreamItemsRecord(subsequentResultRecord)) {
527+
for (const child of subsequentResultRecord.children) {
528+
if (child.filtered) {
529+
continue;
530+
}
531+
const pendingSource = isStreamItemsRecord(child)
532+
? child.streamRecord
533+
: child;
534+
if (!pendingSource.pendingSent) {
535+
newPendingSources.add(pendingSource);
536+
}
537+
this._publish(child);
538+
}
554539
if (subsequentResultRecord.isFinalRecord) {
555540
newPendingSources.delete(subsequentResultRecord.streamRecord);
556541
completedResults.push(
@@ -585,6 +570,18 @@ export class IncrementalPublisher {
585570
continue;
586571
}
587572
for (const deferredGroupedFieldSetRecord of subsequentResultRecord.deferredGroupedFieldSetRecords) {
573+
for (const child of deferredGroupedFieldSetRecord.children) {
574+
if (child.filtered) {
575+
continue;
576+
}
577+
const pendingSource = isStreamItemsRecord(child)
578+
? child.streamRecord
579+
: child;
580+
if (!pendingSource.pendingSent) {
581+
newPendingSources.add(pendingSource);
582+
}
583+
this._publish(child);
584+
}
588585
if (!deferredGroupedFieldSetRecord.sent) {
589586
deferredGroupedFieldSetRecord.sent = true;
590587
const incrementalResult: IncrementalDeferResult =
@@ -613,6 +610,8 @@ export class IncrementalPublisher {
613610
let idWithLongestPath: string | undefined;
614611
for (const deferredFragmentRecord of deferredFragmentRecords) {
615612
const id = deferredFragmentRecord.id;
613+
// TODO add test
614+
/* c8 ignore next 3 */
616615
if (id === undefined) {
617616
continue;
618617
}
@@ -668,39 +667,27 @@ export class IncrementalPublisher {
668667

669668
if (subsequentResultRecord._pending.size > 0) {
670669
this._introduce(subsequentResultRecord);
671-
} else if (
672-
subsequentResultRecord.deferredGroupedFieldSetRecords.size > 0 ||
673-
subsequentResultRecord.children.size > 0
674-
) {
670+
} else if (subsequentResultRecord.deferredGroupedFieldSetRecords.size > 0) {
675671
this._push(subsequentResultRecord);
676672
}
677673
}
678674

679-
private _getChildren(
680-
erroringIncrementalDataRecord: IncrementalDataRecord,
681-
): ReadonlySet<SubsequentResultRecord> {
682-
const children = new Set<SubsequentResultRecord>();
683-
if (isDeferredGroupedFieldSetRecord(erroringIncrementalDataRecord)) {
684-
for (const erroringIncrementalResultRecord of erroringIncrementalDataRecord.deferredFragmentRecords) {
685-
for (const child of erroringIncrementalResultRecord.children) {
686-
children.add(child);
687-
}
688-
}
689-
} else {
690-
for (const child of erroringIncrementalDataRecord.children) {
691-
children.add(child);
692-
}
693-
}
694-
return children;
695-
}
696-
697675
private _getDescendants(
698676
children: ReadonlySet<SubsequentResultRecord>,
699677
descendants = new Set<SubsequentResultRecord>(),
700678
): ReadonlySet<SubsequentResultRecord> {
701679
for (const child of children) {
702680
descendants.add(child);
703-
this._getDescendants(child.children, descendants);
681+
if (isStreamItemsRecord(child)) {
682+
this._getDescendants(child.children, descendants);
683+
} else {
684+
for (const deferredGroupedFieldSetRecord of child.deferredGroupedFieldSetRecords) {
685+
this._getDescendants(
686+
deferredGroupedFieldSetRecord.children,
687+
descendants,
688+
);
689+
}
690+
}
704691
}
705692
return descendants;
706693
}
@@ -736,12 +723,6 @@ export class IncrementalPublisher {
736723
}
737724
}
738725

739-
function isDeferredGroupedFieldSetRecord(
740-
incrementalDataRecord: unknown,
741-
): incrementalDataRecord is DeferredGroupedFieldSetRecord {
742-
return incrementalDataRecord instanceof DeferredGroupedFieldSetRecord;
743-
}
744-
745726
function isStreamItemsRecord(
746727
subsequentResultRecord: unknown,
747728
): subsequentResultRecord is StreamItemsRecord {
@@ -764,6 +745,7 @@ export class DeferredGroupedFieldSetRecord {
764745
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
765746
groupedFieldSet: GroupedFieldSet;
766747
shouldInitiateDefer: boolean;
748+
children: Set<SubsequentResultRecord>;
767749
errors: Array<GraphQLError>;
768750
data: ObjMap<unknown> | undefined;
769751
sent: boolean;
@@ -778,6 +760,7 @@ export class DeferredGroupedFieldSetRecord {
778760
this.deferredFragmentRecords = opts.deferredFragmentRecords;
779761
this.groupedFieldSet = opts.groupedFieldSet;
780762
this.shouldInitiateDefer = opts.shouldInitiateDefer;
763+
this.children = new Set();
781764
this.errors = [];
782765
this.sent = false;
783766
}
@@ -788,7 +771,6 @@ export class DeferredFragmentRecord {
788771
path: ReadonlyArray<string | number>;
789772
label: string | undefined;
790773
id: string | undefined;
791-
children: Set<SubsequentResultRecord>;
792774
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
793775
errors: Array<GraphQLError>;
794776
filtered: boolean;
@@ -798,7 +780,6 @@ export class DeferredFragmentRecord {
798780
constructor(opts: { path: Path | undefined; label: string | undefined }) {
799781
this.path = pathToArray(opts.path);
800782
this.label = opts.label;
801-
this.children = new Set();
802783
this.filtered = false;
803784
this.deferredGroupedFieldSetRecords = new Set();
804785
this.errors = [];

src/execution/__tests__/defer-test.ts

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -946,41 +946,29 @@ describe('Execute: defer directive', () => {
946946
},
947947
},
948948
},
949-
pending: [{ id: '0', path: ['hero'] }],
949+
pending: [
950+
{ id: '0', path: ['hero'] },
951+
{ id: '1', path: ['hero', 'nestedObject'] },
952+
{ id: '2', path: ['hero', 'nestedObject', 'deeperObject'] },
953+
],
950954
hasNext: true,
951955
},
952956
{
953-
pending: [{ id: '1', path: ['hero', 'nestedObject'] }],
954957
incremental: [
955958
{
956959
data: { bar: 'bar' },
957-
id: '0',
958-
subPath: ['nestedObject', 'deeperObject'],
960+
id: '2',
959961
},
960-
],
961-
completed: [{ id: '0' }],
962-
hasNext: true,
963-
},
964-
{
965-
pending: [{ id: '2', path: ['hero', 'nestedObject', 'deeperObject'] }],
966-
incremental: [
967962
{
968963
data: { baz: 'baz' },
969-
id: '1',
970-
subPath: ['deeperObject'],
964+
id: '2',
971965
},
972-
],
973-
hasNext: true,
974-
completed: [{ id: '1' }],
975-
},
976-
{
977-
incremental: [
978966
{
979967
data: { bak: 'bak' },
980968
id: '2',
981969
},
982970
],
983-
completed: [{ id: '2' }],
971+
completed: [{ id: '0' }, { id: '1' }, { id: '2' }],
984972
hasNext: false,
985973
},
986974
]);
@@ -1023,34 +1011,27 @@ describe('Execute: defer directive', () => {
10231011
},
10241012
},
10251013
pending: [
1026-
{ id: '0', path: ['hero'] },
1014+
{ id: '0', path: ['hero', 'nestedObject', 'deeperObject'] },
10271015
{ id: '1', path: ['hero', 'nestedObject', 'deeperObject'] },
10281016
],
10291017
hasNext: true,
10301018
},
10311019
{
1032-
pending: [{ id: '2', path: ['hero', 'nestedObject', 'deeperObject'] }],
10331020
incremental: [
10341021
{
10351022
data: {
10361023
foo: 'foo',
10371024
},
1038-
id: '1',
1025+
id: '0',
10391026
},
1040-
],
1041-
completed: [{ id: '0' }, { id: '1' }],
1042-
hasNext: true,
1043-
},
1044-
{
1045-
incremental: [
10461027
{
10471028
data: {
10481029
bar: 'bar',
10491030
},
1050-
id: '2',
1031+
id: '1',
10511032
},
10521033
],
1053-
completed: [{ id: '2' }],
1034+
completed: [{ id: '0' }, { id: '1' }],
10541035
hasNext: false,
10551036
},
10561037
]);

src/execution/buildFieldPlan.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ export function buildFieldPlan(
6565
for (const [responseKey, fieldDetailsList] of fields) {
6666
const deferUsageSet = new Set<DeferUsage>();
6767
let inOriginalResult = false;
68+
let inParentResult = false;
6869
for (const fieldDetails of fieldDetailsList) {
6970
const deferUsage = fieldDetails.deferUsage;
7071
if (deferUsage === undefined) {
7172
inOriginalResult = true;
7273
continue;
74+
} else if (parentDeferUsages.has(deferUsage)) {
75+
inParentResult = true;
7376
}
7477
deferUsageSet.add(deferUsage);
7578
if (!knownDeferUsages.has(deferUsage)) {
@@ -79,13 +82,10 @@ export function buildFieldPlan(
7982
}
8083
if (inOriginalResult) {
8184
deferUsageSet.clear();
82-
} else {
85+
} else if (inParentResult) {
8386
deferUsageSet.forEach((deferUsage) => {
84-
const ancestors = getAncestors(deferUsage);
85-
for (const ancestor of ancestors) {
86-
if (deferUsageSet.has(ancestor)) {
87-
deferUsageSet.delete(deferUsage);
88-
}
87+
if (!parentDeferUsages.has(deferUsage)) {
88+
deferUsageSet.delete(deferUsage);
8989
}
9090
});
9191
}
@@ -153,13 +153,3 @@ export function buildFieldPlan(
153153
newDeferUsages: Array.from(newDeferUsages),
154154
};
155155
}
156-
157-
function getAncestors(deferUsage: DeferUsage): ReadonlyArray<DeferUsage> {
158-
const ancestors: Array<DeferUsage> = [];
159-
let parentDeferUsage: DeferUsage | undefined = deferUsage.parentDeferUsage;
160-
while (parentDeferUsage !== undefined) {
161-
ancestors.unshift(parentDeferUsage);
162-
parentDeferUsage = parentDeferUsage.parentDeferUsage;
163-
}
164-
return ancestors;
165-
}

src/execution/collectFields.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { getDirectiveValues } from './values.js';
2828

2929
export interface DeferUsage {
3030
label: string | undefined;
31-
parentDeferUsage: DeferUsage | undefined;
3231
}
3332

3433
export interface FieldDetails {
@@ -159,7 +158,6 @@ function collectFieldsImpl(
159158
operation,
160159
variableValues,
161160
selection,
162-
parentDeferUsage,
163161
);
164162

165163
collectFieldsImpl(
@@ -179,7 +177,6 @@ function collectFieldsImpl(
179177
operation,
180178
variableValues,
181179
selection,
182-
parentDeferUsage,
183180
);
184181

185182
if (
@@ -223,7 +220,6 @@ function getDeferUsage(
223220
operation: OperationDefinitionNode,
224221
variableValues: { [variable: string]: unknown },
225222
node: FragmentSpreadNode | InlineFragmentNode,
226-
parentDeferUsage: DeferUsage | undefined,
227223
): DeferUsage | undefined {
228224
const defer = getDirectiveValues(GraphQLDeferDirective, node, variableValues);
229225

@@ -242,7 +238,6 @@ function getDeferUsage(
242238

243239
return {
244240
label: typeof defer.label === 'string' ? defer.label : undefined,
245-
parentDeferUsage,
246241
};
247242
}
248243

0 commit comments

Comments
 (0)