@@ -354,14 +354,50 @@ incremental portion of the Execution Plan.
354354### Yielding Incremental Results
355355
356356The procedure for yielding incremental results is specified by the
357- {YieldIncrementalResults()} algorithm.
357+ {YieldIncrementalResults()} algorithm. The incremental state is stored within a
358+ graph, with root nodes representing the currently pending delivery groups.
359+
360+ For example, given the following operation:
361+
362+ ``` graphql example
363+ {
364+ ... SlowFragment @defer
365+ fastField
366+ }
367+
368+ fragment SlowFragment on Query {
369+ ... SlowestFragment @defer
370+ slowField
371+ }
372+
373+ fragment SlowestFragment on Query {
374+ slowestField
375+ }
376+ ```
377+
378+ A valid GraphQL executor deferring ` SlowFragment ` must include a ` pending ` entry
379+ to that effect within the initial result, while the ` pending ` entry for
380+ ` SlowestFragment ` should be delivered together with ` SlowFragment ` .
381+
382+ Delivery group nodes may have three different types of child nodes:
383+
384+ 1 . Other delivery group nodes, i.e. the node representing ` SlowFragment ` should
385+ have a child node representing ` SlowestFragment ` .
386+ 2 . Pending incremental data nodes, i.e. the node for ` SlowFragment ` should
387+ initially have a node for ` slowField ` .
388+ 3 . Completed incremental data nodes, i.e. when ` slowField ` is completed, the
389+ pending incremental data node for ` slowField ` should be replaced with a node
390+ representing the completed data.
391+
392+ The {YieldIncrementalResults()} algorithm is responsible for updating the graph
393+ as it yields the incremental results.
358394
359395YieldIncrementalResults(data, errors, incrementalDataRecords):
360396
361397- Let {graph} be the result of {GraphFromRecords(incrementalDataRecords)}.
362398- Let {rootNodes} be the result of {GetNewRootNodes(graph)}.
363399- Update {graph} to the subgraph rooted at nodes in {rootNodes}.
364- - Yield the result of {GetInitialResult(data, errors, pendingResults )}.
400+ - Yield the result of {GetInitialResult(data, errors, rootNodes )}.
365401- For each completed child Pending Incremental Data node of a root node in
366402 {graph}:
367403 - Let {incrementalDataRecord} be the Pending Incremental Data for that node;
@@ -373,7 +409,7 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
373409 - Append {GetCompletedEntry(parent, errors)} to {completed}.
374410 - Remove {node} and all of its descendant nodes from {graph}, except for
375411 any descendant Incremental Data Record nodes with other parents.
376- - Yield the result of {GetIncrementalResult (graph, completed)}.
412+ - Yield the result of {GetSubsequentResult (graph, completed)}.
377413 - Continue to the next completed Pending Incremental Data node.
378414 - Replace {node} in {graph} with a new node corresponding to the Completed
379415 Incremental Data for {result}.
@@ -397,11 +433,11 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
397433 - Append {GetCompletedEntry(completedDeferredFragment)} to {completed}.
398434 - Remove {completedDeferredFragment} from {graph}, promoting its child
399435 Deferred Fragment nodes to root nodes.
400- - Let {newRootNodes} be the result of {GetNewRootNodes(graph)}.
436+ - Let {newRootNodes} be the result of {GetNewRootNodes(graph, rootNodes )}.
401437 - Add all nodes in {newRootNodes} to {rootNodes}.
402438 - Update {graph} to the subgraph rooted at nodes in {rootNodes}.
403439 - Let {pending} be the result of {GetPendingEntry(newRootNodes)}.
404- - Yield the result of {GetIncrementalResult (graph, incremental, completed,
440+ - Yield the result of {GetSubsequentResult (graph, incremental, completed,
405441 pending)}.
406442- Complete this incremental result stream.
407443
@@ -418,35 +454,55 @@ GraphFromRecords(incrementalDataRecords, graph):
418454 to {newGraph}, or the {parent} is not defined.
419455- Return {newGraph}.
420456
421- GetNewRootNodes(graph):
457+ The {GetNewRootNodes()} algorithm is responsible for determining the new root
458+ nodes that must be reported as pending. Any delivery groups without any
459+ execution groups should not be reported as pending, and any child delivery
460+ groups for these "empty" delivery groups should be reported as pending in their
461+ stead.
462+
463+ GetNewRootNodes(graph, oldRootNodes):
422464
423- - Initialize {newPendingResults } to the empty set.
465+ - Initialize {newRootNodes } to the empty set.
424466- Initialize {rootNodes} to the set of root nodes in {graph}.
425467- For each {rootNode} of {rootNodes}:
426468 - If {rootNode} has no children Pending Incremental Data nodes:
427469 - Let {children} be the set of child Deferred Fragment nodes of {rootNode}.
428470 - Add each of the nodes in {children} to {rootNodes}.
429471 - Continue to the next {rootNode} of {rootNodes}.
430- - Add {rootNode} to {newPendingResults}.
431- - Return {newPendingResults}.
472+ - If {oldRootNodes} does not contain {rootNode}, add {rootNode} to
473+ {newRootNodes}.
474+ - Return {newRootNodes}.
475+
476+ Formatting of the initial result is defined by the {GetInitialResult()}
477+ algorithm. It will only be called when there is an incremental result stream,
478+ and so ` hasNext ` will always be set to {true}.
432479
433480GetInitialResult(data, errors, pendingResults):
434481
435482- Let {pending} be the result of {GetPendingEntry(pendingResults)}.
436483- Let {hasNext} be {true}.
437484- Return an unordered map containing {data}, {errors}, {pending}, and {hasNext}.
438485
439- GetPendingEntry(pendingResults):
486+ Formatting the ` pending ` of initial and subsequentResults is defined by the
487+ {GetPendingEntry()} algorithm. Given a set of new root nodes added to the graph,
488+ {GetPendingEntry()} returns a list of formatted ` pending ` entries.
489+
490+ GetPendingEntry(newRootNodes):
440491
441492- Initialize {pending} to an empty list.
442- - For each {pendingResult } of {pendingResult }:
443- - Let {id} be a unique identifier for {pendingResult }.
444- - Let {path} and {label} be the corresponding entries on {pendingResult }.
493+ - For each {newRootNode } of {newRootNodes }:
494+ - Let {id} be a unique identifier for {newRootNode }.
495+ - Let {path} and {label} be the corresponding entries on {newRootNode }.
445496 - Let {pendingEntry} be an unordered map containing {id}, {path}, and {label}.
446497 - Append {pendingEntry} to {pending}.
447498- Return {pending}.
448499
449- GetIncrementalResult(graph, completed, incremental, pending):
500+ Formatting of subsequent incremental results is defined by the
501+ {GetSubsequentResult()} algorithm. Given the current graph, and any ` completed ` ,
502+ ` incremental ` , and ` pending ` entries, it produces an appropriately formatted
503+ subsequent incremental response.
504+
505+ GetSubsequentResult(graph, completed, incremental, pending):
450506
451507- Let {hasNext} be {false} if {graph} is empty, otherwise, {true}.
452508- Let {incrementalResult} be an unordered map containing {hasNext}.
@@ -458,6 +514,10 @@ GetIncrementalResult(graph, completed, incremental, pending):
458514 - Set the corresponding entry on {incrementalResult} to {pending}.
459515- Return {incrementalResult}.
460516
517+ Formatting of subsequent incremental results is defined by the
518+ {GetSubsequentResult()} algorithm. Execution groups are tagged with the ` id ` and
519+ ` subPath ` combination optimized to produce the shortest ` subPath ` .
520+
461521GetIncrementalEntry(incrementalDataRecord, graph):
462522
463523- Let {deferredFragments} be the Deferred Fragments incrementally completed by
@@ -473,6 +533,9 @@ GetIncrementalEntry(incrementalDataRecord, graph):
473533- Let {id} be the unique identifier for {bestDeferredFragment}.
474534- Return an unordered map containing {id}, {subPath}, {data}, and {errors}.
475535
536+ Formatting of completed incremental results is defined by the
537+ {GetCompletedEntry()} algorithm.
538+
476539GetCompletedEntry(pendingResult, errors):
477540
478541- Let {id} be the unique identifier for {pendingResult}.
0 commit comments