Skip to content

Proposal: Spec-suggested internally deferring of fields and/or @splitSchema #198

@magicmark

Description

@magicmark

👋

Starting (yet another, sorry!) thread about this topic.

Previous:

tl;dr consider this:

Schema

#!subgraph Foo

type Query {
  slowField1: String
  bar: Bar
}

type Bar @key(fields: "id") { id: ID }

#!subgraph Bar

type Bar @key(fields: "id") {
  id: ID
  slowField2: String
}

Query

query {
  slowField1
  bar {
    slowField2
  }
}

The Problem

😱 slowField1 and slowField2 are waterfalled:

Router    | [============== POST /graphql ==================================================]
Foo       |    [==== { slowField1 bar { id }  } ==== ]
Bar       |                                           [==== { ... on Bar { slowField2 } ====]

...when really, we want something like this:

Router    | [============== POST /graphql ==================================]
Foo       |    [== { bar { id } } == ] (quick!)
Foo       |    [==== { slowField1 } ================ ]
Bar       |                           [==== { ... on Bar { slowField2 } ====]

Currently, there's no signal to the gateway that { slowField1 bar { id } } can/should be split, or streamed.

Proposal 1

As has been discussed in a few places, the general class of solution here involves streaming fields back to the gateway.

It seems that where possible, this should happen, as it's a strict improvement and solves performance edge case above.

@michaelstaib mentions:

We have a prototype for that and it is a significant perf upgrade... however the source schema must implement the latest defer spec proposal to be efficient.
~ https://discord.com/channels/625400653321076807/997185800129237022/1410588639108530278

Rather than leaving this up to "implementation detail", I propose this be encoded in the spec that if source schemas support @defer, then this must happen where possible. This seems sufficiently tricky/non obvious to include in the spec.

(...maybe only after incremental delivery is accepted upstream into the GraphQL Specification?)

Alternate: @splitSchema

(feel free to bikeshed the directive name)

What about schemas/gateways that do not support @defer or streaming?

There's an alternative (that we're pursuing today) which can be implemented as a compile step over source schemas to manually split them in order to force a the router to send concurrent requests (to the same server):

#!subgraph Foo

type Query {
  slowField1: String @splitSchema(shard: "slow_field")
  bar: Bar
}

type Bar @key(fields: "id") { id: ID }

---> becomes

#!subgraph Foo-Base-Shard

type Query {
  bar: Bar
}

type Bar @key(fields: "id") { id: ID }

#!subgraph Foo-slow_field-Shard

type Query {
  slowField1: String
}

...the server is still deployed once, but is advertised twice as seperate subgraphs. This results in parallel execution of the slow fields, as desired.

But this should not be necessary to actually split the schema.

Proposal: @splitSchema (again, name can be bikeshedded...) should be a native signal to the router to split the selection set as specified, removing the need to run a compile step on the schemas files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions