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

Undefined variable reference in generated subgraph query #3112

Closed
carldunham opened this issue Jul 30, 2024 · 10 comments · Fixed by #3119
Closed

Undefined variable reference in generated subgraph query #3112

carldunham opened this issue Jul 30, 2024 · 10 comments · Fixed by #3119
Labels

Comments

@carldunham
Copy link

carldunham commented Jul 30, 2024

Describe the bug

This repo includes schema and a sample query that fails validation using the new Rust validation code.

To Reproduce

Steps to reproduce the behavior:

  1. Setup services: clone the repo, run make run
  2. Submit request in query.graphql, provide a value for "time" (can be any enum value).
  3. See error

Expected behavior

The query should succeed.

Output

{
  "errors": [
    {
      "message": "Variable \"$time\" is not defined.",
      "locations": [
        {
          "line": 1,
          "column": 572
        }
      ],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED"
      }
    },
    {
      "message": "Variable \"$time\" is not defined.",
      "locations": [
        {
          "line": 1,
          "column": 572
        }
      ],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED"
      }
    }
  ]
}

yet $time is clearly defined and used.

Desktop (please complete the following information):

  • OS: iOS
  • Version

Additional context

Router version v1.52.0
Rover version 0.12.2

@carldunham
Copy link
Author

Note that the error is repeated. When running older versions of Router, there was not a validation error, but invalid subgraph queries where generated. So it may be that the validation errors are coming from validating the query plan queries, not the main query itself. In those versions, you can also see that the time variable is not included in some nodes that include it.

@carldunham
Copy link
Author

Running this with router v1.45.1 gives a different error. I've included the query plan here too:

{
  "data": {
    "feed": {
      "elements": null,
      "__typename": "User",
      "id": ""
    },
    "identity": null
  },
  "errors": [
    {
      "message": "HTTP fetch failed from 'a': 422: Unprocessable Entity",
      "extensions": {
        "code": "SUBREQUEST_HTTP_ERROR",
        "service": "a",
        "reason": "422: Unprocessable Entity",
        "http": {
          "status": 422
        }
      }
    },
    {
      "message": "Variable \"$time\" is not defined by operation \"UserProfile__a__3\".",
      "locations": [
        {
          "line": 1,
          "column": 572
        }
      ],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED"
      }
    }
  ],
  "extensions": {
    "apolloQueryPlan": {
      "object": {
        "kind": "QueryPlan",
        "node": {
          "kind": "Parallel",
          "nodes": [
            {
              "kind": "Sequence",
              "nodes": [
                {
                  "kind": "Fetch",
                  "serviceName": "b",
                  "variableUsages": [],
                  "operation": "query UserProfile__b__0{feed:userInfoByName(name:\"\"){...userProfileFeedStructuredDataFragment ...on User{...userProfileFeedStructuredDataFragment}}}fragment userProfileFeedStructuredDataFragment on UserInfo{__typename id}",
                  "operationName": "UserProfile__b__0",
                  "operationKind": "query",
                  "id": null,
                  "inputRewrites": null,
                  "outputRewrites": null,
                  "schemaAwareHash": "5fc5cc7a9dccd37fa60d4c039b72978b4cf7fbb48fd034b9f80e70a6c0b95586",
                  "authorization": {
                    "is_authenticated": false,
                    "scopes": [],
                    "policies": []
                  }
                },
                {
                  "kind": "Flatten",
                  "path": [
                    "feed"
                  ],
                  "node": {
                    "kind": "Fetch",
                    "serviceName": "a",
                    "requires": [
                      {
                        "kind": "InlineFragment",
                        "typeCondition": "User",
                        "selections": [
                          {
                            "kind": "Field",
                            "name": "__typename"
                          },
                          {
                            "kind": "Field",
                            "name": "id"
                          }
                        ]
                      }
                    ],
                    "variableUsages": [
                      "time"
                    ],
                    "operation": "query UserProfile__a__1($representations:[_Any!]!$time:TimeRange){_entities(representations:$representations){...on User{elements(time:$time){edges{node{__typename ...on PostInfo{__typename commentInfo{__typename lastAuthorNote{__typename user{__typename ...on UnavailableUser{__typename id}}}}}}}}}}}",
                    "operationName": "UserProfile__a__1",
                    "operationKind": "query",
                    "id": null,
                    "inputRewrites": null,
                    "outputRewrites": null,
                    "schemaAwareHash": "ab243653c1159d74c4717b924beb9d5b61e1f8b7e915f0eb9769fb294df461a8",
                    "authorization": {
                      "is_authenticated": false,
                      "scopes": [],
                      "policies": []
                    }
                  }
                },
                {
                  "kind": "Flatten",
                  "path": [
                    "feed",
                    "elements",
                    "edges",
                    "@",
                    "node",
                    "commentInfo",
                    "lastAuthorNote",
                    "user"
                  ],
                  "node": {
                    "kind": "Fetch",
                    "serviceName": "b",
                    "requires": [
                      {
                        "kind": "InlineFragment",
                        "typeCondition": "UnavailableUser",
                        "selections": [
                          {
                            "kind": "Field",
                            "name": "__typename"
                          },
                          {
                            "kind": "Field",
                            "name": "id"
                          }
                        ]
                      }
                    ],
                    "variableUsages": [],
                    "operation": "query UserProfile__b__2($representations:[_Any!]!){_entities(representations:$representations){...on UnavailableUser{__typename isPermanentlySuspended}}}",
                    "operationName": "UserProfile__b__2",
                    "operationKind": "query",
                    "id": null,
                    "inputRewrites": null,
                    "outputRewrites": null,
                    "schemaAwareHash": "5002545f4885a405e1aa40355d6e15e0b17aea1a6d27d41b4c8f2e6fc1e680c7",
                    "authorization": {
                      "is_authenticated": false,
                      "scopes": [],
                      "policies": []
                    }
                  }
                }
              ]
            },
            {
              "kind": "Sequence",
              "nodes": [
                {
                  "kind": "Fetch",
                  "serviceName": "a",
                  "variableUsages": [],
                  "operation": "query UserProfile__a__3{identity{upvotedPosts{edges{node{__typename ...on Item{__typename}...on PostInfo{__typename commentInfo{__typename lastAuthorNote{__typename user{__typename ...on UnavailableUser{...userProfileFeedStructuredDataFragment}}}}}}}}downvotedPosts{edges{node{__typename ...on Item{__typename}...on PostInfo{__typename commentInfo{__typename lastAuthorNote{__typename user{__typename ...on UnavailableUser{...userProfileFeedStructuredDataFragment}}}}}}}}}}fragment userProfileFeedStructuredDataFragment on UserInfo{__typename id ...on User{elements(time:$time){edges{node{__typename}}}}}",
                  "operationName": "UserProfile__a__3",
                  "operationKind": "query",
                  "id": null,
                  "inputRewrites": null,
                  "outputRewrites": null,
                  "schemaAwareHash": "d12821696b8f892f32d14d379098133d7766565eec359c63bfad804f551c0c02",
                  "authorization": {
                    "is_authenticated": false,
                    "scopes": [],
                    "policies": []
                  }
                },
                {
                  "kind": "Parallel",
                  "nodes": [
                    {
                      "kind": "Flatten",
                      "path": [
                        "identity",
                        "upvotedPosts",
                        "edges",
                        "@",
                        "node",
                        "commentInfo",
                        "lastAuthorNote",
                        "user"
                      ],
                      "node": {
                        "kind": "Fetch",
                        "serviceName": "b",
                        "requires": [
                          {
                            "kind": "InlineFragment",
                            "typeCondition": "UnavailableUser",
                            "selections": [
                              {
                                "kind": "Field",
                                "name": "__typename"
                              },
                              {
                                "kind": "Field",
                                "name": "id"
                              }
                            ]
                          }
                        ],
                        "variableUsages": [],
                        "operation": "query UserProfile__b__4($representations:[_Any!]!){_entities(representations:$representations){...on UnavailableUser{__typename isPermanentlySuspended}}}",
                        "operationName": "UserProfile__b__4",
                        "operationKind": "query",
                        "id": null,
                        "inputRewrites": null,
                        "outputRewrites": null,
                        "schemaAwareHash": "579e20fda4bc09213dc0481b685e5dc09427aea0b516faf42daf7fb187502ec3",
                        "authorization": {
                          "is_authenticated": false,
                          "scopes": [],
                          "policies": []
                        }
                      }
                    },
                    {
                      "kind": "Flatten",
                      "path": [
                        "identity",
                        "downvotedPosts",
                        "edges",
                        "@",
                        "node",
                        "commentInfo",
                        "lastAuthorNote",
                        "user"
                      ],
                      "node": {
                        "kind": "Fetch",
                        "serviceName": "b",
                        "requires": [
                          {
                            "kind": "InlineFragment",
                            "typeCondition": "UnavailableUser",
                            "selections": [
                              {
                                "kind": "Field",
                                "name": "__typename"
                              },
                              {
                                "kind": "Field",
                                "name": "id"
                              }
                            ]
                          }
                        ],
                        "variableUsages": [],
                        "operation": "query UserProfile__b__5($representations:[_Any!]!){_entities(representations:$representations){...on UnavailableUser{__typename isPermanentlySuspended}}}",
                        "operationName": "UserProfile__b__5",
                        "operationKind": "query",
                        "id": null,
                        "inputRewrites": null,
                        "outputRewrites": null,
                        "schemaAwareHash": "f5f8264c18ca96ba2a5ab7e1a067d027b1ec444e2791a9e13c99198986d1cad9",
                        "authorization": {
                          "is_authenticated": false,
                          "scopes": [],
                          "policies": []
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

@goto-bus-stop
Copy link
Member

goto-bus-stop commented Aug 7, 2024

Interesting, thanks for the report and the detailed reproduction! This looks like a bug in the JS query planner. The generated subgraph query is the one that doesn't have the right variables defined.
Did it work in a previous Router version?

@carldunham
Copy link
Author

Did it work in a previous Router version?

We have seen this in v1.33.2, but not sure about earlier versions. What would be one to test?

@goto-bus-stop
Copy link
Member

Sorry, does that mean it worked in 1.33.2, or that it already didn't work in 1.33.2? 😄

I'm just looking to find out if this is a recent regression in query planning or if it's something that has existed for a long time.

@carldunham
Copy link
Author

We have seen this in v1.33.2

That means we are seeing this bug in v1.33.2. it is easily reproducible in my test repo with whatever version you care to try.

@BrynCooke BrynCooke transferred this issue from apollographql/router Aug 12, 2024
@goto-bus-stop
Copy link
Member

OK perfect, thank you. It looks similar to an issue we had in the work-in-progress Rust query planner that has since been fixed.

Moved the issue to the federation repo so the JS query planner team can have a look at prioritising this.

@goto-bus-stop goto-bus-stop changed the title Regression in Query Validation and Planning Undefined variable reference in generated subgraph query Aug 12, 2024
@duckki
Copy link
Contributor

duckki commented Aug 14, 2024

I agree this looks similar to the Rust QP bug that @goto-bus-stop fixed recently.

@clenfest
Copy link
Contributor

Hey @carldunham, can you confirm that this is not an issue if you have query fragment generation turned on? i.e. the following in your router yaml:

supergraph:
  generate_query_fragments: true

Not sure if that's an option for you while waiting for the fix.

@carldunham
Copy link
Author

Hi, Chris. I see the same behavior with that setting. Tried setting it to true and to false, still having the same result.

clenfest added a commit that referenced this issue Aug 15, 2024
Fixes #3112

Before creating an Operation, we call `collectUsedVariables`, which will only pull values from the selection set and not from fragments. This isn't great, because a variable used in a fragment won't be collected, and it doesn't make sense to collect variables from fragments because it's before they are optimized and many will be unused.

The inelegant solution I came up with is to pass in available variables in calls to `optimize` or  `generateQueryFragments` for an operation where we can add back in the unused variables. This should be ok, because we are guaranteed that exactly one of them will get called by `toPlanNode`. Pretty sure there won't be too much overhead added because we'll only call this once per subgraph fetch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants