graphql-go-tools icon indicating copy to clipboard operation
graphql-go-tools copied to clipboard

Could not plan the operation missing path

Open argoyle opened this issue 1 year ago • 25 comments

I have a federated graph using a version of the federation example as my gateway.

Most of my backends extend the Company-type and add fields to it.

Some queries now fails with the below error but not all and I see no pattern to which fail and which succeed. In some cases it's even different for queries which target the same backend that behaves differently.

internal: bad datasource configuration - could not plan the operation. missing path: [query.company.__typename]

What might be the problem? What can I do to provide more information to identify the problem?

argoyle avatar Jun 23 '24 13:06 argoyle

Reverting back to github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.46 seems to make it behave correctly again.

argoyle avatar Jun 23 '24 14:06 argoyle

Can you publish a test or example that triggers the issue?

jensneuse avatar Jun 23 '24 14:06 jensneuse

Would love to. Not sure really were to begin since I'm not sure what is causing the problem though. Currently looking through the diff between rc.46 and rc.49 to get some kind if hint. :-)

argoyle avatar Jun 23 '24 14:06 argoyle

Hi @argoyle will it be possible for you to share subgraphs involved in your query and query itself?

e.g. try to cut subgraphs and query to the smalles reproducible amount

devsergiy avatar Jun 23 '24 14:06 devsergiy

Sure thing.

Failing query:

curl 'http://localhost:4444/query' \
  -H 'Accept-Language: en,sv;q=0.9,en-US;q=0.8' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'DNT: 1' \
  -H 'Origin: http://localhost:3300' \
  -H 'Pragma: no-cache' \
  -H 'Referer: http://localhost:3300/' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: same-site' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' \
  -H 'accept: */*' \
  -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI...<redacted>' \
  -H 'content-type: application/json' \
  -H 'sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  --data-raw $'{"operationName":"EntrySeries","variables":{"companyId":"018f88a0-f02d-701b-ba35-126a347ad77f"},"query":"query EntrySeries($companyId: ID\u0021) {\\n  company(id: $companyId) {\\n    id\\n    entrySeries {\\n      ...entrySeries\\n      __typename\\n    }\\n    __typename\\n  }\\n}\\n\\nfragment entrySeries on EntrySeries {\\n  id\\n  name\\n  __typename\\n}"}'

Working query (running from same frontend-page more or less in parallell):

curl 'http://localhost:4444/query' \
  -H 'Accept-Language: en,sv;q=0.9,en-US;q=0.8' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'DNT: 1' \
  -H 'Origin: http://localhost:3300' \
  -H 'Pragma: no-cache' \
  -H 'Referer: http://localhost:3300/' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: same-site' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' \
  -H 'accept: */*' \
  -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI...<redacted>' \
  -H 'content-type: application/json' \
  -H 'sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  --data-raw $'{"operationName":"FiscalYears","variables":{"companyId":"018f88a0-f02d-701b-ba35-126a347ad77f"},"query":"query FiscalYears($companyId: ID\u0021) {\\n  company(id: $companyId) {\\n    id\\n    fiscalYears {\\n      id\\n      start\\n      end\\n      __typename\\n    }\\n    __typename\\n  }\\n}"}'

company-service schema:

type Company @key(fields: "id") {
  id: ID!
  name: String!
}

type Query @extends {
  companies: [Company!]!
  company(id: ID!): Company!
}

accounting-service schema:

directive @goField(
  forceResolver: Boolean
  name: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

type Company @key(fields: "id") @extends {
  id: ID!
  entrySeries: [EntrySeries!]! @goField(forceResolver: true)
  fiscalYears: [FiscalYear!]! @goField(forceResolver: true)
}

type EntrySeries {
  id: ID!
  name: String!
}

type FiscalYear {
  id: ID!
  start: String!
  end: String!
}

I have six other subgraphs which also extend the Company type in an identical manner but with other fields of course.

argoyle avatar Jun 23 '24 18:06 argoyle

I noticed this in the diff between rc.46 and rc.49: image

Could it be that the planning visitor is being used by multiple requests at the same time somehow and the objects and field become reset mid-planning in some of the requests?

argoyle avatar Jun 23 '24 18:06 argoyle

Hmmm, no, there is a mutex before that of course. It's iterating until it has tried 100 times without finding the field.

argoyle avatar Jun 24 '24 05:06 argoyle

Hmmm, the problem seems to be that when using a fragment spread in Apollo, __typename is automatically included in the query and that messes up the planning. Hope that gives an indication on how to resolve the issue.

argoyle avatar Jun 24 '24 06:06 argoyle

It's most definitely the fact that a fragment is present. Just including __typename in the query works correctly.

argoyle avatar Jun 24 '24 06:06 argoyle

@jensneuse @devsergiy Added a reproduction test in #837

argoyle avatar Jun 28 '24 11:06 argoyle

Or more correct, I just added __typename to an existing test which made it fail with "my" error. 😊

argoyle avatar Jul 01 '24 13:07 argoyle

Enabling loads of Planner debug-config it seems to lose __typename when in the ConfigurationVisitor:

Initial node suggestions:


{"ds":0,"path":"query.hero","typeName":"Query","fieldName":"hero","isRootNode":true, "isSelected": true,"select reason": []}
{"ds":0,"path":"query.hero.name","typeName":"Character","fieldName":"name","isRootNode":false, "isSelected": true,"select reason": []}
{"ds":0,"path":"query.hero.__typename","typeName":"Character","fieldName":"__typename","isRootNode":false, "isSelected": true,"select reason": []}
[configurationVisitor]:  EnterSelectionSet ref: 2
[configurationVisitor]:  EnterField ref: 2 fieldName: hero typeName: Query
[configurationVisitor]:  saveAddedPath {"ds":0,"path":"query","shouldWalkFields":true,"pathType":"parent"}
[configurationVisitor]:  saveAddedPath {"ds":0,"path":"query.hero","fieldRef":  2,"typeName":"Query","shouldWalkFields":true,"isRootNode":true,"pathType":"field"}
[configurationVisitor]:  EnterSelectionSet ref: 1
[configurationVisitor]:  EnterField ref: 3 fieldName: name typeName: Character
[configurationVisitor]:  saveAddedPath {"ds":0,"path":"query.hero.name","fieldRef":  3,"typeName":"Character","shouldWalkFields":true,"isRootNode":false,"pathType":"field"}
[configurationVisitor]:  LeaveField ref: 3 fieldName: name typeName: Character
[configurationVisitor]:  EnterField ref: 4 fieldName: __typename typeName: Character
[configurationVisitor]:  LeaveField ref: 4 fieldName: __typename typeName: Character
[configurationVisitor]:  LeaveSelectionSet ref: 1
[configurationVisitor]:  LeaveField ref: 2 fieldName: hero typeName: Query
[configurationVisitor]:  LeaveSelectionSet ref: 2


Operation after initial run:

{
    hero {
        name
        __typename
    }
}


Planning paths after initial run

Should revisit: true
Has new fields: false
Has missing paths: true
Has fields waiting for dependency: false


Missing paths:

query.hero.__typename

And then it iterates 100 times with similar output.

argoyle avatar Jul 02 '24 15:07 argoyle

Hmmm, that might be due to the FakePlanner having a PlanningBehavior that doesn't include typename though. If I modify that it seems to be planning correctly. Back to digging again.

argoyle avatar Jul 02 '24 16:07 argoyle

Ok, perhaps one more nugget of information. Each query works on it's own as long as my two subscriptions have not already ran. If I run my three queries from Insomnia directly after starting the federation gateway they also work from my Vue-app including the subscriptions. 🙈

argoyle avatar Jul 03 '24 15:07 argoyle

It's my subscription which return the Company-type that is causing the problem.

argoyle avatar Jul 03 '24 15:07 argoyle

Which also introduces a third subgraph which extend the Company-type. I'll try to modify my current test to also plan the subscription first.

argoyle avatar Jul 03 '24 16:07 argoyle

Still unable to reproduce this in a test 🙁 It's losing the Company.__typename in the secondary run for some reason.

argoyle avatar Jul 04 '24 10:07 argoyle

If I add the Company.name to the queries it magically starts to work again. So the problem only exists if I select the key, __typename as well as whatever the other subgraph has extended the Company-type with. As said in an earlier comment this was introduced in rc47.

argoyle avatar Jul 04 '24 12:07 argoyle

Adding Company.name would then of course introduce an extra call to the company subgraph which would not really be needed since both id and __typename should be available from any of the already used subgraphs. I'm still at loss of what the problem is here. Would @devsergiy have any thoughts on what part of the rc47 changes might be related?

argoyle avatar Jul 09 '24 09:07 argoyle

Here is a 15k lines log with loads of debug-data. To me it looks like it should find Company.__typename in the first planner but still it claims that it's missing. The log also contains the log for the subscription which makes it fail. goland.log

argoyle avatar Jul 09 '24 11:07 argoyle

Hmmm, on the other hand it seems to want to get __typename from another DS (12485775574321252452) which is not part of any of the two planners (5424679272531918615 and 6927017134761466251).

In the second run.

Initial nodes: {"ds":5424679272531918615,"path":"query.company.__typename","typeName":"Company","fieldName":"__typename","isRootNode":true, "isSelected": true,"select reason": [stage2: node on the same source as selected parent]} nodes after applying hints

And then after applying hints: {"ds":12485775574321252452,"path":"query.company.__typename","typeName":"Company","fieldName":"__typename","isRootNode":true, "isSelected": true,"select reason": [provided by planner as required by @key]}

Not sure why it's changing DS at that point. The DS it's selecting is the main one used in the subscription though.

argoyle avatar Jul 09 '24 12:07 argoyle

If I exclude __typename like this it works: https://github.com/wundergraph/graphql-go-tools/pull/847

Not sure what I break by doing it though.

argoyle avatar Jul 09 '24 13:07 argoyle

Hi @argoyle

sorry for the big delay

could you try out a version from this PR I believe it should fix your problems with a typenames

Thanks, Wundergraph Team

devsergiy avatar Aug 19 '24 09:08 devsergiy

No worries at all. That one works perfectly! 🎉 🚀

argoyle avatar Aug 19 '24 09:08 argoyle

Looking forward to a future release. Thanks @devsergiy and the rest of the Wundergraph team. ❤️

argoyle avatar Aug 19 '24 09:08 argoyle