Browse sections

Background jobs & queues

Background jobs let a caller hand work to the platform and return immediately: enqueue an invocation over HTTP, get a jobId back at once, and let the queue run the function with retries, backoff, and a dead-letter queue — no Redis, no worker fleet, no polling loop to host.

Jobs run the same functions you deploy for HTTP traffic, with the same secrets, layers, and observability. The only difference is the trigger: a durable queue row instead of a live request.

Job lifecycle

Every job moves through a small, explicit state machine. A worker claims PENDING jobs in batches, runs them as RUNNING, and records SUCCEEDED or schedules a retry on failure. When attempts are exhausted the job lands in DEAD — the dead-letter state — where you can inspect the error and requeue it manually.

State machine of a background job: PENDING to RUNNING, retry loop with backoff back to PENDING, then SUCCEEDED, or DEAD after max attempts with a manual requeue arrow
Job states. A stuck RUNNING job is returned to PENDING by the visibility-timeout reaper after 22.5 minutes.

Enqueue and poll

Enqueue with one HTTP call — the response is 202 Accepted with the job id. Poll the job endpoint (or subscribe to run events in the console) to read the result:

curl
# Enqueue: returns immediately with a job id (the function runs in the background)
curl -X POST "https://api.inquir.org/functions/{functionId}/invoke-async" \
  -H "Authorization: Bearer $INQUIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event": {"orderId": "ord_123"}}'
# -> { "jobId": "…", "status": "PENDING" }

# Poll until the job reaches a terminal state
curl "https://api.inquir.org/jobs/{jobId}" \
  -H "Authorization: Bearer $INQUIR_API_KEY"
# -> { "status": "SUCCEEDED", "resultJson": … }  (or FAILED / DEAD with lastError)

Retries and backoff

Retries are opt-in per enqueue: pass maxAttempts (up to 100) and failed attempts are re-queued with exponential backoff — 1 s, 2 s, 4 s and so on, capped at 5 minutes, with jitter to avoid thundering herds.

  • maxAttempts counts the total attempts, not just retries; the default is 1 (no retry).
  • A job that stays RUNNING past the visibility timeout (22.5 min) is presumed crashed and returned to the queue — write handlers so a second delivery is safe.
  • Delayed starts are supported via a schedule offset of up to 7 days — useful for reminders and cooldown windows.

Idempotency and concurrency control

Two enqueue-time options keep duplicate work and stampedes out of your system:

  • idempotencyKey — enqueueing twice with the same key returns the original job instead of creating a duplicate (the response marks it deduplicated).
  • concurrencyKey + concurrencyLimit — cap how many jobs sharing a key run at once (per customer, per repository, per anything you choose).
  • priority — higher-priority jobs are claimed first when the queue is deep.

Dead-letter queue

When the last attempt fails, the job becomes DEAD with its final error attached, and a terminal FAILED invocation is recorded for history. Dead jobs stay visible in the console: fix the cause, press requeue, and the job returns to PENDING with a fresh attempt budget.

Jobs vs pipelines vs durable orchestration

Reach for a job when one function should run once in the background. Reach for a pipeline when several functions form a workflow with branching and fan-out. Reach for durable orchestration when the flow needs to sleep on timers or wait for external events for hours or days. All three share the same queue machinery underneath.