From 20fad456510e844c5f16203eee2a7828b5d8fa2f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:27:52 +0000 Subject: [PATCH] Make OrganizationDataStoresRegistry deterministic on overlap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort findMany() by `key` for a stable winner when multiple rows assign the same `${orgId}:${kind}`, and log an error identifying the winning and ignored rows instead of overwriting silently. Does not fail the load — failing the registry would break every customer, not just the misconfigured orgs. Co-Authored-By: Matt Aitken --- .../organizationDataStoresRegistry.server.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/services/dataStores/organizationDataStoresRegistry.server.ts b/apps/webapp/app/services/dataStores/organizationDataStoresRegistry.server.ts index f2ef02cc3..838dd5ae5 100644 --- a/apps/webapp/app/services/dataStores/organizationDataStoresRegistry.server.ts +++ b/apps/webapp/app/services/dataStores/organizationDataStoresRegistry.server.ts @@ -33,10 +33,18 @@ export class OrganizationDataStoresRegistry { } async loadFromDatabase(): Promise { - const rows = await this._prisma.organizationDataStore.findMany(); + // Sort by `key` (unique, immutable) to ensure a deterministic winner when the + // same `${orgId}:${kind}` appears in multiple rows. The registry must never + // throw on overlap — failing the load would break every customer, not just the + // misconfigured orgs — so we keep the first entry and log an error instead. + const rows = await this._prisma.organizationDataStore.findMany({ + orderBy: { key: "asc" }, + }); const secretStore = getSecretStore("DATABASE", { prismaClient: this._prisma }); const lookup = new Map(); + /** Tracks which row's `key` already owns each `${orgId}:${kind}` so we can log conflicts. */ + const winnerByLookupKey = new Map(); for (const row of rows) { let parsed: ParsedDataStore | null = null; @@ -75,8 +83,16 @@ export class OrganizationDataStoresRegistry { } for (const orgId of row.organizationIds) { - const key = `${orgId}:${row.kind}`; - lookup.set(key, parsed); + const lookupKey = `${orgId}:${row.kind}`; + const existingWinner = winnerByLookupKey.get(lookupKey); + if (existingWinner) { + console.error( + `[OrganizationDataStoresRegistry] Overlapping OrganizationDataStore assignment for orgId="${orgId}" kind=${row.kind}: already routed to "${existingWinner}", ignoring "${row.key}". Pick one store per (org, kind) to resolve.` + ); + continue; + } + winnerByLookupKey.set(lookupKey, row.key); + lookup.set(lookupKey, parsed); } }