fix(run-store): fix batch idempotency lookup on the dedicated run-ops store (#4271)
## Summary `batchTrigger` requests that set a per-item `idempotencyKey` failed with a 500 when the run-store is split across databases: the per-item idempotency lookup errored before any run was created. Batches without per-item keys, single `trigger` idempotency, and batch-level (`idempotency-key` header) idempotency were unaffected. ## Root cause `findRunsByIdempotencyKeys` built its `UNION ALL` of per-key point-lookups with `@trigger.dev/database`'s `Prisma.sql` / `Prisma.join`, then executed it on whichever store client it was handed. On the dedicated run-ops store that client is a *separate* generated Prisma client, and a `Sql` object from a different generated client is not recognized: the bare `$queryRaw(Prisma.join(...))` form dropped the query text entirely (`Argument \`query\` is missing`). The tagged-template form is no better here: joining nested `Prisma.sql` fragments across the two clients mis-numbers the bound parameters (`syntax error at or near "$1"`). ## Fix Build the lookup as a plain parameterized string and run it via `$queryRawUnsafe` with positional placeholders and bound values, so it no longer depends on which generated client executes it. The query text contains only static SQL and integer placeholders; every value (`runtimeEnvironmentId`, `taskIdentifier`, each key) is bound, so it is not a raw-interpolation site. Same per-key point-lookup shape as before, no change on the single-client path. Verified end-to-end against a bundled build with the run-store split enabled: before the fix, `batchTrigger` with a per-item key 500s; after, it returns the runs and dedups correctly across fresh, repeat, and mixed batches.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
area: webapp
|
||||
type: fix
|
||||
---
|
||||
|
||||
Fix batchTrigger requests that set a per-item idempotency key failing with an error instead of creating and deduplicating the runs
|
||||
@@ -72,6 +72,7 @@ export interface RunOpsCapableClient {
|
||||
// Standalone entity keyed by (environmentId, name); present on both schemas.
|
||||
waitpointTag: RunOpsDelegate<"upsert" | "findMany">;
|
||||
$queryRaw: PrismaClient["$queryRaw"];
|
||||
$queryRawUnsafe: PrismaClient["$queryRawUnsafe"];
|
||||
$executeRaw: PrismaClient["$executeRaw"];
|
||||
}
|
||||
|
||||
@@ -1691,11 +1692,16 @@ export class PostgresRunStore implements RunStore {
|
||||
return [];
|
||||
}
|
||||
const prisma = (client ?? this.readOnlyPrisma) as RunOpsCapableClient;
|
||||
const branches = args.idempotencyKeys.map(
|
||||
(key) =>
|
||||
Prisma.sql`SELECT "friendlyId", "idempotencyKey", "idempotencyKeyExpiresAt" FROM "TaskRun" WHERE "runtimeEnvironmentId" = ${args.runtimeEnvironmentId} AND "taskIdentifier" = ${args.taskIdentifier} AND "idempotencyKey" = ${key}`
|
||||
const params: string[] = [];
|
||||
const branches = args.idempotencyKeys.map((key) => {
|
||||
const base = params.length;
|
||||
params.push(args.runtimeEnvironmentId, args.taskIdentifier, key);
|
||||
return `SELECT "friendlyId", "idempotencyKey", "idempotencyKeyExpiresAt" FROM "TaskRun" WHERE "runtimeEnvironmentId" = $${base + 1} AND "taskIdentifier" = $${base + 2} AND "idempotencyKey" = $${base + 3}`;
|
||||
});
|
||||
return prisma.$queryRawUnsafe<IdempotencyKeyRunMatch[]>(
|
||||
branches.join(" UNION ALL "),
|
||||
...params
|
||||
);
|
||||
return prisma.$queryRaw<IdempotencyKeyRunMatch[]>(Prisma.join(branches, " UNION ALL "));
|
||||
}
|
||||
|
||||
// --- run-ops persistence ---
|
||||
|
||||
Reference in New Issue
Block a user