-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Spec edits for incremental delivery, Section 3 only #1132
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
base: incremental-integration
Are you sure you want to change the base?
Spec edits for incremental delivery, Section 3 only #1132
Conversation
Updated to reflect spec draft graphql/graphql-spec#1132 Also changed the argument order to match the spec draft
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of nit-picky comments but I think we're pretty close! Do we have a glossary somewhere? I think we need to be really crisp on terms like "result", "response", "payload" and the like.
spec/Section 3 -- Type System.md
Outdated
responses: the initial response containing all non-deferred data, while | ||
subsequent responses include deferred data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that we've refined the terminology around this: there's one response. The response for stream/defer is a stream consisting of an initial result payload followed by a number of incremental payloads. I couldn't find where we discussed this, so please correct as appropriate.
responses: the initial response containing all non-deferred data, while | |
subsequent responses include deferred data. | |
payloads: the initial payload containing all non-deferred data, while subsequent | |
payloads include deferred data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benjie after the last discussion we had, I decided to drop payload
. The response section now says:
The result of a GraphQL request must be either a single initial response or an
incremental stream. The response will be an incremental stream when the GraphQL
service has deferred or streamed data as a result of the@defer
or@stream
directives. When the result of the GraphQL operation is an incremental stream,
the first value will be an initial response, followed by one or more subsequent
responses.
spec/Section 3 -- Type System.md
Outdated
- `if: Boolean! = true` - When `true`, field _should_ be streamed (see related | ||
note below). When `false`, the field must not be streamed and all list items | ||
must be initially included. Defaults to `true` when omitted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "and" here implies this is an additional behaviour
- `if: Boolean! = true` - When `true`, field _should_ be streamed (see related | |
note below). When `false`, the field must not be streamed and all list items | |
must be initially included. Defaults to `true` when omitted. | |
- `if: Boolean! = true` - When `true`, field _should_ be streamed (see related | |
note below). When `false`, the field must behave as if the `@stream` directive | |
is not present—it must not be streamed and all of the list items must be | |
included. Defaults to `true` when omitted. |
d5322ae
to
32785b8
Compare
@benjie I added a rough glossary here: graphql/defer-stream-wg#106, I'll keep refining it as we go |
176172f
to
7c0ba73
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes look correct to me, I don't think the definition of the @stream/@defer
directives have changed in a very long time, so this feels good.
cf072a4
to
47f362c
Compare
0640179
to
b3187e0
Compare
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benoit 'BoD' Lubek <BoD@JRAF.org>
b3187e0
to
3b8799e
Compare
3b8799e
to
64dc38f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good; all my suggestions are minor except the last one which I think warrants some extra work.
consisting of an _initial execution result_ containing all non-deferred data, | ||
followed by one or more _subsequent execution result_ including the deferred |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Terminology:
- initial execution result - confirms to execution result (just adds a couple more fields) 👍
- subsequent execution result - sounds like it should conform to execution result, but doesn't (
{hasNext: false}
very obviously fails this) 👎
If we continue with this naming, we should revisit the definition of execution result.
Did we already discuss changing to subsequent incremental execution result? To my ear the word incremental1 makes it much clearer that the payload won't necessarily conform to execution result (a bit like "partial" - suddenly all the fields are no longer required).
Footnotes
-
You could think of each additional payload in a subscription as being a "subsequent execution result", but the term "incremental execution result" would not fit for subscriptions - it's clearly a different beast. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benjie I decided to go with subsequent execution result instead of incremental execution result because we also need a name for the object in the incremental
entry. As of the last discussion we are calling it an incremental result, (along with the other objects completed result, pending result).
The argument about subscriptions is compelling so I'm open to changing it, but I'm concerned having both an Incremental Execution Result
and an Incremental Result
will be confusing.
query myQuery($shouldDefer: Boolean! = true) { | ||
user { | ||
name | ||
...someFragment @defer(label: "someLabel", if: $shouldDefer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...someFragment @defer(label: "someLabel", if: $shouldDefer) | |
...someFragment @defer(if: $shouldDefer, label: "someLabel") |
query myQuery($shouldStream: Boolean! = true) { | ||
user { | ||
friends(first: 10) | ||
@stream(label: "friendsStream", initialCount: 5, if: $shouldStream) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stream(label: "friendsStream", initialCount: 5, if: $shouldStream) { | |
@stream(if: $shouldStream, label: "friendsStream", initialCount: 5) { |
- `if: Boolean! = true` - When `true`, fragment _should_ be deferred (see | ||
related note below). When `false`, fragment must not be deferred. Defaults to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor, but we would expect this note to be in the same section. Having it under @stream
's heading is unexpected without further signposting.
`@defer` and/or `@stream` directives. This also applies to the `initialCount` | ||
argument on the `@stream` directive. Clients must be able to process a streamed | ||
field result that contains more initial list items than what was specified in | ||
the `initialCount` argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it warrants its own header, similar to Supporting Subscriptions at Scale (beautiful bit of alliteration there!), that can be referenced by both blocks.
Also, this feels like it should be normative. Putting "must" in a non-normative note feels wrong.
We should also be much clearer that this is on a case by case basis, so for example:
{
list @stream {
field1
...@defer {
field2
}
}
}
In this case, different entries in the streamed list may or may not defer field2
- the @defer
is not ignored "wholesale" but on an execution-position by execution-position basis.
Also, I think we should more strongly empower schema authors to opt out when they see fit, because there are many situations where streaming may add complexity without reducing latency for the client. For example: reverse cursor pagination...
query Things($cursor: String) {
things(first: 1000, after: $cursor) {
nodes @stream(initialCount: 2) { id name }
}
}
^ for this, it can clearly stream from the datasource; however:
query ThingsReverse($cursor: String) {
things(last: 1000, before: $cursor) {
nodes @stream(initialCount: 2) { id name }
}
}
^ in this case, you can't necessarily stream from the datasource, because the last record to come from the datasource (assuming you're fetching in reverse, discovering each next row as you go) is actually the first record you should return to the user.
Similarly:
query ThingsAwkward($cursor: String) {
things(first: 1000, last: 100, after: $cursor) {
nodes @stream(initialCount: 2) { id name }
}
}
^ in this case we may not know which the last 100 will be until we've fetched them all. (This is of course a pathological query.)
Of course streaming the underlying data from your data source and streaming the result of the field's execution are two different things (and happen at different positions in the waterfall), so YMMV.
Extracted from the full PR (#1110) and targeting an integration branch to aid in review.
Helpful reference material:
Response format examples: graphql/defer-stream-wg#69
Glossary: graphql/defer-stream-wg#106
GraphQL Conf talk: https://www.youtube.com/watch?v=LEyDeNoobT0
Response types