GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix dataloader dispatch in @defer when multiple deferred fragments exist by timward60 · Pull Request #4270 · graphql-java/graphql-java

Conversation

@timward60

@timward60 timward60 commented Feb 24, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Title

Fix dataloader dispatch in @defer when multiple deferred fragments exist (#4269)

Description

Problem

When a query contains multiple @defer fragments at the same level, dataloaders invoked inside those deferred fragments are never dispatched. The deferred payloads hang until the request times out.

This was introduced in #3980 ("make dataloader work inside defer blocks").

Root Cause

In DeferredExecutionSupportImpl.createDeferredFragmentCall(), the AlternativeCallContext is constructed with deferredFields.size() — the total field count across all @defer fragments — instead of the field count for the specific fragment:

// BUG: deferredFields.size() is the total across ALL @defer fragments
AlternativeCallContext alternativeCallContext = new AlternativeCallContext(level, deferredFields.size());

// But mergedFields is only the fields for THIS specific fragment
List<MergedField> mergedFields = deferredExecutionToFields.get(deferredExecution);

Each DeferredFragmentCall only invokes its own fields, so the fetch counter will only reach the count of fields in that fragment, never the inflated total. Both PerLevelDataLoaderDispatchStrategy.fieldFetched() and ExhaustedDataLoaderDispatchStrategy.deferFieldFetched() compare against this inflated count to decide when to dispatch, so dispatch never triggers.

Example: 2 @defer fragments with 1 field each → expectedFirstLevelFetchCount = 2, but each fragment only fetches 1 field → dispatch condition 1 == 2 is never satisfied → dataloaders hang.

Fix

Pass mergedFields.size() (the per-fragment field count) instead of deferredFields.size():

 private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution) {
     int level = parameters.getPath().getLevel() + 1;
-    AlternativeCallContext alternativeCallContext = new AlternativeCallContext(level, deferredFields.size());
-
-    List<MergedField> mergedFields = deferredExecutionToFields.get(deferredExecution);
+    List<MergedField> mergedFields = deferredExecutionToFields.get(deferredExecution);
+    AlternativeCallContext alternativeCallContext = new AlternativeCallContext(level, mergedFields.size());

This is safe for mixed deferred/non-deferred selection sets because the constructor already partitions fields — non-deferred fields are routed into nonDeferredFieldNames and never added to deferredExecutionToFields.

Tests Added

Two new test cases in DeferWithDataLoaderTest:

  • multiple separate defer fragments with dataloader fields dispatch correctly — Two separate @defer blocks (departments and expensiveDepartments) on shops, each backed by dataloaders. Without the fix, all 3 dispatch strategy variants time out at ~10s each. With the fix, they complete in ~1s total.
  • mixed deferred and non-deferred fields with dataloaders dispatch correctly — Non-deferred departments alongside a single deferred expensiveDepartments, confirming the fix correctly handles mixed selection sets.

Both tests cover all three dispatch strategy modes: default (PerLevel), ENABLE_DATA_LOADER_CHAINING, and ENABLE_DATA_LOADER_EXHAUSTED_DISPATCHING.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
timward60 and others added 4 commits February 17, 2026 09:53
@timward60
Add tests for dataloader dispatch with multiple @defer fragments
d9c873d
Add two test cases to DeferWithDataLoaderTest:
- Multiple separate @defer fragments with dataloader fields
- Mixed deferred and non-deferred fields with dataloaders

Both tests verify dispatch works correctly across all three dispatch
strategy modes. Without the corresponding fix, the first test times out
because dataloaders are never dispatched.

Co-authored-by: Copilot <[email protected]>
@timward60
Fix dataloader dispatch in @defer with multiple deferred fragments
54bb6c2
Use mergedFields.size() (per-fragment field count) instead of
deferredFields.size() (total across all fragments) when constructing
AlternativeCallContext. The inflated count prevented dispatch strategies
from ever triggering dispatch since each fragment only fetches its own
fields.

Fixes the bug introduced in graphql-java#3980.

Co-authored-by: Copilot <[email protected]>
@timward60
Merge branch 'master' into fix/defer-dataloader-dispatch-field-count
2385ec3
@andimarek
Merge branch 'master' into fix/defer-dataloader-dispatch-field-count
67133cb
@github-actions

github-actions Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Test Report

Test Results

Java Version Total Passed Failed Errors Skipped
Java 11 5677 (+6 🟢) 5620 (+6 🟢) 0 (±0) 0 (±0) 57 (±0)
Java 17 5677 (+6 🟢) 5619 (+6 🟢) 0 (±0) 0 (±0) 58 (±0)
Java 21 5677 (+6 🟢) 5619 (+6 🟢) 0 (±0) 0 (±0) 58 (±0)
Java 25 5677 (+6 🟢) 5619 (+6 🟢) 0 (±0) 0 (±0) 58 (±0)
jcstress 32 (±0) 32 (±0) 0 (±0) 0 (±0) 0 (±0)
Total 22740 (+24 🟢) 22509 (+24 🟢) 0 (±0) 0 (±0) 231 (±0)

Code Coverage (Java 25)

Metric Covered Missed Coverage vs Master
Lines 28698 3126 90.2% ±0.0%
Branches 8332 1510 84.7% ±0.0%
Methods 7681 1224 86.3% ±0.0%

No per-class coverage changes detected.

Full HTML report: build artifact jacoco-html-report

Updated: 2026-03-07 01:04:31 UTC

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andimarek
andimarek merged commit 1babf52 into graphql-java:master Mar 9, 2026
10 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@andimarek

andimarek commented Mar 9, 2026

Copy link
Copy Markdown
Member

Thanks @timward60

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@dondonz dondonz mentioned this pull request Apr 27, 2026
Dataloader dispatch never triggers inside @defer when multiple deferred fragments exist #4269
Closed
@timward60
timward60 deleted the fix/defer-dataloader-dispatch-field-count branch September 17, 2026 04:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@timward60 @andimarek
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.