-
Notifications
You must be signed in to change notification settings - Fork 254
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
inefficient query plans when using @context #3140
Conversation
…nt in some circumstances. Fix this by having all conditions go through the handleRequires() code path
…ing through subgraphs
🦋 Changeset detectedLatest commit: a04979f The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Deploy Preview for apollo-federation-docs canceled.
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
query-graphs-js/src/querygraph.ts
Outdated
@@ -965,6 +954,22 @@ function federateSubgraphs(supergraph: Schema, subgraphs: QueryGraph[]): QueryGr | |||
|
|||
} | |||
|
|||
// add contextual argument maps to builder | |||
for (const [i, subgraph] of subgraphs.entries()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previously this was calculated in a single loop above -> any reason to move it down here? I think we only need the builder.setContextMaps(...)
outside of the loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's like that because we only need to do the calculations once at the end. The fact that it was inside the loop was causing naming colisions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed this synchronously with Chris, will summarize the feedback:
- The changes to query graph creation are fine, and can be in their own PR (it's been split off into Fixes issue where there can be naming collisions in contextual parameter names #3155 ).
handleRequires()
needs to be split up, so that we may reuse the first half of it. Specifically, the part that applies the condition'sOpPathTree
and hoists (and maybe merges) the created nodes up the ancestors of the fetch dependency graph where possible. This function would return the main group that the start of the conditions merged into, along with any unmerged created groups.- The second half of
handleRequires()
is effectively the part specific to@requires
, and takes in the outputs of the first function (along with e.g. current group/path). It's responsible for adding the inputs to the node querying the@requires
field, and setting up parent/child relationships. - The idea here is that we can make an analogous second-half function for
@fromContext
-containing nodes, which will add the context arguments/input rewrites to the node, along with setting up parent/child relationships. - IIRC we've been choosing to put the subgraph jump (if needed) at the
@context
type and not near the@fromContext
field, so I believe we'd need to insert the subgraph jump in the first-half function.
- The second half of
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The splitting of handleRequires()
generally looks fine to me (would change a bit in the results passed between functions), but some changes are needed in how it's called in computeGroupsForTree()
. Going to pair program on that with @clenfest.
@@ -4227,7 +4229,7 @@ function computeGroupsForTree( | |||
}); | |||
updateCreatedGroups(createdGroups, newGroup); | |||
newGroup.addParents(conditionsGroups.map((conditionGroup) => { | |||
// If `conditionGroup` parent is `group`, that is the same as `newGroup` current parent, then we can infer the path of `newGroup` into | |||
// If `conditionGroup` parent is `group`, that is the same as `groupCopy` current parent, then we can infer the path of `groupCopy` into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the @key
-handling logic, which still uses newGroup
(it's not actually copying the current group, but genuinely creating a new group based off the @key
edge's tail).
@@ -4363,11 +4365,16 @@ function computeGroupsForTree( | |||
contextToConditionsGroups, | |||
}; | |||
|
|||
if (conditions && edge.conditions) { | |||
if (conditions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that conditions
being truthy from the OpPathTree
just tells us something wants to fetch data here, for use either immediately (@requires
edge, InterfaceObjectFakeDownCast
edge) or later (@fromContext
edge), potentially multiple of these. conditions
meant truthy means we'd need to execute the first-half function handleRequiresTree()
. edge.conditions
being additionally truthy means this edge requires at least some of those conditions
to be provided as input (via _entities
), so we'd also want to execute the @requires
second-half function, createPostRequiresGroup()
.
query-planner-js/src/buildPlan.ts
Outdated
path: path.forNewKeyFetch(createFetchInitialPath(dependencyGraph.supergraphSchema, entityType, context)), | ||
createdGroups, | ||
}; | ||
return { group, path, createdGroups, unmergedGroups: [], extraParent: undefined, newGroupIsUnneeded: undefined }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit misleading here to indicate unmergedGroups
as []
, as no merging of created groups takes place at all in this branch (so it's more like unmergedGroups
should be createdGroups
).
I think it'd be more clear here to:
- Swap
createdGroups
with anoCreatedGroups
boolean. - Have
unmergedGroups
becreatedGroups
when no merging took place. - Use
unmergedGroups
in the other half ofhandleRequires()
, in the place ofcreatedGroups
.
query-planner-js/src/buildPlan.ts
Outdated
const newContextToConditionsGroups = new Map<string, FetchGroup[]>([...contextToConditionsGroups]); | ||
for (const context of contextToSelection) { | ||
newContextToConditionsGroups.set(context, [group]); | ||
if (requireResult.createdGroups.length === 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was curious about why we had this if
block, synced with @clenfest but we didn't see a reason here.
query-planner-js/src/buildPlan.ts
Outdated
newContextToConditionsGroups.set(context, [group, ...conditionsGroups]); | ||
// If a new group is created for the conditions, make sure that we add that as a dependency | ||
// so that using a context will not occur within the same fetch group as it is retrieved | ||
if (!requireResult.newGroupIsUnneeded || group.isTopLevel) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why we'd only set the condition groups when this boolean expression is true, in general we need these condition groups so the downstream @fromContext
will be able to depend on these.
Co-authored-by: Chris Lenfest <clenfest@apollographql.com>
✅ Docs Preview ReadyNo new or changed pages found. |
Detected Secrets🔴 Secrets FoundIf you are seeing this message, it means that the secret scanning tool that Apollo uses to prevent secrets from entering our repositories has identified a secret in your branch. Information about what was detected and steps to move forward are below. If the secret scanner detected a valid secret, please take immediate action to remove it from your branch. Apollo also recommends that you invalidate the secret in the system it grants access to. While there are additional steps to prepare the branch for merging, protecting the secret is the top priority. If the scanner detected a value that is not actually secret or it detected a value that was a secret but has been invalidated, follow the steps in the "Resolution Process" section below to mark it as a false/benign positive. One common reason for benign positive detections is test values. For example, if you are committing a value (like a private key, JWT, etc.) that would usually be secret but is being used in a test case to validate functionality, a detection will be generated. This is safe to allowlist. Values Detected
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished pairing with @clenfest , code should be simpler now around __typename
additions and we should be reusing the @requires
optimizations for @fromContext
selection sets now.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @apollo/composition@2.9.3 ### Patch Changes - Updated dependencies \[[`345661c558773e4eb5d5f0b28464a8d1acdc2a2d`](345661c), [`e00e1c9892b48ac89823597113989830877966ef`](e00e1c9), [`cc4573471696ef78d04fa00c4cf8e5c50314ba9f`](cc45734), [`062572b3253e8640b60a0bf58b83945094b76b6f`](062572b), [`df5eb3cb0e2b4802fcd425ab9c23714de2707db3`](df5eb3c), [`1c99cb0dcc6c639ac351210932623ab0bd6907e4`](1c99cb0)]: - @apollo/query-graphs@2.9.3 - @apollo/federation-internals@2.9.3 ## @apollo/gateway@2.9.3 ### Patch Changes - Updated dependencies \[[`345661c558773e4eb5d5f0b28464a8d1acdc2a2d`](345661c), [`7fe1465cf35c2efe37ac5c73fac2b7ea04173318`](7fe1465), [`cc4573471696ef78d04fa00c4cf8e5c50314ba9f`](cc45734), [`062572b3253e8640b60a0bf58b83945094b76b6f`](062572b), [`df5eb3cb0e2b4802fcd425ab9c23714de2707db3`](df5eb3c), [`1c99cb0dcc6c639ac351210932623ab0bd6907e4`](1c99cb0)]: - @apollo/query-planner@2.9.3 - @apollo/federation-internals@2.9.3 - @apollo/composition@2.9.3 ## @apollo/federation-internals@2.9.3 ### Patch Changes - fix: normalize field set selection sets ([#3162](#3162)) `FieldSet` scalar represents a selection set without outer braces. This means that users could potentially specify some selections that could be normalized (i.e. eliminate duplicate field selections, hoist/collapse unnecessary inline fragments, etc). Previously we were using `@requires` field set selection AS-IS for edge conditions. With this change we will now normalize the `FieldSet` selections before using them as fetch node conditions. - Fixed missing referenced variables in the `variableUsages` field of fetch operations ([#3166](#3166)) Query variables used in fetch operation should be listed in the `variableUsages` field. However, there was a bug where variables referenced by query-level directives could be missing in the field. - Fix fragment generation recursion logic to apply minification on all subselections. ([#3158](#3158)) - Fixed a bug that `__typename` with applied directives gets lost in fetch operations. ([#3164](#3164)) The sibling typename optimization used by query planner simplifies operations by folding `__typename` selections into their sibling selections. However, that optimization does not account for directives or aliases. The bug was applying the optimization even if the `__typename` has directives on it, which caused the selection to lose its directives. Now, `__typename` with directives (or aliases) are excluded from the optimization. ## @apollo/query-graphs@2.9.3 ### Patch Changes - Fixes edge case where contextual arguments can yield inefficient query plans. Also fixes naming of query plan arguments which can be a problem when using contextual variables in multiple subgraphs ([#3140](#3140)) - When eliminating suboptimal indirect paths during query planning, properly check for a direct `@key` edge at the end of the potential direct path. ([#3160](#3160)) - Fixed missing referenced variables in the `variableUsages` field of fetch operations ([#3166](#3166)) Query variables used in fetch operation should be listed in the `variableUsages` field. However, there was a bug where variables referenced by query-level directives could be missing in the field. - Updated dependencies \[[`cc4573471696ef78d04fa00c4cf8e5c50314ba9f`](cc45734), [`062572b3253e8640b60a0bf58b83945094b76b6f`](062572b), [`df5eb3cb0e2b4802fcd425ab9c23714de2707db3`](df5eb3c), [`1c99cb0dcc6c639ac351210932623ab0bd6907e4`](1c99cb0)]: - @apollo/federation-internals@2.9.3 ## @apollo/query-planner@2.9.3 ### Patch Changes - Fixes edge case where contextual arguments can yield inefficient query plans. Also fixes naming of query plan arguments which can be a problem when using contextual variables in multiple subgraphs ([#3140](#3140)) - Ensure all useless fetch groups are removed ([#3163](#3163)) When removing "useless" fetch nodes/groups we remove them in-place while still iterating over the same list. This leads to potentially skipping processing of some the children fetch nodes, as when we remove nodes we left shift all remaining children but the iterator keeps the old position unchanged effectively skipping next child. - fix: normalize field set selection sets ([#3162](#3162)) `FieldSet` scalar represents a selection set without outer braces. This means that users could potentially specify some selections that could be normalized (i.e. eliminate duplicate field selections, hoist/collapse unnecessary inline fragments, etc). Previously we were using `@requires` field set selection AS-IS for edge conditions. With this change we will now normalize the `FieldSet` selections before using them as fetch node conditions. - Fixed missing referenced variables in the `variableUsages` field of fetch operations ([#3166](#3166)) Query variables used in fetch operation should be listed in the `variableUsages` field. However, there was a bug where variables referenced by query-level directives could be missing in the field. - Fixed a bug that `__typename` with applied directives gets lost in fetch operations. ([#3164](#3164)) The sibling typename optimization used by query planner simplifies operations by folding `__typename` selections into their sibling selections. However, that optimization does not account for directives or aliases. The bug was applying the optimization even if the `__typename` has directives on it, which caused the selection to lose its directives. Now, `__typename` with directives (or aliases) are excluded from the optimization. - Updated dependencies \[[`345661c558773e4eb5d5f0b28464a8d1acdc2a2d`](345661c), [`e00e1c9892b48ac89823597113989830877966ef`](e00e1c9), [`cc4573471696ef78d04fa00c4cf8e5c50314ba9f`](cc45734), [`062572b3253e8640b60a0bf58b83945094b76b6f`](062572b), [`df5eb3cb0e2b4802fcd425ab9c23714de2707db3`](df5eb3c), [`1c99cb0dcc6c639ac351210932623ab0bd6907e4`](1c99cb0)]: - @apollo/query-graphs@2.9.3 - @apollo/federation-internals@2.9.3 ## @apollo/subgraph@2.9.3 ### Patch Changes - Updated dependencies \[[`cc4573471696ef78d04fa00c4cf8e5c50314ba9f`](cc45734), [`062572b3253e8640b60a0bf58b83945094b76b6f`](062572b), [`df5eb3cb0e2b4802fcd425ab9c23714de2707db3`](df5eb3c), [`1c99cb0dcc6c639ac351210932623ab0bd6907e4`](1c99cb0)]: - @apollo/federation-internals@2.9.3 ## apollo-federation-integration-testsuite@2.9.3 Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Fixes edge case where contextual arguments can lead to inefficient query plans. Also fixes naming of query plan arguments which can be a problem when using contextual variables in multiple subraphs.