Skip to content
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

Unexpected query planner error with fragments #2592

Closed
utay opened this issue May 17, 2023 · 10 comments · Fixed by #2594
Closed

Unexpected query planner error with fragments #2592

utay opened this issue May 17, 2023 · 10 comments · Fixed by #2594
Assignees

Comments

@utay
Copy link

utay commented May 17, 2023

The query planner fails unexpectedly with the following error:
{
"message": "value retrieval failed: router bridge error: the deno runtime raised an error: request: couldn't receive response couldn't deserialize payload 5425825029548289619: deno: couldn't deserialize response : Error(\"invalid neither null nor empty object: found Object {\\\"code\\\": String(\\\"QUERY_PLANNING_FAILED\\\"), \\\"exception\\\": Object {\\\"stacktrace\\\": Array [String(\\\"Cannot add selection of field \\\\\\\"NBAPlayer.id\\\\\\\" to selection set of parent type \\\\\\\"PlayerInterface\\\\\\\"\\\")]}}\", line: 0, column: 0)``.",
"extensions": {
"code": "INTERNAL_SERVER_ERROR"
}
},

Minimal schema to reproduce
type Query {
  nbaCard: NBACard!
}

interface PlayerInterface {
  id: Int!
  slug: String!
}

interface CardInterface {
  id: Int!
  player: PlayerInterface!
}

type NBAPlayer implements PlayerInterface {
  id: Int!
  slug: String!
}

type NBACard implements CardInterface {
  id: Int!
  player: NBAPlayer!
}
Query to reproduce
{
    nbaCard {
      ...Fragment1
      ...Fragment2
    }
}

fragment Fragment1 on NBACard {
  player {
    id
  }
}

fragment Fragment2 on CardInterface {
  ...Fragment2_bis
}

fragment Fragment2_bis on CardInterface {
  player {
    slug
    __typename
  }
}

Notes:

  • There's only one subgraph involved in the schema and query
  • If I remove __typename from Fragment2_bis OR if I include Fragment2_bis directly in the query, it works
  • I guess there's some sort of merging error because one player is of type NBAPlayer and the other of type PlayerInterface

Please let me know if I should report this issue on the JS repository (https://github.com/apollographql/federation) or feel free to do so!

@abernix
Copy link
Member

abernix commented May 17, 2023

Thanks for opening this! This might belong on the federation repository, but before I make the suggestion (or transfer it over there) — what version of the Router are you running? That'll determine the version of Federation at play.

@utay
Copy link
Author

utay commented May 18, 2023

Thanks! I run the router version 1.18.0

@abernix
Copy link
Member

abernix commented May 18, 2023

Thanks. That would be Federation 2.4.2, then. I think this is fixed in Federation 2.4.3.

Let me demonstrate how I've validated this. First, at face-value, I'll point out the schema you shared is a non-federated schema. Running the Router or the Gateway requires a supergraph (which we also call a "core schema", sometimes). I definitely believe you know that, but it's worth pointing out since we need that supergraph to run query planning on (even if there is just one subgraph).

If I assume the schema you provided is offered as an example of a single subgraph that you're (somewhere outside of this issue) composing into a supergraph (for example, using rover supergraph compose), then it would yield a supergraph that looks like this:

schema
  @link(url: "https://specs.apollo.dev/link/v1.0")
  @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
{
  query: Query
}

directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE

directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION

directive @join__graph(name: String!, url: String!) on ENUM_VALUE

directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE

directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR

directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION

directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA

interface CardInterface
  @join__type(graph: A)
{
  id: Int!
  player: PlayerInterface!
}

scalar join__FieldSet

enum join__Graph {
  A @join__graph(name: "a", url: "https://films.example.com")
}

scalar link__Import

enum link__Purpose {
  """
  `SECURITY` features provide metadata necessary to securely resolve fields.
  """
  SECURITY

  """
  `EXECUTION` features provide metadata necessary for operation execution.
  """
  EXECUTION
}

type NBACard implements CardInterface
  @join__implements(graph: A, interface: "CardInterface")
  @join__type(graph: A)
{
  id: Int!
  player: NBAPlayer!
}

type NBAPlayer implements PlayerInterface
  @join__implements(graph: A, interface: "PlayerInterface")
  @join__type(graph: A)
{
  id: Int!
  slug: String!
}

interface PlayerInterface
  @join__type(graph: A)
{
  id: Int!
  slug: String!
}

type Query
  @join__type(graph: A)
{
  nbaCard: NBACard!
}

If I run this with Federation 2.4.2's query planner, I reliably reproduce the error you gave. If I run it with Federation 2.4.3, then I get a valid query plan:

QueryPlan {
  Fetch(service: "a") {
    {
      nbaCard {
        player {
          __typename
          slug
          id
        }
      }
    }
  },
}

We updated the query planner to v2.4.5 just this morning (via some amends commits to the automated PR opened by Renovate apollographql/router#3107, so this should be fixed in the next release of the Router. We're working on the details of that releasing timing right now.

@utay
Copy link
Author

utay commented May 18, 2023

This all makes sense, thank you for the details @abernix! Looking forward to the next release :)

@utay
Copy link
Author

utay commented May 21, 2023

We upgraded the router to 1.19.0 which fixed the bug mentioned above. However, with the same schema, the query planner still fails with:
{
"message": "value retrieval failed: router bridge error: the deno runtime raised an error: request: couldn't receive response couldn't deserialize payload 12544047767499609238: deno: couldn't deserialize response : Error(\"invalid neither null nor empty object: found Object {\\\"code\\\": String(\\\"QUERY_PLANNING_FAILED\\\"), \\\"exception\\\": Object {\\\"stacktrace\\\": Array [String(\\\"Cannot add selection of field \\\\\\\"NBAPlayer.id\\\\\\\" to selection set of parent type \\\\\\\"PlayerInterface\\\\\\\"\\\")]}}\", line: 0, column: 0)``.",
"extensions": {
"code": "INTERNAL_SERVER_ERROR"
}
}

for this query:

{
  nbaCard {
    ...Fragment1
    ...Fragment2
    ...Fragment3
  }
}

fragment Fragment1 on NBACard {
  player {
    id
  }
}

fragment Fragment2 on CardInterface {
  player {
    slug
  }
}

fragment Fragment3 on CardInterface {
  player {
    __typename
  }
}

@abernix
Copy link
Member

abernix commented May 22, 2023

@utay Glad that fixed the first error. For the new bug, shouldn't you have a Fragment3 defined in that operation you provided? (If you agree, the error message makes this only quasi-clear and could be better.)

@utay
Copy link
Author

utay commented May 22, 2023

Hmm but Fragment3 is defined, right? See the query above, it's not the same as the first one

@abernix
Copy link
Member

abernix commented May 22, 2023

Sorry, I forgot to scroll inside the codeblock. It is there and it is defined. 😉

@abernix abernix transferred this issue from apollographql/router May 22, 2023
@pcmanus pcmanus self-assigned this May 22, 2023
@abernix
Copy link
Member

abernix commented May 22, 2023

@utay I transferred this over to the federation repository. Someone is going to take a look at it from that side. Any fix will surface in the Router via, presumably, a patch update.

Thanks again for logging this!

pcmanus added a commit to pcmanus/federation that referenced this issue May 22, 2023
When checking for name fragments to reuse, when some fields implementing
interfaces use subtyping (meaning that the type of the field
implementation is a sub-type of the type declared for that field in the
interface), an assertion error of the form `Cannot add selection of
field X to selection set of parent type Y` was sometimes raised (see
the comments in the commit for mode details).

Fixes apollographql#2592.
@utay
Copy link
Author

utay commented May 22, 2023

Thank you @abernix and @pcmanus! Awesome to see that a PR is already opened :)

pcmanus pushed a commit that referenced this issue May 24, 2023
When checking for name fragments to reuse, when some fields implementing
interfaces use subtyping (meaning that the type of the field
implementation is a sub-type of the type declared for that field in the
interface), an assertion error of the form `Cannot add selection of
field X to selection set of parent type Y` was sometimes raised (see
the comments in the commit for mode details).

Fixes #2592.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants