Skip to content
Chapter 66Lesson 8

Quiz - Background work

Quiz progress

0 / 0

A Server Action sends the invitation email the user is waiting on, and then fires an analytics event nobody is waiting on. A teammate, chasing a faster “invitation sent” toast, moves both calls into after(). Which part of that change is the bug?

Deferring the analytics event — it now runs after the response and could be lost on a rare timeout.
Deferring the invitation email — the action returns “sent” before the email is actually accepted, so a failure in the callback is silent and unretried while the user has already been told it worked.
Both moves are fine — after() runs on the same invocation, so the work still completes before the function shuts down.

Vercel cron delivery is best-effort: a scheduled run can be both skipped and delivered twice. Which handler design makes both of those non-events at once, with no dedup key?

A reconciliation UPDATE ... WHERE status='trialing' AND period_end < now() — a missed tick is caught up by the next run, and a duplicate tick matches nothing because the first run already moved those rows.
Compute the delta since the last successful run from a stored timestamp, so each tick only processes what changed since last time.
Add a cron:<name>:<date> claim row so a duplicate tick is rejected, and accept that a missed tick simply drops that hour’s work.

A workload needs to escalate from the cheap tiers to Trigger.dev only when it trips one of five named conditions. Which of these is a genuine trigger condition rather than a “vibe”?

A weekly email blast to 50,000 users that must respect the provider’s rate limit — one trigger fanning out to many capped child runs.
A single ~3-second third-party call whose result the user is waiting on — slow enough to feel, so it deserves its own worker.
Pulling a Server Action’s body into a dedicated worker file so the action stays thin and the logic is easier to locate.

In a multi-tenant SaaS, you want each org’s exports to run one at a time while different orgs run in parallel — and the export task already has a predeclared export queue. Which Trigger.dev v4 call achieves this?

await exportCsv.trigger(
{ organizationId, since },
{ concurrencyKey: organizationId },
);
await exportCsv.trigger(
{ organizationId, since },
{ queue: { name: `org-${organizationId}`, concurrencyLimit: 1 } },
);

A task loops over an org’s members and emails each one. It throws a transient error on member 200, and the run-level retry restarts the body from the top. Members 1-199 each get a second email. What is the correct fix?

Guard each send with a per-member idempotency key (idempotencyKeys.create([member.id, 'notify'], { scope: 'run' })) so a parent retry re-issues the same keys and already-completed sends return cached.
Wrap each send in a try/catch with its own retry loop so a single member’s blip never bubbles up to a run-level retry.
Lower maxAttempts to 1 so the run never retries and no member is ever emailed twice.

Inside a durable task you need a two-second pause between export pages. Why is await wait.for({ seconds: 2 }) correct where await new Promise(r => setTimeout(r, 2000)) is wrong?

wait.for checkpoints and frees the worker — no compute billed while it sleeps, and a crash mid-wait resumes on a new worker; setTimeout holds the worker idle and its timer evaporates with the process on a crash.
wait.for is more precise about the duration, whereas setTimeout can drift by several seconds under load.
They are equivalent inside a task; wait.for is just the more idiomatic spelling.

A task hands a backend partner a render job and must resume only when the partner POSTs back — no polling, no glue webhook. Which is the correct waitpoint shape?

Create a token with an explicit timeout, hand the partner token.url as the callback, await wait.forToken(token.id), and treat !result.ok as a timeout to fail the run.
Create a token, hand the partner token.id, then poll a status endpoint in a wait.for loop until the render appears.
Create a token with the default timeout and hand the partner token.publicAccessToken so it can complete the token from its server.

A task fans out 200 image-resize sub-jobs — all your own Trigger.dev tasks — and a final step must run only after all 200 settle. What’s the right v4 approach?

await resizeTask.batchTriggerAndWait(...) with a per-child idempotency key, then inspect each result’s ok for failures.
await wait.forWaitpoint(tokenIds, { all: true }) to park on every child’s token at once.
Create 200 tokens, hand none of them out, and Promise.all over wait.forToken so the runtime fills them in as the children finish.

A deploy introduces a brand-new task that a Server Action triggers. To avoid a runtime failure where live app code calls a task version the workers don’t have yet, in what order must the two deploys land?

Deploy the task to the Trigger.dev workers first, then deploy the app that triggers it — the callee before the caller.
Deploy the app first so the trigger call exists, then deploy the task so the worker is ready to receive it.
The order doesn’t matter — the SDK queues the trigger until the matching task version appears.

Quiz complete

Score by topic