Skip to content

Commit b40c76b

Browse files
committed
lazily create Deferred Fragment Records
corresponds to graphql/graphql-js#4153
1 parent 5e9ea96 commit b40c76b

File tree

1 file changed

+69
-78
lines changed

1 file changed

+69
-78
lines changed

spec/Section 6 -- Execution.md

+69-78
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,12 @@ ExecuteRootSelectionSet(variableValues, initialValue, objectType, selectionSet,
329329
serial):
330330

331331
- If {serial} is not provided, initialize it to {false}.
332-
- Let {groupedFieldSet} and {newDeferUsages} be the result of
333-
{CollectFields(objectType, selectionSet, variableValues)}.
332+
- Let {groupedFieldSet} be the result of {CollectFields(objectType,
333+
selectionSet, variableValues)}.
334334
- Let {executionPlan} be the result of {BuildExecutionPlan(groupedFieldSet)}.
335335
- Let {data} and {incrementalDataRecords} be the result of
336-
{ExecuteExecutionPlan(newDeferUsages, executionPlan, objectType, initialValue,
337-
variableValues, serial)}.
336+
{ExecuteExecutionPlan(executionPlan, objectType, initialValue, variableValues,
337+
serial)}.
338338
- Let {errors} be the list of all _field error_ raised while completing {data}.
339339
- If {incrementalDataRecords} is empty, return an unordered map containing
340340
{data} and {errors}.
@@ -405,11 +405,28 @@ GraphFromRecords(incrementalDataRecords, graph):
405405
- Let {newGraph} be a new directed acyclic graph containing all of the nodes and
406406
edges in {graph}.
407407
- For each {incrementalDataRecord} of {incrementalDataRecords}:
408+
- Let {deferUsageSet} be the Defer Usages incrementally completed by
409+
{incrementalDataRecord} at {path}.
410+
- For each {deferUsage} of {deferUsageSet}:
411+
- If {newGraph} does not contain a Deferred Fragment node representing the
412+
completion of {deferUsage} at {path}, reset {newGraph} to the result of
413+
{GraphWithDeferredFragmentRecord(deferUsage, path, newGraph)}.
408414
- Add {incrementalDataRecord} to {newGraph} as a new Pending Data node
409-
directed from the {pendingResults} that it completes, adding each of
410-
{pendingResults} to {newGraph} as a new node directed from its {parent},
411-
recursively adding each {parent} until {incrementalDataRecord} is connected
412-
to {newGraph}, or the {parent} is not defined.
415+
directed from the {deferredFragments} that it completes.
416+
- Return {newGraph}.
417+
418+
GraphWithDeferredFragmentRecord(deferUsage, path, graph):
419+
420+
- Let {parentDeferUsage} and {label} be the corresponding entries on
421+
{deferUsage}.
422+
- If {parentDeferUsage} is defined and {graph} does not contain a Deferred
423+
Fragment node representing the completion of {parentDeferUsage} at {path}, let
424+
{newGraph} be the result of {GraphWithDeferredFragmentRecord(parentDeferUsage,
425+
path, newGraph)}; otherwise, let {newGraph} be a new directed acyclic graph
426+
containing all of the nodes and edges in {graph}.
427+
- Let {deferredFragment} be a new unordered map containing {path} and {label}.
428+
- Add {deferredFragment} to {newGraph} as a new Deferred Fragment node directed
429+
from {parent}.
413430
- Return {newGraph}.
414431

415432
GetNonEmptyNewPending(graph):
@@ -454,8 +471,8 @@ GetIncrementalResult(graph, incremental, completed, pending):
454471

455472
GetIncrementalEntry(incrementalDataRecord, graph):
456473

457-
- Let {deferredFragments} be the Deferred Fragments incrementally completed by
458-
{incrementalDataRecord} at {path}.
474+
- Let {deferredFragments} be the Deferred Fragment nodes within {graph}
475+
incrementally completed by {incrementalDataRecord} at {path}.
459476
- Let {result} be the result of {incrementalDataRecord}.
460477
- Let {data} and {errors} be the corresponding entries on {result}.
461478
- Let {releasedDeferredFragments} be the members of {deferredFragments} that are
@@ -496,67 +513,48 @@ To execute a execution plan, the object value being evaluated and the object
496513
type need to be known, as well as whether the non-deferred grouped field set
497514
must be executed serially, or may be executed in parallel.
498515

499-
ExecuteExecutionPlan(newDeferUsages, executionPlan, objectType, objectValue,
500-
variableValues, serial, path, deferUsageSet, deferMap):
516+
ExecuteExecutionPlan(executionPlan, objectType, objectValue, variableValues,
517+
serial, path, deferUsageSet):
501518

502519
- If {path} is not provided, initialize it to an empty list.
503-
- Let {newDeferMap} be the result of {GetNewDeferMap(newDeferUsages, path,
504-
deferMap)}.
505520
- Let {groupedFieldSet} and {newGroupedFieldSets} be the corresponding entries
506521
on {executionPlan}.
507522
- Allowing for parallelization, perform the following steps:
508523
- Let {data} and {nestedIncrementalDataRecords} be the result of running
509524
{ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue,
510-
variableValues, path, deferUsageSet, newDeferMap)} _serially_ if {serial} is
511-
{true}, _normally_ (allowing parallelization) otherwise.
525+
variableValues, path, deferUsageSet)} _serially_ if {serial} is {true},
526+
_normally_ (allowing parallelization) otherwise.
512527
- Let {incrementalDataRecords} be the result of
513528
{CollectExecutionGroups(objectType, objectValue, variableValues,
514529
newGroupedFieldSets, path, newDeferMap)}.
515530
- Append all items in {nestedIncrementalDataRecords} to
516531
{incrementalDataRecords}.
517532
- Return {data} and {incrementalDataRecords}.
518533

519-
GetNewDeferMap(newDeferUsages, path, deferMap):
520-
521-
- If {newDeferUsages} is empty, return {deferMap}:
522-
- Let {newDeferMap} be a new unordered map containing all entries in {deferMap}.
523-
- For each {deferUsage} in {newDeferUsages}:
524-
- Let {parentDeferUsage} and {label} be the corresponding entries on
525-
{deferUsage}.
526-
- Let {parent} be the entry in {deferMap} for {parentDeferUsage}.
527-
- Let {newDeferredFragment} be an unordered map containing {parent}, {path}
528-
and {label}.
529-
- Set the entry for {deferUsage} in {newDeferMap} to {newDeferredFragment}.
530-
- Return {newDeferMap}.
531-
532534
CollectExecutionGroups(objectType, objectValue, variableValues,
533-
newGroupedFieldSets, path, deferMap):
535+
newGroupedFieldSets, path):
534536

535537
- Initialize {incrementalDataRecords} to an empty list.
536538
- For each {deferUsageSet} and {groupedFieldSet} in {newGroupedFieldSets}:
537-
- Let {deferredFragments} be an empty list.
538-
- For each {deferUsage} in {deferUsageSet}:
539-
- Let {deferredFragment} be the entry for {deferUsage} in {deferMap}.
540-
- Append {deferredFragment} to {deferredFragments}.
541539
- Let {incrementalDataRecord} represent the future execution of
542540
{ExecuteExecutionGroup(groupedFieldSet, objectType, objectValue,
543-
variableValues, deferredFragments, path, deferUsageSet, deferMap)},
544-
incrementally completing {deferredFragments} at {path}.
541+
variableValues, path, deferUsageSet)}, incrementally completing
542+
{deferUsageSet} at {path}.
545543
- Append {incrementalDataRecord} to {incrementalDataRecords}.
546544
- Schedule initiation of execution of {incrementalDataRecord} following any
547545
implementation specific deferral.
548546
- Return {incrementalDataRecords}.
549547

550548
Note: {incrementalDataRecord} can be safely initiated without blocking
551-
higher-priority data once any of {deferredFragments} are released as pending.
549+
higher-priority data once any of {deferUsageSet} at {path} are released as
550+
pending.
552551

553552
ExecuteExecutionGroup(groupedFieldSet, objectType, objectValue, variableValues,
554-
path, deferUsageSet, deferMap):
553+
path, deferUsageSet):
555554

556555
- Let {data} and {incrementalDataRecords} be the result of running
557556
{ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue,
558-
variableValues, path, deferUsageSet, deferMap)} _normally_ (allowing
559-
parallelization).
557+
variableValues, path, deferUsageSet)} _normally_ (allowing parallelization).
560558
- Let {errors} be the list of all _field error_ raised while completing {data}.
561559
- Return an unordered map containing {data}, {errors}, and
562560
{incrementalDataRecords}.
@@ -571,7 +569,7 @@ Each represented field in the grouped field set produces an entry into a
571569
response map.
572570

573571
ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue, variableValues,
574-
path, deferUsageSet, deferMap):
572+
path, deferUsageSet):
575573

576574
- Initialize {resultMap} to an empty ordered map.
577575
- Initialize {incrementalDataRecords} to an empty list.
@@ -583,7 +581,7 @@ path, deferUsageSet, deferMap):
583581
- If {fieldType} is defined:
584582
- Let {responseValue} and {fieldIncrementalDataRecords} be the result of
585583
{ExecuteField(objectType, objectValue, fieldType, fields, variableValues,
586-
path, deferUsageSet, deferMap)}.
584+
path, deferUsageSet)}.
587585
- Set {responseValue} as the value for {responseKey} in {resultMap}.
588586
- Append all items in {fieldIncrementalDataRecords} to
589587
{incrementalDataRecords}.
@@ -747,6 +745,8 @@ Defer Usages contain the following information:
747745
any, otherwise {undefined}.
748746
- {parentDeferUsage}: a Defer Usage corresponding to the `@defer` directive
749747
enclosing this `@defer` directive, if any, otherwise {undefined}.
748+
- {depth}: the depth within the overall result corresponding to the deferred
749+
fields.
750750

751751
The {parentDeferUsage} entry is used to build distinct Execution Groups as
752752
discussed within the Execution Plan Generation section below.
@@ -761,18 +761,12 @@ A Grouped Field Set is an ordered map of keys to lists of Field Details. The
761761
keys are the same as that of the response, the alias for the field, if defined,
762762
otherwise the field name.
763763

764-
The {CollectFields()} algorithm returns:
765-
766-
- {groupedFieldSet}: the Grouped Field Set for the fields in the selection set.
767-
- {newDeferUsages}: a list of new Defer Usages encountered during this field
768-
collection.
769-
770-
CollectFields(objectType, selectionSet, variableValues, deferUsage,
764+
CollectFields(objectType, selectionSet, variableValues, deferUsage, depth,
771765
visitedFragments):
772766

767+
- If {depth} is not provided, initialize it to {0}.
773768
- If {visitedFragments} is not provided, initialize it to the empty set.
774769
- Initialize {groupedFields} to an empty ordered map of lists.
775-
- Initialize {newDeferUsages} to an empty list.
776770
- For each {selection} in {selectionSet}:
777771
- If {selection} provides the directive `@skip`, let {skipDirective} be that
778772
directive.
@@ -816,19 +810,18 @@ visitedFragments):
816810
- If {deferDirective} is defined:
817811
- Let {label} be the corresponding entry on {deferDirective}.
818812
- Let {parentDeferUsage} be {deferUsage}.
819-
- Let {fragmentDeferUsage} be an unordered map containing {label} and
820-
{parentDeferUsage}.
813+
- Let {fragmentDeferUsage} be an unordered map containing {label},
814+
{parentDeferUsage}, and {depth}.
821815
- Otherwise, let {fragmentDeferUsage} be {deferUsage}.
822-
- Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
823-
of calling {CollectFields(objectType, fragmentSelectionSet,
824-
variableValues, fragmentDeferUsage, visitedFragments)}.
816+
- Let {fragmentGroupedFieldSet} be the result of calling
817+
{CollectFields(objectType, fragmentSelectionSet, variableValues,
818+
fragmentDeferUsage, depth, visitedFragments)}.
825819
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
826820
- Let {responseKey} be the response key shared by all fields in
827821
{fragmentGroup}.
828822
- Let {groupForResponseKey} be the list in {groupedFields} for
829823
{responseKey}; if no such list exists, create it as an empty list.
830824
- Append all items in {fragmentGroup} to {groupForResponseKey}.
831-
- Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
832825
- If {selection} is an {InlineFragment}:
833826
- Let {fragmentType} be the type condition on {selection}.
834827
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
@@ -844,20 +837,19 @@ visitedFragments):
844837
- If {deferDirective} is defined:
845838
- Let {label} be the corresponding entry on {deferDirective}.
846839
- Let {parentDeferUsage} be {deferUsage}.
847-
- Let {fragmentDeferUsage} be an unordered map containing {label} and
848-
{parentDeferUsage}.
840+
- Let {fragmentDeferUsage} be an unordered map containing {label},
841+
{parentDeferUsage}, and {depth}.
849842
- Otherwise, let {fragmentDeferUsage} be {deferUsage}.
850-
- Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
851-
of calling {CollectFields(objectType, fragmentSelectionSet,
852-
variableValues, fragmentDeferUsage, visitedFragments)}.
843+
- Let {fragmentGroupedFieldSet} be the result of calling
844+
{CollectFields(objectType, fragmentSelectionSet, variableValues,
845+
fragmentDeferUsage, depth, visitedFragments)}.
853846
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
854847
- Let {responseKey} be the response key shared by all fields in
855848
{fragmentGroup}.
856849
- Let {groupForResponseKey} be the list in {groupedFields} for
857850
{responseKey}; if no such list exists, create it as an empty list.
858851
- Append all items in {fragmentGroup} to {groupForResponseKey}.
859-
- Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
860-
- Return {groupedFields} and {newDeferUsages}.
852+
- Return {groupedFields}.
861853

862854
DoesFragmentTypeApply(objectType, fragmentType):
863855

@@ -928,7 +920,7 @@ finally completes that value either by recursively executing another selection
928920
set or coercing a scalar value.
929921

930922
ExecuteField(objectType, objectValue, fieldType, fieldDetailsList,
931-
variableValues, path, deferUsageSet, deferMap):
923+
variableValues, path, deferUsageSet):
932924

933925
- Let {fieldDetails} be the first entry in {fieldDetailsList}.
934926
- Let {field} be the corresponding entry on {fieldDetails}.
@@ -939,7 +931,7 @@ variableValues, path, deferUsageSet, deferMap):
939931
- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
940932
argumentValues)}.
941933
- Return the result of {CompleteValue(fieldType, fields, resolvedValue,
942-
variableValues, path, deferUsageSet, deferMap)}.
934+
variableValues, path, deferUsageSet)}.
943935

944936
### Coercing Field Arguments
945937

@@ -1027,7 +1019,7 @@ the expected return type. If the return type is another Object type, then the
10271019
field execution process continues recursively.
10281020

10291021
CompleteValue(fieldType, fieldDetailsList, result, variableValues, path,
1030-
deferUsageSet, deferMap):
1022+
deferUsageSet):
10311023

10321024
- If the {fieldType} is a Non-Null type:
10331025
- Let {innerType} be the inner type of {fieldType}.
@@ -1041,23 +1033,24 @@ deferUsageSet, deferMap):
10411033
- If {result} is not a collection of values, raise a _field error_.
10421034
- Let {innerType} be the inner type of {fieldType}.
10431035
- Return the result of {CompleteListValue(innerType, fieldDetailsList, result,
1044-
variableValues, path, deferUsageSet, deferMap)}.
1036+
variableValues, path, deferUsageSet)}.
10451037
- If {fieldType} is a Scalar or Enum type:
10461038
- Return the result of {CoerceResult(fieldType, result)}.
10471039
- If {fieldType} is an Object, Interface, or Union type:
10481040
- If {fieldType} is an Object type.
10491041
- Let {objectType} be {fieldType}.
10501042
- Otherwise if {fieldType} is an Interface or Union type.
10511043
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
1052-
- Let {groupedFieldSet} and {newDeferUsages} be the result of calling
1053-
{CollectSubfields(objectType, fieldDetailsList, variableValues)}.
1044+
- Let {depth} be the length of {path}.
1045+
- Let {groupedFieldSet} be the result of calling {CollectSubfields(objectType,
1046+
fieldDetailsList, variableValues, depth)}.
10541047
- Let {executionPlan} be the result of {BuildExecutionPlan(groupedFieldSet,
10551048
deferUsageSet)}.
1056-
- Return the result of {ExecuteExecutionPlan(newDeferUsages, executionPlan,
1057-
objectType, result, variableValues, false, path, deferUsageSet, deferMap)}.
1049+
- Return the result of {ExecuteExecutionPlan(executionPlan, objectType,
1050+
result, variableValues, false, path, deferUsageSet)}.
10581051

10591052
CompleteListValue(innerType, fieldDetailsList, result, variableValues, path,
1060-
deferUsageSet, deferMap):
1053+
deferUsageSet):
10611054

10621055
- Initialize {items} and {incrementalDataRecords} to empty lists.
10631056
- Let {index} be {0}.
@@ -1136,22 +1129,20 @@ sub-selections.
11361129
After resolving the value for `me`, the selection sets are merged together so
11371130
`firstName` and `lastName` can be resolved for one value.
11381131

1139-
CollectSubfields(objectType, fieldDetailsList, variableValues):
1132+
CollectSubfields(objectType, fieldDetailsList, variableValues, depth):
11401133

11411134
- Initialize {groupedFieldSet} to an empty ordered map of lists.
1142-
- Initialize {newDeferUsages} to an empty list.
11431135
- For each {fieldDetails} in {fieldDetailsList}:
11441136
- Let {field} and {deferUsage} be the corresponding entries on {fieldDetails}.
11451137
- Let {fieldSelectionSet} be the selection set of {field}.
11461138
- If {fieldSelectionSet} is null or empty, continue to the next field.
1147-
- Let {subGroupedFieldSet} and {subNewDeferUsages} be the result of
1148-
{CollectFields(objectType, fieldSelectionSet, variableValues, deferUsage)}.
1139+
- Let {subGroupedFieldSet} be the result of {CollectFields(objectType,
1140+
fieldSelectionSet, variableValues, deferUsage, depth)}.
11491141
- For each {subGroupedFieldSet} as {responseKey} and {subfields}:
11501142
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
11511143
{responseKey}; if no such list exists, create it as an empty list.
11521144
- Append all fields in {subfields} to {groupForResponseKey}.
1153-
- Append all defer usages in {subNewDeferUsages} to {newDeferUsages}.
1154-
- Return {groupedFieldSet} and {newDeferUsages}.
1145+
- Return {groupedFieldSet}.
11551146

11561147
### Handling Field Errors
11571148

0 commit comments

Comments
 (0)