-
-
Notifications
You must be signed in to change notification settings - Fork 818
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
upgrade executor to non-duplicating incremental delivery format #6243
Conversation
🦋 Changeset detectedLatest commit: 2c6fb0f The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Benchmark Results
|
🚀 Snapshot Release (
|
Package | Version | Info |
---|---|---|
@graphql-tools/delegate |
10.0.19-alpha-20240813170106-2c6fb0fc8fa322bcea8c10c35972ba621a04c4a4 |
npm ↗︎ unpkg ↗︎ |
@graphql-tools/executor |
2.0.0-alpha-20240813170106-2c6fb0fc8fa322bcea8c10c35972ba621a04c4a4 |
npm ↗︎ unpkg ↗︎ |
@graphql-tools/stitch |
9.2.11-alpha-20240813170106-2c6fb0fc8fa322bcea8c10c35972ba621a04c4a4 |
npm ↗︎ unpkg ↗︎ |
@graphql-tools/utils |
10.6.0-alpha-20240813170106-2c6fb0fc8fa322bcea8c10c35972ba621a04c4a4 |
npm ↗︎ unpkg ↗︎ |
💻 Website PreviewThe latest changes are available as preview in: https://4fbf8b14.graphql-tools.pages.dev |
Some additional issues:
|
For changeset, you can run |
We can use the approach from graphql/graphql-js#4043 which improves code readability and includes a significant performance benefit. At graphql-js, we have not yet adopted this approach, because we will be following the spec more closely, at least during the initial adoption period. |
I forgot to mention that another BREAKING CHANGE is that incremental delivery no longer is supported within subscriptions. In theory, we can remote |
I've updated this PR to re-enable early execution by default. |
Is it possible that we would want this executor to emit the old duplicated format by default and enable the new format under a flag? |
I believe I've fixed the issue with aborting from incremental delivery despite pending long-running promises, still avoiding the use of The key was to realize that the I've added a test demonstrating this. |
I've added the new unit tests for |
Thinking about this more, this is definitely possible in terms of functionality, but there would be a breaking change in terms of the types -- or we would have to introduce a new set of types for the new format with maybe a prefix or suffix. I am not unsure how to proceed, definitely easier just to make it a BREAKING CHANGE, so at this point I will leave this PR as is => From my perspective, this is now ready for review! 🚀 |
6d3be83
to
06a5da9
Compare
8f272eb
to
1a42467
Compare
@yaacovCR Now catching up with your work and rewatching your video from GraphQL Conf was very helpful! 🙏 I am fine with shipping this as a breaking change. But, we should have a middleware/wrapper function that takes the execute function and emits the old format. That way we can at least provide people with a migration path within Yoga that can not upgrade all their client's code immediately! |
Sounds good, I can work on that. |
8170044
to
d921d60
Compare
I added an argument to toggle whether incremental is disabled for subscriptions so that should cover that breaking change. I'm trying to decide whether to add an argument for deduplication or whether to add a wrapper, I'm thinking of going with the former. As an aside, I think tests are broken separate from this PR. |
006da37
to
e7cb921
Compare
7d42c65
to
903dead
Compare
@yaacovCR can you please rebase? I want to have an up2date alpha release fo testing on GraphQL Yoga! |
f5533b6
to
7dfd592
Compare
Done. |
.changeset/fifty-bobcats-jog.md
Outdated
deduplicateDefers: false, | ||
sendIncrementalErrorsAsNull: true, | ||
sendPathAndLabelOnIncremental: true, | ||
errorWithIncrementalSubscription: false, |
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.
When providing these parameters, I still receive {"data":{},"pending":[{"id":"0","path":[]}],"hasNext":true}
instead of {"data":{},"hasNext":true}
and also id
payloads.
Array [
Object {
"data": Object {},
"hasNext": true,
+ "pending": Array [
+ Object {
+ "id": "0",
+ "path": Array [],
+ },
+ ],
},
+ Object {
+ "completed": Array [
Object {
+ "id": "0",
+ },
+ ],
"hasNext": false,
"incremental": Array [
Object {
"data": Object {
"a": "a",
},
+ "id": "0",
"path": Array [],
},
],
},
]
Seems like these should also be omitted to be fully backwards-compatible? (See failing tests in dotansimha/graphql-yoga#3402)
Regarding all the granular options - do you think we need to allow such granularly for the user? I think from our perspective they would only care about "old" spec and "new" spec, not on using only parts of the new spec? 🤔
So maybe instead we should use a version number? Do you see benefit in setting some of the flags and omitting others from an end-user and feedback-user perspective?
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 don’t think extra graphql fields are technically backwards incompatible as anything custom goes into extensions.
But I’m certainly open to an additional option to suppress the pending/completed which would require setting sendPathAndLabelOnIncremental to true.
We could also add a version option that implies a different combination of defaults so that the granular options could be simplified
I think I am also noticing that the errors are now sent after data, which is not preferred according to the specification it should also be fixed
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 don’t think extra graphql fields are technically backward incompatible as anything custom goes into extensions.
It is still a different output than expected, and strict clients might react funky... I am unaware of any clients that do so right now, but I can already imagine this question coming up from a user. Since it seems straightforward to implement, let's add it.
We could also add a version option that implies a different combination of defaults so that the granular options could be simplified
Is there some reference version name e.g. date or similar used within the working group context to differentiate the different drafts?
What do you think about grouping the defer/stream-related options within an object? For the user-facing API, they would be more organized and separated from the other options.
execute({
...otherArgs,
incrementalDelivery: {
deduplicateDefers: false,
sendIncrementalErrorsAsNull: true,
sendPathAndLabelOnIncremental: true,
errorWithIncrementalSubscription: false,
}
})
In addition or instead, if the granular configuration of deduplicateDefers
, sendIncrementalErrorsAsNull
, sendPathAndLabelOnIncremental
, errorWithIncrementalSubscription
(and also an option for dropping the additional new properties - we can do something like the following:
execute({
...otherArgs,
incrementalDelivery: "2024-08-working-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.
I reworked it as follows as described in the changeset:
Library users can explicitly opt in to the older format by call execute
with the following option:
const result = await execute({
...,
incrementalPreset: 'v17.0.0-alpha.2',
});
The default value for incrementalPreset
when omitted is 'v17.0.0-alpha.3'
, which enables the new behaviors described above. The new behaviors can also be disabled granularly as follows:
const result = await execute({
...,
deferWithoutDuplication: false,
useIncrementalNotifications: false,
errorOnSubscriptionWithIncrementalDelivery: false,
});
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.
There is some renaming and some consolidation => the use of ids is intimately connected to the use of pending/completed for notifications in whose absence it makes sense to send incremental errors as null entries. This seems more concise.
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 am also totally fine with any regrouping/renaming tweaks you might want to make. in terms of levels of granularity, I think it might theoretically make sense to preserve ability to toggle notifications separately than deduplication, but I am not aware of any actual use case yet for that. I believe the long-term plan is definitely to revisit incremental delivery with subscriptions so that has a definite argument for retaining as a separate parameter although the truth is, it could all be revisited later. I don’t have much strong feeling about any of this portion of the api….
cd51a69
to
d2229c4
Compare
75e5235
to
9b9bd6e
Compare
d34fb65
to
b8bf584
Compare
includes new options to allow for prior branching format
bee8e8b
to
203a033
Compare
Closing this for now. I think it overall might be easier to upstream the changes from this executor into graph JS rather than porting over incremental delivery, fragment arguments, and the other recent changes at graphql js around validation. Let me know if there is any renewed interest on your end! |
Description
GraphQL Incremental Delivery is moving to a new response format without duplication.
This PR updates the executor within graphql-tools to follow the new format, with my best effort to integrate the changes made within the graphql-tools executor, largely around abortion.
Notes:
mergeIncrementalResults()
), and Yoga tests that rely on it pass,but there should probably eventually be new unit testsnew unit tests have been added within the existingmergeIncrementalResults()
tests. The function still work with the old response format.ids
and be required to lookup the path based on the information frompending
.This version is a straight-copy fromSome believe that despite theoretical causes for concern, a better default would be to enable early execution. This version enables early execution by default, with a flag to disable, differing from graphql-js where early execution will probably be disabled by default, at least initially.graphql-js
where a decision has been made to disable early execution by default.although this may mean that abortion might get stuck while waiting for the next payload if the underlying iterator does not properly handle a. The rationale for avoidingreturn()
Promise.race
is the memory leak that may occur with long-running promises.This change should probably be discussed further before merging.See the comment below for how this is accomplished.Type of change
Please delete options that are not relevant.
expected)
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can
reproduce. Please also list any relevant details for your test configuration
Test Environment:
@graphql-tools/...
: latestChecklist:
CONTRIBUTING doc and the
style guidelines of this project