Skip to content

feat: improve performance for subgraph batch publishing#2949

Merged
wilsonrivera merged 20 commits into
mainfrom
wilson/eng-9716-controlplane-optimize-publishfederatedsubgraphs-performance
Jun 10, 2026
Merged

feat: improve performance for subgraph batch publishing#2949
wilsonrivera merged 20 commits into
mainfrom
wilson/eng-9716-controlplane-optimize-publishfederatedsubgraphs-performance

Conversation

@wilsonrivera

@wilsonrivera wilsonrivera commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Improvements

    • Faster, batched fetching when publishing federated subgraphs for improved publish performance.
    • Consolidated authorization checks and clearer handling of missing subgraphs to reduce publish failures.
    • Stronger, more consistent database transaction handling for schema versions and compositions.
  • Bug Fixes

    • Deduplicated subgraph results to prevent inconsistent publish outcomes.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/docs-website.
  • I have read the Contributors Guide.

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@coderabbitai

coderabbitai Bot commented Jun 10, 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: 5285ca84-70eb-4bf5-986e-616b90ee4cb5

📥 Commits

Reviewing files that changed from the base of the PR and between 5011ba7 and 3f661f3.

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

Walkthrough

Bulk subgraph lookup replaces per-item resolution in publishFederatedSubgraphs, publish authorization switches to RBAC span checks, SubgraphRepository gains batched name queries and concurrency improvements, and composition/schema-version repository methods run core DB operations inside explicit transactions.

Changes

Subgraph Publishing Flow and Repository Data-Path Refactor

Layer / File(s) Summary
SubgraphRepository schema-write and batch-processing
controlplane/src/core/repositories/SubgraphRepository.ts
Adds getSubgraphsByNames with deduplication and 100-item chunking, deduplicates getSubgraphsMatching output, extends UpdateSubgraphSchemaData with optional subgraph DTO, reworks writeSchemaAndCollectAffected to accept injected split-config feature and use trimEnd() for SDL comparisons, and refactors batchWriteAndCollect to fetch split-config feature once and run concurrent writes with merged affected-graph/flag aggregation.
Publish flow: bulk resolution, matching, and RBAC
controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
publishFederatedSubgraphs now calls subgraphRepo.getSubgraphsByNames(...), matches resolved subgraphs to requested SDL entries case-insensitively, computes missing names and returns ERR_NOT_FOUND when applicable, enforces write access via authContext.rbac.hasSubGraphWriteAccess(subgraph) wrapped in withSpan, computes routerCompatibilityVersion once, and includes the subgraph DTO in batch-write items.
FederatedGraphRepository addSchemaVersion transaction rewrite
controlplane/src/core/repositories/FederatedGraphRepository.ts
Reorganizes imports, narrows crypto/type imports, and moves addSchemaVersion logic into a single this.db.transaction(...) callback using tx, performing federated-graph lookup, schemaVersion insert, composed-schema and feature-flag mapping updates, and composition persistence; returns `{ composedSchemaVersionId, routerCompatibilityVersion }
GraphCompositionRepository transaction-scoped persistence
controlplane/src/core/repositories/GraphCompositionRepository.ts
Moves addComposition database operations into an explicit transaction callback using the transaction-scoped client tx, removes an unused targets import, and tightens the updated-subgraph predicate while preserving diff/insert behavior.
Worker Sentry flush and tracing passthrough
controlplane/src/core/composition/composeGraphs.worker.ts, controlplane/src/core/tracing.ts
composeGraphs worker now calls Sentry.flush(2000) on shutdown; withSpan passes fn directly into Sentry.startSpan instead of wrapping it.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wundergraph/cosmo#2903: Overlaps on refactoring FederatedGraphRepository.addSchemaVersion and GraphCompositionRepository.addComposition.
  • wundergraph/cosmo#2899: Related to the batch subgraph publish flow and RPC/CLI wiring for publishFederatedSubgraphs.
  • wundergraph/cosmo#2931: Touches Sentry flush behavior in the compose worker.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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 accurately describes the primary change: optimizing batch publishing performance for subgraphs through bulk operations instead of per-request lookups, as evidenced by the refactored publishFederatedSubgraphs function and new getSubgraphsByNames bulk query method.
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 and usage tips.

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

Caution

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

⚠️ Outside diff range comments (1)
controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts (1)

117-160: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing subgraph detection is broken after bulk fetch refactor.

The old per-entry loop populated notFound when a subgraph didn't exist. The new code iterates over results from getSubgraphsByNames, which only returns existing subgraphs. If a requested subgraph doesn't exist in the database, it simply won't appear in the results—but nothing detects this.

notFound is initialized at line 119 but never populated, making the check at line 149 dead code. Users who request a non-existent subgraph will silently succeed with a partial publish instead of receiving an ERR_NOT_FOUND error.

🐛 Proposed fix to detect missing subgraphs
   // Resolve every requested subgraph; all of them must already exist.
   let now = performance.now();
   const resolved: { subgraph: SubgraphDTO; schema: string }[] = [];
   const notFound: string[] = [];
   const typeErrors: string[] = [];
-  // for (const entry of requestedEntries) {
-  for (const subgraph of await subgraphRepo.getSubgraphsByNames(requestedEntries.map((e) => e.name), namespace.id)) {
-    // const subgraph = await subgraphRepo.byName(entry.name, req.namespace);
-    // if (!subgraph) {
-    //   notFound.push(entry.name);
-    //   continue;
-    // }
+  const requestedNames = requestedEntries.map((e) => e.name);
+  const subgraphsByName = new Map(
+    (await subgraphRepo.getSubgraphsByNames(requestedNames, namespace.id)).map((s) => [s.name, s]),
+  );
+
+  for (const entry of requestedEntries) {
+    const subgraph = subgraphsByName.get(entry.name);
+    if (!subgraph) {
+      notFound.push(entry.name);
+      continue;
+    }

     if (subgraph.type === 'grpc_plugin') {
       typeErrors.push(
         `Subgraph "${subgraph.name}" is a plugin. Please use the 'wgc router plugin publish' command to publish it.`,
       );
       continue;
     }
     if (subgraph.type === 'grpc_service') {
       typeErrors.push(
         `Subgraph "${subgraph.name}" is a grpc service. Please use the 'wgc grpc-service publish' command to publish it.`,
       );
       continue;
     }

-    const schema = requestedEntries.find((re) => re.name === subgraph.name)!.schema;
-    // const schema = entry.schema;
+    const schema = entry.schema;
     resolved.push({ subgraph, schema });
   }
🤖 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/bufservices/subgraph/publishFederatedSubgraphs.ts`
around lines 117 - 160, The bulk-fetch removed detection of missing subgraphs;
after calling subgraphRepo.getSubgraphsByNames(...) compute the set of fetched
names and compare it to requestedEntries (use requestedEntries.map(e=>e.name))
to find any missing names, populate the existing notFound array with those
missing names, and early-return the ERR_NOT_FOUND response as the code already
expects; also ensure when building resolved you only call
requestedEntries.find(...) for names that exist in the fetched results (or use a
map from requested name to schema) so you don't rely on a non-null assertion.
🧹 Nitpick comments (1)
controlplane/src/core/repositories/SubgraphRepository.ts (1)

1199-1201: ⚡ Quick win

Use linear-time deduplication in the hot path.

filter(...findIndex(...)) is O(n²). This path is now used by bulk fetching, so switching to a Set/Map-based dedupe avoids quadratic growth.

♻️ Suggested refactor
-    return subgraphs
-      .filter((sg, index, self) => self.findIndex((x) => x.targetId === sg.targetId) === index)
-      .map((sg) => {
+    const uniqueByTargetId = new Map<string, (typeof subgraphs)[number]>();
+    for (const sg of subgraphs) {
+      if (!uniqueByTargetId.has(sg.targetId)) {
+        uniqueByTargetId.set(sg.targetId, sg);
+      }
+    }
+
+    return [...uniqueByTargetId.values()].map((sg) => {
🤖 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/SubgraphRepository.ts` around lines 1199 -
1201, The current dedupe uses filter(...findIndex(...)) which is O(n²); replace
it with a linear-time Set/Map-based approach: iterate over the subgraphs array
once, track seen targetIds (e.g., const seen = new Set<string>() or a
Map<string, Subgraph>), collect only the first occurrence per sg.targetId into a
new array, then .map over that array; update the code around the subgraphs
variable (the chain using .filter(...findIndex(...)).map(...)) so it uses the
single-pass dedupe before mapping, preserving the original order and behavior.
🤖 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/bufservices/subgraph/publishFederatedSubgraphs.ts`:
- Around line 147-148: Replace all raw console.log timing instrumentation in
publishFederatedSubgraphs.ts (e.g., the statement at the shown diff and the
other occurrences around lines referenced) with calls to the module's structured
logger (logger), using template literals and including structured fields (e.g.,
elapsedMs: <value>, operation: 'loadSubgraphs' or other descriptive operation
names, and any request/context IDs available). Locate the timing calculations in
functions such as publishFederatedSubgraphs and the surrounding helper blocks
where performance.now() is used and replace console.log('load subgraphs: ' +
(performance.now() - now)) and similar prints with logger.debug or logger.info
as appropriate, passing both a template literal message and/or an object with
elapsedMs and context to preserve filtering and correlation. If the timing is
only temporary debug instrumentation and not needed, remove the statements
entirely instead of logging. Ensure all replacements use template literals
rather than string concatenation.

In `@controlplane/src/core/repositories/FederatedGraphRepository.ts`:
- Line 2: The file imports only KeyObject from 'node:crypto' but later calls
randomUUID(), causing an unresolved symbol; update the import to also bring in
randomUUID from 'node:crypto' (so the import line that currently imports
KeyObject in FederatedGraphRepository includes randomUUID) so calls to
randomUUID() in the FederatedGraphRepository class or related functions resolve
correctly.

In `@controlplane/src/core/repositories/SubgraphRepository.ts`:
- Around line 1092-1109: getSubgraphsByNames currently returns only existing
SubgraphDTOs which hides missing names; change it to preserve cardinality by
returning both found and missing names (e.g. { found: SubgraphDTO[], missing:
string[] }) so callers like publishFederatedSubgraphs can detect absent entries
and raise ERR_NOT_FOUND. Implement this by keeping the original uniqueNames
input list, collecting subgraphs as now via getSubgraphsMatching, then computing
missing = uniqueInputNames.filter(n => !found.map(s => s.name).includes(n));
update the method signature and all callers (notably publishFederatedSubgraphs)
to handle the new return shape and throw or abort when missing.length > 0.

---

Outside diff comments:
In `@controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts`:
- Around line 117-160: The bulk-fetch removed detection of missing subgraphs;
after calling subgraphRepo.getSubgraphsByNames(...) compute the set of fetched
names and compare it to requestedEntries (use requestedEntries.map(e=>e.name))
to find any missing names, populate the existing notFound array with those
missing names, and early-return the ERR_NOT_FOUND response as the code already
expects; also ensure when building resolved you only call
requestedEntries.find(...) for names that exist in the fetched results (or use a
map from requested name to schema) so you don't rely on a non-null assertion.

---

Nitpick comments:
In `@controlplane/src/core/repositories/SubgraphRepository.ts`:
- Around line 1199-1201: The current dedupe uses filter(...findIndex(...)) which
is O(n²); replace it with a linear-time Set/Map-based approach: iterate over the
subgraphs array once, track seen targetIds (e.g., const seen = new Set<string>()
or a Map<string, Subgraph>), collect only the first occurrence per sg.targetId
into a new array, then .map over that array; update the code around the
subgraphs variable (the chain using .filter(...findIndex(...)).map(...)) so it
uses the single-pass dedupe before mapping, preserving the original order and
behavior.
🪄 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: c07a2be7-b663-4fb0-836d-a94a90855a30

📥 Commits

Reviewing files that changed from the base of the PR and between b9405fa and 88e4442.

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

Comment thread controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts Outdated
Comment thread controlplane/src/core/repositories/FederatedGraphRepository.ts
Comment thread controlplane/src/core/repositories/SubgraphRepository.ts
@wilsonrivera
wilsonrivera changed the base branch from main to wilson/eng-9669-controlplane-improve-composition-queries June 10, 2026 15:51
…s' into wilson/eng-9716-controlplane-optimize-publishfederatedsubgraphs-performance

# Conflicts:
#	controlplane/src/core/repositories/GraphCompositionRepository.ts
#	controlplane/src/core/repositories/SubgraphRepository.ts
Base automatically changed from wilson/eng-9669-controlplane-improve-composition-queries to main June 10, 2026 16:09
@wilsonrivera
wilsonrivera marked this pull request as ready for review June 10, 2026 18:25
@wilsonrivera
wilsonrivera requested a review from a team as a code owner June 10, 2026 18:25
…hfederatedsubgraphs-performance

# Conflicts:
#	controlplane/src/core/repositories/SubgraphRepository.ts

@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

Caution

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

⚠️ Outside diff range comments (2)
controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts (1)

120-159: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Compute missing names from the fetched rows, not the type-filtered resolved list.

Entries rejected as grpc_plugin or grpc_service are skipped before resolvedSubgraphNames is built, so existing subgraphs of those types get reported as missing and the method returns ERR_NOT_FOUND before it ever surfaces typeErrors. Track the fetched names separately and use those for the missing-name check.

💡 Suggested fix
-    for (const subgraph of await subgraphRepo.getSubgraphsByNames(
+    const fetchedSubgraphs = await subgraphRepo.getSubgraphsByNames(
       requestedEntries.map((e) => e.name),
       namespace.id,
-    )) {
+    );
+    const fetchedSubgraphNames = new Set(fetchedSubgraphs.map((sg) => sg.name));
+
+    for (const subgraph of fetchedSubgraphs) {
       if (subgraph.type === 'grpc_plugin') {
         typeErrors.push(
           `Subgraph "${subgraph.name}" is a plugin. Please use the 'wgc router plugin publish' command to publish it.`,
@@
-    const resolvedSubgraphNames = new Set(resolved.map((re) => re.subgraph.name));
     const requestedSubgraphNames = new Set(requestedEntries.map((re) => re.name));
-    const notFoundSubgraphNames = [...requestedSubgraphNames.difference(resolvedSubgraphNames)];
+    const notFoundSubgraphNames = [...requestedSubgraphNames.difference(fetchedSubgraphNames)];
🤖 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/bufservices/subgraph/publishFederatedSubgraphs.ts`
around lines 120 - 159, The missing-name check uses resolvedSubgraphNames (which
excludes entries filtered out as 'grpc_plugin' or 'grpc_service'), causing
existing plugin/service subgraphs to be reported as missing; instead collect the
fetched rows' names from the result of subgraphRepo.getSubgraphsByNames (e.g.,
build a fetchedSubgraphNames Set from the looped `subgraph` values) and compute
notFoundSubgraphNames = requestedSubgraphNames.difference(fetchedSubgraphNames);
keep the existing typeErrors accumulation and only use resolved (with schema)
for later processing (resolved, resolvedSubgraphNames etc.).
controlplane/src/core/repositories/SubgraphRepository.ts (1)

1119-1135: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align bulk name lookup with the caller's case-insensitive matching.

publishFederatedSubgraphs now associates request entries with re.name.toLowerCase() === subgraph.name.toLowerCase(), but this helper still does an exact targets.name lookup. A request for products will therefore still miss an existing Products subgraph and come back as ERR_NOT_FOUND, so the new case-insensitive publish path does not work end-to-end.

🤖 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/SubgraphRepository.ts` around lines 1119 -
1135, getSubgraphsByNames currently does an exact match on schema.targets.name
which breaks the new case-insensitive publish flow; change the lookup to compare
lowercase names: transform chunkOfNames to all-lowercase and update the
condition to use a lowercase DB-side expression (e.g.
lower(schema.targets.name)) with inArray so the SQL compares
lower(schema.targets.name) IN chunkOfNamesLowercase; keep other conditions and
still call getSubgraphsMatching with the updated conditions (referencing
getSubgraphsByNames, schema.targets.name, inArray, and getSubgraphsMatching).
🧹 Nitpick comments (2)
controlplane/src/core/repositories/GraphCompositionRepository.ts (1)

50-52: ⚡ Quick win

Drop the indexOf lookups from subgraph diffing.

subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)] is just subgraph.schemaVersionId, but it adds extra linear scans in both diff passes on the publish path.

♻️ Proposed fix
-    const subgraphSchemaVersionIds = composedSubgraphs.map((subgraph) => subgraph.schemaVersionId);
-
       const updatedSubgraphs = composedSubgraphs.filter((subgraph) => {
         const prevSubgraph = prevCompositionSubgraphs.find((prevSubgraph) => prevSubgraph.id === subgraph.id);
-        return (
-          prevSubgraph && prevSubgraph.schemaVersionId !== subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)]
-        );
+        return prevSubgraph && prevSubgraph.schemaVersionId !== subgraph.schemaVersionId;
       });

       const unchangedSubgraphs = composedSubgraphs.filter((subgraph) =>
         prevCompositionSubgraphs.some(
           (prevSubgraph) =>
             prevSubgraph.id === subgraph.id &&
-            prevSubgraph.schemaVersionId === subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)],
+            prevSubgraph.schemaVersionId === subgraph.schemaVersionId,
         ),
       );

Also applies to: 117-129

🤖 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/GraphCompositionRepository.ts` around
lines 50 - 52, The diff uses composedSubgraphs.indexOf(subgraph) to look up
schemaVersionIds (e.g.,
subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)]) which causes
unnecessary O(n) scans; replace those indexOf-based lookups with direct access
to subgraph.schemaVersionId wherever they occur (for example in the code paths
around previousComposition and the second diff pass noted at lines ~117-129).
Update all occurrences (composedSubgraphs.indexOf(subgraph) usages) in
GraphCompositionRepository to use the subgraph object directly
(subgraph.schemaVersionId) to eliminate extra linear scans.
controlplane/src/core/repositories/FederatedGraphRepository.ts (1)

688-710: ⚡ Quick win

Add an explicit return type to this public method.

This signature just changed from returning a DTO to returning a narrower result object, so keeping it inferred makes the public contract easier to widen accidentally later.

♻️ Proposed fix
   public async addSchemaVersion({
     targetId,
     composedSDL,
     clientSchema,
     compositionErrors,
     compositionWarnings,
     composedSubgraphs,
     composedById,
     schemaVersionId,
     isFeatureFlagComposition,
     featureFlagId,
   }: {
     targetId: string;
     schemaVersionId: string;
     composedSDL?: string;
     clientSchema?: string;
     compositionErrors?: Error[];
     compositionWarnings?: Warning[];
     composedSubgraphs: CompositionSubgraphRecord[];
     composedById: string;
     isFeatureFlagComposition: boolean;
     featureFlagId: string;
-  }) {
+  }): Promise<{ composedSchemaVersionId: string; routerCompatibilityVersion: string } | undefined> {

As per coding guidelines, **/*.{ts,tsx}: Use explicit type annotations for function parameters and return types 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/FederatedGraphRepository.ts` around lines
688 - 710, The public method addSchemaVersion in FederatedGraphRepository
currently relies on inferred returns—add an explicit return type annotation
(e.g., Promise<AddSchemaVersionResult> or the existing DTO type used elsewhere)
to the method signature so the contract is fixed; locate the addSchemaVersion
declaration and replace the inferred return with the appropriate exported type
(Promise<...>), ensuring the chosen type matches the narrower result object now
returned and update any imports/usages if necessary.

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/GraphCompositionRepository.ts`:
- Around line 43-159: The addComposition() flow is not atomic: graphCompositions
is inserted separately from graphCompositionSubgraphs so a later failure can
leave inconsistent state (and FederatedGraphRepository.addSchemaVersion() may
have already advanced composedSchemaVersionId). Fix by making addComposition()
accept and use a transaction-scoped DB handle (or accept a Transaction/tx
parameter) and perform the insert into graphCompositions and the subsequent
insert into graphCompositionSubgraphs within the same transaction so both commit
or roll back together; update callers (e.g.,
FederatedGraphRepository.addSchemaVersion) to pass the transaction through and
use the transaction when calling addComposition().

In `@controlplane/src/core/repositories/SubgraphRepository.ts`:
- Around line 1159-1167: The helper getSubgraphNameByIds removes organization
scoping and needs to re-add the organization filter to prevent cross-tenant data
leaks: in the query that selects from targets and innerJoin(subgraphs...) add
the condition eq(targets.organizationId, this.organizationId) to the where
clause (alongside eq(targets.type, 'subgraph') and inArray(subgraphs.id,
chunkOfIds)), ensuring the method (getSubgraphNameByIds) only returns names for
the current organization when resolving via targets and subgraphs.

---

Outside diff comments:
In `@controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts`:
- Around line 120-159: The missing-name check uses resolvedSubgraphNames (which
excludes entries filtered out as 'grpc_plugin' or 'grpc_service'), causing
existing plugin/service subgraphs to be reported as missing; instead collect the
fetched rows' names from the result of subgraphRepo.getSubgraphsByNames (e.g.,
build a fetchedSubgraphNames Set from the looped `subgraph` values) and compute
notFoundSubgraphNames = requestedSubgraphNames.difference(fetchedSubgraphNames);
keep the existing typeErrors accumulation and only use resolved (with schema)
for later processing (resolved, resolvedSubgraphNames etc.).

In `@controlplane/src/core/repositories/SubgraphRepository.ts`:
- Around line 1119-1135: getSubgraphsByNames currently does an exact match on
schema.targets.name which breaks the new case-insensitive publish flow; change
the lookup to compare lowercase names: transform chunkOfNames to all-lowercase
and update the condition to use a lowercase DB-side expression (e.g.
lower(schema.targets.name)) with inArray so the SQL compares
lower(schema.targets.name) IN chunkOfNamesLowercase; keep other conditions and
still call getSubgraphsMatching with the updated conditions (referencing
getSubgraphsByNames, schema.targets.name, inArray, and getSubgraphsMatching).

---

Nitpick comments:
In `@controlplane/src/core/repositories/FederatedGraphRepository.ts`:
- Around line 688-710: The public method addSchemaVersion in
FederatedGraphRepository currently relies on inferred returns—add an explicit
return type annotation (e.g., Promise<AddSchemaVersionResult> or the existing
DTO type used elsewhere) to the method signature so the contract is fixed;
locate the addSchemaVersion declaration and replace the inferred return with the
appropriate exported type (Promise<...>), ensuring the chosen type matches the
narrower result object now returned and update any imports/usages if necessary.

In `@controlplane/src/core/repositories/GraphCompositionRepository.ts`:
- Around line 50-52: The diff uses composedSubgraphs.indexOf(subgraph) to look
up schemaVersionIds (e.g.,
subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)]) which causes
unnecessary O(n) scans; replace those indexOf-based lookups with direct access
to subgraph.schemaVersionId wherever they occur (for example in the code paths
around previousComposition and the second diff pass noted at lines ~117-129).
Update all occurrences (composedSubgraphs.indexOf(subgraph) usages) in
GraphCompositionRepository to use the subgraph object directly
(subgraph.schemaVersionId) to eliminate extra linear scans.
🪄 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: 638623c9-7071-45cc-b62e-6f20c0c4901c

📥 Commits

Reviewing files that changed from the base of the PR and between 88e4442 and a930bc1.

📒 Files selected for processing (7)
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
  • controlplane/src/core/composition/composeGraphs.worker.ts
  • controlplane/src/core/repositories/FeatureFlagRepository.ts
  • controlplane/src/core/repositories/FederatedGraphRepository.ts
  • controlplane/src/core/repositories/GraphCompositionRepository.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/tracing.ts
✅ Files skipped from review due to trivial changes (2)
  • controlplane/src/core/tracing.ts
  • controlplane/src/core/composition/composeGraphs.worker.ts

Comment thread controlplane/src/core/repositories/GraphCompositionRepository.ts Outdated
Comment thread controlplane/src/core/repositories/SubgraphRepository.ts

@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)
controlplane/src/core/repositories/GraphCompositionRepository.ts (1)

111-132: ⚡ Quick win

Simplify schema version comparison by using the property directly.

subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)] is equivalent to subgraph.schemaVersionId since subgraphSchemaVersionIds is derived from composedSubgraphs.map(s => s.schemaVersionId). The index lookup adds unnecessary complexity and O(n) overhead per iteration.

♻️ Proposed simplification
       const updatedSubgraphs = composedSubgraphs.filter((subgraph) => {
         const prevSubgraph = prevCompositionSubgraphs.find((prevSubgraph) => prevSubgraph.id === subgraph.id);
-        return (
-          prevSubgraph &&
-          prevSubgraph.schemaVersionId !== subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)]
-        );
+        return prevSubgraph && prevSubgraph.schemaVersionId !== subgraph.schemaVersionId;
       });

       const unchangedSubgraphs = composedSubgraphs.filter((subgraph) =>
         prevCompositionSubgraphs.some(
           (prevSubgraph) =>
-            prevSubgraph.id === subgraph.id &&
-            prevSubgraph.schemaVersionId === subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)],
+            prevSubgraph.id === subgraph.id && prevSubgraph.schemaVersionId === subgraph.schemaVersionId,
         ),
       );
🤖 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/GraphCompositionRepository.ts` around
lines 111 - 132, The comparison uses
subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)] which is redundant
and O(n) per check; update the filters in GraphCompositionRepository (the
updatedSubgraphs and unchangedSubgraphs computations) to compare
prevSubgraph.schemaVersionId directly to subgraph.schemaVersionId (e.g., use
prevCompositionSubgraphs.find(...) and compare prevSubgraph.schemaVersionId !==
subgraph.schemaVersionId for updated, and === for unchanged) to remove the
indexOf lookups and simplify the logic.
🤖 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/FederatedGraphRepository.ts`:
- Around line 758-772: The code inserts a row into
federatedGraphsToFeatureFlagSchemaVersions using
federatedGraph.composedSchemaVersionId despite that field being nullable; add a
guard in the block where isFeatureFlagComposition is true to verify
federatedGraph.composedSchemaVersionId is present before calling tx.insert on
federatedGraphsToFeatureFlagSchemaVersions (or otherwise handle the missing base
composition by creating it or returning a controlled error), and if missing,
throw a clear error or create the base composition so
baseCompositionSchemaVersionId (target column) never receives null; update the
logic around isFeatureFlagComposition, federatedGraph.composedSchemaVersionId,
and the tx.insert(...) arguments accordingly.

---

Nitpick comments:
In `@controlplane/src/core/repositories/GraphCompositionRepository.ts`:
- Around line 111-132: The comparison uses
subgraphSchemaVersionIds[composedSubgraphs.indexOf(subgraph)] which is redundant
and O(n) per check; update the filters in GraphCompositionRepository (the
updatedSubgraphs and unchangedSubgraphs computations) to compare
prevSubgraph.schemaVersionId directly to subgraph.schemaVersionId (e.g., use
prevCompositionSubgraphs.find(...) and compare prevSubgraph.schemaVersionId !==
subgraph.schemaVersionId for updated, and === for unchanged) to remove the
indexOf lookups and simplify the logic.
🪄 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: d49fd99a-b482-4034-916c-28128144c83a

📥 Commits

Reviewing files that changed from the base of the PR and between cdb4fc5 and 5011ba7.

📒 Files selected for processing (3)
  • controlplane/src/core/repositories/FederatedGraphRepository.ts
  • controlplane/src/core/repositories/GraphCompositionRepository.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • controlplane/src/core/repositories/SubgraphRepository.ts

Comment thread controlplane/src/core/repositories/FederatedGraphRepository.ts
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.76923% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.56%. Comparing base (b9405fa) to head (3f661f3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...lplane/src/core/repositories/SubgraphRepository.ts 90.69% 12 Missing ⚠️
.../src/core/repositories/FederatedGraphRepository.ts 97.01% 2 Missing ⚠️
...rc/core/repositories/GraphCompositionRepository.ts 98.05% 2 Missing ⚠️
...plane/src/core/composition/composeGraphs.worker.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2949      +/-   ##
==========================================
+ Coverage   65.13%   65.56%   +0.43%     
==========================================
  Files         327      327              
  Lines       47138    46918     -220     
  Branches     5241     5250       +9     
==========================================
+ Hits        30703    30763      +60     
+ Misses      16411    16131     -280     
  Partials       24       24              
Files with missing lines Coverage Δ
.../bufservices/subgraph/publishFederatedSubgraphs.ts 82.08% <100.00%> (-1.00%) ⬇️
controlplane/src/core/tracing.ts 100.00% <100.00%> (ø)
...plane/src/core/composition/composeGraphs.worker.ts 0.00% <0.00%> (ø)
.../src/core/repositories/FederatedGraphRepository.ts 70.57% <97.01%> (+14.87%) ⬆️
...rc/core/repositories/GraphCompositionRepository.ts 98.33% <98.05%> (ø)
...lplane/src/core/repositories/SubgraphRepository.ts 88.89% <90.69%> (-0.01%) ⬇️
🚀 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 merged commit 746c876 into main Jun 10, 2026
13 of 14 checks passed
@wilsonrivera
wilsonrivera deleted the wilson/eng-9716-controlplane-optimize-publishfederatedsubgraphs-performance branch June 10, 2026 20:59
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