chore: import upstream snapshot with attribution
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
||||
import type { CreateBackgroundWorkerRequestBody } from "@trigger.dev/core/v3";
|
||||
import { BackgroundWorkerId } from "@trigger.dev/core/v3/isomorphic";
|
||||
import {
|
||||
isUniqueConstraintError,
|
||||
type BackgroundWorker,
|
||||
type PrismaClientOrTransaction,
|
||||
type WorkerDeployment,
|
||||
} from "@trigger.dev/database";
|
||||
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
|
||||
import { ServiceValidationError } from "../common.server";
|
||||
import { stripBackgroundWorkerMetadataForStorage } from "../stripBackgroundWorkerMetadataForStorage.server";
|
||||
|
||||
/**
|
||||
* Idempotent on `(project, environment, version)` for sequential calls, not concurrent calls.
|
||||
*
|
||||
* Failure shapes the caller distinguishes:
|
||||
* - `ServiceValidationError` (409): definitive — contentHash drift means a different build
|
||||
* is being pushed under the same deployment version, so the caller should fail the
|
||||
* deployment instead of waiting for a timeout.
|
||||
* - Plain `Error`: transient — two attempts raced the `create()` call and the loser caught
|
||||
* the unique-index violation. The caller should propagate this as 5xx so the CLI's
|
||||
* retry/backoff hits findFirst on the next attempt and returns the winner's row.
|
||||
*/
|
||||
export async function findOrCreateBackgroundWorker(
|
||||
environment: AuthenticatedEnvironment,
|
||||
deployment: WorkerDeployment,
|
||||
body: CreateBackgroundWorkerRequestBody,
|
||||
prisma: PrismaClientOrTransaction
|
||||
): Promise<BackgroundWorker> {
|
||||
const existing = await prisma.backgroundWorker.findFirst({
|
||||
where: {
|
||||
projectId: environment.projectId,
|
||||
runtimeEnvironmentId: environment.id,
|
||||
version: deployment.version,
|
||||
},
|
||||
});
|
||||
|
||||
if (existing && existing.contentHash === body.metadata.contentHash) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
throw new ServiceValidationError(
|
||||
"A background worker for this deployment version already exists with a different content hash",
|
||||
409
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return await prisma.backgroundWorker.create({
|
||||
data: {
|
||||
...BackgroundWorkerId.generate(),
|
||||
version: deployment.version,
|
||||
runtimeEnvironmentId: environment.id,
|
||||
projectId: environment.projectId,
|
||||
metadata: stripBackgroundWorkerMetadataForStorage(body.metadata),
|
||||
contentHash: body.metadata.contentHash,
|
||||
cliVersion: body.metadata.cliPackageVersion,
|
||||
sdkVersion: body.metadata.packageVersion,
|
||||
supportsLazyAttempts: body.supportsLazyAttempts,
|
||||
engine: body.engine,
|
||||
runtime: body.metadata.runtime,
|
||||
runtimeVersion: body.metadata.runtimeVersion,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
// Concurrent attempts raced past `findFirst` and both reached `create`. Surface
|
||||
// a clear, non-Prisma error so the 5xx the caller returns isn't an opaque
|
||||
// P2002 — the CLI's retry will then hit `findFirst` and find the winner's row.
|
||||
// Intentionally NOT a ServiceValidationError so the caller doesn't fail-deploy
|
||||
// on a transient race.
|
||||
if (isUniqueConstraintError(error, ["projectId", "runtimeEnvironmentId", "version"])) {
|
||||
throw new Error(
|
||||
"Concurrent background worker registration detected for this deployment version; please retry"
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user