Skip to content

Commit

Permalink
incremental: disable early execution by default (#4097)
Browse files Browse the repository at this point in the history
depends on #4098
  • Loading branch information
yaacovCR authored Jun 13, 2024
1 parent 89f9223 commit 75dca3d
Show file tree
Hide file tree
Showing 5 changed files with 685 additions and 155 deletions.
13 changes: 11 additions & 2 deletions src/execution/IncrementalGraph.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BoxedPromiseOrValue } from '../jsutils/BoxedPromiseOrValue.js';
import { isPromise } from '../jsutils/isPromise.js';
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js';

Expand Down Expand Up @@ -120,7 +121,12 @@ export class IncrementalGraph {
incrementalDataRecord.streamItemQueue,
);
} else {
const result = incrementalDataRecord.result.value;
const deferredGroupedFieldSetResult = incrementalDataRecord.result;
const result =
deferredGroupedFieldSetResult instanceof BoxedPromiseOrValue
? deferredGroupedFieldSetResult.value
: deferredGroupedFieldSetResult().value;

if (isPromise(result)) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
result.then((resolved) => this._enqueue(resolved));
Expand Down Expand Up @@ -299,7 +305,10 @@ export class IncrementalGraph {
let incrementalDataRecords: Array<IncrementalDataRecord> = [];
let streamItemRecord: StreamItemRecord | undefined;
while ((streamItemRecord = streamItemQueue.shift()) !== undefined) {
let result = streamItemRecord.value;
let result =
streamItemRecord instanceof BoxedPromiseOrValue
? streamItemRecord.value
: streamItemRecord().value;
if (isPromise(result)) {
if (items.length > 0) {
this._enqueue({
Expand Down
171 changes: 150 additions & 21 deletions src/execution/__tests__/defer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,16 @@ const query = new GraphQLObjectType({

const schema = new GraphQLSchema({ query });

async function complete(document: DocumentNode, rootValue: unknown = { hero }) {
async function complete(
document: DocumentNode,
rootValue: unknown = { hero },
enableEarlyExecution = false,
) {
const result = await experimentalExecuteIncrementally({
schema,
document,
rootValue,
enableEarlyExecution,
});

if ('initialResult' in result) {
Expand Down Expand Up @@ -247,6 +252,118 @@ describe('Execute: defer directive', () => {
},
]);
});
it('Does not execute deferred fragments early when not specified', async () => {
const document = parse(`
query HeroNameQuery {
hero {
id
...NameFragment @defer
}
}
fragment NameFragment on Hero {
name
}
`);
const order: Array<string> = [];
const result = await complete(document, {
hero: {
...hero,
id: async () => {
await resolveOnNextTick();
await resolveOnNextTick();
order.push('slow-id');
return hero.id;
},
name: () => {
order.push('fast-name');
return hero.name;
},
},
});

expectJSON(result).toDeepEqual([
{
data: {
hero: {
id: '1',
},
},
pending: [{ id: '0', path: ['hero'] }],
hasNext: true,
},
{
incremental: [
{
data: {
name: 'Luke',
},
id: '0',
},
],
completed: [{ id: '0' }],
hasNext: false,
},
]);
expect(order).to.deep.equal(['slow-id', 'fast-name']);
});
it('Does execute deferred fragments early when specified', async () => {
const document = parse(`
query HeroNameQuery {
hero {
id
...NameFragment @defer
}
}
fragment NameFragment on Hero {
name
}
`);
const order: Array<string> = [];
const result = await complete(
document,
{
hero: {
...hero,
id: async () => {
await resolveOnNextTick();
await resolveOnNextTick();
order.push('slow-id');
return hero.id;
},
name: () => {
order.push('fast-name');
return hero.name;
},
},
},
true,
);

expectJSON(result).toDeepEqual([
{
data: {
hero: {
id: '1',
},
},
pending: [{ id: '0', path: ['hero'] }],
hasNext: true,
},
{
incremental: [
{
data: {
name: 'Luke',
},
id: '0',
},
],
completed: [{ id: '0' }],
hasNext: false,
},
]);
expect(order).to.deep.equal(['fast-name', 'slow-id']);
});
it('Can defer fragments on the top level Query field', async () => {
const document = parse(`
query HeroNameQuery {
Expand Down Expand Up @@ -1492,20 +1609,24 @@ describe('Execute: defer directive', () => {
}
}
`);
const result = await complete(document, {
a: {
b: {
c: {
d: 'd',
nonNullErrorField: async () => {
await resolveOnNextTick();
return null;
const result = await complete(
document,
{
a: {
b: {
c: {
d: 'd',
nonNullErrorField: async () => {
await resolveOnNextTick();
return null;
},
},
},
someField: 'someField',
},
someField: 'someField',
},
});
true,
);
expectJSON(result).toDeepEqual([
{
data: {
Expand Down Expand Up @@ -1564,12 +1685,16 @@ describe('Execute: defer directive', () => {
}
}
`);
const result = await complete(document, {
hero: {
...hero,
nonNullName: () => null,
const result = await complete(
document,
{
hero: {
...hero,
nonNullName: () => null,
},
},
});
true,
);
expectJSON(result).toDeepEqual({
data: {
hero: null,
Expand All @@ -1596,12 +1721,16 @@ describe('Execute: defer directive', () => {
}
}
`);
const result = await complete(document, {
hero: {
...hero,
nonNullName: () => null,
const result = await complete(
document,
{
hero: {
...hero,
nonNullName: () => null,
},
},
});
true,
);
expectJSON(result).toDeepEqual([
{
data: {},
Expand Down
Loading

0 comments on commit 75dca3d

Please sign in to comment.