Skip to content

feat: merge entity fetches and then schedule optimally - #3146

Merged
ysmolski merged 19 commits into
mainfrom
yury/mf-before-schedule
Aug 13, 2026
Merged

feat: merge entity fetches and then schedule optimally#3146
ysmolski merged 19 commits into
mainfrom
yury/mf-before-schedule

Conversation

@ysmolski

@ysmolski ysmolski commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This PR adds two feature flags for the Cosmo's Query Planner:

engine.enable_multi_fetch

This option merges entity fetches that target the same subgraph and
execute at the same point in the query plan
into a single request with aliased _entities fields.
This reduces the number of requests sent to subgraphs.
env: ENGINE_ENABLE_MULTI_FETCH

engine.enable_schedule_fetches

This feature replaces the wave-based fetch execution
with a dependency-aware fetch scheduler.
Independent fetch chains progress as soon as
their own dependencies complete,
instead of waiting for the slowest fetch in each wave.
env: ENGINE_ENABLE_SCHEDULE_FETCHES

By default, these features are disabled:

engine:
  enable_multi_fetch: false       
  enable_schedule_fetches: false 

Built on top of wundergraph/graphql-go-tools#1612

Summary by CodeRabbit

  • New Features

    • Added optional multi-fetch execution to merge compatible entity fetches.
    • Added dependency-aware fetch scheduling, including parallel execution of independent fetch chains.
    • Both options are configurable through YAML and environment variables and remain disabled by default.
  • Documentation

    • Updated router configuration reference and examples with the new execution settings.
  • Tests

    • Added coverage for multi-fetch behavior, scheduling combinations, parallel chains, query plans, responses, and request counts.

ysmolski added 3 commits July 24, 2026 15:33
Use the scheduling improvement in the engine's post-planner stage.

scheduleFetches is a dependency-aware scheduler that emits nested
Sequence/Parallel trees, collapsing independent chains onto their own
branches instead of synchronizing them at wave barriers.
…579-reduce-federated-query-latency-by-improving-fetch-grouping

# Conflicts:
#	router-tests/go.mod
#	router-tests/go.sum
#	router/go.mod
#	router/go.sum
@ysmolski
ysmolski requested a review from a team as a code owner August 7, 2026 15:11
@github-actions github-actions Bot added the router label Aug 7, 2026
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4cc64e7b-626c-41cc-bca7-fb178f60d853

📥 Commits

Reviewing files that changed from the base of the PR and between 331e9c2 and 540bcf9.

⛔ Files ignored due to path filters (2)
  • router-tests/go.sum is excluded by !**/*.sum
  • router/go.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • router-tests/go.mod
  • router/go.mod
🚧 Files skipped from review as they are similar to previous changes (2)
  • router-tests/go.mod
  • router/go.mod

Walkthrough

The router adds disabled-by-default configuration flags for multi-fetch and dependency-aware fetch scheduling. It propagates these flags into executor and planner postprocessing, updates the GraphQL tools dependency, and adds integration tests with query-plan fixtures.

Changes

Fetch scheduling controls

Layer / File(s) Summary
Execution configuration contract
router/pkg/config/config.go, router/pkg/config/config.schema.json, router/pkg/config/testdata/*, docs-website/router/configuration.mdx
Adds enable_multi_fetch and enable_schedule_fetches configuration settings. Both default to false and are documented in YAML examples.
Executor and planner option propagation
router/core/executor.go, router/core/factoryresolver.go, router/core/operation_planner.go, router/go.mod, router-tests/go.mod
Derives postprocessor options from execution flags, propagates multi-fetch settings to the planner, preserves configured options, and updates graphql-go-tools to v2.16.0.
Multi-fetch integration validation
router-tests/operations/multi_fetches_scheduling_test.go, router-tests/operations/testdata/fixtures/query_plans/*
Tests merged entity fetches, default separate fetches, parallel fetch chains, query plans, responses, and subgraph request counts.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: ⚪ Minimal · up to 540bc

The feature flags default to disabled, and no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary changes: merging entity fetches and optimizing fetch scheduling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-6b8f2917d53672ea28e575f62c688b5f83fe53b2-nonroot

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.37%. Comparing base (70cd61d) to head (540bcf9).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3146      +/-   ##
==========================================
- Coverage   62.40%   62.37%   -0.03%     
==========================================
  Files         263      263              
  Lines       31048    31070      +22     
==========================================
+ Hits        19375    19381       +6     
+ Misses      10163    10159       -4     
- Partials     1510     1530      +20     
Files with missing lines Coverage Δ
router/core/executor.go 89.70% <100.00%> (+0.47%) ⬆️
router/core/factoryresolver.go 80.75% <100.00%> (+0.03%) ⬆️
router/core/operation_planner.go 81.05% <100.00%> (+0.61%) ⬆️
router/pkg/config/config.go 84.68% <ø> (+1.68%) ⬆️

... and 14 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
router-tests/modules/query_stats_test.go (1)

171-173: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Apply the same order-independent assertion to the simple-query case.

This case now treats SubgraphRootFields order as an implementation detail. The sibling case at lines 51-70 still compares the same field with assert.Equal, which is order-sensitive. If the schedule tree shape changes traversal order for that query too, that assertion becomes flaky.

♻️ Proposed change at lines 51-70
-				assert.Equal(t, []core.SubgraphRootField{
+				// The list is deduplicated in fetch-tree traversal order, which is
+				// an implementation detail of the schedule tree shape: assert as a set.
+				assert.ElementsMatch(t, []core.SubgraphRootField{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router-tests/modules/query_stats_test.go` around lines 171 - 173, Update the
sibling simple-query test’s assertion for SubgraphRootFields to use the same
order-independent ElementsMatch assertion as the query-stats case, while
preserving the existing expected values and test coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@router/core/plan_generator.go`:
- Around line 292-294: Update PlanPreparedOperation’s planner setup to derive
postprocessor options from the configured execution settings instead of
unconditionally calling postprocess.EnableMultiFetch(). Pass those effective
options into postprocess.NewProcessor, preserving both DisableScheduleFetches
and DisableMultiFetch behavior, and add regression coverage for each setting.

---

Nitpick comments:
In `@router-tests/modules/query_stats_test.go`:
- Around line 171-173: Update the sibling simple-query test’s assertion for
SubgraphRootFields to use the same order-independent ElementsMatch assertion as
the query-stats case, while preserving the existing expected values and test
coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 87f69643-4ba7-4e29-96a8-ce79ed1d3cb0

📥 Commits

Reviewing files that changed from the base of the PR and between 5edbee2 and 1db3ab9.

⛔ Files ignored due to path filters (2)
  • router-tests/go.sum is excluded by !**/*.sum
  • router/go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • router-tests/go.mod
  • router-tests/modules/query_stats_test.go
  • router-tests/operations/testdata/fixtures/query_plans/only_query_plan.json
  • router-tests/operations/testdata/fixtures/query_plans/query_plan_with_trace_no_data.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan_operation_name.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan_operation_name_sanitized_no_data.json
  • router-tests/protocol/testdata/tracing.json
  • router/core/executor.go
  • router/core/factoryresolver.go
  • router/core/operation_planner.go
  • router/core/plan_generator.go
  • router/go.mod
  • router/pkg/config/config.go
  • router/pkg/config/config.schema.json
  • router/pkg/config/testdata/config_defaults.json
  • router/pkg/config/testdata/config_full.json

Comment thread router/core/plan_generator.go Outdated

@claude claude Bot 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.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
router-tests/operations/multi_fetch_test.go (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for DisableScheduleFetches.

The PR adds two execution flags: disable_multi_fetch and disable_schedule_fetches. This test covers only DisableMultiFetch. DisableScheduleFetches switches the planner back to the legacy wave-based fetch organizers, which is the escape hatch for this change. That path has no integration coverage in the provided files.

Add a third subtest that sets cfg.DisableScheduleFetches = true and asserts the same response body. The response must stay identical regardless of the scheduling strategy.

🧪 Proposed additional subtest
	t.Run("returns the same result when fetch scheduling is disabled", func(t *testing.T) {
		t.Parallel()

		testenv.Run(t, &testenv.Config{
			ModifyEngineExecutionConfiguration: func(cfg *config.EngineExecutionConfiguration) {
				cfg.DisableScheduleFetches = true
			},
		}, func(t *testing.T, xEnv *testenv.Environment) {
			res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{Query: multiFetchQuery})
			require.JSONEq(t, multiFetchExpectedResponse, res.Body)
		})
	})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router-tests/operations/multi_fetch_test.go` at line 13, Add a third parallel
subtest in TestMultiFetch that sets DisableScheduleFetches to true through
ModifyEngineExecutionConfiguration, executes multiFetchQuery, and asserts the
response matches multiFetchExpectedResponse with JSON equality, preserving the
existing test setup and result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@router-tests/operations/multi_fetch_test.go`:
- Line 13: Add a third parallel subtest in TestMultiFetch that sets
DisableScheduleFetches to true through ModifyEngineExecutionConfiguration,
executes multiFetchQuery, and asserts the response matches
multiFetchExpectedResponse with JSON equality, preserving the existing test
setup and result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fca06bdc-6397-4212-a300-d7ec341ac034

📥 Commits

Reviewing files that changed from the base of the PR and between cfb3956 and 9b5a317.

📒 Files selected for processing (9)
  • router-tests/modules/query_stats_test.go
  • router-tests/operations/multi_fetch_test.go
  • router-tests/operations/testdata/fixtures/query_plans/only_query_plan.json
  • router-tests/operations/testdata/fixtures/query_plans/query_plan_with_trace_no_data.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan_operation_name.json
  • router-tests/operations/testdata/fixtures/query_plans/response_with_query_plan_operation_name_sanitized_no_data.json
  • router/pkg/config/testdata/config_defaults.json
  • router/pkg/config/testdata/config_full.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/pkg/config/testdata/config_full.json
  • router/pkg/config/testdata/config_defaults.json

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@router-tests/protocol/integration_test.go`:
- Around line 691-693: Update the assertion message in the integration test
around the matched operation check to format the expectEmployeeOps []string with
%v instead of %s, so failures display the expected operation names correctly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5775ad7f-81a0-47a3-8d8b-9709334743dc

📥 Commits

Reviewing files that changed from the base of the PR and between d385685 and 811da76.

📒 Files selected for processing (1)
  • router-tests/protocol/integration_test.go

Comment thread router-tests/protocol/integration_test.go Outdated

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
router/pkg/config/config.schema.json (1)

2777-2787: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Describe authorization-server URLs as discovery metadata only.

authorization_server_urls does not establish token trust or validate the JWT iss claim. The statement that this field can “trust more than one authorization server” can cause operators to rely on an issuer restriction that the router does not enforce. State that configured JWKS signing keys are the trust boundary, and remove the per-issuer validation implication.

Based on learnings: AuthorizationServerURL and AuthorizationServerURLs provide RFC 9728 discovery metadata only; token validation is anchored in configured JWKS keys and does not validate JWT iss against these fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/pkg/config/config.schema.json` around lines 2777 - 2787, Update the
description for authorization_server_urls to describe AuthorizationServerURL and
AuthorizationServerURLs as RFC 9728 discovery metadata only. Remove the claims
that these fields establish trust, restrict issuers, or imply JWT iss
validation, and state that token trust is determined by configured JWKS signing
keys.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@router/pkg/config/config.schema.json`:
- Around line 2777-2787: Update the description for authorization_server_urls to
describe AuthorizationServerURL and AuthorizationServerURLs as RFC 9728
discovery metadata only. Remove the claims that these fields establish trust,
restrict issuers, or imply JWT iss validation, and state that token trust is
determined by configured JWKS signing keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 066530e9-b2f1-4698-b803-58a2ca751932

📥 Commits

Reviewing files that changed from the base of the PR and between d385685 and 909ba56.

⛔ Files ignored due to path filters (2)
  • router-tests/go.sum is excluded by !**/*.sum
  • router/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • router-tests/go.mod
  • router-tests/operations/multi_fetch_test.go
  • router/core/executor.go
  • router/core/factoryresolver.go
  • router/core/plan_generator.go
  • router/go.mod
  • router/pkg/config/config.go
  • router/pkg/config/config.schema.json
  • router/pkg/config/testdata/config_defaults.json
  • router/pkg/config/testdata/config_full.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/pkg/config/testdata/config_full.json
  • router-tests/operations/multi_fetch_test.go

@ysmolski
ysmolski requested a review from a team as a code owner August 12, 2026 14:00
@mintlify

mintlify Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
wundergraphinc 🟢 Ready View Preview Aug 12, 2026, 2:01 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@SkArchon SkArchon 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.

Code itself is ok so approving.

However my main concern is that we have the "query-plan-skip" tag for query plan changes for the customer (which is what the query planner tests are for), I would verify if at least the error is the same as the baseline error before merging (You could open a no-op PR with a comment change and add the "query-plan" label, and compare the results from there and this PR).

Comment thread docs-website/router/configuration.mdx
Comment thread router-tests/go.mod Outdated
@ysmolski

Copy link
Copy Markdown
Contributor Author

I have tested QPlanner here: https://github.com/wundergraph/cosmo-celestial/pull/2094. There is a slight performance regression detected which comes from Audit FIxes released in the engine some time ago.

@claude claude Bot 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.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@ysmolski
ysmolski merged commit 86ca1fd into main Aug 13, 2026
41 checks passed
@ysmolski
ysmolski deleted the yury/mf-before-schedule branch August 13, 2026 14:27
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