chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:32:57 +08:00
commit cd420f9332
4811 changed files with 884702 additions and 0 deletions
@@ -0,0 +1,210 @@
import { heteroPostgresTest } from "@internal/testcontainers";
import type { PrismaClient } from "@trigger.dev/database";
import { expect, vi } from "vitest";
import { ControlPlaneCache } from "~/v3/runOpsMigration/controlPlaneCache.server";
import { ControlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
// Control-plane datasource repoint (legacy DB -> new DB).
//
// Post-repoint the control plane lives on the new DB, so we model the new topology by seeding the
// control-plane rows on the new side (`prisma17`) and injecting it as both the resolver's primary
// and replica. `prisma14` stands in for the pre-repoint legacy source for the cross-version
// transition test. NEVER mock — we seed and read the real testcontainer clients, and we observe
// the DB boundary via a $extends query counter.
// Cross-DB testcontainer spin-up + queries can exceed the 5s default on the first test.
vi.setConfig({ testTimeout: 60_000 });
let seedCounter = 0;
/**
* Wraps a real testcontainer PrismaClient with a `$extends` query hook that increments a counter
* on every actual operation. NOT a mock: the returned client still issues the real query and
* returns real rows — we only observe the DB boundary (the countQueries pattern).
*/
function countQueries(client: PrismaClient): { client: PrismaClient; reads: () => number } {
let count = 0;
const extended = client.$extends({
query: {
async $allOperations({ args, query }) {
count++;
return query(args);
},
},
}) as unknown as PrismaClient;
return { client: extended, reads: () => count };
}
/** Seeds org -> project -> env + a pinned BackgroundWorker (+task) + TaskQueue + TaskSchedule. */
async function seedControlPlane(prisma: PrismaClient) {
const n = seedCounter++;
const org = await prisma.organization.create({
data: { title: `Org ${n}`, slug: `org-${n}` },
});
const project = await prisma.project.create({
data: {
name: `Project ${n}`,
slug: `project-${n}`,
externalRef: `proj_${n}`,
organizationId: org.id,
},
});
const environment = await prisma.runtimeEnvironment.create({
data: {
type: "PRODUCTION",
slug: `env-${n}`,
projectId: project.id,
organizationId: org.id,
apiKey: `tr_prod_${n}`,
pkApiKey: `pk_prod_${n}`,
shortcode: `short_${n}`,
},
});
const worker = await prisma.backgroundWorker.create({
data: {
friendlyId: `worker_${n}`,
contentHash: `hash_${n}`,
projectId: project.id,
runtimeEnvironmentId: environment.id,
version: `2024.1.${n}`,
metadata: {},
engine: "V2",
},
});
await prisma.backgroundWorkerTask.create({
data: {
friendlyId: `task_${n}`,
slug: `my-task-${n}`,
filePath: "index.ts",
exportName: "myTask",
workerId: worker.id,
runtimeEnvironmentId: environment.id,
projectId: project.id,
},
});
const queue = await prisma.taskQueue.create({
data: {
friendlyId: `queue_${n}`,
name: `task/my-task-${n}`,
runtimeEnvironmentId: environment.id,
projectId: project.id,
workers: { connect: { id: worker.id } },
},
});
const schedule = await prisma.taskSchedule.create({
data: {
friendlyId: `schedule_${n}`,
taskIdentifier: `my-task-${n}`,
generatorExpression: "0 * * * *",
projectId: project.id,
},
});
return { org, project, environment, worker, queue, schedule };
}
// --- Repoint resolution (split ON, CP on the new DB) ---------
heteroPostgresTest(
"control-plane references resolve against the repointed (new-DB) CP client",
async ({ prisma17 }) => {
const { environment, worker } = await seedControlPlane(prisma17);
const resolver = new ControlPlaneResolver({
controlPlanePrimary: prisma17,
controlPlaneReplica: prisma17,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
expect(await resolver.resolveEnv(environment.id)).toMatchObject({ id: environment.id });
expect(
await resolver.resolveWorkerVersion({
environmentId: environment.id,
backgroundWorkerId: worker.id,
})
).not.toBeNull();
}
);
// --- Relaxed-cache (no latency regression) -------------------------
heteroPostgresTest("relaxed (longer TTL) cache still hits on the new DB", async ({ prisma17 }) => {
const { environment } = await seedControlPlane(prisma17);
const { client: counting, reads } = countQueries(prisma17);
const resolver = new ControlPlaneResolver({
controlPlanePrimary: counting,
controlPlaneReplica: counting,
// Relaxed: a much longer TTL than the default — same-provider resolution is cheap.
cache: new ControlPlaneCache({ ttlMs: 300_000, maxEntries: 10_000 }),
splitEnabled: () => true,
});
expect(await resolver.resolveEnv(environment.id)).toMatchObject({ id: environment.id });
expect(reads()).toBe(1);
// Second read served from the relaxed cache — no extra DB round-trip.
await resolver.resolveEnv(environment.id);
expect(reads()).toBe(1);
});
// --- Cross-version transition (legacy DB -> new DB) -----------------------
heteroPostgresTest(
"resolution is byte-identical across the legacy-DB -> new-DB host transition",
async ({ prisma14, prisma17, pinnedCollation }) => {
// Seed identical control-plane shapes on the pre-repoint (legacy) and post-repoint
// (new) sides.
const before = await seedControlPlane(prisma14);
const after = await seedControlPlane(prisma17);
const resolver14 = new ControlPlaneResolver({
controlPlanePrimary: prisma14,
controlPlaneReplica: prisma14,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const resolver17 = new ControlPlaneResolver({
controlPlanePrimary: prisma17,
controlPlaneReplica: prisma17,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const env14 = await resolver14.resolveEnv(before.environment.id);
const env17 = await resolver17.resolveEnv(after.environment.id);
// Same resolution shape across the version boundary (ids differ per-seed; structure identical).
expect(Object.keys(env14 ?? {}).sort()).toEqual(Object.keys(env17 ?? {}).sort());
expect(env14?.type).toBe(env17?.type);
expect(env14?.archivedAt).toBe(env17?.archivedAt);
// ORDER BY on a representative text-heavy column must agree across the version boundary, using
// the pinned ICU collation the hetero fixture exposes so the comparison is apples-to-apples.
const slugs = ["banana", "Apple", "cherry", "Äpfel", "apple"];
const orderBy = async (prisma: PrismaClient) => {
const rows = await prisma.$queryRawUnsafe<{ s: string }[]>(
`SELECT s FROM (VALUES ('${slugs.join("'),('")}')) AS t(s) ORDER BY s COLLATE "${pinnedCollation}"`
);
return rows.map((r) => r.s);
};
expect(await orderBy(prisma14)).toEqual(await orderBy(prisma17));
}
);
// --- Single-DB no-op (passthrough preserved) -----------------------
heteroPostgresTest(
"single-DB passthrough (split OFF) runs plain in-DB joins with no cache",
async ({ prisma17 }) => {
const { environment } = await seedControlPlane(prisma17);
const { client: counting, reads } = countQueries(prisma17);
const resolver = new ControlPlaneResolver({
controlPlanePrimary: counting,
controlPlaneReplica: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
await resolver.resolveEnv(environment.id);
await resolver.resolveEnv(environment.id);
// No cache when split is OFF — every call hits the DB, identical to today's passthrough.
expect(reads()).toBe(2);
}
);
@@ -0,0 +1,750 @@
import { heteroPostgresTest } from "@internal/testcontainers";
import type { PrismaClient } from "@trigger.dev/database";
import { describe, expect, it, vi } from "vitest";
import { ControlPlaneCache } from "~/v3/runOpsMigration/controlPlaneCache.server";
import {
ControlPlaneReferenceError,
ControlPlaneResolver,
} from "~/v3/runOpsMigration/controlPlaneResolver.server";
// Cross-DB testcontainer spin-up + queries can exceed the 5s default on the first test.
vi.setConfig({ testTimeout: 60_000 });
// --- test helpers ----------------------------------------------------------
let seedCounter = 0;
/**
* Wraps a real testcontainer PrismaClient with a `$extends` query hook that increments a
* counter on every actual operation. NOT a mock: the returned client still issues the real
* query and returns real rows — we only observe the DB boundary.
*/
function countQueries(client: PrismaClient): { client: PrismaClient; reads: () => number } {
let count = 0;
const extended = client.$extends({
query: {
async $allOperations({ args, query }) {
count++;
return query(args);
},
},
}) as unknown as PrismaClient;
return { client: extended, reads: () => count };
}
async function seedControlPlane(prisma: PrismaClient) {
const n = seedCounter++;
const org = await prisma.organization.create({
data: { title: `Org ${n}`, slug: `org-${n}` },
});
const project = await prisma.project.create({
data: {
name: `Project ${n}`,
slug: `project-${n}`,
externalRef: `proj_${n}`,
organizationId: org.id,
},
});
const environment = await prisma.runtimeEnvironment.create({
data: {
type: "PRODUCTION",
slug: `env-${n}`,
projectId: project.id,
organizationId: org.id,
apiKey: `tr_prod_${n}`,
pkApiKey: `pk_prod_${n}`,
shortcode: `short_${n}`,
},
});
return { org, project, environment };
}
async function seedWorker(
prisma: PrismaClient,
ctx: { projectId: string; environmentId: string },
opts?: { promote?: boolean }
) {
const n = seedCounter++;
const worker = await prisma.backgroundWorker.create({
data: {
friendlyId: `worker_${n}`,
contentHash: `hash_${n}`,
projectId: ctx.projectId,
runtimeEnvironmentId: ctx.environmentId,
version: `2024.1.${n}`,
metadata: {},
engine: "V2",
},
});
const task = await prisma.backgroundWorkerTask.create({
data: {
friendlyId: `task_${n}`,
slug: `my-task-${n}`,
filePath: "index.ts",
exportName: "myTask",
workerId: worker.id,
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
},
});
const queue = await prisma.taskQueue.create({
data: {
friendlyId: `queue_${n}`,
name: `task/my-task-${n}`,
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
workers: { connect: { id: worker.id } },
},
});
let deployment = null;
if (opts?.promote) {
deployment = await prisma.workerDeployment.create({
data: {
friendlyId: `deployment_${n}`,
contentHash: `hash_${n}`,
version: worker.version,
shortCode: `dep_${n}`,
type: "MANAGED",
status: "DEPLOYED",
projectId: ctx.projectId,
environmentId: ctx.environmentId,
workerId: worker.id,
},
});
await prisma.workerDeploymentPromotion.create({
data: {
label: "current",
deploymentId: deployment.id,
environmentId: ctx.environmentId,
},
});
}
return { worker, task, queue, deployment };
}
// --- cache unit tests (no DB) ----------------------------------------------
describe("ControlPlaneCache", () => {
it("caches null as a confirmed absence (distinct from a miss)", () => {
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
expect(cache.getEnv("env_x")).toBeUndefined();
cache.setEnv("env_x", null);
expect(cache.getEnv("env_x")).toBeNull();
});
it("invalidateEnv drops the entry (next read is a miss)", () => {
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
cache.setEnv("env_y", { id: "env_y" } as any);
cache.invalidateEnv("env_y");
expect(cache.getEnv("env_y")).toBeUndefined();
});
it("invalidating one key does not affect another", () => {
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
cache.setEnv("env_a", { id: "env_a" } as any);
cache.setEnv("env_b", { id: "env_b" } as any);
cache.invalidateEnv("env_a");
expect(cache.getEnv("env_a")).toBeUndefined();
expect(cache.getEnv("env_b")).toMatchObject({ id: "env_b" });
});
});
// --- resolveEnv -------------------------------------------------------------
heteroPostgresTest(
"resolveEnv returns the cross-DB env row and caches it",
async ({ prisma14 }) => {
const { environment, org } = await seedControlPlane(prisma14);
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache,
splitEnabled: () => true,
});
const first = await resolver.resolveEnv(environment.id);
expect(first).toMatchObject({
id: environment.id,
projectId: environment.projectId,
organizationId: org.id,
type: "PRODUCTION",
archivedAt: null,
});
expect(reads()).toBe(1);
const second = await resolver.resolveEnv(environment.id);
expect(second).toEqual(first);
expect(reads()).toBe(1);
}
);
heteroPostgresTest("resolveEnv caches a null absence", async ({ prisma14 }) => {
const cache = new ControlPlaneCache();
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache,
splitEnabled: () => true,
});
expect(await resolver.resolveEnv("env_does_not_exist")).toBeNull();
expect(reads()).toBe(1);
expect(await resolver.resolveEnv("env_does_not_exist")).toBeNull();
expect(reads()).toBe(1);
});
heteroPostgresTest(
"resolveEnv passthrough (split OFF) hits the DB every time, no cache",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
await resolver.resolveEnv(environment.id);
await resolver.resolveEnv(environment.id);
expect(reads()).toBe(2);
}
);
// --- resolveWorkerVersion ---------------------------------------------------
heteroPostgresTest(
"resolveWorkerVersion (pinned) returns worker/tasks/queues and caches it",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker, task, queue } = await seedWorker(prisma14, {
projectId: project.id,
environmentId: environment.id,
});
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const first = await resolver.resolveWorkerVersion({
environmentId: environment.id,
backgroundWorkerId: worker.id,
});
expect(first?.worker.id).toBe(worker.id);
expect(first?.tasks.map((t) => t.id)).toContain(task.id);
expect(first?.queues.map((q) => q.id)).toContain(queue.id);
expect(first?.deployment).toBeNull();
const readsAfterFirst = reads();
expect(readsAfterFirst).toBeGreaterThanOrEqual(1);
const second = await resolver.resolveWorkerVersion({
environmentId: environment.id,
backgroundWorkerId: worker.id,
});
expect(second?.worker.id).toBe(worker.id);
expect(reads()).toBe(readsAfterFirst);
}
);
heteroPostgresTest(
"resolveWorkerVersion (current deployment) resolves the promoted worker",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker, deployment } = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id },
{ promote: true }
);
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const first = await resolver.resolveWorkerVersion({ environmentId: environment.id });
expect(first?.worker.id).toBe(worker.id);
expect(first?.deployment?.id).toBe(deployment?.id);
const readsAfterFirst = reads();
const second = await resolver.resolveWorkerVersion({ environmentId: environment.id });
expect(second?.worker.id).toBe(worker.id);
expect(reads()).toBe(readsAfterFirst);
}
);
heteroPostgresTest(
"resolveWorkerVersion passthrough (split OFF) re-reads every call",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker } = await seedWorker(prisma14, {
projectId: project.id,
environmentId: environment.id,
});
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
await resolver.resolveWorkerVersion({
environmentId: environment.id,
backgroundWorkerId: worker.id,
});
const readsAfterFirst = reads();
await resolver.resolveWorkerVersion({
environmentId: environment.id,
backgroundWorkerId: worker.id,
});
expect(reads()).toBe(readsAfterFirst * 2);
}
);
// --- assertEnvExists --------------------------------------------------------
heteroPostgresTest(
"assertEnvExists resolves for a seeded env, caches, and throws for a missing one",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
await expect(resolver.assertEnvExists(environment.id)).resolves.toBeUndefined();
expect(reads()).toBe(1);
await expect(resolver.assertEnvExists(environment.id)).resolves.toBeUndefined();
expect(reads()).toBe(1);
await expect(resolver.assertEnvExists("env_missing")).rejects.toBeInstanceOf(
ControlPlaneReferenceError
);
}
);
heteroPostgresTest(
"assertEnvExists passthrough (split OFF) is a no-op: never reads, never throws",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
// Split OFF = single DB, run and env co-located, so there is nothing to assert
// and the hot-path read is skipped entirely — resolves for present and missing.
await expect(resolver.assertEnvExists(environment.id)).resolves.toBeUndefined();
await expect(resolver.assertEnvExists("env_missing")).resolves.toBeUndefined();
expect(reads()).toBe(0);
}
);
// --- resolveAuthenticatedEnv ------------------------------------------------
heteroPostgresTest(
"resolveAuthenticatedEnv returns the toAuthenticated shape and caches it",
async ({ prisma14 }) => {
const { environment, project, org } = await seedControlPlane(prisma14);
const { client: counting, reads } = countQueries(prisma14);
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache,
splitEnabled: () => true,
});
const first = await resolver.resolveAuthenticatedEnv(environment.id);
expect(first).not.toBeNull();
expect(first!.id).toBe(environment.id);
expect(first!.slug).toBe(environment.slug);
expect(first!.type).toBe("PRODUCTION");
expect(first!.organizationId).toBe(org.id);
expect(first!.projectId).toBe(project.id);
expect(first!.project.id).toBe(project.id);
expect(first!.project.externalRef).toBe(project.externalRef);
expect(first!.organization.id).toBe(org.id);
expect(first!.organization.title).toBe(org.title);
// concurrencyLimitBurstFactor is coerced to a plain number by toAuthenticated().
expect(typeof first!.concurrencyLimitBurstFactor).toBe("number");
expect(reads()).toBe(1);
const second = await resolver.resolveAuthenticatedEnv(environment.id);
expect(second).toEqual(first);
expect(reads()).toBe(1);
expect(await resolver.resolveAuthenticatedEnv("env_missing")).toBeNull();
}
);
heteroPostgresTest(
"resolveAuthenticatedEnv populates parentEnvironment { id, apiKey } for a branch env",
async ({ prisma14 }) => {
const m = seedCounter++;
const org = await prisma14.organization.create({
data: { title: `Org wp ${m}`, slug: `org-wp-${m}` },
});
const project = await prisma14.project.create({
data: {
name: `P wp ${m}`,
slug: `p-wp-${m}`,
externalRef: `proj_wp_${m}`,
organizationId: org.id,
},
});
const parent = await prisma14.runtimeEnvironment.create({
data: {
type: "PREVIEW",
slug: `preview-parent-${m}`,
projectId: project.id,
organizationId: org.id,
apiKey: `tr_parent_key_${m}`,
pkApiKey: `pk_parent_${m}`,
shortcode: `sc_parent_${m}`,
},
});
const branch = await prisma14.runtimeEnvironment.create({
data: {
type: "PREVIEW",
slug: `preview-branch-${m}`,
branchName: "feat/x",
projectId: project.id,
organizationId: org.id,
apiKey: `tr_branch_key_${m}`,
pkApiKey: `pk_branch_${m}`,
shortcode: `sc_branch_${m}`,
parentEnvironmentId: parent.id,
},
});
const resolver = new ControlPlaneResolver({
controlPlaneReplica: prisma14,
controlPlanePrimary: prisma14,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const env = await resolver.resolveAuthenticatedEnv(branch.id);
expect(env).not.toBeNull();
expect(env!.apiKey).toBe(`tr_branch_key_${m}`);
expect(env!.parentEnvironment).not.toBeNull();
expect(env!.parentEnvironment!.id).toBe(parent.id);
expect(env!.parentEnvironment!.apiKey).toBe(`tr_parent_key_${m}`);
const noParent = await resolver.resolveAuthenticatedEnv(parent.id);
expect(noParent!.parentEnvironment).toBeNull();
}
);
heteroPostgresTest(
"resolveAuthenticatedEnv passthrough (split OFF) hits the DB every time, no cache",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
await resolver.resolveAuthenticatedEnv(environment.id);
await resolver.resolveAuthenticatedEnv(environment.id);
expect(reads()).toBe(2);
}
);
heteroPostgresTest(
"resolveAuthenticatedEnv carries the `git` column (cached across calls)",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const gitMeta = { commitSha: "abc123", branchName: "main" };
await prisma14.runtimeEnvironment.update({
where: { id: environment.id },
data: { git: gitMeta },
});
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => true,
});
const first = await resolver.resolveAuthenticatedEnv(environment.id);
expect(first).not.toBeNull();
expect(first!.git).toEqual(gitMeta);
expect(reads()).toBe(1);
// Served from cache, still carrying `git`.
const second = await resolver.resolveAuthenticatedEnv(environment.id);
expect(second!.git).toEqual(gitMeta);
expect(reads()).toBe(1);
}
);
// --- invalidation over the DB boundary -------------------------------------
heteroPostgresTest(
"invalidateEnvironment forces resolveEnv/resolveAuthenticatedEnv to re-read after a write",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
const resolver = new ControlPlaneResolver({
controlPlaneReplica: prisma14,
controlPlanePrimary: prisma14,
cache,
splitEnabled: () => true,
});
// Warm both env-scoped slots.
expect((await resolver.resolveEnv(environment.id))!.maximumConcurrencyLimit).not.toBe(999);
expect((await resolver.resolveAuthenticatedEnv(environment.id))!.paused).toBe(false);
// Control-plane write + invalidation (as a write site would do).
await prisma14.runtimeEnvironment.update({
where: { id: environment.id },
data: { maximumConcurrencyLimit: 999, paused: true },
});
resolver.invalidateEnvironment(environment.id);
expect((await resolver.resolveEnv(environment.id))!.maximumConcurrencyLimit).toBe(999);
expect((await resolver.resolveAuthenticatedEnv(environment.id))!.paused).toBe(true);
}
);
heteroPostgresTest(
"without invalidation a cached env stays stale after a control-plane write (fail-before contrast)",
async ({ prisma14 }) => {
const { environment } = await seedControlPlane(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: prisma14,
controlPlanePrimary: prisma14,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => true,
});
const before = (await resolver.resolveEnv(environment.id))!.maximumConcurrencyLimit;
await prisma14.runtimeEnvironment.update({
where: { id: environment.id },
data: { maximumConcurrencyLimit: 777 },
});
// No invalidation: the cache still serves the pre-write value (this is the bug the
// write-site invalidation fixes).
expect((await resolver.resolveEnv(environment.id))!.maximumConcurrencyLimit).toBe(before);
// And with invalidation it re-reads.
resolver.invalidateEnvironment(environment.id);
expect((await resolver.resolveEnv(environment.id))!.maximumConcurrencyLimit).toBe(777);
}
);
heteroPostgresTest(
"invalidateOrganization forces every env of the org to re-read after an org write",
async ({ prisma14 }) => {
const { org: organization, project } = await seedControlPlane(prisma14);
// A second env in the same org.
const m = seedCounter++;
const secondEnv = await prisma14.runtimeEnvironment.create({
data: {
type: "STAGING",
slug: `env-second-${m}`,
projectId: project.id,
organizationId: organization.id,
apiKey: `tr_stg_${m}`,
pkApiKey: `pk_stg_${m}`,
shortcode: `short_stg_${m}`,
},
});
const firstEnv = await prisma14.runtimeEnvironment.findFirstOrThrow({
where: { projectId: project.id, type: "PRODUCTION" },
});
const resolver = new ControlPlaneResolver({
controlPlaneReplica: prisma14,
controlPlanePrimary: prisma14,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => true,
});
// Warm both envs' authEnv slots.
expect((await resolver.resolveAuthenticatedEnv(firstEnv.id))!.organization.runsEnabled).toBe(
true
);
expect((await resolver.resolveAuthenticatedEnv(secondEnv.id))!.organization.runsEnabled).toBe(
true
);
// Org-level write (runsEnabled) + a single org invalidation.
await prisma14.organization.update({
where: { id: organization.id },
data: { runsEnabled: false },
});
resolver.invalidateOrganization(organization.id);
// BOTH envs re-read and now observe the org change, with no reverse org->env index.
expect((await resolver.resolveAuthenticatedEnv(firstEnv.id))!.organization.runsEnabled).toBe(
false
);
expect((await resolver.resolveAuthenticatedEnv(secondEnv.id))!.organization.runsEnabled).toBe(
false
);
}
);
// --- resolveRunLockedWorker -------------------------------------------------
heteroPostgresTest(
"resolveRunLockedWorker returns lockedBy (task+worker+deployment) and lockedToVersion, caches it",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker, task, deployment } = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id },
{ promote: true }
);
const { client: counting, reads } = countQueries(prisma14);
const cache = new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 });
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache,
splitEnabled: () => true,
});
const first = await resolver.resolveRunLockedWorker({
lockedById: task.id,
lockedToVersionId: worker.id,
});
expect(first).not.toBeNull();
expect(first!.lockedBy!.id).toBe(task.id);
expect(first!.lockedBy!.filePath).toBe(task.filePath);
expect(first!.lockedBy!.slug).toBe(task.slug);
expect(first!.lockedBy!.exportName).toBe(task.exportName);
expect(first!.lockedBy!.machineConfig).toEqual(task.machineConfig);
expect(first!.lockedBy!.worker.id).toBe(worker.id);
expect(first!.lockedBy!.worker.version).toBe(worker.version);
expect(first!.lockedBy!.worker.deployment!.friendlyId).toBe(deployment!.friendlyId);
expect(first!.lockedToVersion!.version).toBe(worker.version);
expect(first!.lockedToVersion!.supportsLazyAttempts).toBe(worker.supportsLazyAttempts);
const readsAfterFirst = reads();
expect(readsAfterFirst).toBeGreaterThanOrEqual(1);
const second = await resolver.resolveRunLockedWorker({
lockedById: task.id,
lockedToVersionId: worker.id,
});
expect(second).toEqual(first);
expect(reads()).toBe(readsAfterFirst);
}
);
heteroPostgresTest(
"resolveRunLockedWorker returns null lockedBy/lockedToVersion when ids are absent",
async ({ prisma14 }) => {
const { client: counting } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => true,
});
const resolved = await resolver.resolveRunLockedWorker({
lockedById: null,
lockedToVersionId: null,
});
expect(resolved).not.toBeNull();
expect(resolved!.lockedBy).toBeNull();
expect(resolved!.lockedToVersion).toBeNull();
}
);
heteroPostgresTest(
"resolveRunLockedWorker resolves lockedBy only when lockedToVersionId is absent",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { task } = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id },
{ promote: true }
);
const { client: counting } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => true,
});
const result = await resolver.resolveRunLockedWorker({ lockedById: task.id });
expect(result).not.toBeNull();
expect(result!.lockedBy!.id).toBe(task.id);
expect(result!.lockedBy!.slug).toBe(task.slug);
expect(result!.lockedToVersion).toBeNull();
}
);
heteroPostgresTest(
"resolveRunLockedWorker resolves lockedToVersion only when lockedById is absent",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker } = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id },
{ promote: true }
);
const { client: counting } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => true,
});
const result = await resolver.resolveRunLockedWorker({ lockedToVersionId: worker.id });
expect(result).not.toBeNull();
expect(result!.lockedToVersion!.version).toBe(worker.version);
expect(result!.lockedBy).toBeNull();
}
);
heteroPostgresTest(
"resolveRunLockedWorker passthrough (split OFF) re-reads every call",
async ({ prisma14 }) => {
const { environment, project } = await seedControlPlane(prisma14);
const { worker, task } = await seedWorker(prisma14, {
projectId: project.id,
environmentId: environment.id,
});
const { client: counting, reads } = countQueries(prisma14);
const resolver = new ControlPlaneResolver({
controlPlaneReplica: counting,
controlPlanePrimary: counting,
cache: new ControlPlaneCache(),
splitEnabled: () => false,
});
await resolver.resolveRunLockedWorker({ lockedById: task.id, lockedToVersionId: worker.id });
const readsAfterFirst = reads();
await resolver.resolveRunLockedWorker({ lockedById: task.id, lockedToVersionId: worker.id });
expect(reads()).toBe(readsAfterFirst * 2);
}
);
@@ -0,0 +1,64 @@
import { heteroPostgresTest } from "@internal/testcontainers";
import { PrismaClient } from "@trigger.dev/database";
import { describe, expect, vi } from "vitest";
import { probeDistinctDatabases } from "~/v3/runOpsMigration/distinctDbSentinel.server";
// Spinning up two separate postgres clusters and probing each can exceed the 5s default.
vi.setConfig({ testTimeout: 60_000 });
function urlWithDatabase(uri: string, database: string): string {
const url = new URL(uri);
url.pathname = `/${database}`;
return url.toString();
}
describe("probeDistinctDatabases", () => {
heteroPostgresTest(
"reports distinct for two separate physical clusters",
async ({ uri14, uri17 }) => {
const result = await probeDistinctDatabases(uri14, uri17);
expect(result).toEqual({ distinct: true });
}
);
heteroPostgresTest(
"reports NOT distinct, citing the same physical database, when both URLs point at it",
async ({ uri14 }) => {
const result = await probeDistinctDatabases(uri14, uri14);
expect(result.distinct).toBe(false);
if (result.distinct === false) {
expect(result.reason).toMatch(/same physical database/i);
}
}
);
heteroPostgresTest(
"reports distinct for two databases in the SAME cluster",
async ({ postgresContainer14, uri14 }) => {
const otherDb = `sentinel_other_${Date.now()}`;
const admin = new PrismaClient({
datasources: {
db: { url: urlWithDatabase(postgresContainer14.getConnectionUri(), "postgres") },
},
});
try {
await admin.$executeRawUnsafe(`CREATE DATABASE "${otherDb}"`);
} finally {
await admin.$disconnect();
}
const otherUrl = urlWithDatabase(uri14, otherDb);
const result = await probeDistinctDatabases(uri14, otherUrl);
expect(result).toEqual({ distinct: true });
}
);
heteroPostgresTest(
"fails closed to NOT distinct when a probe cannot reach a database",
async ({ uri14 }) => {
const unreachable = "postgresql://nobody:nobody@127.0.0.1:1/does_not_exist";
const result = await probeDistinctDatabases(uri14, unreachable);
expect(result.distinct).toBe(false);
}
);
});
@@ -0,0 +1,265 @@
// The webapp adapter presents the cross-DB app ControlPlaneResolver as the run-engine seam.
// Proven over real testcontainers (never mocked): resolveEnv maps onto the MinimalAuthenticatedEnv
// superset; resolveWorkerVersion forwards the env type so the engine dequeue dispatch (DEV
// most-recent / MANAGED promotion) runs; assertEnvExists delegates and rejects on a missing env.
import { heteroPostgresTest } from "@internal/testcontainers";
import type { PrismaClient } from "@trigger.dev/database";
import { CURRENT_DEPLOYMENT_LABEL } from "@trigger.dev/core/v3/isomorphic";
import { describe, expect, vi } from "vitest";
import { ControlPlaneCache } from "~/v3/runOpsMigration/controlPlaneCache.server";
import { ControlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
import { RunEngineControlPlaneResolver } from "~/v3/runOpsMigration/runEngineControlPlaneResolver.server";
vi.setConfig({ testTimeout: 60_000 });
let n = 0;
function buildAppResolver(controlPlane: PrismaClient, opts?: { splitEnabled?: boolean }) {
return new ControlPlaneResolver({
controlPlanePrimary: controlPlane,
controlPlaneReplica: controlPlane,
cache: new ControlPlaneCache({ ttlMs: 60_000, maxEntries: 100 }),
splitEnabled: () => opts?.splitEnabled ?? false,
});
}
/**
* Wraps a real testcontainer PrismaClient with a `$extends` query hook counting DB operations.
* Not a mock — the real query still runs; we only observe the boundary to prove cache hits.
*/
function countQueries(client: PrismaClient): { client: PrismaClient; reads: () => number } {
let count = 0;
const extended = client.$extends({
query: {
async $allOperations({ args, query }) {
count++;
return query(args);
},
},
}) as unknown as PrismaClient;
return { client: extended, reads: () => count };
}
async function seedEnv(prisma: PrismaClient, type: "PRODUCTION" | "DEVELOPMENT") {
const suffix = `re-${n++}`;
const organization = await prisma.organization.create({
data: { title: `Org ${suffix}`, slug: `org-${suffix}` },
});
const project = await prisma.project.create({
data: {
name: `Project ${suffix}`,
slug: `project-${suffix}`,
externalRef: `proj_${suffix}`,
organizationId: organization.id,
},
});
const environment = await prisma.runtimeEnvironment.create({
data: {
type,
slug: suffix,
projectId: project.id,
organizationId: organization.id,
apiKey: `tr_${suffix}`,
pkApiKey: `pk_${suffix}`,
shortcode: `short_${suffix}`,
maximumConcurrencyLimit: 9,
},
});
return { organization, project, environment, suffix };
}
async function seedWorker(
prisma: PrismaClient,
ctx: { projectId: string; environmentId: string; suffix: string },
opts: { promote?: boolean; deploy?: boolean }
) {
const worker = await prisma.backgroundWorker.create({
data: {
friendlyId: `worker_${ctx.suffix}`,
contentHash: `hash_${ctx.suffix}`,
projectId: ctx.projectId,
runtimeEnvironmentId: ctx.environmentId,
version: `2024.1.${ctx.suffix}`,
metadata: {},
engine: "V2",
},
});
const task = await prisma.backgroundWorkerTask.create({
data: {
friendlyId: `task_${ctx.suffix}`,
slug: "my-task",
filePath: "index.ts",
exportName: "myTask",
workerId: worker.id,
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
},
});
const queue = await prisma.taskQueue.create({
data: {
friendlyId: `queue_${ctx.suffix}`,
name: "task/my-task",
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
type: "VIRTUAL",
workers: { connect: { id: worker.id } },
tasks: { connect: { id: task.id } },
},
});
if (opts.deploy) {
const deployment = await prisma.workerDeployment.create({
data: {
friendlyId: `deployment_${ctx.suffix}`,
contentHash: worker.contentHash,
version: worker.version,
shortCode: `short_${ctx.suffix}`,
imageReference: `image:${ctx.suffix}`,
status: "DEPLOYED",
projectId: ctx.projectId,
environmentId: ctx.environmentId,
workerId: worker.id,
type: "MANAGED",
},
});
if (opts.promote) {
await prisma.workerDeploymentPromotion.create({
data: {
label: CURRENT_DEPLOYMENT_LABEL,
deploymentId: deployment.id,
environmentId: ctx.environmentId,
},
});
}
return { worker, task, queue, deployment };
}
return { worker, task, queue };
}
describe("RunEngineControlPlaneResolver adapter", () => {
heteroPostgresTest(
"resolveEnv maps app ResolvedEnv onto ResolvedEngineEnv",
async ({ prisma14 }) => {
const { organization, project, environment } = await seedEnv(prisma14, "PRODUCTION");
const adapter = new RunEngineControlPlaneResolver(buildAppResolver(prisma14));
const env = await adapter.resolveEnv(environment.id);
expect(env).not.toBeNull();
expect(env!.id).toBe(environment.id);
expect(env!.type).toBe("PRODUCTION");
expect(env!.projectId).toBe(project.id);
expect(env!.organizationId).toBe(organization.id);
// Nested + concurrency fields the run-engine MinimalAuthenticatedEnvironment requires.
expect(env!.project.id).toBe(project.id);
expect(env!.organization.id).toBe(organization.id);
expect(env!.maximumConcurrencyLimit).toBe(9);
expect(env!.concurrencyLimitBurstFactor.toNumber()).toBe(2);
expect(env!.archivedAt).toBeNull();
expect(await adapter.resolveEnv("env_missing")).toBeNull();
}
);
heteroPostgresTest(
"resolveWorkerVersion (deployed, no workerId) resolves the promoted MANAGED deployment",
async ({ prisma14 }) => {
const { project, environment, suffix } = await seedEnv(prisma14, "PRODUCTION");
const seeded = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id, suffix },
{ deploy: true, promote: true }
);
const adapter = new RunEngineControlPlaneResolver(buildAppResolver(prisma14));
const version = await adapter.resolveWorkerVersion({
environmentId: environment.id,
type: "PRODUCTION",
});
expect(version).not.toBeNull();
expect(version!.worker.id).toBe(seeded.worker.id);
expect(version!.deployment?.id).toBe(
"deployment" in seeded ? seeded.deployment.id : undefined
);
expect(version!.tasks.map((t) => t.slug)).toContain("my-task");
}
);
heteroPostgresTest(
"resolveWorkerVersion (DEVELOPMENT, no workerId) resolves the most-recent worker (no deployment)",
async ({ prisma14 }) => {
const { project, environment, suffix } = await seedEnv(prisma14, "DEVELOPMENT");
const seeded = await seedWorker(
prisma14,
{ projectId: project.id, environmentId: environment.id, suffix },
{ deploy: false }
);
const adapter = new RunEngineControlPlaneResolver(buildAppResolver(prisma14));
const version = await adapter.resolveWorkerVersion({
environmentId: environment.id,
type: "DEVELOPMENT",
});
expect(version).not.toBeNull();
expect(version!.worker.id).toBe(seeded.worker.id);
expect(version!.deployment).toBeNull();
}
);
heteroPostgresTest(
"assertEnvExists (split ON) resolves for a present env, rejects for a missing one",
async ({ prisma14 }) => {
const { environment } = await seedEnv(prisma14, "PRODUCTION");
// split ON: the only mode where assertEnvExists asserts (split OFF is a no-op,
// covered in controlPlaneResolver.server.test.ts).
const adapter = new RunEngineControlPlaneResolver(
buildAppResolver(prisma14, { splitEnabled: true })
);
await expect(adapter.assertEnvExists(environment.id)).resolves.toBeUndefined();
await expect(adapter.assertEnvExists("env_missing")).rejects.toThrow();
}
);
heteroPostgresTest(
"resolveAuthenticatedEnv delegates to the app resolver, returns `git`, and is cached",
async ({ prisma14 }) => {
const { environment } = await seedEnv(prisma14, "PRODUCTION");
const gitMeta = { commitSha: "deadbeef", branchName: "main" };
await prisma14.runtimeEnvironment.update({
where: { id: environment.id },
data: { git: gitMeta },
});
// split ON so the delegated app resolver caches; the counter proves the second call
// is a cache hit rather than re-querying $replica directly (the pre-fix behavior).
const { client: counting, reads } = countQueries(prisma14);
const adapter = new RunEngineControlPlaneResolver(
buildAppResolver(counting, { splitEnabled: true })
);
const first = await adapter.resolveAuthenticatedEnv(environment.id);
expect(first).not.toBeNull();
expect(first!.id).toBe(environment.id);
expect(first!.git).toEqual(gitMeta);
expect(reads()).toBe(1);
const second = await adapter.resolveAuthenticatedEnv(environment.id);
expect(second!.git).toEqual(gitMeta);
expect(reads()).toBe(1);
}
);
heteroPostgresTest(
"resolveAuthenticatedEnv returns null for a deleted project",
async ({ prisma14 }) => {
const { environment, project } = await seedEnv(prisma14, "PRODUCTION");
await prisma14.project.update({
where: { id: project.id },
data: { deletedAt: new Date() },
});
const adapter = new RunEngineControlPlaneResolver(buildAppResolver(prisma14));
expect(await adapter.resolveAuthenticatedEnv(environment.id)).toBeNull();
}
);
});