Skip to content

Commit

Permalink
maintain defer ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed Dec 31, 2023
1 parent 2bdcd8b commit 39e798e
Showing 1 changed file with 106 additions and 40 deletions.
146 changes: 106 additions & 40 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@ Defer Usage records contain information derived from the presence of a `@defer`
directive on a fragment and are structures containing:

- {label}: value of the corresponding argument to the `@defer` directive.
- {parentDeferUsage}: the parent Defer Usage record corresponding to the
deferred fragment enclosing this deferred fragment, not defined if this Defer
Usage record is deferred directly by the initial result.

As an example, collecting the fields of this selection set would return field
details related to two instances of the field `a` and one of field `b`:
Expand Down Expand Up @@ -422,10 +425,8 @@ parentDeferUsage, deferUsage):
- If {selection} is a {Field}:
- Let {responseKey} be the response key of {selection} (the alias if
defined, otherwise the field name).
- Let {fieldDeferUsage} be {deferUsage} if {deferUsage} is defined;
otherwise, let {fieldDeferUsage} be {parentDeferUsage}.
- Let {fieldDetails} be a new Field Details record created from {selection}
and {fieldDeferUsage}.
and {deferUsage}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append {fieldDetails} to the {groupForResponseKey}.
Expand Down Expand Up @@ -453,7 +454,7 @@ parentDeferUsage, deferUsage):
- Let {label} be the value or the variable to {deferDirective}'s {label}
argument.
- Let {fragmentDeferUsage} be a new Defer Usage record created from
{label}.
{label} and {parentDeferUsage}.
- Otherwise:
- Let {fragmentDeferUsage} be {deferUsage}.
- Let {fragmentGroupedFieldSet} be the result of calling
Expand Down Expand Up @@ -481,7 +482,7 @@ parentDeferUsage, deferUsage):
- Let {label} be the value or the variable to {deferDirective}'s {label}
argument.
- Let {fragmentDeferUsage} be a new Defer Usage record created from
{label}.
{label} and {parentDeferUsage}.
- Otherwise:
- Let {fragmentDeferUsage} be {deferUsage}.
- Let {fragmentGroupedFieldSet} be the result of calling
Expand Down Expand Up @@ -580,30 +581,39 @@ GetDeferUsageSet(fieldDetailsList, parentDeferUsages, knownDeferUsages):
- Initialize {newDeferUsages} to the empty set.
- Initialize {deferUsageSet} to the empty set.
- Let {inInitialResult} be {false}.
- Let {inParentResult} be {false}.
- For each {fieldDetails} in {fieldDetailsList}:
- Let {deferUsage} be the corresponding entry on {fieldDetails}.
- If {deferUsage} is not defined:
- Let {inInitialResult} be {true}.
- Continue to the next {fieldDetails} in {fieldDetailsList}.
- Otherwise, if {deferUsage} is in {parentDeferUsages}:
- Let {inParentResult} be {true}.
- Add {deferUsage} to {deferUsageSet}.
- If {deferUsage} is not in {knownDeferUsages}:
- Add {deferUsage} to {newDeferUsages}.
- If {inInitialResult} is true, reset {deferUsageSet} to the empty set;
otherwise, if {inParentResult} is {true}, let {deferUsageSet} be the result of
otherwise, let {deferUsageSet} be the result of
{FilterDeferUsages(deferUsageSet)}.
- Return {deferUsageSet} and {newDeferUsages}.

FilterDeferUsages(deferUsages, parentDeferUsages):
FilterDeferUsages(deferUsages):

- Initialize {filteredDeferUsages} to the empty set.
- For each {deferUsage} in {deferUsages}:
- If {deferUsage} is in {parentDeferUsages}:
- Add {deferUsage} to {filteredDeferUsages}.
- Let {ancestors} be the result of {GetAncestors(deferUsage)}.
- For each {ancestor} of {ancestors}:
- If {ancestor} is in {deferUsages}.
- Continue to the next {deferUsage} in {deferUsages}.
- Add {deferUsage} to {filteredDeferUsages}.
- Return {filteredDeferUsages}.

GetAncestors(deferUsage):

- Initialize {ancestors} to an empty list.
- Let {parentDeferUsage} be the corresponding entry on {deferUsage}.
- If {parentDeferUsage} is not defined, return {ancestors}.
- Append {parentDeferUsage} to {ancestors}.
- Append all the items in {GetAncestors(parentDeferUsage)} to {ancestors}.
- Return {ancestors}.

ShouldInitiateDefer(deferUsageSet, parentDeferUsageSet):

- Let {shouldInitiateDefer} be {false}.
Expand Down Expand Up @@ -649,9 +659,8 @@ ProcessIncrementalDigests(incrementalDigests, originalPendingMap):
- Let {pendingInfo} be the entry in {currentPendingMap} for
{deferredFragment}.
- If {pendingInfo} is not defined:
- Let {id} be a unique identifier for this pending result.
- Let {count} be {0}.
- Let {pendingInfo} be an unordered map containing {id} and {count}.
- Let {pendingInfo} be an unordered map containing {count}.
- Set the entry for {deferredFragment} in {currentPendingMap} to
{pendingInfo}.
- Increment {count} on {pendingInfo}.
Expand All @@ -660,11 +669,26 @@ ProcessIncrementalDigests(incrementalDigests, originalPendingMap):
- If {newPendingResult} is a deferred fragment:
- Let {pendingInfo} be the entry in {currentPendingMap} for
{newPendingResult}.
- If {pendingInfo} is defined:
- Let {id} be the corresponding entry on {pendingInfo}.
- Let {parent} and {parentPendingInfo} be the result of
{GetParentAndPendingInfo(pendingInfo, currentPendingMap)}.
- If {parent} is not defined:
- Let {newPendingInfo} be a new unordered map containing all of the
entries on {pendingInfo}.
- Let {id} be a unique identifier for this pending result.
- Set the corresponding entry on {newPendingInfo} to {id}.
- Set the entry for {newPendingResult} in {currentPendingMap} to
{newPendingInfo}.
- Let {pendingEntry} be an unordered map containing {path}, {label}, and
{id}.
- Append {pendingEntry} to {pending}.
- Otherwise:
- Let {newPendingInfo} be an unordered map containing all of the entries
on {parentPendingInfo}.
- Let {children} be a new list containing all of the entries on {children}
on {pendingInfo}.
- Append {newPendingResult} to {children}.
- Set the corresponding entry on {newPendingInfo} to {children}.
- Set the entry for {parent} in {currentPendingMap} to {newPendingInfo}.
- Otherwise:
- Let {id} be a unique identifier for this pending result.
- Let {pendingEntry} be an unordered map containing {path}, {label}, and
Expand All @@ -675,6 +699,15 @@ ProcessIncrementalDigests(incrementalDigests, originalPendingMap):
{pendingInfo}.
- Return {currentPendingMap}, {pending}, and {futures}.

GetParentAndPendingInfo(pendingInfo, pendingMap):

- Let {ancestors} be the corresponding entry on {pendingInfo}.
- For each {ancestor} of {ancestors}:
- Let {ancestorPendingInfo} be the entry in {pendingMap} for {ancestor}.
- If {ancestorPendingInfo} is defined, return {ancestor} and
{ancestorPendingInfo}.
- Return.

### Yielding Subsequent Results

The procedure for yielding subsequent results is specified by the
Expand All @@ -683,24 +716,28 @@ initiated. Then, any completed future executions are processed to determine the
payload to be yielded. Finally, if any pending results remain, the procedure is
repeated recursively.

YieldSubsequentResults(originalPendingMap, maybeUninitiatedFutures,
initiatedFutures):
YieldSubsequentPayloads(originalPendingMap, newFutures, initiatedFutures,
pendingFutures):

- Initialize {maybeCompletedFutures} to a list containing all items in
{initiatedFutures}.
- For each {maybeUninitiatedFuture} in {maybeUninitiatedFutures}:
- If {maybeUninitiatedFuture} has not been initiated, initiate it.
- Append {maybeUninitiatedFuture} to {maybeCompletedFutures}.
- If {pendingFutures} is not provided, initialize it to an empty list.
- For each {future} in {newFutures}:
- If {future} contributes to a pending result that has been sent:
- If {future} has not been initiated, initiate it.
- Append {future} to {maybeCompletedFutures}.
- Otherwise:
- Append {future} to {pendingFutures}.
- Wait for any future execution contained in {maybeCompletedFutures} to
complete.
- Let {pendingMap}, {payload}, {newFutures}, and {remainingFutures} be the
result of {ProcessCompletedFutures(originalPendingMap,
- Let {pendingMap}, {payload}, {newestFutures}, {remainingFutures}, and
{pendingFutures} be the result of {ProcessCompletedFutures(originalPendingMap,
maybeCompletedFutures)}.
- If {payload} is defined, yield {payload}.
- If {pendingMap} is empty:
- Complete this subsequent result stream and return.
- Yield the results of {YieldSubsequentResults(pendingMap, newFutures,
remainingFutures)}.
- Yield the results of {YieldSubsequentResults(pendingMap, newestFutures,
remainingFutures, pendingFutures)}.

### Processing Completed Futures

Expand All @@ -719,8 +756,9 @@ for any completed futures, as long as the new Incremental Digests do not contain
any new pending results. If they do, first a new payload is yielded, notifying
the client that new pending results have been encountered.

ProcessCompletedFutures(originalPendingMap, maybeCompletedFutures, pending,
incremental, completed, incrementalDigests, remainingFutures):
ProcessCompletedFutures(originalPendingMap, maybeCompletedFutures,
pendingFutures, pending, incremental, completed, incrementalDigests,
remainingFutures):

- Let {pendingMap} be {originalPendingMap}.
- If {pending}, {incremental}, {completed}, {incrementalDigests},
Expand All @@ -734,10 +772,18 @@ incremental, completed, incrementalDigests, remainingFutures):
- Let {pendingMap}, {resultIncremental}, {resultCompleted}, and
{resultIncrementalDigests} be the result of calling
{GetUpdatesForStreamItems(pendingMap, result)}.
- Let {remainingPendingFutures} be {pendingFutures}.
- Otherwise:
- Let {pendingMap}, {resultIncremental}, {resultCompleted}, and
{resultIncrementalDigests} be the result of calling
- Let {pendingMap}, {resultPending}, {resultIncremental}, {resultCompleted},
and {resultIncrementalDigests} be the result of calling
{GetUpdatesForDeferredResult(pendingMap, result)}.
- Append all items in {resultPending} to {pending}.
- Initialize {releasedFutures} and {remainingPendingFutures} to empty lists.
- For each {future} in {pendingFutures}:
- If {future} contributes to a result in {resultPending}:
- Append {future} to {releasedFutures}.
- Otherwise:
- Append {future} to {remainingPendingFutures}.
- Append all items in {resultIncremental} to {incremental}.
- Append all items in {resultCompleted} to {completed}.
- For each {resultIncrementalDigest} in {resultIncrementalDigests}:
Expand All @@ -748,16 +794,18 @@ incremental, completed, incrementalDigests, remainingFutures):
- If {supplementalIncrementalDigests} is empty:
- Let {pendingMap}, {newPending}, and {newFutures} be the result of
{ProcessIncrementalDigests(incrementalDigests, pendingMap)}.
- Append all items in {newPending} to {pending}.
- Append all items in {releasedPending} and {newPending} to {pending}.
- Let {hasNext} be {true} if {pendingMap} is not empty, otherwise {false}.
- Let {payload} be the result of {GetSubsequentPayload(pending, incremental,
completed, hasNext)}.
- Return {pendingMap}, {payload}, {newFutures}, {remainingFutures}.
- Return {pendingMap}, {payload}, {newFutures}, {remainingFutures} and
{remainingPendingFutures}.
- Let {pendingMap}, {newPending}, and {newFutures} be the result of
{ProcessIncrementalDigests(supplementalIncrementalDigests, pendingMap)}.
- Append all items in {newPending} to {pending}.
- Return the result of {ProcessCompletedFutures(pendingMap, newFutures, pending,
incremental, completed, incrementalDigests, remainingFutures)}.
- Append all items in {releasedPending} and {newPending} to {pending}.
- Return the result of {ProcessCompletedFutures(pendingMap, newFutures,
remainingPendingFutures, pending, incremental, completed, incrementalDigests,
remainingFutures)}.

GetUpdatesForStreamItems(pendingMap, streamItems):

Expand Down Expand Up @@ -786,8 +834,8 @@ GetUpdatesForDeferredResult(pendingMap, deferredResult):

- Let {pendingMap} be a new unordered map containing all of the entries in
{originalPendingMap}.
- Initialize {incremental}, {completed}, and {incrementalDigests} to empty
lists.
- Initialize {pending}, {incremental}, {completed}, and {incrementalDigests} to
empty lists.
- Let {deferredFragments}, {data}, and {errors} be the corresponding entries on
{deferredResult}.
- If {data} is {null}:
Expand All @@ -813,12 +861,23 @@ GetUpdatesForDeferredResult(pendingMap, deferredResult):
- Add {deferredResult} to {completedDeferredResults}.
- If {count} on {newPendingInfo} is equal to {0}:
- Remove the entry for {deferredFragment} on {pendingMap}.
- Let {id} be the corresponding entry on {newPendingInfo}.
- Let {id} and {children} be the corresponding entries on {newPendingInfo}.
- Let {completedEntry} be an unordered map containing {id}.
- Append {completedEntry} to {completed}.
- For each {completedDeferredResult} in {completedDeferredResults} on
{newPendingInfo}:
- Add {completedDeferredResult} to {completedDeferredResults}.
- For each {child} in {children}:
- Let {childPendingInfo} be the entry on {pendingMap} for {child}.
- Let {newChildPendingInfo} be a new unordered map containing all entries
on {childPendingInfo}.
- Let {id} be a unique identifier for this pending result.
- Set the corresponding entry on {newChildPendingInfo} to {id}.
- Set the entry on {pendingMap} for {child} to {newChildPendingInfo}.
- Let {path} and {label} be the corresponding entries on {child}.
- Let {pendingEntry} be an unordered map containing {path}, {label}, and
{id}.
- Append {pendingEntry} to {pending}.
- For each {deferredResult} in {completedDeferredResults}:
- Let {id} and {subPath} be the results of {GetIdAndSubPath(deferredResult)}.
- Let {data} and {errors} be the corresponding entries on {deferredResult}.
Expand Down Expand Up @@ -846,7 +905,8 @@ GetUpdatesForDeferredResult(pendingMap, deferredResult):
- Set the corresponding entry on {newPendingInfo} to
{completedDeferredResults}.
- Remove {deferredResult} from {completedDeferredResults}.
- Return {pendingMap}, {incremental}, {completed}, and {incrementalDigests}.
- Return {pendingMap}, {pending}, {incremental}, {completed}, and
{incrementalDigests}.

GetSubsequentPayload(pending, incremental, completed, hasNext):

Expand Down Expand Up @@ -891,7 +951,7 @@ path, deferUsageSet, deferMap):
{newGroupedFieldSetsRequiringDeferral} be the corresponding entries on
{fieldPlan}.
- Let {newDeferMap} and {newPendingResults} be the result of
{GetNewDeferredFragments(newDeferUsages, path, deferMap)}.
{GetNewDeferredFragments(newDeferUsages, deferUsageSet, path, deferMap)}.
- Allowing for parallelization, perform the following steps:
- Let {data} and {nestedIncrementalDigests} be the result of running
{ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue,
Expand All @@ -917,8 +977,14 @@ GetNewDeferredFragments(newDeferUsages, path, deferMap):
- Let {newDeferMap} be a new unordered map of Defer Usage records to Deferred
Fragment records containing all of the entries in {deferMap}.
- For each {deferUsage} in {newDeferUsages}:
- Initialize {ancestors} to an empty list.
- Let {deferUsageAncestors} be the result of {GetAncestors(deferUsage)}.
- For each {deferUsageAncestor} of {deferUsageAncestors}:
- Let {ancestor} be the entry in {deferMap} for {deferUsageAncestors}.
- Append {ancestor} to {ancestors}.
- Let {label} be the corresponding entry on {deferUsage}.
- Let {newDeferredFragment} be an unordered map containing {path} and {label}.
- Let {newDeferredFragment} be an unordered map containing {ancestors}, {path}
and {label}.
- Set the entry for {deferUsage} in {newDeferMap} to {newDeferredFragment}.
- Append {newDeferredFragment} to {newDeferredFragments}.
- Return {newDeferMap} and {newDeferredFragments}.
Expand Down

0 comments on commit 39e798e

Please sign in to comment.