Skip to content

feat: extract cdn upload to outside the transaction - #2939

Closed
wilsonrivera wants to merge 2 commits into
mainfrom
wilson/eng-9699-controlplane-upload-cdn-outside-of-transaction
Closed

feat: extract cdn upload to outside the transaction#2939
wilsonrivera wants to merge 2 commits into
mainfrom
wilson/eng-9699-controlplane-upload-cdn-outside-of-transaction

Conversation

@wilsonrivera

@wilsonrivera wilsonrivera commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Refactor

    • Composition subsystem now uses an internal queued processor for blob and router-config actions.
    • Composer now constructs its dependencies internally instead of receiving them from callers.
  • Bug Fixes

    • Deployment error reporting now includes errors from queued blob-storage and router-config work, improving accuracy of deployment status.

Checklist

Open Source AI Manifesto

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

@wilsonrivera
wilsonrivera requested a review from a team as a code owner June 9, 2026 11:53
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR introduces CompositionBlobStorageQueue and routes blob/router-config writes and deletions through it; CompositionService now accepts the queue and enqueues operations instead of calling blobStorage/composer uploads directly. Composer no longer receives repository instances; it constructs repositories internally using private fields. Handlers and repository call-sites instantiate and drain the queue after transactions.

Changes

Queue-based blob storage and Composer repository encapsulation refactor

Layer / File(s) Summary
Blob storage contract and PutObjectParams type
controlplane/src/core/blobstorage/index.ts
Adds PutObjectParams<Metadata> and updates BlobStorage.putObject() to accept this typed parameter object.
CompositionBlobStorageQueue implementation
controlplane/src/core/services/CompositionBlobStorageQueue.ts
New traced queue with enqueue helpers for uploads/deletions/router-config uploads and drainQueue() that executes actions sequentially and returns deployment errors for router-config admission/upload errors and delete failures.
Composer refactoring to private repository fields
controlplane/src/core/composition/composer.ts
Composer now constructs repositories internally from organizationId using #private fields; exports ComposeAndUploadRouterConfigParams and updates composer methods to use private repos.
CompositionService refactor to use queue
controlplane/src/core/services/CompositionService.ts
Constructor changed to accept CompositionBlobStorageQueue; mapper, feature-flag, and router-config writes/deletes are enqueued instead of uploaded synchronously; Composer is constructed with reduced dependencies.
Handler and service call-sites instantiating and draining queue
controlplane/src/core/bufservices/*, controlplane/src/core/bufservices/*, controlplane/src/core/bufservices/*
Multiple bufservice handlers and related flows instantiate CompositionBlobStorageQueue, pass it into CompositionService inside DB transactions, then call drainQueue() after transactions and merge results into deploymentErrors.
Repository call-sites using new Composer constructor
controlplane/src/core/repositories/FederatedGraphRepository.ts, controlplane/src/core/repositories/SubgraphRepository.ts
Updated Composer instantiation to pass organizationId (and client/proxy) instead of injecting repo instances.
Sentry flush timeout cleanup
controlplane/src/core/composition/composeGraphs.worker.ts
Specifies Sentry.flush(2000) in the worker cleanup path.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wundergraph/cosmo#2899: Touches publishFederatedSubgraphs flow that now uses CompositionBlobStorageQueue.
  • wundergraph/cosmo#2847: Refactors CompositionService split-config/feature-flag upload behavior, overlapping composition/upload handling changes.
  • wundergraph/cosmo#2935: Modifies CompositionService internals and deployment orchestration that intersect with this PR's queue-based changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% 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: extract cdn upload to outside the transaction' directly and accurately describes the main change across the pull request: moving CDN/blob-storage upload operations outside database transactions.
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.

@Aenimus
Aenimus requested a review from gausie June 9, 2026 11:57

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

Caution

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

⚠️ Outside diff range comments (4)
controlplane/src/core/services/CompositionService.ts (1)

559-567: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Surface mapper upload failures instead of swallowing them.

updateMapperForFederatedGraph() now enqueues mapper.json writes, but CompositionBlobStorageQueue.processQueue() currently ignores upload-blob exceptions (controlplane/src/core/services/CompositionBlobStorageQueue.ts:94-98). That means a failed mapper upload is reported as a successful deployment even though the DB hashes were already updated and routers may read stale manifests.

🤖 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 559 - 567,
The mapper upload failures are being swallowed by
CompositionBlobStorageQueue.processQueue, so calls from
updateMapperForFederatedGraph (which uses
this.blobStorageQueue.enqueueBlobUpload) appear to succeed even when the
underlying 'upload-blob' operation fails; modify processQueue's 'upload-blob'
handler to stop catching and discarding exceptions — instead log the error with
context and rethrow (or propagate) the exception so enqueueBlobUpload/
updateMapperForFederatedGraph can await the result and abort the deployment on
failure; ensure enqueueBlobUpload returns a Promise that resolves/rejects with
the underlying upload result and that updateMapperForFederatedGraph awaits it so
failed uploads surface to callers.
controlplane/src/core/bufservices/federated-graph/moveFederatedGraph.ts (1)

145-172: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Drain the blob queue after the transaction commits.

Line 172 still waits for cbsq.processQueue() inside the opts.db.transaction(...) callback, so the CDN/router-config work keeps the transaction open across external I/O. Hoist the queue out of the callback and process it only after the transaction resolves, otherwise this path still holds locks/connections for the full upload duration.

🤖 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/federated-graph/moveFederatedGraph.ts`
around lines 145 - 172, The code currently calls cbsq.processQueue() inside the
opts.db.transaction(...) callback which keeps the DB transaction open during
external I/O; move the call to cbsq.processQueue() out of the transaction so
queue processing runs after the transaction resolves. Concretely, keep
constructing CompositionBlobStorageQueue (cbsq) and calling
compositionService.composeAndDeployFederatedGraph(...) inside the transaction
but do not await cbsq.processQueue() there; instead return any needed metadata
from the transaction and, after the transaction promise resolves, call await
cbsq.processQueue() and push its results into deploymentErrors so the
CDN/uploads run after commit. Ensure cbsq remains in scope (hoisted) so it’s
available post-transaction.
controlplane/src/core/bufservices/contract/createContract.ts (1)

186-213: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move queue creation and draining out of the transaction.

Line 212 still awaits cbsq.processQueue() before the transaction callback returns, so the CDN/admission work is still happening under the open DB transaction. In this file the queue is also built with tx, which hard-binds the post-commit path to the transaction object. Hoist the queue to the outer scope, construct it with opts.db, and only drain it after opts.db.transaction(...) resolves.

🤖 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/contract/createContract.ts` around lines
186 - 213, Hoist construction of CompositionBlobStorageQueue out of the DB
transaction: create the cbsq before calling opts.db.transaction(...) and
instantiate it with opts.db (not tx) and the same other params (logger,
opts.blobStorage, authContext.organizationId, { cdnBaseUrl: opts.cdnBaseUrl,
webhookJWTSecret: opts.admissionWebhookJWTSecret }, opts.chClient,
opts.webhookProxyUrl). Inside the transaction, continue to create
CompositionService and call composeAndDeployFederatedGraph using the tx for DB
operations but do NOT await cbsq.processQueue() there; instead collect
deploymentErrors/compositionErrors/compositionWarnings from the transaction
result and only call await cbsq.processQueue() after the
opts.db.transaction(...) promise resolves so queue draining runs post-commit.
Ensure any references to tx remain for DB actions and that CompositionService
still uses tx for DB access while the queue uses opts.db.
controlplane/src/core/repositories/FederatedGraphRepository.ts (1)

1451-1457: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Use the transaction client for Composer in this transactional block.

At Line 1456, Composer is created with this.db even though this logic runs inside this.db.transaction(...). That lets Composer writes escape the current transaction and can leave persisted composition state even if the surrounding transaction fails.

Suggested fix
-      const composer = new Composer(this.logger, this.db, this.organizationId, chClient, webhookProxyUrl);
+      const composer = new Composer(this.logger, tx, this.organizationId, chClient, webhookProxyUrl);
🤖 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
1451 - 1457, Inside the this.db.transaction(...) block the Composer is being
instantiated with this.db which allows its writes to escape the transaction;
change the Composer creation to use the transaction client (tx) instead of
this.db so Composer's operations participate in the surrounding transaction
(i.e., replace new Composer(this.logger, this.db, ...) with new
Composer(this.logger, tx, ...) in FederatedGraphRepository where Composer is
constructed).
🧹 Nitpick comments (4)
controlplane/src/core/blobstorage/index.ts (1)

23-29: ⚡ Quick win

Use an interface for this exported params contract.

PutObjectParams is a public object shape, so it should follow the repo convention and be declared as an interface.

Suggested change
-export type PutObjectParams<Metadata extends Record<string, string>> = {
+export interface PutObjectParams<Metadata extends Record<string, string>> {
   key: string;
   abortSignal?: AbortSignal;
   body: Buffer;
   contentType: string;
   metadata?: Metadata;
-};
+}

As per coding guidelines, "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/blobstorage/index.ts` around lines 23 - 29, Replace the
exported type alias PutObjectParams with an exported interface so the public
params contract follows repo conventions; declare it as export interface
PutObjectParams<Metadata extends Record<string, string>> { key: string;
abortSignal?: AbortSignal; body: Buffer; contentType: string; metadata?:
Metadata; } preserving the generic parameter name Metadata and all field names
and optional markers exactly as in the current definition (use the same symbol
PutObjectParams to avoid breaking references).

Source: Coding guidelines

controlplane/src/core/composition/composer.ts (2)

142-157: ⚡ Quick win

Prefer an interface for this exported object shape.

This is a public params contract, so keeping it as an interface will match the repo’s TS conventions and make later extension/merging easier.

Suggested change
-export type ComposeAndUploadRouterConfigParams = {
+export interface ComposeAndUploadRouterConfigParams {
   admissionConfig: {
     jwtSecret: string;
     cdnBaseUrl: string;
@@
   federatedGraphAdmissionWebhookSecret?: string;
   actorId: string;
   pathOverride?: { ready: string; draft: string };
-};
+}

As per coding guidelines, "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/composition/composer.ts` around lines 142 - 157,
Replace the exported type alias ComposeAndUploadRouterConfigParams with an
exported interface of the same name: change "export type
ComposeAndUploadRouterConfigParams = { ... }" to "export interface
ComposeAndUploadRouterConfigParams { ... }" preserving all properties
(admissionConfig, baseCompositionRouterExecutionConfig,
baseCompositionSchemaVersionId, blobStorage,
featureFlagRouterExecutionConfigByFeatureFlagName, federatedGraphId,
organizationId, federatedGraphAdmissionWebhookURL,
federatedGraphAdmissionWebhookSecret, actorId, pathOverride) and their types;
keep the export name unchanged so existing references compile and allow future
extension/merging per project convention.

Source: Coding guidelines


386-398: ⚡ Quick win

Add an explicit return type to this public method.

This signature changed in the PR, so it is a good point to lock the returned shape down explicitly instead of relying on inference.

Suggested change
   async composeAndUploadRouterConfig({
@@
     federatedGraphAdmissionWebhookSecret,
     actorId,
     pathOverride,
-  }: ComposeAndUploadRouterConfigParams) {
+  }: ComposeAndUploadRouterConfigParams): Promise<{ errors: ComposeDeploymentError[] }> {

As per coding guidelines, "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/composition/composer.ts` around lines 386 - 398, The
public async function composeAndUploadRouterConfig currently relies on inferred
return type; add an explicit return annotation (e.g. async
composeAndUploadRouterConfig(...): Promise<RouterConfigUploadResult> { ... })
that represents the actual resolved shape, create or import a named
interface/type (RouterConfigUploadResult or similarly descriptive name) that
matches the function's returned object (status, url/id, any metadata or error
info), and update any related exports/uses to consume that type so callers and
the public API are type-locked instead of inferred.

Source: Coding guidelines

controlplane/src/core/services/CompositionBlobStorageQueue.ts (1)

35-46: ⚡ Quick win

Add explicit void return types to enqueue methods.

Please annotate these methods with : void for consistency with the TS guideline.

Suggested fix
-  enqueueBlobUpload<Metadata extends Record<string, string>>(params: PutObjectParams<Metadata>) {
+  enqueueBlobUpload<Metadata extends Record<string, string>>(params: PutObjectParams<Metadata>): void {
     this.#queue.push({ action: 'upload-blob', params });
   }

-  enqueueBlobDeletion(graph: FederatedGraphDTO, key: string) {
+  enqueueBlobDeletion(graph: FederatedGraphDTO, key: string): void {
     this.#queue.push({ action: 'delete-blob', graph, key });
   }

   enqueueRouterConfigUpload(
     graph: FederatedGraphDTO,
     params: Omit<ComposeAndUploadRouterConfigParams, 'blobStorage' | 'admissionConfig'>,
-  ) {
+  ): void {

As per coding guidelines, **/*.{ts,tsx} should 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/services/CompositionBlobStorageQueue.ts` around lines
35 - 46, Add explicit ": void" return annotations to the enqueue methods to
satisfy the TypeScript guideline: update enqueueBlobUpload<Metadata extends
Record<string, string>>(params: PutObjectParams<Metadata>),
enqueueBlobDeletion(graph: FederatedGraphDTO, key: string), and
enqueueRouterConfigUpload(graph: FederatedGraphDTO, params:
Omit<ComposeAndUploadRouterConfigParams, 'blobStorage' | 'admissionConfig'>) to
declare their return type as void while leaving their bodies unchanged.

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/bufservices/federated-graph/migrateFromApollo.ts`:
- Line 187: The call to cbsq.processQueue() in migrateFromApollo is discarding
deployment errors from CompositionBlobStorageQueue.processQueue(), so migration
can report success even if deferred router-config/blob uploads failed; update
migrateFromApollo to capture the return value (or thrown errors) from
processQueue() and surface failures — e.g., if processQueue() returns an error
list or throws, log and propagate them (fail the migration by throwing or
returning an error result) so composeAndDeployFederatedGraph()'s deferred upload
errors do not get ignored; ensure you reference and handle
CompositionBlobStorageQueue.processQueue()'s result in the migrateFromApollo
flow.

In `@controlplane/src/core/bufservices/monograph/updateMonograph.ts`:
- Around line 140-149: The CompositionBlobStorageQueue is being drained via
cbsq.processQueue() while still inside the DB transaction and before
subgraphRepo.update(...), which causes actions enqueued during the update to be
missed and performs external side effects inside the transaction; change the
flow so subgraphRepo.update(...) (and any DB transaction commit) completes
first, then call cbsq.processQueue() outside the transaction and await its
result, capturing and handling any deployment errors returned (do not ignore
them), and ensure any webhook/CH calls from CompositionBlobStorageQueue happen
only after the DB transaction is committed.

In `@controlplane/src/core/composition/composer.ts`:
- Around line 166-177: The constructor currently instantiates transaction-scoped
repositories (`#federatedGraphRepo`, `#subgraphRepo`, `#contractRepo`,
`#graphCompositionRepository`) from the provided db which causes queued
composeAndUploadRouterConfig() runs to use a completed transaction; change the
implementation so Composer does not cache those repo instances: either store the
raw db/organizationId and lazily instantiate new repositories on demand via
private factory/getter methods (creating new FederatedGraphRepository,
SubgraphRepository, ContractRepository, GraphCompositionRepository each use) or
ensure the queued path (CompositionService) recreates a fresh Composer after
commit using a non-transactional db handle; update references to the cached
fields to use the new factory/getters or reconstructed Composer instead.

In `@controlplane/src/core/services/CompositionBlobStorageQueue.ts`:
- Around line 99-104: The upload failure in CompositionBlobStorageQueue is being
swallowed inside the 'upload-blob' case; update the handler that calls
this.blobStorage.putObject(entry.params) to catch the error and surface it
instead of ignoring it — e.g., log the error with context (including
entry.params identifiers) via the existing logger or processLogger and rethrow
or mark the queue entry as failed/put on a dead-letter queue so callers don't
assume success; locate the 'upload-blob' case in CompositionBlobStorageQueue and
change the empty catch to emit a clear error and propagate failure rather than
silently ignoring it.

---

Outside diff comments:
In `@controlplane/src/core/bufservices/contract/createContract.ts`:
- Around line 186-213: Hoist construction of CompositionBlobStorageQueue out of
the DB transaction: create the cbsq before calling opts.db.transaction(...) and
instantiate it with opts.db (not tx) and the same other params (logger,
opts.blobStorage, authContext.organizationId, { cdnBaseUrl: opts.cdnBaseUrl,
webhookJWTSecret: opts.admissionWebhookJWTSecret }, opts.chClient,
opts.webhookProxyUrl). Inside the transaction, continue to create
CompositionService and call composeAndDeployFederatedGraph using the tx for DB
operations but do NOT await cbsq.processQueue() there; instead collect
deploymentErrors/compositionErrors/compositionWarnings from the transaction
result and only call await cbsq.processQueue() after the
opts.db.transaction(...) promise resolves so queue draining runs post-commit.
Ensure any references to tx remain for DB actions and that CompositionService
still uses tx for DB access while the queue uses opts.db.

In `@controlplane/src/core/bufservices/federated-graph/moveFederatedGraph.ts`:
- Around line 145-172: The code currently calls cbsq.processQueue() inside the
opts.db.transaction(...) callback which keeps the DB transaction open during
external I/O; move the call to cbsq.processQueue() out of the transaction so
queue processing runs after the transaction resolves. Concretely, keep
constructing CompositionBlobStorageQueue (cbsq) and calling
compositionService.composeAndDeployFederatedGraph(...) inside the transaction
but do not await cbsq.processQueue() there; instead return any needed metadata
from the transaction and, after the transaction promise resolves, call await
cbsq.processQueue() and push its results into deploymentErrors so the
CDN/uploads run after commit. Ensure cbsq remains in scope (hoisted) so it’s
available post-transaction.

In `@controlplane/src/core/repositories/FederatedGraphRepository.ts`:
- Around line 1451-1457: Inside the this.db.transaction(...) block the Composer
is being instantiated with this.db which allows its writes to escape the
transaction; change the Composer creation to use the transaction client (tx)
instead of this.db so Composer's operations participate in the surrounding
transaction (i.e., replace new Composer(this.logger, this.db, ...) with new
Composer(this.logger, tx, ...) in FederatedGraphRepository where Composer is
constructed).

In `@controlplane/src/core/services/CompositionService.ts`:
- Around line 559-567: The mapper upload failures are being swallowed by
CompositionBlobStorageQueue.processQueue, so calls from
updateMapperForFederatedGraph (which uses
this.blobStorageQueue.enqueueBlobUpload) appear to succeed even when the
underlying 'upload-blob' operation fails; modify processQueue's 'upload-blob'
handler to stop catching and discarding exceptions — instead log the error with
context and rethrow (or propagate) the exception so enqueueBlobUpload/
updateMapperForFederatedGraph can await the result and abort the deployment on
failure; ensure enqueueBlobUpload returns a Promise that resolves/rejects with
the underlying upload result and that updateMapperForFederatedGraph awaits it so
failed uploads surface to callers.

---

Nitpick comments:
In `@controlplane/src/core/blobstorage/index.ts`:
- Around line 23-29: Replace the exported type alias PutObjectParams with an
exported interface so the public params contract follows repo conventions;
declare it as export interface PutObjectParams<Metadata extends Record<string,
string>> { key: string; abortSignal?: AbortSignal; body: Buffer; contentType:
string; metadata?: Metadata; } preserving the generic parameter name Metadata
and all field names and optional markers exactly as in the current definition
(use the same symbol PutObjectParams to avoid breaking references).

In `@controlplane/src/core/composition/composer.ts`:
- Around line 142-157: Replace the exported type alias
ComposeAndUploadRouterConfigParams with an exported interface of the same name:
change "export type ComposeAndUploadRouterConfigParams = { ... }" to "export
interface ComposeAndUploadRouterConfigParams { ... }" preserving all properties
(admissionConfig, baseCompositionRouterExecutionConfig,
baseCompositionSchemaVersionId, blobStorage,
featureFlagRouterExecutionConfigByFeatureFlagName, federatedGraphId,
organizationId, federatedGraphAdmissionWebhookURL,
federatedGraphAdmissionWebhookSecret, actorId, pathOverride) and their types;
keep the export name unchanged so existing references compile and allow future
extension/merging per project convention.
- Around line 386-398: The public async function composeAndUploadRouterConfig
currently relies on inferred return type; add an explicit return annotation
(e.g. async composeAndUploadRouterConfig(...): Promise<RouterConfigUploadResult>
{ ... }) that represents the actual resolved shape, create or import a named
interface/type (RouterConfigUploadResult or similarly descriptive name) that
matches the function's returned object (status, url/id, any metadata or error
info), and update any related exports/uses to consume that type so callers and
the public API are type-locked instead of inferred.

In `@controlplane/src/core/services/CompositionBlobStorageQueue.ts`:
- Around line 35-46: Add explicit ": void" return annotations to the enqueue
methods to satisfy the TypeScript guideline: update enqueueBlobUpload<Metadata
extends Record<string, string>>(params: PutObjectParams<Metadata>),
enqueueBlobDeletion(graph: FederatedGraphDTO, key: string), and
enqueueRouterConfigUpload(graph: FederatedGraphDTO, params:
Omit<ComposeAndUploadRouterConfigParams, 'blobStorage' | 'admissionConfig'>) to
declare their return type as void while leaving their bodies unchanged.
🪄 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: 301eba46-f498-42ca-8789-2ce0fa1c9471

📥 Commits

Reviewing files that changed from the base of the PR and between 35a0ad6 and 0c8ec2f.

📒 Files selected for processing (30)
  • controlplane/src/core/blobstorage/index.ts
  • controlplane/src/core/bufservices/contract/createContract.ts
  • controlplane/src/core/bufservices/contract/updateContract.ts
  • controlplane/src/core/bufservices/feature-flag/createFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/deleteFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/enableFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/recomposeFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/updateFeatureFlag.ts
  • controlplane/src/core/bufservices/federated-graph/createFederatedGraph.ts
  • controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts
  • controlplane/src/core/bufservices/federated-graph/moveFederatedGraph.ts
  • controlplane/src/core/bufservices/federated-graph/updateFederatedGraph.ts
  • controlplane/src/core/bufservices/graph/recomposeGraph.ts
  • controlplane/src/core/bufservices/graph/setGraphRouterCompatibilityVersion.ts
  • controlplane/src/core/bufservices/monograph/publishMonograph.ts
  • controlplane/src/core/bufservices/monograph/updateMonograph.ts
  • controlplane/src/core/bufservices/proposal/createProposal.ts
  • controlplane/src/core/bufservices/proposal/updateProposal.ts
  • controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/fixSubgraphSchema.ts
  • controlplane/src/core/bufservices/subgraph/moveSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
  • controlplane/src/core/bufservices/subgraph/updateSubgraph.ts
  • controlplane/src/core/composition/composeGraphs.worker.ts
  • controlplane/src/core/composition/composer.ts
  • controlplane/src/core/repositories/FederatedGraphRepository.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/services/CompositionBlobStorageQueue.ts
  • controlplane/src/core/services/CompositionService.ts

};
}

await cbsq.processQueue();

@coderabbitai coderabbitai Bot Jun 9, 2026

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't discard post-transaction deployment errors here.

processQueue() returns deployment errors from the deferred router-config/blob work (controlplane/src/core/services/CompositionBlobStorageQueue.ts:61-111), but this line ignores them. After moving uploads out of composeAndDeployFederatedGraph(), that means migration can still emit success audit/webhook flow and return OK even when the actual deployment failed.

🤖 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/federated-graph/migrateFromApollo.ts` at
line 187, The call to cbsq.processQueue() in migrateFromApollo is discarding
deployment errors from CompositionBlobStorageQueue.processQueue(), so migration
can report success even if deferred router-config/blob uploads failed; update
migrateFromApollo to capture the return value (or thrown errors) from
processQueue() and surface failures — e.g., if processQueue() returns an error
list or throws, log and propagate them (fail the migration by throwing or
returning an error result) so composeAndDeployFederatedGraph()'s deferred upload
errors do not get ignored; ensure you reference and handle
CompositionBlobStorageQueue.processQueue()'s result in the migrateFromApollo
flow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We are not doing anything with the deployment errors here

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.

@wilsonrivera, understood — MigrateFromApolloResponse only carries response (code/details) and token, so there's no field to surface deployment errors to the caller, unlike the composition-related handlers. That makes the intentional discard reasonable given the current proto contract.

One minor note: the errors are silently dropped without any trace. If the deferred uploads do fail, it could be hard to diagnose in production. It might be worth at least logging the result, e.g.:

const deploymentErrors = await cbsq.processQueue();
if (deploymentErrors.length > 0) {
  logger.warn({ deploymentErrors }, 'Apollo migration completed but deferred uploads had errors');
}

That said, I'll leave it to your discretion.


✏️ Learnings added
Learnt from: wilsonrivera
Repo: wundergraph/cosmo PR: 2939
File: controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts:187-187
Timestamp: 2026-06-09T12:56:28.385Z
Learning: In `controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts`, the result of `cbsq.processQueue()` is intentionally not captured or returned. This is because `MigrateFromApolloResponse` (proto) only has `response` (code/details) and `token` fields — it has no `deploymentErrors` field — so there is no mechanism to surface blob-storage queue errors to the caller. The design decision is to silently discard these errors for the migration flow.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: thisisnithin
Repo: wundergraph/cosmo PR: 2771
File: controlplane/src/core/services/ApiGenerator.ts:4-7
Timestamp: 2026-04-16T18:53:03.303Z
Learning: In the cosmo controlplane codebase, the `traced` decorator is intentionally not applied to static methods when they are lightweight utility functions that perform no I/O. When reviewing `controlplane/**/*.ts`, do not flag missing tracing on static methods that are purely computational/helpers (e.g., building query conditions, generating patterns/keys) and do not call databases, network/HTTP, files, or other external side effects. If a static method does perform I/O or other observable external work, tracing omission should be flagged.

Learnt from: Aenimus
Repo: wundergraph/cosmo PR: 2860
File: controlplane/src/core/repositories/SubgraphRepository.ts:579-579
Timestamp: 2026-05-13T00:24:13.515Z
Learning: In the wundergraph/cosmo controlplane, the project targets Node.js 22+ and uses TypeScript with "lib": ["ESNext"] and "target": "esnext". Therefore, it is valid to use iterator helper methods (e.g., Iterator.prototype.map and related methods) on built-in iterators such as the iterators returned by Map.prototype.keys(), Map.prototype.values(), and Map.prototype.entries(). Do not flag usages of .map() or other iterator helper methods on these built-in iterators for runtime or TypeScript compatibility concerns in this codebase.

Comment on lines +140 to +149
const cbsq = new CompositionBlobStorageQueue(
logger,
opts.db,
opts.blobStorage,
authContext.organizationId,
{ cdnBaseUrl: opts.cdnBaseUrl, webhookJWTSecret: opts.admissionWebhookJWTSecret },
opts.chClient,
opts.webhookProxyUrl,
);

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.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Queue draining is placed at the wrong point and drops later queued work.

At Line 174, processQueue() runs before Line 177 (subgraphRepo.update(...)), so any actions enqueued during subgraph update are never processed. It also performs external side effects inside the DB transaction and ignores returned deployment errors.

Suggested flow adjustment
-    return opts.db.transaction(async (tx) => {
+    const cbsq = new CompositionBlobStorageQueue(
+      logger,
+      opts.db,
+      opts.blobStorage,
+      authContext.organizationId,
+      { cdnBaseUrl: opts.cdnBaseUrl, webhookJWTSecret: opts.admissionWebhookJWTSecret },
+      opts.chClient,
+      opts.webhookProxyUrl,
+    );
+
+    const result = await opts.db.transaction(async (tx) => {
       ...
-      const cbsq = new CompositionBlobStorageQueue(...);
       const compositionService = new CompositionService(..., cbsq, ...);
       await fedGraphRepo.update(...);
-      await cbsq.processQueue();
       await subgraphRepo.update(..., compositionService);
       ...
       return {
         response: { code: EnumStatusCode.OK },
         compositionErrors: [],
       };
     });
+
+    const deploymentErrors = await cbsq.processQueue();
+    // handle deploymentErrors (map to response/ERR_DEPLOYMENT_FAILED or raise)
+    return result;

Also applies to: 174-175, 177-193

🤖 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/monograph/updateMonograph.ts` around lines
140 - 149, The CompositionBlobStorageQueue is being drained via
cbsq.processQueue() while still inside the DB transaction and before
subgraphRepo.update(...), which causes actions enqueued during the update to be
missed and performs external side effects inside the transaction; change the
flow so subgraphRepo.update(...) (and any DB transaction commit) completes
first, then call cbsq.processQueue() outside the transaction and await its
result, capturing and handling any deployment errors returned (do not ignore
them), and ensure any webhook/CH calls from CompositionBlobStorageQueue happen
only after the DB transaction is committed.

Comment thread controlplane/src/core/composition/composer.ts
Comment on lines +99 to +104
case 'upload-blob': {
try {
await this.blobStorage.putObject(entry.params);
} catch {
// ignore
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not silently swallow blob upload failures.

At Line 102, upload failures are ignored, so callers can return success while queued blob artifacts are missing.

Suggested fix
         case 'upload-blob': {
           try {
             await this.blobStorage.putObject(entry.params);
-          } catch {
-            // ignore
+          } catch (err) {
+            this.logger.error({ err, key: entry.params.key }, 'Failed to upload blob');
+            if (err instanceof Error) {
+              errors.push({
+                message: `Failed to upload blob "${entry.params.key}": ${err.message}`,
+              });
+            }
           }
           break;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case 'upload-blob': {
try {
await this.blobStorage.putObject(entry.params);
} catch {
// ignore
}
case 'upload-blob': {
try {
await this.blobStorage.putObject(entry.params);
} catch (err) {
this.logger.error({ err, key: entry.params.key }, 'Failed to upload blob');
if (err instanceof Error) {
errors.push({
message: `Failed to upload blob "${entry.params.key}": ${err.message}`,
});
}
}
break;
}
🤖 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/CompositionBlobStorageQueue.ts` around lines
99 - 104, The upload failure in CompositionBlobStorageQueue is being swallowed
inside the 'upload-blob' case; update the handler that calls
this.blobStorage.putObject(entry.params) to catch the error and surface it
instead of ignoring it — e.g., log the error with context (including
entry.params identifiers) via the existing logger or processLogger and rethrow
or mark the queue entry as failed/put on a dead-letter queue so callers don't
assume success; locate the 'upload-blob' case in CompositionBlobStorageQueue and
change the empty catch to emit a clear error and propagate failure rather than
silently ignoring it.

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.84043% with 57 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.25%. Comparing base (35a0ad6) to head (0aa3886).
⚠️ Report is 61 commits behind head on main.

Files with missing lines Patch % Lines
...e/src/core/services/CompositionBlobStorageQueue.ts 84.52% 13 Missing ⚠️
...e/bufservices/federated-graph/migrateFromApollo.ts 0.00% 11 Missing ⚠️
...rvices/graph/setGraphRouterCompatibilityVersion.ts 0.00% 11 Missing ⚠️
.../src/core/bufservices/monograph/updateMonograph.ts 0.00% 11 Missing ⚠️
controlplane/src/core/composition/composer.ts 80.76% 5 Missing ⚠️
...core/bufservices/feature-flag/updateFeatureFlag.ts 92.30% 1 Missing ⚠️
...src/core/bufservices/subgraph/fixSubgraphSchema.ts 0.00% 1 Missing ⚠️
...lane/src/core/bufservices/subgraph/moveSubgraph.ts 94.73% 1 Missing ⚠️
...plane/src/core/composition/composeGraphs.worker.ts 0.00% 1 Missing ⚠️
.../src/core/repositories/FederatedGraphRepository.ts 0.00% 1 Missing ⚠️
... and 1 more

❌ Your patch check has failed because the patch coverage (84.84%) 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    #2939      +/-   ##
==========================================
+ Coverage   65.12%   65.25%   +0.12%     
==========================================
  Files         327      328       +1     
  Lines       47130    47272     +142     
  Branches     5239     5256      +17     
==========================================
+ Hits        30695    30846     +151     
+ Misses      16411    16402       -9     
  Partials       24       24              
Files with missing lines Coverage Δ
controlplane/src/core/blobstorage/index.ts 76.92% <ø> (ø)
...ne/src/core/bufservices/contract/createContract.ts 78.57% <100.00%> (+1.11%) ⬆️
...ne/src/core/bufservices/contract/updateContract.ts 78.26% <100.00%> (+1.11%) ⬆️
...core/bufservices/feature-flag/createFeatureFlag.ts 77.83% <100.00%> (+1.07%) ⬆️
...core/bufservices/feature-flag/deleteFeatureFlag.ts 88.07% <100.00%> (+0.81%) ⬆️
...core/bufservices/feature-flag/enableFeatureFlag.ts 79.16% <100.00%> (+1.68%) ⬆️
...e/bufservices/feature-flag/recomposeFeatureFlag.ts 96.85% <100.00%> (+0.24%) ⬆️
...ufservices/federated-graph/createFederatedGraph.ts 73.09% <100.00%> (+1.13%) ⬆️
.../bufservices/federated-graph/moveFederatedGraph.ts 86.77% <100.00%> (+0.66%) ⬆️
...ufservices/federated-graph/updateFederatedGraph.ts 87.19% <100.00%> (+0.83%) ⬆️
... and 20 more
🚀 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.

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/services/CompositionBlobStorageQueue.ts (1)

61-111: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Queue is never cleared after draining, causing duplicate processing on subsequent calls.

The drainQueue() method iterates over this.#queue but never clears it. If drainQueue() is called multiple times (e.g., due to retry logic or code flow changes), all actions will be re-processed, leading to duplicate uploads/deletions.

🐛 Proposed fix to clear the queue
   async drainQueue(): Promise<PlainMessage<DeploymentError>[]> {
     const errors: PlainMessage<DeploymentError>[] = [];
     if (this.#queue.length === 0) {
       return errors;
     }

+    const actions = this.#queue;
+    this.#queue = [];
+
     const composer = new Composer(this.logger, this.db, this.organizationId, this.chClient, this.webhookProxyUrl);
-    for (const entry of this.#queue) {
+    for (const entry of actions) {
       switch (entry.action) {
🤖 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/CompositionBlobStorageQueue.ts` around lines
61 - 111, drainQueue() iterates this.#queue but never clears it, causing
duplicate re-processing; modify drainQueue() to ensure the queue is cleared when
processing begins or when processing completes (use a try/finally) so entries
aren't re-run on subsequent calls—for example, capture the current batch (or set
this.#queue = []) before/inside processing and place the clear in a finally
block to guarantee cleanup even on errors; reference the drainQueue method and
the private field this.#queue when making the change.
♻️ Duplicate comments (2)
controlplane/src/core/services/CompositionBlobStorageQueue.ts (1)

99-104: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistent error handling: upload-blob logs but doesn't report errors, unlike delete-blob.

The delete-blob case (lines 84-95) pushes errors to the returned array so callers can see deployment failures. The upload-blob case only logs, meaning callers will see success even when blob uploads fail. This asymmetry can cause data inconsistency where the DB transaction committed but artifacts are missing.

If blob upload failures should be non-fatal (fire-and-forget), document this explicitly. Otherwise, align with delete-blob behavior.

This was flagged in a past review—logging was added but errors still aren't surfaced to callers.

🤖 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/CompositionBlobStorageQueue.ts` around lines
99 - 104, The upload-blob branch currently swallows errors by only calling
this.logger.error in the case 'upload-blob' block where
this.blobStorage.putObject(entry.params) fails; update it to surface failures
the same way as the delete-blob path by pushing a failure object/error into the
method's returned results array (or rethrow if the desired behavior is to fail
fast) so callers see upload failures; reference the case 'upload-blob', the call
to this.blobStorage.putObject(entry.params), and the logger.error invocation and
mirror the error-pushing logic used in the case 'delete-blob' so behavior is
consistent or explicitly document otherwise.
controlplane/src/core/bufservices/monograph/updateMonograph.ts (1)

140-148: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Queue is created and drained inside the transaction, defeating the PR's purpose.

Unlike all other handlers in this PR, updateMonograph creates CompositionBlobStorageQueue inside the DB transaction (lines 140-148) and drains it inside the transaction (line 193). This keeps CDN uploads within the transaction, which contradicts the PR objective to "extract CDN upload outside the transaction."

This causes:

  • Extended transaction duration due to external I/O
  • Risk of transaction timeouts on slow CDN operations
  • Potential DB inconsistency if CDN operations fail mid-transaction

Move cbsq instantiation before the transaction and drainQueue() after it, matching the pattern in other handlers (deleteFeatureFlag, createFederatedGraph, etc.).

🐛 Suggested structure
+    const cbsq = new CompositionBlobStorageQueue(
+      logger,
+      opts.db,
+      opts.blobStorage,
+      authContext.organizationId,
+      { cdnBaseUrl: opts.cdnBaseUrl, webhookJWTSecret: opts.admissionWebhookJWTSecret },
+      opts.chClient,
+      opts.webhookProxyUrl,
+    );
+
     return opts.db.transaction(async (tx) => {
       // ... existing code ...
-      const cbsq = new CompositionBlobStorageQueue(
-        logger,
-        opts.db,
-        opts.blobStorage,
-        authContext.organizationId,
-        { cdnBaseUrl: opts.cdnBaseUrl, webhookJWTSecret: opts.admissionWebhookJWTSecret },
-        opts.chClient,
-        opts.webhookProxyUrl,
-      );
       // ... compositionService and updates ...
-      await cbsq.drainQueue();
       // ... audit log and webhooks ...
-      return { response: { code: EnumStatusCode.OK }, compositionErrors: [] };
+      return { compositionErrors: [], deploymentErrors: [] };
     });
+
+    const queueErrors = await cbsq.drainQueue();
+    // Handle queueErrors in response...

Also applies to: 193-193

🤖 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/monograph/updateMonograph.ts` around lines
140 - 148, The CompositionBlobStorageQueue is created and drained inside
updateMonograph's DB transaction; instantiate the queue (new
CompositionBlobStorageQueue(...)) before beginning the transaction and remove
its usage from inside the transaction block, then call cbsq.drainQueue() only
after the transaction has committed (or rolled back) so CDN uploads occur
outside the transaction; update references in updateMonograph accordingly to
mirror the pattern used by deleteFeatureFlag/createFederatedGraph (create queue
before transaction, drainQueue() after).
🧹 Nitpick comments (1)
controlplane/src/core/services/CompositionBlobStorageQueue.ts (1)

103-103: 💤 Low value

Use template literal instead of string concatenation.

As per coding guidelines, prefer template literals over string concatenation.

♻️ Suggested fix
-            this.logger.error(`Failed to upload blob "${entry.params.key}": ${err}`);
+            this.logger.error(`Failed to upload blob "${entry.params.key}": ${String(err)}`);

Note: Using String(err) makes the coercion explicit and handles non-Error objects cleanly.

🤖 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/CompositionBlobStorageQueue.ts` at line 103,
Replace the string concatenation in the error log with a template literal and
explicitly coerce the error to a string; locate the logger call inside
CompositionBlobStorageQueue (the upload/failure handling block where
this.logger.error is called for entry.params.key) and change the message to use
a template literal like `Failed to upload blob "${entry.params.key}":
${String(err)}` so non-Error values are handled consistently.

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.

Outside diff comments:
In `@controlplane/src/core/services/CompositionBlobStorageQueue.ts`:
- Around line 61-111: drainQueue() iterates this.#queue but never clears it,
causing duplicate re-processing; modify drainQueue() to ensure the queue is
cleared when processing begins or when processing completes (use a try/finally)
so entries aren't re-run on subsequent calls—for example, capture the current
batch (or set this.#queue = []) before/inside processing and place the clear in
a finally block to guarantee cleanup even on errors; reference the drainQueue
method and the private field this.#queue when making the change.

---

Duplicate comments:
In `@controlplane/src/core/bufservices/monograph/updateMonograph.ts`:
- Around line 140-148: The CompositionBlobStorageQueue is created and drained
inside updateMonograph's DB transaction; instantiate the queue (new
CompositionBlobStorageQueue(...)) before beginning the transaction and remove
its usage from inside the transaction block, then call cbsq.drainQueue() only
after the transaction has committed (or rolled back) so CDN uploads occur
outside the transaction; update references in updateMonograph accordingly to
mirror the pattern used by deleteFeatureFlag/createFederatedGraph (create queue
before transaction, drainQueue() after).

In `@controlplane/src/core/services/CompositionBlobStorageQueue.ts`:
- Around line 99-104: The upload-blob branch currently swallows errors by only
calling this.logger.error in the case 'upload-blob' block where
this.blobStorage.putObject(entry.params) fails; update it to surface failures
the same way as the delete-blob path by pushing a failure object/error into the
method's returned results array (or rethrow if the desired behavior is to fail
fast) so callers see upload failures; reference the case 'upload-blob', the call
to this.blobStorage.putObject(entry.params), and the logger.error invocation and
mirror the error-pushing logic used in the case 'delete-blob' so behavior is
consistent or explicitly document otherwise.

---

Nitpick comments:
In `@controlplane/src/core/services/CompositionBlobStorageQueue.ts`:
- Line 103: Replace the string concatenation in the error log with a template
literal and explicitly coerce the error to a string; locate the logger call
inside CompositionBlobStorageQueue (the upload/failure handling block where
this.logger.error is called for entry.params.key) and change the message to use
a template literal like `Failed to upload blob "${entry.params.key}":
${String(err)}` so non-Error values are handled consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: df00fd5c-7d32-43f1-83a3-392fd8e1b4bc

📥 Commits

Reviewing files that changed from the base of the PR and between 0c8ec2f and 0aa3886.

📒 Files selected for processing (21)
  • controlplane/src/core/bufservices/contract/createContract.ts
  • controlplane/src/core/bufservices/contract/updateContract.ts
  • controlplane/src/core/bufservices/feature-flag/createFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/deleteFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/enableFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/recomposeFeatureFlag.ts
  • controlplane/src/core/bufservices/feature-flag/updateFeatureFlag.ts
  • controlplane/src/core/bufservices/federated-graph/createFederatedGraph.ts
  • controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts
  • controlplane/src/core/bufservices/federated-graph/moveFederatedGraph.ts
  • controlplane/src/core/bufservices/federated-graph/updateFederatedGraph.ts
  • controlplane/src/core/bufservices/graph/recomposeGraph.ts
  • controlplane/src/core/bufservices/graph/setGraphRouterCompatibilityVersion.ts
  • controlplane/src/core/bufservices/monograph/publishMonograph.ts
  • controlplane/src/core/bufservices/monograph/updateMonograph.ts
  • controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/moveSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
  • controlplane/src/core/bufservices/subgraph/updateSubgraph.ts
  • controlplane/src/core/services/CompositionBlobStorageQueue.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • controlplane/src/core/bufservices/contract/updateContract.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraphs.ts
  • controlplane/src/core/bufservices/feature-flag/recomposeFeatureFlag.ts
  • controlplane/src/core/bufservices/graph/recomposeGraph.ts
  • controlplane/src/core/bufservices/federated-graph/updateFederatedGraph.ts
  • controlplane/src/core/bufservices/graph/setGraphRouterCompatibilityVersion.ts
  • controlplane/src/core/bufservices/feature-flag/createFeatureFlag.ts
  • controlplane/src/core/bufservices/contract/createContract.ts
  • controlplane/src/core/bufservices/federated-graph/moveFederatedGraph.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts
  • controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts
  • controlplane/src/core/bufservices/subgraph/moveSubgraph.ts
  • controlplane/src/core/bufservices/monograph/publishMonograph.ts
  • controlplane/src/core/bufservices/feature-flag/updateFeatureFlag.ts

@github-actions

Copy link
Copy Markdown

This PR was marked stale due to lack of activity. It will be closed in 14 days.

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.

1 participant