Skip to content

Commit 235cfc3

Browse files
committed
chore(scripts): register a consumer task for S3, fix S10 CH insert fingerprint
S3 uat-ck-queue had no BackgroundWorkerTask pointing at it, so get_queue's consumerTasks was empty and the agent's honest "undeployed" answer preempted the invisible-holder scenario. Registers one on the env's current worker (minting a minimal V2 one if the env has none yet). S10's printed ClickHouse INSERT omitted error_fingerprint, so both materialized views (errors_v1, error_occurrences_v1) silently excluded the row - get_error/list_errors could never find it. Also fixes a SQL-string escaping bug where the stack trace's \n broke ClickHouse's JSON parser. Prints the ask-able error_<fingerprint> id.
1 parent 8f6f008 commit 235cfc3

1 file changed

Lines changed: 113 additions & 13 deletions

File tree

scripts/seed-dashboard-agent-uat.ts

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,76 @@ async function upsertQueue(
201201
});
202202
}
203203

204+
// get_queue's `consumerTasks` (internal-packages/dashboard-agent/src/tool-api.ts,
205+
// consumerTasksForQueue) is read off the env's CURRENT worker's tasks - for a
206+
// DEVELOPMENT env that's the latest BackgroundWorker by createdAt (never a deployment;
207+
// see findCurrentWorkerFromEnvironment in workerDeployment.server.ts) - matching a
208+
// BackgroundWorkerTask whose queueConfig.name equals the queue name. Without one, the
209+
// queue looks unconsumed and the agent's honest "no deployed consumer" diagnosis
210+
// preempts whatever the scenario is actually testing.
211+
async function ensureConsumerTask(
212+
ctx: Ctx,
213+
env: RuntimeEnvironment,
214+
queue: { id: string; name: string; concurrencyLimit: number | null },
215+
taskSlug: string
216+
) {
217+
let currentWorker = await ctx.prisma.backgroundWorker.findFirst({
218+
where: { runtimeEnvironmentId: env.id },
219+
orderBy: { createdAt: "desc" },
220+
});
221+
if (!currentWorker) {
222+
// A fresh per-member dev env (no `trigger dev` session yet) has no worker at all -
223+
// mint a minimal one so the scenario is seedable without that manual step. Tagged
224+
// "uat-dev-worker-1" so `clean` can remove it; a real `trigger dev` session
225+
// afterward naturally supersedes it as the env's current worker.
226+
currentWorker = await ctx.prisma.backgroundWorker.upsert({
227+
where: {
228+
projectId_runtimeEnvironmentId_version: {
229+
projectId: ctx.projectId,
230+
runtimeEnvironmentId: env.id,
231+
version: "uat-dev-worker-1",
232+
},
233+
},
234+
create: {
235+
friendlyId: generateFriendlyId("worker"),
236+
// engine defaults to V1 - determineEngineVersion() reads the LATEST worker's engine
237+
// to gate every queue/run route for the env, so an unset engine here would 400
238+
// every queue lookup in this dev env, not just this fixture's own queue.
239+
engine: "V2",
240+
contentHash: "uat-dev-worker-hash",
241+
sdkVersion: "0.0.0-uat",
242+
cliVersion: "0.0.0-uat",
243+
projectId: ctx.projectId,
244+
runtimeEnvironmentId: env.id,
245+
version: "uat-dev-worker-1",
246+
metadata: {},
247+
},
248+
update: {},
249+
});
250+
}
251+
252+
await ctx.prisma.backgroundWorkerTask.upsert({
253+
where: { workerId_slug: { workerId: currentWorker.id, slug: taskSlug } },
254+
create: {
255+
friendlyId: generateFriendlyId("task"),
256+
projectId: ctx.projectId,
257+
runtimeEnvironmentId: env.id,
258+
workerId: currentWorker.id,
259+
slug: taskSlug,
260+
filePath: "src/trigger/uat-fixtures.ts",
261+
queueConfig: { name: queue.name, concurrencyLimit: queue.concurrencyLimit },
262+
queueId: queue.id,
263+
triggerSource: "STANDARD",
264+
},
265+
update: {
266+
queueConfig: { name: queue.name, concurrencyLimit: queue.concurrencyLimit },
267+
queueId: queue.id,
268+
},
269+
});
270+
271+
return currentWorker;
272+
}
273+
204274
type RunFields = {
205275
idempotencyKey: string;
206276
env: RuntimeEnvironment;
@@ -344,6 +414,9 @@ async function seedCkInvisible(ctx: Ctx) {
344414
const queue = await upsertQueue(ctx, ctx.devEnv, queueName, 3);
345415
record("S3", "queue", queue.friendlyId, `${queueName} (concurrencyKey, limit 3)`);
346416

417+
const consumerWorker = await ensureConsumerTask(ctx, ctx.devEnv, queue, "uat-ck-consumer-task");
418+
record("S3", "consumer task", "uat-ck-consumer-task", `on worker ${consumerWorker.version}`);
419+
347420
const run = await upsertRun(ctx, {
348421
idempotencyKey: "uat-ck-admitted",
349422
env: ctx.devEnv,
@@ -623,27 +696,44 @@ async function seedRecurred(ctx: Ctx) {
623696
},
624697
update: { status: "RESOLVED", resolvedAt, resolvedInVersion: "uat", resolvedBy: user?.id },
625698
});
699+
// get_error/list_errors ask for the friendly `error_<fingerprint>` id (ErrorId.toFriendlyId,
700+
// apps/webapp/app/presenters/v3/ApiErrorGroupPresenter.server.ts) - name it explicitly so
701+
// the tester doesn't have to fish it out of a list_errors call first.
702+
const askableId = `error_${errorFingerprint}`;
626703
record("S10", "ErrorGroupState", errorGroup.id, `resolvedAt=${resolvedAt.toISOString()}`);
704+
record("S10", "ask-able id", askableId, `taskIdentifier=${taskIdentifier}`);
627705

628706
const version = Date.now();
707+
// ClickHouse SQL string literals backslash-unescape before the JSON parser ever sees the
708+
// value, so JSON.stringify's `\n` (2 chars) becomes a raw newline byte inside the JSON
709+
// text - invalid JSON. Escape backslashes first so ClickHouse's unescape leaves `\n`
710+
// intact for the JSON parser; escape quotes after (order matters, or '' would double-escape).
629711
const errorJson = JSON.stringify({
630712
data: {
631713
type: "Error",
632714
message: "uat recurred fixture error",
633715
stack: "Error: uat recurred fixture error\n at uatFixture (uat.ts:1:1)",
634716
},
635-
}).replace(/'/g, "''");
717+
})
718+
.replace(/\\/g, "\\\\")
719+
.replace(/'/g, "''");
636720

637-
console.log("\nS10: Postgres side done. ClickHouse errors_v1 is a materialized view over");
638-
console.log("task_runs_v2 - run this manually to make the error 'recur' after resolvedAt:\n");
721+
console.log("\nS10: Postgres side done. ClickHouse errors_v1 AND error_occurrences_v1 are both");
722+
console.log(
723+
"materialized views over task_runs_v2 (matched on error_fingerprint != '' + a failure"
724+
);
725+
console.log("status) - run this manually to make the error 'recur' after resolvedAt. Omitting");
726+
console.log("error_fingerprint here silently excludes the row from BOTH views, so get_error and");
727+
console.log(`list_errors both miss it. Ask about: ${askableId}\n`);
639728
console.log(
640729
`clickhouse-client --query "INSERT INTO trigger_dev.task_runs_v2 ` +
641730
`(environment_id, organization_id, project_id, run_id, friendly_id, environment_type, ` +
642-
`engine, status, task_identifier, queue, task_version, error, created_at, updated_at, _version) ` +
731+
`engine, status, task_identifier, error_fingerprint, queue, task_version, error, ` +
732+
`created_at, updated_at, _version) ` +
643733
`VALUES ('${ctx.devEnv.id}', '${ctx.orgId}', '${ctx.projectId}', 'uat-recurred-run', ` +
644734
`'run_uatrecurred', 'DEVELOPMENT', 'V2', 'COMPLETED_WITH_ERRORS', '${taskIdentifier}', ` +
645-
`'uat-recurred-task', 'uat', '${errorJson}', '${formatChDateTime(lastSeen)}', ` +
646-
`'${formatChDateTime(lastSeen)}', ${version})"\n`
735+
`'${errorFingerprint}', 'uat-recurred-task', 'uat', '${errorJson}', ` +
736+
`'${formatChDateTime(lastSeen)}', '${formatChDateTime(lastSeen)}', ${version})"\n`
647737
);
648738
}
649739

@@ -724,23 +814,33 @@ async function clean(ctx: ProjectCtx) {
724814
},
725815
});
726816

727-
// BackgroundWorker -> WorkerDeployment is onDelete: Cascade.
728-
await ctx.prisma.backgroundWorker.deleteMany({
817+
await ctx.prisma.errorGroupState.deleteMany({
818+
where: {
819+
environmentId: { in: boundedIn(envs.map((e) => e.id)) },
820+
taskIdentifier: "uat-recurred-task",
821+
},
822+
});
823+
824+
// The consumer-task fixture row first: ensureConsumerTask may have attached it to the
825+
// env's REAL current worker (not a fixture), so this must run before any worker delete.
826+
await ctx.prisma.backgroundWorkerTask.deleteMany({
729827
where: {
730828
runtimeEnvironmentId: { in: boundedIn(envs.map((e) => e.id)) },
731-
version: "uat-dirty-1",
829+
slug: "uat-ck-consumer-task",
732830
},
733831
});
734832

735-
await ctx.prisma.errorGroupState.deleteMany({
833+
// Fixture workers only ("uat-dirty-1" for S6, "uat-dev-worker-1" when ensureConsumerTask
834+
// had to mint one). BackgroundWorker -> WorkerDeployment/BackgroundWorkerTask is Cascade.
835+
await ctx.prisma.backgroundWorker.deleteMany({
736836
where: {
737-
environmentId: { in: boundedIn(envs.map((e) => e.id)) },
738-
taskIdentifier: "uat-recurred-task",
837+
runtimeEnvironmentId: { in: boundedIn(envs.map((e) => e.id)) },
838+
version: { in: ["uat-dirty-1", "uat-dev-worker-1"] },
739839
},
740840
});
741841

742842
console.log(
743-
`Cleaned ${runIds.length} runs, uat-* queues, dirty-deploy worker, error group ` +
843+
`Cleaned ${runIds.length} runs, uat-* queues, dirty-deploy worker, consumer task, error group ` +
744844
`(swept ${envs.length} envs in the project).`
745845
);
746846
}

0 commit comments

Comments
 (0)