@@ -351,14 +351,50 @@ incremental portion of the Execution Plan.
351
351
### Yielding Incremental Results
352
352
353
353
The 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.
355
391
356
392
YieldIncrementalResults(data, errors, incrementalDataRecords):
357
393
358
394
- Let {graph} be the result of {GraphFromRecords(incrementalDataRecords)}.
359
395
- Let {rootNodes} be the result of {GetNewRootNodes(graph)}.
360
396
- 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 )}.
362
398
- For each completed child Pending Incremental Data node of a root node in
363
399
{graph}:
364
400
- Let {incrementalDataRecord} be the Pending Incremental Data for that node;
@@ -370,7 +406,7 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
370
406
- Append {GetCompletedEntry(parent, errors)} to {completed}.
371
407
- Remove {node} and all of its descendant nodes from {graph}, except for
372
408
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)}.
374
410
- Continue to the next completed Pending Incremental Data node.
375
411
- Replace {node} in {graph} with a new node corresponding to the Completed
376
412
Incremental Data for {result}.
@@ -394,11 +430,11 @@ YieldIncrementalResults(data, errors, incrementalDataRecords):
394
430
- Append {GetCompletedEntry(completedDeferredFragment)} to {completed}.
395
431
- Remove {completedDeferredFragment} from {graph}, promoting its child
396
432
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 )}.
398
434
- Add all nodes in {newRootNodes} to {rootNodes}.
399
435
- Update {graph} to the subgraph rooted at nodes in {rootNodes}.
400
436
- 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,
402
438
pending)}.
403
439
- Complete this incremental result stream.
404
440
@@ -415,35 +451,55 @@ GraphFromRecords(incrementalDataRecords, graph):
415
451
to {newGraph}, or the {parent} is not defined.
416
452
- Return {newGraph}.
417
453
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):
419
461
420
- - Initialize {newPendingResults } to the empty set.
462
+ - Initialize {newRootNodes } to the empty set.
421
463
- Initialize {rootNodes} to the set of root nodes in {graph}.
422
464
- For each {rootNode} of {rootNodes}:
423
465
- If {rootNode} has no children Pending Incremental Data nodes:
424
466
- Let {children} be the set of child Deferred Fragment nodes of {rootNode}.
425
467
- Add each of the nodes in {children} to {rootNodes}.
426
468
- 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}.
429
476
430
477
GetInitialResult(data, errors, pendingResults):
431
478
432
479
- Let {pending} be the result of {GetPendingEntry(pendingResults)}.
433
480
- Let {hasNext} be {true}.
434
481
- Return an unordered map containing {data}, {errors}, {pending}, and {hasNext}.
435
482
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):
437
488
438
489
- 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 }.
442
493
- Let {pendingEntry} be an unordered map containing {id}, {path}, and {label}.
443
494
- Append {pendingEntry} to {pending}.
444
495
- Return {pending}.
445
496
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):
447
503
448
504
- Let {hasNext} be {false} if {graph} is empty, otherwise, {true}.
449
505
- Let {incrementalResult} be an unordered map containing {hasNext}.
@@ -455,6 +511,10 @@ GetIncrementalResult(graph, completed, incremental, pending):
455
511
- Set the corresponding entry on {incrementalResult} to {pending}.
456
512
- Return {incrementalResult}.
457
513
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
+
458
518
GetIncrementalEntry(incrementalDataRecord, graph):
459
519
460
520
- Let {deferredFragments} be the Deferred Fragments incrementally completed by
@@ -470,6 +530,9 @@ GetIncrementalEntry(incrementalDataRecord, graph):
470
530
- Let {id} be the unique identifier for {bestDeferredFragment}.
471
531
- Return an unordered map containing {id}, {subPath}, {data}, and {errors}.
472
532
533
+ Formatting of completed incremental results is defined by the
534
+ {GetCompletedEntry()} algorithm.
535
+
473
536
GetCompletedEntry(pendingResult, errors):
474
537
475
538
- Let {id} be the unique identifier for {pendingResult}.
0 commit comments