Skip to content

Commit a846e72

Browse files
matt-aitkenclaude
andcommitted
debug: disable all workers + pg_stat_activity diagnostics for hang investigation
- Fix SCHEDULE_WORKER_ENABLED (was 'false', must be '0' to disable) - Add RUN_ENGINE_WORKER_ENABLED: '0' (was missing, defaulted to '1') - Add BATCH_QUEUE_WORKER_ENABLED: 'false' (not controlled by WORKER_ENABLED) - Add LEGACY/COMMON/TTL worker disabling env vars for completeness - Add pg_stat_activity polling every 10s when WEBAPP_TEST_VERBOSE=1 - Add per-step timing logs in seedTestEnvironment to identify which DB operation hangs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent acdbbb6 commit a846e72

2 files changed

Lines changed: 39 additions & 4 deletions

File tree

apps/webapp/test/helpers/seedTestEnvironment.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ export async function seedTestEnvironment(prisma: PrismaClient) {
1010
const apiKey = `tr_dev_${randomHex(24)}`;
1111
const pkApiKey = `pk_dev_${randomHex(24)}`;
1212

13+
const t0 = Date.now();
14+
process.stderr.write(`[seed] creating organization (${suffix})...\n`);
15+
1316
const organization = await prisma.organization.create({
1417
data: {
1518
title: `e2e-test-org-${suffix}`,
1619
slug: `e2e-org-${suffix}`,
1720
v3Enabled: true,
1821
},
1922
});
23+
process.stderr.write(`[seed] organization created in ${Date.now() - t0}ms\n`);
2024

2125
const project = await prisma.project.create({
2226
data: {
@@ -27,6 +31,7 @@ export async function seedTestEnvironment(prisma: PrismaClient) {
2731
engine: "V2",
2832
},
2933
});
34+
process.stderr.write(`[seed] project created in ${Date.now() - t0}ms\n`);
3035

3136
const environment = await prisma.runtimeEnvironment.create({
3237
data: {
@@ -39,6 +44,7 @@ export async function seedTestEnvironment(prisma: PrismaClient) {
3944
organizationId: organization.id,
4045
},
4146
});
47+
process.stderr.write(`[seed] environment created in ${Date.now() - t0}ms\n`);
4248

4349
return { organization, project, environment, apiKey };
4450
}

internal-packages/testcontainers/src/webapp.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,16 @@ export async function startWebapp(
6969
ELECTRIC_ORIGIN: "http://localhost:3060",
7070
REDIS_HOST: redis.host,
7171
REDIS_PORT: String(redis.port),
72-
// Disable background workers and logical replication: they are irrelevant for auth
73-
// tests and in CI the replication slot creation holds a snapshot lock that blocks
74-
// the test process's Prisma writes to the same DB.
75-
WORKER_ENABLED: "false",
72+
// Disable all background workers. Each worker has its own env var and its own
73+
// check idiom ("0" vs "false" vs boolean), so we set all of them explicitly.
74+
WORKER_ENABLED: "false", // disables workerQueue.initialize() (checked === "true")
75+
RUN_ENGINE_WORKER_ENABLED: "0", // disables run engine workers (checked === "0", default "1")
76+
SCHEDULE_WORKER_ENABLED: "0", // disables schedule engine worker (checked === "0")
77+
BATCH_QUEUE_WORKER_ENABLED: "false", // disables batch queue consumers (BoolEnv)
78+
LEGACY_RUN_ENGINE_WORKER_ENABLED: "0", // disables legacy run engine worker
79+
COMMON_WORKER_ENABLED: "0", // disables common worker
80+
RUN_ENGINE_TTL_SYSTEM_DISABLED: "true", // disables TTL expiry system (BoolEnv)
81+
RUN_ENGINE_TTL_CONSUMERS_DISABLED: "true", // disables TTL consumers (BoolEnv)
7682
RUN_REPLICATION_ENABLED: "0",
7783
NODE_PATH: nodePath,
7884
},
@@ -153,6 +159,7 @@ export async function startTestServer(): Promise<TestServer> {
153159
let prisma: PrismaClient | undefined;
154160
let stopWebapp: (() => Promise<void>) | undefined;
155161
let webapp: WebappInstance;
162+
let diagInterval: ReturnType<typeof setInterval> | undefined;
156163

157164
try {
158165
const pg = await createPostgresContainer(network);
@@ -163,6 +170,27 @@ export async function startTestServer(): Promise<TestServer> {
163170

164171
prisma = new PrismaClient({ datasources: { db: { url: pg.url } } });
165172
await prisma.$connect(); // pre-warm pool; surface connection failures before tests start
173+
174+
// Periodically dump pg_stat_activity when debugging to identify hangs.
175+
if (process.env.WEBAPP_TEST_VERBOSE) {
176+
diagInterval = setInterval(async () => {
177+
try {
178+
const rows = await prisma!.$queryRawUnsafe<Record<string, unknown>[]>(`
179+
SELECT pid, state, wait_event_type, wait_event,
180+
LEFT(query, 300) AS query,
181+
EXTRACT(EPOCH FROM (now() - state_change))::int AS state_age_secs,
182+
EXTRACT(EPOCH FROM (now() - query_start))::int AS query_age_secs
183+
FROM pg_stat_activity
184+
WHERE datname = current_database() AND pid != pg_backend_pid()
185+
ORDER BY query_start NULLS LAST
186+
`);
187+
process.stderr.write(`[pg_stat_activity] ${JSON.stringify(rows)}\n`);
188+
} catch {
189+
// Diagnostic failures are non-fatal; ignore them.
190+
}
191+
}, 10_000);
192+
}
193+
166194
const started = await startWebapp(pg.url, { host: rc.getHost(), port: rc.getPort() });
167195
webapp = started.instance;
168196
stopWebapp = started.stop;
@@ -176,6 +204,7 @@ export async function startTestServer(): Promise<TestServer> {
176204
}
177205

178206
const stop = async () => {
207+
if (diagInterval) clearInterval(diagInterval);
179208
await stopWebapp!().catch((err) => console.error("stopWebapp failed:", err));
180209
await prisma!.$disconnect().catch((err) => console.error("prisma.$disconnect failed:", err));
181210
await pgContainer!.stop().catch((err) => console.error("pgContainer.stop failed:", err));

0 commit comments

Comments
 (0)