Skip to content

Commit 20fad45

Browse files
Make OrganizationDataStoresRegistry deterministic on overlap
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 <matt@mattaitken.com>
1 parent 40f95a7 commit 20fad45

1 file changed

Lines changed: 19 additions & 3 deletions

File tree

apps/webapp/app/services/dataStores/organizationDataStoresRegistry.server.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,18 @@ export class OrganizationDataStoresRegistry {
3333
}
3434

3535
async loadFromDatabase(): Promise<void> {
36-
const rows = await this._prisma.organizationDataStore.findMany();
36+
// Sort by `key` (unique, immutable) to ensure a deterministic winner when the
37+
// same `${orgId}:${kind}` appears in multiple rows. The registry must never
38+
// throw on overlap — failing the load would break every customer, not just the
39+
// misconfigured orgs — so we keep the first entry and log an error instead.
40+
const rows = await this._prisma.organizationDataStore.findMany({
41+
orderBy: { key: "asc" },
42+
});
3743
const secretStore = getSecretStore("DATABASE", { prismaClient: this._prisma });
3844

3945
const lookup = new Map<string, ParsedDataStore>();
46+
/** Tracks which row's `key` already owns each `${orgId}:${kind}` so we can log conflicts. */
47+
const winnerByLookupKey = new Map<string, string>();
4048

4149
for (const row of rows) {
4250
let parsed: ParsedDataStore | null = null;
@@ -75,8 +83,16 @@ export class OrganizationDataStoresRegistry {
7583
}
7684

7785
for (const orgId of row.organizationIds) {
78-
const key = `${orgId}:${row.kind}`;
79-
lookup.set(key, parsed);
86+
const lookupKey = `${orgId}:${row.kind}`;
87+
const existingWinner = winnerByLookupKey.get(lookupKey);
88+
if (existingWinner) {
89+
console.error(
90+
`[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.`
91+
);
92+
continue;
93+
}
94+
winnerByLookupKey.set(lookupKey, row.key);
95+
lookup.set(lookupKey, parsed);
8096
}
8197
}
8298

0 commit comments

Comments
 (0)