From bf6a974b5840b9196728f40a2df2b587cc04ec09 Mon Sep 17 00:00:00 2001 From: TrinaryCompute Date: Fri, 17 Jul 2026 00:46:41 -0600 Subject: [PATCH] fix(postgres): scope the cross-process merge guard to the project (#2267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## fix(postgres): scope the cross-process merge guard to the project The guard's own comment (`project-engine.ts`) says it checks whether another process is merging a task **"for this project"** — and in SQLite mode the per-project DB file made that scoping implicit. `getActiveMergingTaskImpl`'s `backendMode` branch queries the shared PG `tasks` table with **no `project_id` filter**, so one merging task anywhere serializes merges across **all** projects. ### Production evidence 6-project embedded-PG deployment: **697 cross-project `Merge deferred … is already merging (cross-process guard)` retries in 10 minutes** — six independent repos waiting on each other's serialized merger, collapsing merge throughput ~6x and letting `in-review` pile up to 95 tasks. ### Fix Add the existing `taskProjectScope(layer)` filter to the query's conditions (one line + import). It is a no-op when the layer carries no `projectId`, so single-project deployments and the SQLite path are unchanged. Same pattern as the other project-scoped task queries. Deployed on the affected instance: cross-project merges now proceed in parallel; per-project serialization (the guard's documented intent) is preserved. ## Summary by CodeRabbit * **Bug Fixes** * Improved merge task handling so activity in one project no longer unnecessarily blocks merge operations in other projects. Co-authored-by: TrinaryCompute --- packages/core/src/task-store/remaining-ops-6.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/core/src/task-store/remaining-ops-6.ts b/packages/core/src/task-store/remaining-ops-6.ts index 2988aecfcb..619fb8e7e2 100644 --- a/packages/core/src/task-store/remaining-ops-6.ts +++ b/packages/core/src/task-store/remaining-ops-6.ts @@ -13,6 +13,7 @@ import { BUILTIN_WORKFLOW_SETTINGS } from "../builtin-workflow-settings.js"; import { isBuiltinWorkflowId } from "../builtin-workflows.js"; import { fromJson } from "../db.js"; import * as schema from "../postgres/schema/index.js"; +import { taskProjectScope } from "../postgres/data-layer.js"; import { ensureBranchGroupForSource as ensureBranchGroupForSourceAsync, ensurePrEntityForSource as ensurePrEntityForSourceAsync, getActivePrEntityBySource as getActivePrEntityBySourceAsync, getBranchGroup as getBranchGroupAsync, getBranchGroupByBranchName as getBranchGroupByBranchNameAsync, getBranchGroupBySource as getBranchGroupBySourceAsync, getPrEntity as getPrEntityAsync, getPrThreadState as getPrThreadStateAsync, listActivePrEntities as listActivePrEntitiesAsync, listBranchGroups as listBranchGroupsAsync, listPrThreadStates as listPrThreadStatesAsync, recordPrThreadOutcome as recordPrThreadOutcomeAsync } from "./async-branch-groups.js"; import { getWorkflowWorkItem as getWorkflowWorkItemAsync } from "./async-workflow-workitems.js"; import { type TaskRow } from "./persistence.js"; @@ -635,6 +636,18 @@ export async function getActiveMergingTaskImpl(store: TaskStore, excludeTaskId?: isNull(schema.project.tasks.deletedAt), inArray(schema.project.tasks.status, ["merging", "merging-pr"]), ]; + /* + * FNXC:PostgresProjectIsolation 2026-07-17-00:00: + * The cross-process merge guard is documented (project-engine.ts) as + * "another process is already merging a task for THIS project" — in + * SQLite mode the per-project DB file made that scoping implicit. The + * shared PG tasks table needs the explicit project_id filter; without it + * one merging task anywhere serializes merges across ALL projects + * (observed: 697 cross-project "Merge deferred" retries in 10 minutes on + * a 6-project deployment, collapsing merge throughput ~6x). + */ + const scope = taskProjectScope(layer); + if (scope) conditions.push(scope); if (excludeTaskId) { conditions.push(ne(schema.project.tasks.id, excludeTaskId)); }