Enqueue
Every enqueue returns a structured EnqueueResult. Never infer success from a missing id.
Statuses (0.1)
| Status | Meaning |
|---|---|
created | New row inserted (created=True) |
existed | Active (queue, idempotency_key) already present (created=False) |
result = await tq.enqueue(
DOUBLE,
{"value": 3},
idempotency_key="demo:3",
)
assert result.status in {"created", "existed"}
Uniqueness in 0.1
Reject-only on active (queue, idempotency_key). Duplicate key → existed.
Modes such as replace, preserve_run_at, and statuses like replaced / skipped_locked / conflict are 0.2+ design — not on the current wire surface.
concurrency_key is separate: it caps parallel runners; it is not an enqueue identity key.
Options
TaskQ.enqueue accepts optional scheduled_at, priority, lease_seconds, concurrency_key, affinity_key, retry overrides, headers, and a transactional session or connection.
Also available: enqueue_many (one result per input, in order) and enqueue_raw (requires validate_job_types=False).
Bulk workflow admission
Use enqueue_many when a planner already knows many independent members of one
workflow. Contract 0.6.7 introduced the workflow reservation and set-based
insert behavior retained in 0.6.12: it reserves the workflow members once, inserts the jobs
set-wise, preserves caller order, and sends one wake-up notification. A call is
limited to 1,000 items; chunk larger plans while keeping one caller-owned
transaction, then seal the workflow after every chunk succeeds.
results = await tq.enqueue_many(
PROFILE_REFRESH,
payloads,
idempotency_keys=idempotency_keys,
workflow_id=workflow.workflow_id,
step_keys=step_keys,
session=session,
)
Each member still becomes its own job. Bulk admission only removes the per-member database round trips; it does not combine execution, retries, settlement, suppression, or observability. Use one job per item when those properties must remain independent.
All items in a workflow bulk call must use distinct step_key and
idempotency_key values. The operation is atomic: validation, member-limit,
workflow-conflict, or queue-depth failure inserts nothing. Exact idempotent
replays return existed, even after the workflow is sealed or the queue reaches
its depth cap.
enqueue thousands of times inside one transaction. That
holds the transaction open, repeats admission work, and can consume the shared
TaskQ database even though each eventual job is tiny. Chunk enqueue_many at
1,000 and set an explicit planning-time budget in the producing application.