Skip to content

[Fusion] Fix the gateway sending empty entries for deferred groups whose fields are all skipped - #150

Open
michaelstaib wants to merge 10 commits into
mst/stream-v2from
mst/fusion-skipped-deferred-group
Open

michaelstaib wants to merge 10 commits into
mst/stream-v2from
mst/fusion-skipped-deferred-group

Conversation

@michaelstaib

@michaelstaib michaelstaib commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

When every field of a deferred fragment is skipped by @skip/@include, the gateway no longer announces that group as pending and no longer sends an empty incremental entry for it. This matches the core executor, which sends neither. #145 showed this case: the gateway used to send incremental: [{ id, data: {} }] and a completed entry for the outer group.

Changes in Fusion.Execution:

  • IncrementalPlan records at compile time which of its field selections depend on include conditions. At runtime the executor checks them against the request's include flags, using the compiled operation's conditions with no recomputation.
  • An incremental plan whose fields are all excluded still runs silently when nested groups need its requirements. It announces nothing and delivers nothing of its own.
  • If a silent plan fails, its silent groups are marked failed and the announced groups nested under them complete with the error. A group that a delivering plan also covers keeps that plan's data and completes without the silent plan's errors.
  • OperationCompiler.Compile clears the rented field map, as CompileSelectionSet already does. Pools with a non-clearing policy, used in the tests, otherwise leak fields from an earlier compile.

No plan snapshot or plan JSON changed, so OperationPlanner.Version and the format version stay the same.

DeferTests covers:

The Fusion build has 0 warnings. Fusion.Execution.Tests and DeferTests pass on net10 and net8. Docker-backed tests were not run. No benchmark was run. The new runtime work applies only to deferred requests that have conditional incremental plans.

…d by skip or include

The gateway announced a deferred group as pending and sent an incremental entry with
empty data when every field of the group was excluded by @Skip or @include. Core
announces no such group and sends no entry.

An incremental plan now records the selections of its fields when all of them depend
on an include condition. When none of them is included for a request, the plan is
not run and delivery groups that only such plans deliver with are not announced.
Enclosed groups are announced with their next enclosing active group.

No plan changes, so the existing plan snapshots and OperationPlanner.Version are unchanged.
…heir requirements

A deferred plan whose fields are all excluded by skip or include was dropped, so a nested
deferred group that requires a value only that plan produces ran against the root scope and
delivered nothing. The plan now runs without announcing its groups and without incremental
or completed entries, and nested groups start after it with its scope. A group with no
announced enclosing group is announced in the initial payload, and an enclosed group is
announced when its nearest announced enclosing group completes.

The fields of an incremental plan are now read from its compiled selection set instead of
its syntax tree.

The snapshot of the middle group excluded by skip changes the address value of the inner
group: the inner plan now resolves its lookup key from the scope of the silent middle plan
instead of the outer plan, which yields the id of the root user. The payload sequence is
unchanged.

No plan changes, so OperationPlanner.Version and the plan JSON format are unchanged.
…ed silent plan

A silent plan, whose deferred fields are all excluded by skip or include, has no completed
entry of its own. When its requirement fetch failed, a nested group announced earlier never
completed and the error was dropped. Announced groups whose announcing chain runs through
the failed silent group now complete with the errors of the plan, and their plans are not
started.

No plan changes, so OperationPlanner.Version and the plan JSON format are unchanged.
Copilot AI balanced review requested due to automatic review settings November 1, 2026 07:23
…kipped-deferred-group

# Conflicts:
#	src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/DeferTests.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved execution and scheduling defects can suppress independent deferred data, propagate silent-plan errors to delivering groups, and start nested plans before required parent data is available.

3 open issues
What changed in this PR

This PR addresses the empty deferred payload exposed by #145, bringing Fusion gateway behavior closer to the core executor when all fields in a deferred group are skipped.

Changes:

  • Records conditional field selections and suppresses pending announcements and data entries for excluded groups.
  • Preserves silent execution for nested requirements and adds failure handling for dependent and shared groups.
  • Clears the compiler’s pooled field map and adds regression coverage for conditional deferred delivery.
File Description
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​DeferTests.cs Adds conditional delivery, nested requirement, and failure regression tests.
src/​HotChocolate/​Fusion/​test/​Fusion.AspNetCore.Tests/​__snapshots__/​DeferTests.Defer_Siblings_Should_CompleteSharedGroupWithoutErrors_When_ExcludedSharedPlanFails.yaml Captures shared-group delivery when an excluded plan fails.
src/​HotChocolate/​Fusion/​src/​Fusion.Execution/​Execution/​OperationPlanExecutor.cs Adds silent-group detection, announcement handling, and failure propagation.
src/​HotChocolate/​Fusion/​src/​Fusion.Execution/​Execution/​Nodes/​OperationCompiler.cs Clears rented field maps before compilation.
src/​HotChocolate/​Fusion/​src/​Fusion.Execution/​Execution/​Nodes/​IncrementalPlan.cs Records conditional selections for runtime exclusion checks.

Review effort: Balanced

Comment on lines +342 to +345
&& GetAnnouncingDeliveryGroup(
deliveryGroup.Parent,
activeDeliveryGroupIds,
silentDeliveryGroupIds) is null)
Comment on lines +418 to +420
CompleteAnnouncedGroupsOfFailedSilentGroups(
operationPlan,
deliveryGroups,
if (segment.Kind is not SelectionPathSegmentKind.Field
|| !selectionSet.TryGetSelection(segment.Name, out var selection)
|| selection.IsLeaf
|| selection.NamedType is not IObjectTypeDefinition)
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.

2 participants