Skip to content

Commit 89431c7

Browse files
ericallamTrigger.dev RepoOps
authored andcommitted
fix(webapp): bound metadata flush retries, add optional Redis buffer
The metadata flusher now gives up on a run after a bounded number of version conflicts and comes back to it on the next flush, so one parent being updated by many children at once no longer stalls metadata updates for other runs. Operations for a run that no longer exists are dropped. Buffered parent and root operations can optionally live in Redis instead of process memory, so they survive a webapp restart and only one webapp instance writes a given run at a time: ```bash BATCH_METADATA_OPERATIONS_BUFFER_BACKEND=redis METADATA_BUFFER_REDIS_HOST=your-redis-host # defaults to REDIS_HOST ``` The default is still the in-process buffer. Mono-RevId: 685c35d1d5780cb0a2a19b69da8b7308e3f911a0
1 parent c49fd05 commit 89431c7

8 files changed

Lines changed: 2958 additions & 109 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Parent and root metadata updates from child tasks no longer stall behind a parent run that is being updated by many children at once, and can optionally be buffered in Redis so they survive a webapp restart.

‎apps/webapp/app/env.server.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,32 @@ const EnvironmentSchema = z
11441144
BATCH_METADATA_OPERATIONS_FLUSH_INTERVAL_MS: z.coerce.number().int().default(1000),
11451145
BATCH_METADATA_OPERATIONS_FLUSH_ENABLED: z.string().default("1"),
11461146
BATCH_METADATA_OPERATIONS_FLUSH_LOGGING_ENABLED: z.string().default("1"),
1147+
BATCH_METADATA_OPERATIONS_BUFFER_BACKEND: z.enum(["memory", "redis"]).default("memory"),
1148+
BATCH_METADATA_OPERATIONS_DRAIN_BATCH_SIZE: z.coerce.number().int().default(10),
1149+
BATCH_METADATA_OPERATIONS_MAX_PENDING_PER_RUN: z.coerce.number().int().default(1_000),
1150+
METADATA_BUFFER_REDIS_LOCK_TTL_MS: z.coerce.number().int().default(30_000),
1151+
METADATA_BUFFER_REDIS_HOST: z
1152+
.string()
1153+
.optional()
1154+
.transform((v) => v ?? process.env.REDIS_HOST),
1155+
METADATA_BUFFER_REDIS_PORT: z.coerce
1156+
.number()
1157+
.optional()
1158+
.transform(
1159+
(v) => v ?? (process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined)
1160+
),
1161+
METADATA_BUFFER_REDIS_USERNAME: z
1162+
.string()
1163+
.optional()
1164+
.transform((v) => v ?? process.env.REDIS_USERNAME),
1165+
METADATA_BUFFER_REDIS_PASSWORD: z
1166+
.string()
1167+
.optional()
1168+
.transform((v) => v ?? process.env.REDIS_PASSWORD),
1169+
METADATA_BUFFER_REDIS_TLS_DISABLED: z
1170+
.string()
1171+
.default(process.env.REDIS_TLS_DISABLED ?? "false"),
1172+
METADATA_BUFFER_REDIS_CLUSTER_MODE_ENABLED: z.string().default("0"),
11471173

11481174
// Run Engine 2.0
11491175
RUN_ENGINE_WORKER_COUNT: z.coerce.number().int().default(4),

‎apps/webapp/app/routes/api.v1.runs.$runId.metadata.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,24 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
8080
const { action } = createActionApiRoute(
8181
{
8282
params: ParamsSchema,
83+
headers: z.object({ "x-trigger-request-idempotency-key": z.string().nullish() }),
8384
body: UpdateMetadataRequestBody,
8485
maxContentLength: 1024 * 1024 * 2, // 2MB
8586
method: "PUT",
8687
},
87-
async ({ authentication, body, params }) => {
88+
async ({ authentication, body, params, headers }) => {
8889
const env = authentication.environment;
8990
const runId = params.runId;
9091

9192
// PG-canonical path. If the run is in PG, the existing service
9293
// owns the full request shape including parent/root operations,
9394
// metadataVersion CAS, batching, validation — none of which the
9495
// buffer side needs to reimplement.
95-
const [pgError, pgResult] = await tryCatch(updateMetadataService.call(runId, body, env));
96+
const [pgError, pgResult] = await tryCatch(
97+
updateMetadataService.call(runId, body, env, {
98+
requestIdempotencyKey: headers["x-trigger-request-idempotency-key"] ?? undefined,
99+
})
100+
);
96101
if (pgError) {
97102
if (pgError instanceof ServiceValidationError) {
98103
return json({ error: pgError.message }, { status: pgError.status ?? 422 });

0 commit comments

Comments
 (0)