Job Processing
Overview
Section titled “Overview”This document describes how Queuert processes jobs: transactional design, prepare/complete pattern, and timeout philosophy.
Transactional Design
Section titled “Transactional Design”Queuert’s core design principle is that jobs are created inside the same database transaction as your application state changes. This follows the transactional outbox pattern:
await withTransactionHooks(async (transactionHooks) => db.transaction(async (tx) => { // Application state change const image = await tx.images.create({ ... });
// Job creation in the same transaction // The transaction context property name matches your StateProvider await client.startJobChain({ tx, transactionHooks, typeName: "process-image", input: { imageId: image.id }, }); }),);Why This Matters
Section titled “Why This Matters”- Atomicity: If the transaction rolls back, the job is never created. No orphaned jobs.
- Consistency: The job always references valid application state.
- No dual-write problem: You don’t need to coordinate between your database and a separate job queue.
Extending to Job Processing
Section titled “Extending to Job Processing”The same transactional principle extends to job processing through the prepare/complete pattern:
- Prepare phase: Read application state within a transaction
- Processing phase: Perform side-effects (API calls, file operations) outside the transaction
- Complete phase: Write results back within a transaction
This ensures that job outputs and continuations are also created atomically with any state changes they produce.
Observability events (metrics, span ends, logs) emitted during the prepare and complete phases are transactional — they are buffered and only flushed after the transaction commits. If the transaction rolls back, no observability events leak out.
Prepare/Complete Pattern
Section titled “Prepare/Complete Pattern”Attempt handlers split processing into distinct phases to support both atomic (single-transaction) and staged (long-running) operations. See AttemptHandler TSDoc for the full handler signature and AttemptPrepareOptions for mode details.
- Atomic mode: Prepare and complete run in the same transaction. Suitable for quick operations.
- Staged mode: Prepare runs in one transaction, long-running work happens outside, then complete runs in another transaction. The worker automatically renews the job lease between phases. Implement the processing phase idempotently as it may retry if the worker crashes.
Auto-Setup
Section titled “Auto-Setup”If you don’t call prepare, auto-setup runs based on when you call complete:
- If
prepareis not accessed andcompleteis not called synchronously, auto-setup runs in staged mode - If
completeis called beforeprepare, auto-setup runs in atomic mode (no lease renewal between prepare and complete) - Accessing
prepareafter auto-setup throws: “Prepare cannot be accessed after auto-setup”
This means simple attempt handlers default to staged mode:
attemptHandler: async ({ job, complete }) => { // Transaction already closed, lease renewal running return complete(() => output);};Timeouts
Section titled “Timeouts”Queuert does not provide built-in soft timeout functionality. This is intentional:
- Userland solution is trivial: Combine
AbortSignal.timeout()with the existingsignalparameter usingAbortSignal.any() - Lease mechanism is the hard timeout: If a job doesn’t complete within
leaseMs, the reaper reclaims it and another worker retries
Cooperative Timeouts
Section titled “Cooperative Timeouts”Users implement cooperative timeouts by combining AbortSignal.timeout() with the existing signal parameter using AbortSignal.any().
Hard Timeouts
Section titled “Hard Timeouts”For hard timeouts (forceful termination), the lease mechanism already handles this:
- Configure
leaseMsappropriately for the job type - If the job doesn’t complete or renew its lease in time, the reaper reclaims it
- Another worker can then retry the job
See Also
Section titled “See Also”- Client API — Mutation methods, query methods, awaitJobChain
- In-Process Worker — Worker lifecycle, leasing, reaper
- Adapters — StateAdapter context architecture