Skip to content

RFC: incremental delivery with deduplication + concurrent execution #1034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 65 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
fca1c5d
Introduce @defer and @stream.
robrichard Aug 18, 2022
43e9997
fix typos
robrichard Feb 17, 2021
cb5a3f4
clear up that it is legal to support either defer or stream individually
robrichard Feb 17, 2021
0eb4426
Add sumary of arguments to Type System
robrichard Feb 17, 2021
43bfe01
Update Section 3 -- Type System.md
robrichard May 15, 2021
acb5bf0
clarification on defer/stream requirement
robrichard Nov 19, 2021
abea59b
clarify negative values of initialCount
robrichard Nov 20, 2021
139d69f
allow extensions only subsequent payloads
robrichard Nov 25, 2021
de5004b
fix typo
robrichard Nov 26, 2021
9e89f42
Raise a field error if initialCount is less than zero
robrichard Aug 18, 2022
f894ba3
data is not necessarily an object in subsequent payloads
robrichard Dec 6, 2021
08053d7
add Defer And Stream Directives Are Used On Valid Root Field rule
robrichard Dec 6, 2021
e19246b
wait for parent async record to ensure correct order of payloads
robrichard Aug 18, 2022
2ecd0af
Simplify execution, payloads should begin execution immediately
robrichard Dec 20, 2021
337bb87
Clarify error handling
robrichard Dec 20, 2021
2982dec
add isCompletedIterator to AsyncPayloadRecord to track completed iter…
robrichard Dec 30, 2021
32fb73b
fix typo
robrichard Jan 21, 2022
1ff999e
deferDirective and visitedFragments
robrichard Feb 2, 2022
270b409
stream if argument, indexPath -> itemPath
robrichard Feb 7, 2022
75f2258
Clarify stream only applies to outermost list of multi-dimensional ar…
robrichard Feb 7, 2022
d8c28d1
add validation “Defer And Stream Directive Labels Are Unique”
robrichard Mar 7, 2022
eb3a4e3
Clarification on labels
robrichard Mar 8, 2022
f2b50bf
fix wrong quotes
robrichard Mar 23, 2022
92f02f3
remove label/path requirement
robrichard Mar 23, 2022
049bce8
add missing line
robrichard Jun 9, 2022
9a07500
fix ExecuteRequest
robrichard Jun 9, 2022
7c5e1da
fix response
robrichard Jun 9, 2022
19cb9c3
Align deferred fragment field collection with reference implementation
robrichard Aug 3, 2022
c747f61
spec updates to reflect latest discussions
robrichard Aug 18, 2022
6f3c715
Note about mutation execution order
robrichard Aug 18, 2022
7c9ea0a
minor change for uniqueness
robrichard Aug 18, 2022
d84939e
fix typos
robrichard Aug 18, 2022
1ad7e9c
if: Boolean! = true
robrichard Aug 23, 2022
4b6554e
address pr feedback
robrichard Aug 23, 2022
9103fdb
clarify null behavior of if
robrichard Aug 24, 2022
3944d05
Add error boundary behavior
robrichard Sep 8, 2022
90b31ae
defer/stream response => payload
robrichard Sep 8, 2022
f1c0ec2
event stream => response stream
robrichard Sep 8, 2022
3830406
link to path section
robrichard Sep 8, 2022
f950efb
use case no dash
robrichard Sep 8, 2022
ad5b2e2
remove "or null"
robrichard Sep 8, 2022
c1f3f65
add detailed incremental example
robrichard Sep 8, 2022
2e41749
update label validation rule
robrichard Sep 8, 2022
abb14a0
clarify hasNext on incremental example
robrichard Sep 8, 2022
4ea2a34
clarify canceling of subsequent payloads
robrichard Sep 8, 2022
1565491
Add examples for non-null cases
robrichard Sep 8, 2022
a938f44
typo
robrichard Sep 9, 2022
a301f21
improve non-null example
robrichard Sep 9, 2022
38bfbb9
Add FilterSubsequentPayloads algorithm
robrichard Sep 9, 2022
8d07dee
link to note on should
robrichard Oct 12, 2022
008818d
update on hasNext
robrichard Nov 1, 2022
4adb05a
small fixes (#3)
yaacovCR Nov 7, 2022
ddd0fd7
remove ResolveFIeldGenerator (#4)
yaacovCR Nov 16, 2022
b54c9fe
fix typos (#6)
yaacovCR Nov 18, 2022
02d4676
Add error handling for stream iterators (#5)
yaacovCR Nov 21, 2022
3e74250
Raise a field error if defer/stream encountered during subscription e…
robrichard Nov 22, 2022
cb3ab46
Add validation rule for defer/stream on subscriptions
robrichard Nov 22, 2022
24cf072
clarify label is not required
robrichard Nov 23, 2022
d74430c
fix parentRecord argument in ExecuteStreamField (#7)
yaacovCR Nov 29, 2022
79da712
fix typo
robrichard Dec 5, 2022
8df13da
replace server with service
robrichard Jan 15, 2023
94363c9
CollectFields does not require path or asyncRecord (#11)
yaacovCR Jan 16, 2023
fe9d871
incremental delivery with deduplication, concurrent delivery, and ear…
yaacovCR May 21, 2023
831b10c
scattered fixes, streamlining
yaacovCR Sep 26, 2023
813ea2c
use identifiers instead of records when possible
yaacovCR Sep 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Align deferred fragment field collection with reference implementation
  • Loading branch information
robrichard committed Jan 15, 2023
commit 19cb9c33753fb99b2e77beda03f509e1a8948d87
57 changes: 40 additions & 17 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,9 @@ subsequentPayloads, asyncRecord):

- If {path} is not provided, initialize it to an empty list.
- If {subsequentPayloads} is not provided, initialize it to the empty set.
- Let {groupedFieldSet} be the result of {CollectFields(objectType, objectValue,
selectionSet, variableValues, path subsequentPayloads, asyncRecord)}.
- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
{CollectFields(objectType, objectValue, selectionSet, variableValues, path,
asyncRecord)}.
- Initialize {resultMap} to an empty ordered map.
- For each {groupedFieldSet} as {responseKey} and {fields}:
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
Expand All @@ -423,6 +424,10 @@ subsequentPayloads, asyncRecord):
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
fields, variableValues, path, subsequentPayloads, asyncRecord)}.
- Set {responseValue} as the value for {responseKey} in {resultMap}.
- For each {deferredGroupFieldSet} and {label} in {deferredGroupedFieldsList}
- Call {ExecuteDeferredFragment(label, objectType, objectValue,
deferredGroupFieldSet, path, variableValues, asyncRecord,
subsequentPayloads)}
- Return {resultMap}.

Note: {resultMap} is ordered by which fields appear first in the operation. This
Expand Down Expand Up @@ -574,10 +579,12 @@ is maintained through execution, ensuring that fields appear in the executed
response in a stable and predictable order.

CollectFields(objectType, objectValue, selectionSet, variableValues, path,
subsequentPayloads, asyncRecord, visitedFragments):
asyncRecord, visitedFragments, deferredGroupedFieldsList):

- If {visitedFragments} is not provided, initialize it to the empty set.
- Initialize {groupedFields} to an empty ordered map of lists.
- If {deferredGroupedFieldsList} is not provided, initialize it to an empty
list.
- For each {selection} in {selectionSet}:
- If {selection} provides the directive `@skip`, let {skipDirective} be that
directive.
Expand Down Expand Up @@ -616,13 +623,17 @@ subsequentPayloads, asyncRecord, visitedFragments):
- If {deferDirective} is defined:
- Let {label} be the value or the variable to {deferDirective}'s {label}
argument.
- Call {ExecuteDeferredFragment(label, objectType, objectValue,
fragmentSelectionSet, path, variableValues, asyncRecord,
subsequentPayloads)}.
- Let {deferredGroupedFields} be the result of calling
{CollectFields(objectType, objectValue, fragmentSelectionSet,
variableValues, path, asyncRecord, visitedFragments,
deferredGroupedFieldsList)}.
- Append a record containing {label} and {deferredGroupedFields} to
{deferredGroupedFieldsList}.
- Continue with the next {selection} in {selectionSet}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, objectValue, fragmentSelectionSet,
variableValues, path, subsequentPayloads, asyncRecord, visitedFragments)}.
variableValues, path, asyncRecord, visitedFragments,
deferredGroupedFieldsList)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
Expand All @@ -641,19 +652,24 @@ subsequentPayloads, asyncRecord, visitedFragments):
{variableValues} with the value {true}:
- Let {label} be the value or the variable to {deferDirective}'s {label}
argument.
- Call {ExecuteDeferredFragment(label, objectType, objectValue,
fragmentSelectionSet, path, asyncRecord, subsequentPayloads)}.
- Let {deferredGroupedFields} be the result of calling
{CollectFields(objectType, objectValue, fragmentSelectionSet,
variableValues, path, asyncRecord, visitedFragments,
deferredGroupedFieldsList)}.
- Append a record containing {label} and {deferredGroupedFields} to
{deferredGroupedFieldsList}.
- Continue with the next {selection} in {selectionSet}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, objectValue, fragmentSelectionSet,
variableValues, path, subsequentPayloads, asyncRecord, visitedFragments)}.
variableValues, path, asyncRecord, visitedFragments,
deferredGroupedFieldsList)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all items in {fragmentGroup} to {groupForResponseKey}.
- Return {groupedFields}.
- Return {groupedFields} and {deferredGroupedFieldsList}.

Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
directives may be applied in either order since they apply commutatively.
Expand Down Expand Up @@ -687,22 +703,29 @@ All Async Payload Records are structures containing:

#### Execute Deferred Fragment

ExecuteDeferredFragment(label, objectType, objectValue, fragmentSelectionSet,
path, variableValues, parentRecord, subsequentPayloads):
ExecuteDeferredFragment(label, objectType, objectValue, groupedFieldSet, path,
variableValues, parentRecord, subsequentPayloads):

- Let {deferRecord} be an async payload record created from {label} and {path}.
- Initialize {errors} on {deferRecord} to an empty list.
- Let {dataExecution} be the asynchronous future value of:
- Let {payload} be an unordered map.
- Let {data} be the result of {ExecuteSelectionSet(fragmentSelectionSet,
objectType, objectValue, variableValues, path, subsequentPayloads,
deferRecord)}.
- Initialize {resultMap} to an empty ordered map.
- For each {groupedFieldSet} as {responseKey} and {fields}:
- Let {fieldName} be the name of the first entry in {fields}. Note: This
value is unaffected if an alias is used.
- Let {fieldType} be the return type defined for the field {fieldName} of
{objectType}.
- If {fieldType} is defined:
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
fields, variableValues, path, subsequentPayloads, asyncRecord)}.
- Set {responseValue} as the value for {responseKey} in {resultMap}.
- Append any encountered field errors to {errors}.
- If {parentRecord} is defined:
- Wait for the result of {dataExecution} on {parentRecord}.
- If {errors} is not empty:
- Add an entry to {payload} named `errors` with the value {errors}.
- Add an entry to {payload} named `data` with the value {data}.
- Add an entry to {payload} named `data` with the value {resultMap}.
- Add an entry to {payload} named `label` with the value {label}.
- Add an entry to {payload} named `path` with the value {path}.
- Return {payload}.
Expand Down