@@ -351,14 +351,50 @@ incremental portion of the Execution Plan.
351351### Yielding Incremental Results
352352
353353The procedure for yielding incremental results is specified by the
354- {YieldIncrementalResults()} algorithm.
354+ {YieldIncrementalResults()} algorithm. The incremental state is stored within a
355+ graph, with root nodes representing the currently pending delivery groups.
356+
357+ For example, given the following operation:
358+
359+ ``` graphql example
360+ {
361+ ... SlowFragment @defer
362+ fastField
363+ }
364+
365+ fragment SlowFragment on Query {
366+ ... SlowestFragment @defer
367+ slowField
368+ }
369+
370+ fragment SlowestFragment on Query {
371+ slowestField
372+ }
373+ ```
374+
375+ A valid GraphQL executor deferring ` SlowFragment ` must include a ` pending ` entry
376+ to that effect within the initial result, while the ` pending ` entry for
377+ ` SlowestFragment ` should be delivered together with ` SlowFragment ` .
378+
379+ Delivery group nodes may have three different types of child nodes:
380+
381+ 1 . Other delivery group nodes, i.e. the node representing ` SlowFragment ` should
382+ have a child node representing ` SlowestFragment ` .
383+ 2 . Pending incremental data nodes, i.e. the node for ` SlowFragment ` should
384+ initially have a node for ` slowField ` .
385+ 3 . Completed incremental data nodes, i.e. when ` slowField ` is completed, the
386+ pending incremental data node for ` slowField ` should be replaced with a node
387+ representing the completed data.
388+
389+ The {YieldIncrementalResults()} algorithm is responsible for updating the graph
390+ as it yields the incremental results.
355391
356392YieldIncrementalResults(data, errors, incrementalDataRecords):
357393
358394- Let {graph} be the result of {GraphFromRecords(incrementalDataRecords)}.
359395- Let {rootNodes} be the result of {GetNewRootNodes(graph)}.
360396- Update {graph} to the subgraph rooted at nodes in {rootNodes}.
361- - Yield the result of {GetInitialResult(data, errors, pendingResults )}.
397+ - Yield the result of {GetInitialResult(data, errors, rootNodes )}.
362398- For each completed child Pending Incremental Data node of a root node in
363399 {graph}:
364400 - Let {incrementalDataRecord} be the Pending Incremental Data for that node;
@@ -370,7 +406,7 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
370406 - Append {GetCompletedEntry(parent, errors)} to {completed}.
371407 - Remove {node} and all of its descendant nodes from {graph}, except for
372408 any descendant Incremental Data Record nodes with other parents.
373- - Yield the result of {GetIncrementalResult (graph, completed)}.
409+ - Yield the result of {GetSubsequentResult (graph, completed)}.
374410 - Continue to the next completed Pending Incremental Data node.
375411 - Replace {node} in {graph} with a new node corresponding to the Completed
376412 Incremental Data for {result}.
@@ -394,11 +430,11 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
394430 - Append {GetCompletedEntry(completedDeferredFragment)} to {completed}.
395431 - Remove {completedDeferredFragment} from {graph}, promoting its child
396432 Deferred Fragment nodes to root nodes.
397- - Let {newRootNodes} be the result of {GetNewRootNodes(graph)}.
433+ - Let {newRootNodes} be the result of {GetNewRootNodes(graph, rootNodes )}.
398434 - Add all nodes in {newRootNodes} to {rootNodes}.
399435 - Update {graph} to the subgraph rooted at nodes in {rootNodes}.
400436 - Let {pending} be the result of {GetPendingEntry(newRootNodes)}.
401- - Yield the result of {GetIncrementalResult (graph, incremental, completed,
437+ - Yield the result of {GetSubsequentResult (graph, incremental, completed,
402438 pending)}.
403439- Complete this incremental result stream.
404440
@@ -415,35 +451,55 @@ GraphFromRecords(incrementalDataRecords, graph):
415451 to {newGraph}, or the {parent} is not defined.
416452- Return {newGraph}.
417453
418- GetNewRootNodes(graph):
454+ The {GetNewRootNodes()} algorithm is responsible for determining the new root
455+ nodes that must be reported as pending. Any delivery groups without any
456+ execution groups should not be reported as pending, and any child delivery
457+ groups for these "empty" delivery groups should be reported as pending in their
458+ stead.
459+
460+ GetNewRootNodes(graph, oldRootNodes):
419461
420- - Initialize {newPendingResults } to the empty set.
462+ - Initialize {newRootNodes } to the empty set.
421463- Initialize {rootNodes} to the set of root nodes in {graph}.
422464- For each {rootNode} of {rootNodes}:
423465 - If {rootNode} has no children Pending Incremental Data nodes:
424466 - Let {children} be the set of child Deferred Fragment nodes of {rootNode}.
425467 - Add each of the nodes in {children} to {rootNodes}.
426468 - Continue to the next {rootNode} of {rootNodes}.
427- - Add {rootNode} to {newPendingResults}.
428- - Return {newPendingResults}.
469+ - If {oldRootNodes} does not contain {rootNode}, add {rootNode} to
470+ {newRootNodes}.
471+ - Return {newRootNodes}.
472+
473+ Formatting of the initial result is defined by the {GetInitialResult()}
474+ algorithm. It will only be called when there is an incremental result stream,
475+ and so ` hasNext ` will always be set to {true}.
429476
430477GetInitialResult(data, errors, pendingResults):
431478
432479- Let {pending} be the result of {GetPendingEntry(pendingResults)}.
433480- Let {hasNext} be {true}.
434481- Return an unordered map containing {data}, {errors}, {pending}, and {hasNext}.
435482
436- GetPendingEntry(pendingResults):
483+ Formatting the ` pending ` of initial and subsequentResults is defined by the
484+ {GetPendingEntry()} algorithm. Given a set of new root nodes added to the graph,
485+ {GetPendingEntry()} returns a list of formatted ` pending ` entries.
486+
487+ GetPendingEntry(newRootNodes):
437488
438489- Initialize {pending} to an empty list.
439- - For each {pendingResult } of {pendingResult }:
440- - Let {id} be a unique identifier for {pendingResult }.
441- - Let {path} and {label} be the corresponding entries on {pendingResult }.
490+ - For each {newRootNode } of {newRootNodes }:
491+ - Let {id} be a unique identifier for {newRootNode }.
492+ - Let {path} and {label} be the corresponding entries on {newRootNode }.
442493 - Let {pendingEntry} be an unordered map containing {id}, {path}, and {label}.
443494 - Append {pendingEntry} to {pending}.
444495- Return {pending}.
445496
446- GetIncrementalResult(graph, completed, incremental, pending):
497+ Formatting of subsequent incremental results is defined by the
498+ {GetSubsequentResult()} algorithm. Given the current graph, and any ` completed ` ,
499+ ` incremental ` , and ` pending ` entries, it produces an appropriately formatted
500+ subsequent incremental response.
501+
502+ GetSubsequentResult(graph, completed, incremental, pending):
447503
448504- Let {hasNext} be {false} if {graph} is empty, otherwise, {true}.
449505- Let {incrementalResult} be an unordered map containing {hasNext}.
@@ -455,6 +511,10 @@ GetIncrementalResult(graph, completed, incremental, pending):
455511 - Set the corresponding entry on {incrementalResult} to {pending}.
456512- Return {incrementalResult}.
457513
514+ Formatting of subsequent incremental results is defined by the
515+ {GetSubsequentResult()} algorithm. Execution groups are tagged with the ` id ` and
516+ ` subPath ` combination optimized to produce the shortest ` subPath ` .
517+
458518GetIncrementalEntry(incrementalDataRecord, graph):
459519
460520- Let {deferredFragments} be the Deferred Fragments incrementally completed by
@@ -470,6 +530,9 @@ GetIncrementalEntry(incrementalDataRecord, graph):
470530- Let {id} be the unique identifier for {bestDeferredFragment}.
471531- Return an unordered map containing {id}, {subPath}, {data}, and {errors}.
472532
533+ Formatting of completed incremental results is defined by the
534+ {GetCompletedEntry()} algorithm.
535+
473536GetCompletedEntry(pendingResult, errors):
474537
475538- Let {id} be the unique identifier for {pendingResult}.
0 commit comments