@@ -354,14 +354,50 @@ incremental portion of the Execution Plan.
354
354
### Yielding Incremental Results
355
355
356
356
The 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.
358
394
359
395
YieldIncrementalResults(data, errors, incrementalDataRecords):
360
396
361
397
- Let {graph} be the result of {GraphFromRecords(incrementalDataRecords)}.
362
398
- Let {rootNodes} be the result of {GetNewRootNodes(graph)}.
363
399
- 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 )}.
365
401
- For each completed child Pending Incremental Data node of a root node in
366
402
{graph}:
367
403
- Let {incrementalDataRecord} be the Pending Incremental Data for that node;
@@ -373,7 +409,7 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
373
409
- Append {GetCompletedEntry(parent, errors)} to {completed}.
374
410
- Remove {node} and all of its descendant nodes from {graph}, except for
375
411
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)}.
377
413
- Continue to the next completed Pending Incremental Data node.
378
414
- Replace {node} in {graph} with a new node corresponding to the Completed
379
415
Incremental Data for {result}.
@@ -397,11 +433,11 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
397
433
- Append {GetCompletedEntry(completedDeferredFragment)} to {completed}.
398
434
- Remove {completedDeferredFragment} from {graph}, promoting its child
399
435
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 )}.
401
437
- Add all nodes in {newRootNodes} to {rootNodes}.
402
438
- Update {graph} to the subgraph rooted at nodes in {rootNodes}.
403
439
- 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,
405
441
pending)}.
406
442
- Complete this incremental result stream.
407
443
@@ -418,35 +454,55 @@ GraphFromRecords(incrementalDataRecords, graph):
418
454
to {newGraph}, or the {parent} is not defined.
419
455
- Return {newGraph}.
420
456
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):
422
464
423
- - Initialize {newPendingResults } to the empty set.
465
+ - Initialize {newRootNodes } to the empty set.
424
466
- Initialize {rootNodes} to the set of root nodes in {graph}.
425
467
- For each {rootNode} of {rootNodes}:
426
468
- If {rootNode} has no children Pending Incremental Data nodes:
427
469
- Let {children} be the set of child Deferred Fragment nodes of {rootNode}.
428
470
- Add each of the nodes in {children} to {rootNodes}.
429
471
- 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}.
432
479
433
480
GetInitialResult(data, errors, pendingResults):
434
481
435
482
- Let {pending} be the result of {GetPendingEntry(pendingResults)}.
436
483
- Let {hasNext} be {true}.
437
484
- Return an unordered map containing {data}, {errors}, {pending}, and {hasNext}.
438
485
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):
440
491
441
492
- 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 }.
445
496
- Let {pendingEntry} be an unordered map containing {id}, {path}, and {label}.
446
497
- Append {pendingEntry} to {pending}.
447
498
- Return {pending}.
448
499
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):
450
506
451
507
- Let {hasNext} be {false} if {graph} is empty, otherwise, {true}.
452
508
- Let {incrementalResult} be an unordered map containing {hasNext}.
@@ -458,6 +514,10 @@ GetIncrementalResult(graph, completed, incremental, pending):
458
514
- Set the corresponding entry on {incrementalResult} to {pending}.
459
515
- Return {incrementalResult}.
460
516
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
+
461
521
GetIncrementalEntry(incrementalDataRecord, graph):
462
522
463
523
- Let {deferredFragments} be the Deferred Fragments incrementally completed by
@@ -473,6 +533,9 @@ GetIncrementalEntry(incrementalDataRecord, graph):
473
533
- Let {id} be the unique identifier for {bestDeferredFragment}.
474
534
- Return an unordered map containing {id}, {subPath}, {data}, and {errors}.
475
535
536
+ Formatting of completed incremental results is defined by the
537
+ {GetCompletedEntry()} algorithm.
538
+
476
539
GetCompletedEntry(pendingResult, errors):
477
540
478
541
- Let {id} be the unique identifier for {pendingResult}.
0 commit comments