graphql / graphql-spec

GraphQL is a query language and execution engine tied to any backend service.
https://spec.graphql.org
14.31k stars 1.13k forks source link

Alternative spec implementation of deduplicated incremental delivery #1077

Closed yaacovCR closed 2 months ago

yaacovCR commented 9 months ago

This is an alternative to my most recent PR #1052 with an eye to describing the algorithm in a bit less detail to decrease the size of the spec edits. We also introduce the idea of an observable stream to which events can be pushed using a callback.

This is an alternative as well to @benjie's most recent proposal at #1074.

The main differences as I see them within the given proposal are as follows.

This PR:

  1. will kick off incremental entries shared between sibling defers and unique to sibling defers in parallel rather than waiting for the former to complete.
  2. generates a path-independent field plan for each field within the operation; implementations can use this to memoize/deduplicate if using defer on list items; as well as persisting this across operations.
  3. provides a single-line difference in implementation between early and delayed execution; the algorithm is otherwise the same.

This is also of course an alternative to #742 -- the original PR without deduplication.

netlify[bot] commented 9 months ago

Deploy Preview for graphql-spec-draft ready!

Name Link
Latest commit 3ef628c033a390563f8256514441b63b6f78c92a
Latest deploy log https://app.netlify.com/sites/graphql-spec-draft/deploys/66eb131235e7a90008df92e2
Deploy Preview https://deploy-preview-1077--graphql-spec-draft.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

yaacovCR commented 9 months ago

UPDATED 2024-02-22:

I've separated out stream from this PR for the sake of simplicity; the commit adding defer comes at the tail end of this PR, with some restructuring and moving happening in the initial commits.

The diff for the defer commit is +322 and -51, on net an addition of 271 lines.

For comparison, proposal at #1074 has a diff of +328 and −45, on an addition of 283 lines. (That is the total for the entirety of #1074, as that PR is set to be pulled into a branch already containing essentially the same restructuring work.)

So this proposal by length is on par with #1074, but we also get the additional features mentioned above: (1) parallel execution of all incremental entries (including where sibling defers have overlapping and unique fields), (2) a demonstration of the path-independence of field collection and grouped field set partition, and (3) explicit handling of early execution via 1-line if statements.

To make this magic happen, we do have to expand a bit our spec language. In particular, we add the ability to pass around an event emitter to the data execution layer that can be used to defer execution of deferred fragments until the incremental result stream signals that it has released a fragment as pending. We otherwise retain the simple functional style of the remainder of the spec => and we don't perform any mutations.

I will be updating this PR, probably via rebasing. If something significant changes, I will try to highlight it in the comments here, but basically the plan is just to further simplify, cut down on verbiage, etc.

yaacovCR commented 9 months ago

Here are some initial visualizations of what the graph in YieldIncrementalResults might look like

For operation:

{
  someFieldA
  ... @defer(label: "IntermediateComponent") {
    someFieldB
    ... @defer(label: "SlowComponent") {
      someFieldC
    }
  }
}
graph TD
    A1((IntermediateComponent)) --> B1{"{ someFieldB }"}
    A1 --> A2((SlowComponent))
    A2 --> B2{"{ someFieldC }"}

For operation:

{
  someFieldA
  ... @defer(label: "Sibling1") {
    someFieldB
    someFieldX
  }
  ... @defer(label: "Sibling2") {
    someFieldB
    someFieldY
  }
}
graph TD
    A1((Sibling1)) --> B1{"{ someFieldX }"}
    A1 --> B2{"{ someFieldB }"}
    A2((Sibling2)) --> B2
    A2 --> B3{"{ someFieldY }"}
yaacovCR commented 6 months ago

I've made some minor corrections and dropped the stream from this commit so as to concentrate on defer. Goal is to bring this in line with https://github.com/graphql/graphql-js/pull/4094

yaacovCR commented 2 months ago

Closing in favor of https://github.com/graphql/graphql-spec/pull/1110 !!!