Skip to content

Commit

Permalink
polish: refactor execution plan functions to match spec (#4273)
Browse files Browse the repository at this point in the history
moves simplest case inside the execute[Root|Sub]ExecutionPlan()
functions to better match proposed spec edits

orders function in call order, moving executeSubExecutionPlan down
closer to where it is used

no significant change in performance


![image](https://github.com/user-attachments/assets/221a2696-fd0b-4b53-8519-eb2376327639)
  • Loading branch information
yaacovCR authored Nov 1, 2024
1 parent e4d7e85 commit 308647b
Showing 1 changed file with 158 additions and 114 deletions.
272 changes: 158 additions & 114 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent(
);
}

const collectedFields = collectFields(
const { groupedFieldSet, newDeferUsages } = collectFields(
schema,
fragments,
variableValues,
Expand All @@ -348,24 +348,14 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent(
hideSuggestions,
);

const { groupedFieldSet, newDeferUsages } = collectedFields;
const graphqlWrappedResult =
newDeferUsages.length === 0
? executeRootGroupedFieldSet(
exeContext,
operation.operation,
rootType,
rootValue,
groupedFieldSet,
undefined,
)
: executeExecutionPlan(
exeContext,
rootType,
rootValue,
newDeferUsages,
buildExecutionPlan(groupedFieldSet),
);
const graphqlWrappedResult = executeRootExecutionPlan(
exeContext,
operation.operation,
rootType,
rootValue,
groupedFieldSet,
newDeferUsages,
);

if (isPromise(graphqlWrappedResult)) {
return graphqlWrappedResult.then(
Expand All @@ -388,78 +378,6 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent(
}
}

function executeExecutionPlan(
exeContext: ExecutionContext,
returnType: GraphQLObjectType,
sourceValue: unknown,
newDeferUsages: ReadonlyArray<DeferUsage>,
executionPlan: ExecutionPlan,
path?: Path | undefined,
incrementalContext?: IncrementalContext | undefined,
deferMap?: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path);

const { groupedFieldSet, newGroupedFieldSets } = executionPlan;

const graphqlWrappedResult = executeFields(
exeContext,
returnType,
sourceValue,
path,
groupedFieldSet,
incrementalContext,
newDeferMap,
);

if (newGroupedFieldSets.size > 0) {
const newPendingExecutionGroups = collectExecutionGroups(
exeContext,
returnType,
sourceValue,
path,
incrementalContext?.deferUsageSet,
newGroupedFieldSets,
newDeferMap,
);

return withNewExecutionGroups(
graphqlWrappedResult,
newPendingExecutionGroups,
);
}
return graphqlWrappedResult;
}

function withNewExecutionGroups(
result: PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>>,
newPendingExecutionGroups: ReadonlyArray<PendingExecutionGroup>,
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
if (isPromise(result)) {
return result.then((resolved) => {
addIncrementalDataRecords(resolved, newPendingExecutionGroups);
return resolved;
});
}

addIncrementalDataRecords(result, newPendingExecutionGroups);
return result;
}

function addIncrementalDataRecords(
graphqlWrappedResult: GraphQLWrappedResult<unknown>,
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined,
): void {
if (incrementalDataRecords === undefined) {
return;
}
if (graphqlWrappedResult.incrementalDataRecords === undefined) {
graphqlWrappedResult.incrementalDataRecords = [...incrementalDataRecords];
} else {
graphqlWrappedResult.incrementalDataRecords.push(...incrementalDataRecords);
}
}

function withError(
errors: Array<GraphQLError> | undefined,
error: GraphQLError,
Expand Down Expand Up @@ -616,6 +534,73 @@ export function validateExecutionArgs(
};
}

function executeRootExecutionPlan(
exeContext: ExecutionContext,
operation: OperationTypeNode,
rootType: GraphQLObjectType,
rootValue: unknown,
originalGroupedFieldSet: GroupedFieldSet,
newDeferUsages: ReadonlyArray<DeferUsage>,
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
if (newDeferUsages.length === 0) {
return executeRootGroupedFieldSet(
exeContext,
operation,
rootType,
rootValue,
originalGroupedFieldSet,
undefined,
);
}
const newDeferMap = getNewDeferMap(newDeferUsages, undefined, undefined);

const { groupedFieldSet, newGroupedFieldSets } = buildExecutionPlan(
originalGroupedFieldSet,
);

const graphqlWrappedResult = executeRootGroupedFieldSet(
exeContext,
operation,
rootType,
rootValue,
groupedFieldSet,
newDeferMap,
);

if (newGroupedFieldSets.size > 0) {
const newPendingExecutionGroups = collectExecutionGroups(
exeContext,
rootType,
rootValue,
undefined,
undefined,
newGroupedFieldSets,
newDeferMap,
);

return withNewExecutionGroups(
graphqlWrappedResult,
newPendingExecutionGroups,
);
}
return graphqlWrappedResult;
}

function withNewExecutionGroups(
result: PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>>,
newPendingExecutionGroups: ReadonlyArray<PendingExecutionGroup>,
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
if (isPromise(result)) {
return result.then((resolved) => {
addIncrementalDataRecords(resolved, newPendingExecutionGroups);
return resolved;
});
}

addIncrementalDataRecords(result, newPendingExecutionGroups);
return result;
}

function executeRootGroupedFieldSet(
exeContext: ExecutionContext,
operation: OperationTypeNode,
Expand Down Expand Up @@ -728,6 +713,20 @@ function executeFieldsSerially(
);
}

function addIncrementalDataRecords(
graphqlWrappedResult: GraphQLWrappedResult<unknown>,
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined,
): void {
if (incrementalDataRecords === undefined) {
return;
}
if (graphqlWrappedResult.incrementalDataRecords === undefined) {
graphqlWrappedResult.incrementalDataRecords = [...incrementalDataRecords];
} else {
graphqlWrappedResult.incrementalDataRecords.push(...incrementalDataRecords);
}
}

/**
* Implements the "Executing selection sets" section of the spec
* for fields that may be executed in parallel.
Expand Down Expand Up @@ -1877,29 +1876,74 @@ function collectAndExecuteSubfields(
fieldDetailsList,
);
const { groupedFieldSet, newDeferUsages } = collectedSubfields;
return deferMap === undefined && newDeferUsages.length === 0
? executeFields(
exeContext,
returnType,
result,
path,
groupedFieldSet,
incrementalContext,
undefined,
)
: executeExecutionPlan(
exeContext,
returnType,
result,
newDeferUsages,
buildSubExecutionPlan(
groupedFieldSet,
incrementalContext?.deferUsageSet,
),
path,
incrementalContext,
deferMap,
);
return executeSubExecutionPlan(
exeContext,
returnType,
result,
groupedFieldSet,
newDeferUsages,
path,
incrementalContext,
deferMap,
);
}

function executeSubExecutionPlan(
exeContext: ExecutionContext,
returnType: GraphQLObjectType,
sourceValue: unknown,
originalGroupedFieldSet: GroupedFieldSet,
newDeferUsages: ReadonlyArray<DeferUsage>,
path?: Path | undefined,
incrementalContext?: IncrementalContext | undefined,
deferMap?: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
if (deferMap === undefined && newDeferUsages.length === 0) {
return executeFields(
exeContext,
returnType,
sourceValue,
path,
originalGroupedFieldSet,
incrementalContext,
deferMap,
);
}

const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path);

const { groupedFieldSet, newGroupedFieldSets } = buildSubExecutionPlan(
originalGroupedFieldSet,
incrementalContext?.deferUsageSet,
);

const graphqlWrappedResult = executeFields(
exeContext,
returnType,
sourceValue,
path,
groupedFieldSet,
incrementalContext,
newDeferMap,
);

if (newGroupedFieldSets.size > 0) {
const newPendingExecutionGroups = collectExecutionGroups(
exeContext,
returnType,
sourceValue,
path,
incrementalContext?.deferUsageSet,
newGroupedFieldSets,
newDeferMap,
);

return withNewExecutionGroups(
graphqlWrappedResult,
newPendingExecutionGroups,
);
}
return graphqlWrappedResult;
}

function buildSubExecutionPlan(
Expand Down

0 comments on commit 308647b

Please sign in to comment.