Skip to content

[Fusion] Keep duplicate lookup targets as result cursors - #10613

Merged
michaelstaib merged 11 commits into
mainfrom
tte/fusion-duplicate-lookup-target-cursors
Oct 21, 2026
Merged

michaelstaib merged 11 commits into
mainfrom
tte/fusion-duplicate-lookup-target-cursors

Conversation

@tobias-tengler

@tobias-tengler tobias-tengler commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Stacked on #10612 (copy completed leaf values into duplicate lookup targets). The base branch is tte/fusion-copy-duplicate-lookup-leaf-values, so the diff shows only this change.

When several lookup targets have the same requirement values, the gateway sends one variable set and merges its result into the primary target and every additional target. For the federation benchmark query this means 324 lookup targets but only 7 distinct variable sets, and each duplicate target re-runs path building, variable writing and the full merge, which resolves the target again by walking its path from the root.

This PR keeps the additional targets as cursors into the result document. The merge uses the cursors directly while no value that references an object or a list has been replaced since they were collected, and otherwise resolves the target from the root by its path as before. The paths of the additional targets are created on first access, so error attribution, telemetry and custom source schema clients keep working.

Benchmark query Version Mean Error Ratio
composite-schema main 426.2 µs 7.71 µs 1.00
composite-schema #10612 430.1 µs 7.83 µs 1.01
composite-schema PR 335.0 µs 7.60 µs 0.79
apollo-federation main 435.4 µs 7.59 µs 1.00
apollo-federation #10612 417.6 µs 7.17 µs 0.96
apollo-federation PR 331.7 µs 7.88 µs 0.76
Gateway Version RPS Ratio CPU per request Ratio p99
composite-schema (.NET 10) main 4450 1.00 589.5 µs 1.00 25.31 ms
composite-schema (.NET 10) PR 4875 1.10 488.5 µs 0.83 23.49 ms
apollo-federation (.NET 11) main 4144 1.00 562.5 µs 1.00 25.40 ms
apollo-federation (.NET 11) PR 4474 1.08 459.5 µs 0.82 24.32 ms

The micro benchmark runs the whole federation benchmark query through the gateway executor against canned subgraph responses; this PR takes about a fifth off the request on both families. In the k6 screen (constant scenario, A B B A, 3 × 30 s runs per invocation, PR measured together with #10612) composite-schema gains 425 RPS and apollo-federation 330 RPS, about 6.5σ and 8σ of main's run-to-run noise (σ ≈ 65 and 40 RPS), while gateway CPU per request drops by 17% and 18% and p99 improves on both.

When one lookup result is merged into a primary target and several additional
targets, the primary target is completed from the source data and, when every
completed value is a non-null, non-enum leaf of an object with the same
selection set, the completed rows are copied into each additional target
instead of completing the same source data again. Targets that do not qualify,
merges with errors and pocketed errors keep the existing completion.
Additional targets of a lookup variable set are kept as cursors into the
result document instead of building a path per target. The merge uses the
cursors directly while no value that references an object or a list was
replaced since the targets were collected, and resolves the target from the
root by its path otherwise. The paths of a CompactPathSegment are created on
first access, so error attribution, telemetry and custom clients keep working.
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Patch coverage

98.3% of changed lines covered (230/234)

File Covered Changed Patch %
…/Fusion/src/Fusion.Execution/Text/Json/CompactPathSegment.cs 13 15 86.7% 🟡
…/src/Fusion.Execution/Execution/Results/FetchResultStore.cs 73 75 97.3% 🟡
…/Execution/Results/AdditionalPathAccumulator.cs 104 104 100.0% 🟢
…/Execution/Results/DuplicateTargetCompactPathSource.cs 17 17 100.0% 🟢
…/Execution/Results/FetchResultStore.RepresentationValue.cs 9 9 100.0% 🟢
…/Fusion/src/Fusion.Execution/Execution/ThrowHelper.cs 1 1 100.0% 🟢
…/src/Fusion.Execution/Text/Json/CompositeResultDocument.cs 13 13 100.0% 🟢
Uncovered changed lines (JSON)
{
  "sha": "f40d685d69bfb8f5d1e24762356ebcace669ac57",
  "files": [
    { "path": "src/HotChocolate/Fusion/src/Fusion.Execution/Text/Json/CompactPathSegment.cs", "ranges": [[85, 85], [90, 90]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Results/FetchResultStore.cs", "ranges": [[951, 952]] }
  ]
}

Project coverage: 59.2% (313424/529767 lines)

Resolves the duplicate lookup target copy from #10612 onto cursor targets:
the leaf copy and entity reuse read additional targets through
AdditionalTargets, and TryAssignLeafValue tracks structure replacements like
the other value writers. Drops the row offset copy helpers that #10612 no
longer uses.
- Creating the paths of target cursors now fails when their result document
  is no longer the store's result, instead of reading the rows of a released
  document.
- The additional target accumulator is presized with the number of targets
  and rents its path buffer only when a path is added, so cursor-only
  accumulation no longer grows an unused buffer.
- Tests cover merging after a structure change makes the cursors stale, the
  released document guard, and accumulators that mix paths and cursors.
@michaelstaib
michaelstaib self-requested a review October 21, 2026 04:26
- Paths created from target cursors on first access are allocated instead
  of rented from the request path pool, which never got them back and grew
  for every event of a subscription.
- The benchmark copy passes the capacity hint like production.
- Tests cover the cursor fast path for later variable sets, duplicate
  targets of a source result with errors, and accumulator growth with paths.
- Documentation states the contracts of the accumulator, the cursor paths
  and the target resolver.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 04:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@michaelstaib
michaelstaib merged commit d4eaccc into main Oct 21, 2026
156 checks passed
@michaelstaib
michaelstaib deleted the tte/fusion-duplicate-lookup-target-cursors branch October 21, 2026 05:51
michaelstaib added a commit that referenced this pull request Oct 21, 2026
Takes main's duplicate lookup target copy (#10612) and target cursors
(#10613) and keeps this branch's raw leaf key matching on top. The element
overload of TryCreateVariableValues reports the dedup index again for the
leaf key registration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants