Skip to content

feat: improve performance of subgraph batch-publish#2969

Merged
JivusAyrus merged 12 commits into
mainfrom
suvij/eng-9728-controlplane-parallelize-composition-pipeline-for-batch
Jun 23, 2026
Merged

feat: improve performance of subgraph batch-publish#2969
JivusAyrus merged 12 commits into
mainfrom
suvij/eng-9728-controlplane-parallelize-composition-pipeline-for-batch

Conversation

@JivusAyrus

@JivusAyrus JivusAyrus commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added a batched recomposition and deployment pipeline for affected federated graphs, enabled via a feature flag and using concurrency-limited processing.
  • Refactor

    • Updated the federated subgraph publish flow to use the new batched pipeline after schema updates.
    • Improved performance of affected schema writes and follow-on data collection with optional batch-level memoization for federated-graph and feature-flag queries.
    • Extended feature-flag collection with cache support to reduce redundant lookups during label-matcher based retrieval.
image The above results are for 50 changed subgraphs(local) - Parallel Compositions - Improved the write and collect section to have minimal reads from the db, so it shouldnt exponentially grow with changed subgraphs

Checklist

@JivusAyrus
JivusAyrus requested a review from a team as a code owner June 15, 2026 08:52
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: 2332ce57-8d3e-4b0a-bb76-ebd62cf23ccc

📥 Commits

Reviewing files that changed from the base of the PR and between 1c58b99 and 0fca6d6.

📒 Files selected for processing (1)
  • controlplane/src/core/services/CompositionService.ts
💤 Files with no reviewable changes (1)
  • controlplane/src/core/services/CompositionService.ts

Walkthrough

Introduces a bounded-concurrency batched recomposition/deploy path (recomposeAndDeployAffectedBatch) in CompositionService, adds cross-batch single-flight memoization caches (FeatureFlagCollectCaches, memoizePromise) in FeatureFlagRepository and SubgraphRepository, and switches publishFederatedSubgraphs to invoke the new batch method.

Changes

Batched Composition/Deploy Pipeline

Layer / File(s) Summary
Batch memoization caches
controlplane/src/core/repositories/FeatureFlagRepository.ts, controlplane/src/core/repositories/SubgraphRepository.ts
Exports FeatureFlagCollectCaches type and adds internal memoizePromise helper for single-flight async deduplication. Updates getFeatureFlagsByBaseSubgraphIdAndLabelMatchers to accept and use those caches. SubgraphRepository imports FeatureFlagCollectCaches, extends writeSchemaAndCollectAffected with optional listByFederatedGraphCache and featureFlagCaches parameters, applies them in the feature-subgraph collection path, and constructs and threads both caches through batchWriteAndCollect.
CompositionService batch recompose/deploy implementation
controlplane/src/core/services/CompositionService.ts
Adds p-limit import and COMPOSITION_DEPLOY_CONCURRENCY constant. Implements recomposeAndDeployAffectedBatch with windowed parallel composition for base graphs and feature flags, sequential DB persistence with deferred parallel uploads, and a final parallel mapper rebuild. Adds private helpers composeAffectedBaseGraph, composeAffectedFeatureFlag, persistAndUploadBatch, persistAndQueueBaseUpload, and persistAndQueueFeatureFlagUploads.
Entry point switch
controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
Switches the phase-2 recomposition/deploy call from recomposeAndDeployAffected to recomposeAndDeployAffectedBatch with isFeatureSubgraph: false.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • wundergraph/cosmo#2847: Main PR's recomposeAndDeployAffectedBatch method directly supports the split-config loading composition/deploy flow that this PR gates behind the SPLIT_CONFIG_LOADING_FEATURE_ID feature flag.
  • wundergraph/cosmo#2949: Both PRs touch the subgraph batch publishing pipeline, updating publishFederatedSubgraphs.ts and refactoring SubgraphRepository.ts's batch schema write and affected federated-graph/feature-flag collection logic—the main PR additionally switches to the new CompositionService.recomposeAndDeployAffectedBatch entry point.
  • wundergraph/cosmo#2935: Both PRs modify controlplane/src/core/services/CompositionService.ts in the composition/deployment orchestration path—main PR adds the new batched pipeline and helpers, while the retrieved PR refactors internal composition/deployment helpers that support this new flow.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 'feat: improve performance of subgraph batch-publish' directly and concisely summarizes the main objective of the pull request, which is to enhance the performance of batch publishing operations for subgraphs through parallel compositions and write-collect optimizations.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@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: 2

🧹 Nitpick comments (2)
controlplane/src/core/services/CompositionService.ts (1)

690-708: ⚡ Quick win

Type the contract router config instead of letting it become any.

let contractRouterExecutionConfig; infers any and then flows into saveComposition and contractBaseCompositionDataByContractId.

Proposed change
-          let contractRouterExecutionConfig;
+          let contractRouterExecutionConfig: RouterConfig | undefined;

As per coding guidelines, **/*.{ts,tsx} should “Avoid any type in TypeScript; use specific types or generics.”

🤖 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 `@controlplane/src/core/services/CompositionService.ts` around lines 690 - 708,
The variable contractRouterExecutionConfig is declared without a type
annotation, causing it to be inferred as any type. Add an explicit type
annotation to contractRouterExecutionConfig based on the return type of the
deserializeRouterExecutionConfig function. Determine the correct type (likely
RouterExecutionConfig or similar) from the function's return type definition and
use it in the let statement, optionally allowing for null or undefined if the
function can return those values.

Source: Coding guidelines

controlplane/src/core/repositories/FeatureFlagRepository.ts (1)

68-72: ⚡ Quick win

Use an interface for the exported cache shape.

FeatureFlagCollectCaches is an exported object shape, so keep it aligned with the TypeScript style rule.

Proposed change
-export type FeatureFlagCollectCaches = {
+export interface FeatureFlagCollectCaches {
   featureFlagsByBaseSubgraphId: Map<string, ReturnType<FeatureFlagRepository['getFeatureFlagsByBaseSubgraphId']>>;
   matchedFeatureFlagsByLabelKey: Map<string, ReturnType<FeatureFlagRepository['getMatchedFeatureFlags']>>;
   featureSubgraphsByFlagId: Map<string, ReturnType<FeatureFlagRepository['getFeatureSubgraphsByFeatureFlagId']>>;
-};
+}

As per coding guidelines, **/*.{ts,tsx} should “Prefer interfaces over type aliases for object shapes in TypeScript.”

🤖 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 `@controlplane/src/core/repositories/FeatureFlagRepository.ts` around lines 68
- 72, The FeatureFlagCollectCaches is an exported object shape currently defined
as a type alias, but it should be converted to an interface to align with
TypeScript coding guidelines that prefer interfaces over type aliases for object
shapes. Change the declaration from using `export type FeatureFlagCollectCaches
= {` syntax to `export interface FeatureFlagCollectCaches {` syntax, and remove
the trailing semicolon after the closing brace to match standard interface
declaration patterns.

Source: Coding guidelines

🤖 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 `@controlplane/src/core/repositories/FeatureFlagRepository.ts`:
- Around line 1140-1150: The memoization keys for both
getFeatureFlagsByBaseSubgraphId and getMatchedFeatureFlags calls are incomplete.
The first call only includes baseSubgraphId in its key but the query depends on
namespaceId and excludeDisabled as well, and the second call only includes the
sorted label matchers but also depends on namespaceId and excludeDisabled.
Update the memoization key for the first memoizePromise call to include
baseSubgraphId, namespaceId, and excludeDisabled combined into a single key, and
update the second memoizePromise call's key to include the sorted label
matchers, namespaceId, and excludeDisabled combined into a single key. This
ensures cache entries are properly isolated by all query dimensions to prevent
returning stale data across different namespaces or filter modes.

In `@controlplane/src/core/services/CompositionService.ts`:
- Around line 839-850: The saveRouterConfigHash call on line 839 persists the
router configuration hash before validating the router compatibility version on
lines 842-850, which means if the validation fails, the database is left
pointing to a config path that was never uploaded. Move the validation logic
(the check for readyPathOverride obtained from getLatestPath) BEFORE the
saveRouterConfigHash call, so that the hash is only persisted when the router
compatibility version is confirmed to be valid. This ensures the database state
remains consistent with uploaded configurations.

---

Nitpick comments:
In `@controlplane/src/core/repositories/FeatureFlagRepository.ts`:
- Around line 68-72: The FeatureFlagCollectCaches is an exported object shape
currently defined as a type alias, but it should be converted to an interface to
align with TypeScript coding guidelines that prefer interfaces over type aliases
for object shapes. Change the declaration from using `export type
FeatureFlagCollectCaches = {` syntax to `export interface
FeatureFlagCollectCaches {` syntax, and remove the trailing semicolon after the
closing brace to match standard interface declaration patterns.

In `@controlplane/src/core/services/CompositionService.ts`:
- Around line 690-708: The variable contractRouterExecutionConfig is declared
without a type annotation, causing it to be inferred as any type. Add an
explicit type annotation to contractRouterExecutionConfig based on the return
type of the deserializeRouterExecutionConfig function. Determine the correct
type (likely RouterExecutionConfig or similar) from the function's return type
definition and use it in the let statement, optionally allowing for null or
undefined if the function can return those values.
🪄 Autofix (Beta)

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: b76b482f-e9e0-4101-805f-f58d43758640

📥 Commits

Reviewing files that changed from the base of the PR and between 5815788 and 761e7bf.

📒 Files selected for processing (4)
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
  • controlplane/src/core/repositories/FeatureFlagRepository.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/services/CompositionService.ts

Comment thread controlplane/src/core/repositories/FeatureFlagRepository.ts
Comment thread controlplane/src/core/services/CompositionService.ts Outdated
@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.30584% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.52%. Comparing base (a8cc2af) to head (0fca6d6).

Files with missing lines Patch % Lines
...ntrolplane/src/core/services/CompositionService.ts 82.27% 78 Missing ⚠️

❌ Your patch check has failed because the patch coverage (84.30%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2969       +/-   ##
===========================================
+ Coverage   42.77%   65.52%   +22.74%     
===========================================
  Files         861      335      -526     
  Lines      124386    48671    -75715     
  Branches     9995     5431     -4564     
===========================================
- Hits        53204    31890    -21314     
+ Misses      70837    16755    -54082     
+ Partials      345       26      -319     
Files with missing lines Coverage Δ
.../bufservices/subgraph/publishFederatedSubgraphs.ts 80.67% <100.00%> (ø)
...ane/src/core/repositories/FeatureFlagRepository.ts 88.65% <100.00%> (+0.15%) ⬆️
...lplane/src/core/repositories/SubgraphRepository.ts 89.00% <100.00%> (+0.11%) ⬆️
...ntrolplane/src/core/services/CompositionService.ts 87.87% <82.27%> (-2.89%) ⬇️

... and 526 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.

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

LGTM

@JivusAyrus
JivusAyrus merged commit e920085 into main Jun 23, 2026
13 checks passed
@JivusAyrus
JivusAyrus deleted the suvij/eng-9728-controlplane-parallelize-composition-pipeline-for-batch branch June 23, 2026 08:37
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