Skip to content

Choosing a task backend

This page compares django-ox with the task queues a Django team is most likely to shortlist: django-tasks-db, huey, Celery, django-q2, dramatiq and procrastinate, and, in a second table, with the other database-backed django.tasks backends: steady-queue, dj-queue and django-database-task. Every cell about another project comes from that project's own documentation, source or issue tracker, with the link in the footnotes and the date it was read. Where a project's pages do not say, the cell says so rather than guessing.

The comparison is about fit, not ranking. A team that already runs RabbitMQ and needs to stop running tasks has a different answer from a team that wants one fewer service. The last section says where django-ox is not the right fit.

The table

django-ox django-tasks-db huey Celery django-q2 dramatiq procrastinate
django.tasks backend Yes, native Yes, native 1 Yes. huey.contrib.djhuey ships "a backend for the django.tasks framework (Django 6.0 and newer, or older Djangos using the django-tasks backport)" 13 No. Issue #10062 closed 2026-02-03 25 Pull request #315 open since 2026-02-04, last updated 2026-05-15 39 Not documented 48 Not documented; has its own Django integration 54
Broker needed None. The queue is a table in your database None. Django ORM 1 Redis, SQLite, PostgreSQL, file or memory storage 12 RabbitMQ, Redis or SQS (stable); Zookeeper, Kafka, Pub/Sub (experimental). SQL databases are result backends only 18. Issue #5149, PostgreSQL as a broker, open since 2018-10-25 with 95 upvotes 23 Redis (default), IronMQ, SQS, MongoDB or Django ORM 35 RabbitMQ or Redis 48 None. PostgreSQL is the queue 54
Transactional enqueue Yes. Enqueue is one INSERT on your default database; a task written inside atomic() commits or rolls back with the rows beside it Not documented 1 Not documented 12 No. Django's own docs name a background task as the case for on_commit() 33 Not documented 35 No. Enqueue is a broker send 48 Not documented 55
Retries and backoff Exponential backoff by default; per-task max_attempts and backoff on Django 6.1 or Django 5.2 with django-tasks 0.12+. Backend defaults: MAX_ATTEMPTS, BACKOFF_INITIAL, BACKOFF_MAX. Every attempt's traceback kept. See Per-task policy No retry option in the README or the worker flags 1 2 retries, retry_delay, retry_backoff 12 autoretry_for, retry_backoff, retry_backoff_max (default 600 s), retry_jitter 17 max_attempts (default 0, meaning unlimited) and a retry interval; backoff not documented 36 Exponential backoff; max_retries default 20, min_backoff 15 s, max_backoff 7 days 48 Retry strategy per task 54
Recurring schedules Cron or a fixed interval in settings, or rows created and edited in the Django admin, limited to the tasks the code exposes; every worker dispatches, no scheduler process. See Schedules in the database None. Issue #259 open since 2026-08-10 7 periodic_task(crontab(...)) 12 celery beat, a separate process; "ensure only a single scheduler is running" 20. With django-celery-beat the schedules are rows that "can be managed from the Django Admin interface", and the field naming the task has no list to choose from 21 Schedule model, editable in admin; cron via croniter 37 None built in; APScheduler recommended 49 @app.periodic 54
Unique tasks / deduplicated enqueue @unique or UNIQUE_TASKS returns the existing TaskResult while a match is READY or RUNNING; not an execution-exclusivity guarantee. All four features below require the optional oxpull app and OxpullBackend on Django 6.0+ Issue #164 closed as a duplicate of discussion #143; maintainer: "handled by your business logic" 8 huey's own lock_task() prevents concurrent runs, not duplicate enqueues 15 No general built-in enqueue deduplication. The cookbook uses a custom execution lock; worker_deduplicate_successful_tasks handles successful redeliveries, and SQS FIFO has a broker-specific deduplication mechanism 27 28 29 Not documented 42 Not documented 52 queueing_lock permits one job in todo per chosen lock string, but multiple jobs in doing; a duplicate defer raises AlreadyEnqueued 58
Batch completion callback on_complete is enqueued once every member's row is settled or gone, whatever the outcome; on_success requires all to succeed. Callbacks run at least once. Requires a once-a-minute reconciler schedule "Task Groups" discussion #58, open since 2024-06-20, with 0 comments 9 huey's own chord() runs after members finish or fail permanently, passing failures as huey.Error; a member lost to abrupt consumer shutdown prevents the callback 15 chord runs its callback only if every member succeeds; a failed member produces ChordError, and an errback attached to the callback runs instead. Requires a result backend other than RPC and member results must not be ignored 30 No group completion hook documented; groups label results for polling, while hook is per task 43 42 group.add_completion_callback() requires optional GroupCallbacks middleware; the completion barrier is signalled only on success, so the callback requires every member to succeed 52 A contributor: "there is nothing out of the box"; issue #1219 closed in favour of #1198 59
Rate limits shared across workers Fixed start windows coordinated through one database row per limit name; requires OxpullWorker. Bounded overshoot is possible. Throttled tasks stay READY without spending an attempt; this is not a concurrency cap Not documented in the README, worker flags or Django Tasks API 10 11 34 huey's own rate_limit() uses a fixed-window storage counter keyed by huey name and limit name; workers using the same shared storage use that counter. Checked inside the running task, with throttled calls rescheduled 15 16 Task.rate_limit is per worker instance, not global; the docs advise restricting to a given queue for global limiting 31 Not documented; pull request #350 proposes a per-cluster throttle and remains open 44 45 WindowRateLimiter and BucketRateLimiter use Redis or Memcached for shared limits, called from actor code 52 53 Not documented; issue #1289 open since 2025-01-14 60
Dependency workflows Provisional. depends_on forms a DAG; a node runs only after every upstream succeeds. Requires WORKFLOWS: True, OxpullWorker and a reconciler schedule Not documented in the Tasks API; discussion #58 proposes task groups and follow-up work 34 9 huey's own pipelines with .then() run tasks sequentially; a failure skips the remaining success pipeline 15 chain / link call the next task after success; chord provides fan-in 30 Chains are not success-gated: later tasks are also enqueued after a failure. save_task() has no success check before enqueueing the next chain link. Issue #327, open since 2026-05-05 46 47 pipeline passes results to the next actor; the next step is enqueued only after success 52 No built-in workflows; issue #1198 open since 2024-09-18. Execution locks order jobs whether the preceding job succeeds or not 61 62
Priorities -100 to 100 Yes 3 Yes; on Redis needs 5.0+ and PriorityRedisHuey 12 0 to 255 on RabbitMQ and Redis 22 Not documented 36 Per actor; lower number runs first 48 Yes 54
Time limit on a running task Configured through backend TASK_TIMEOUT, per-queue TASK_TIMEOUTS, or a task's declared timeout on Django 6.1 or Django 5.2 with django-tasks 0.12+. TaskTimeout is raised inside the task at the deadline, followed by a worker recycle if the thread does not stop. The recycle is the whole enforcement on an interpreter with no facility for raising an exception in another thread, and for a thread a coverage tool or debugger is watching. See Production Not documented in the README or the worker flags 1 2 timeout per task or per call; "a TaskTimeout is raised and returned to the caller via the result handle" 12 soft_time_limit raises SoftTimeLimitExceeded inside the task; time_limit terminates the process running it, which is replaced. "Time limits don't currently work on platforms that don't support the SIGUSR1 signal" 19 timeout, default None: "the number of seconds a worker is allowed to spend on a task before it's terminated" 36 time_limit per actor, default 10 minutes, raises TimeLimitExceeded. "Time limits are best-effort. They cannot cancel system calls or any function that doesn't currently hold the GIL under CPython" 48 Not documented on the page checked 54
Worker dies mid-task Lease renewed every LOCK_TIMEOUT / 3; a task whose lease goes stale for LOCK_TIMEOUT is put back to READY, or marked LOST when attempts are spent. See Production Issue #5, open since 2024-06-11: the task 'remains marked as "PROCESSING", and thus is never picked up for re-processing nor marked as completed / failed' 4 "tasks that are mid-execution are lost and will not be retried automatically" 12 acks_late re-delivers; the worker still acknowledges "if the child process executing the task is terminated" 17 Issue #327, open since 2026-05-05: worker death not reported to the monitor, MAX_ATTEMPTS ignored 40 Not stated on the pages checked 48 49 Heartbeat every 10 s; jobs stay in doing until a retry_stalled_jobs periodic task you define picks them up 56
Health and metrics ox_health command, django_ox.stats, a Prometheus endpoint (/ox/metrics), structured log events, admin retry and discard Issue #44, container healthchecks, open since 2026-06-08 5 Signals 12 Flower, a separate process, with Prometheus integration 32 qmonitor, qinfo, Stat 38 Prometheus middleware; not in the default middleware list 50 Statistics via events 54
Databases PostgreSQL, MySQL 8 and SQLite, all run in CI; MariaDB 10.6+ takes the same claim path as MySQL and is not in CI Any Django database 1 Redis, SQLite, PostgreSQL, file, memory 12 Broker, not a database 18 Any Django database through the ORM broker 35 Broker, not a database 48 PostgreSQL 13+ 54
Licence BSD 3-Clause BSD 3-Clause 6 MIT 14 BSD 3-Clause 26 MIT 41 LGPL 3.0 51 MIT 57

Async tasks: django-ox sets supports_async_task, so async def tasks enqueue and run. Celery's most-upvoted open issue is #6552, "Support async function", open since 2020-12-19 with 98 upvotes 24.

The database-backed django.tasks backends

steady-queue, dj-queue and django-database-task also keep the queue in the database and implement the django.tasks backend API. The same rules apply: every cell about another project comes from its README, its PyPI page or its own issue tracker, with the link and the date in the footnotes, and a cell says "not stated" when those pages do not say.

django-ox steady-queue dj-queue django-database-task
Latest release on PyPI See the changelog 0.2.1, 2026-09-06 65 0.14.2, 2026-09-26 69 0.6.0, 2026-09-30 72
django.tasks backend Yes, native Yes, native 63 Yes, native 68 Yes, native 71
Django versions 5.2 LTS with the django-tasks backport, 6.0 and 6.1 6.0 and later, below 7 (django>=6.0,<7) 65 6.0 and 6.1 (django>=6.0.0,<6.2) 69 6.0 and later (Django>=6.0) 72
Databases PostgreSQL, MySQL 8 and SQLite, all run in CI; MariaDB 10.6+ takes the same claim path as MySQL and is not in CI MySQL, PostgreSQL or SQLite; FOR UPDATE SKIP LOCKED on MySQL 8+ and PostgreSQL 9.5+ 63 PostgreSQL first-class, with optional LISTEN/NOTIFY; MySQL and MariaDB supported; SQLite "supported with limits", polling only and no SKIP LOCKED 68 PostgreSQL 14+, MySQL 8.0.11+, MariaDB 10.6+, SQLite (no row-level locking; "development or single-worker deployments"), Oracle 19c+ ("not tested with this package") 71
Transactional enqueue Yes. Enqueue is one INSERT on your default database; a task written inside atomic() commits or rolls back with the rows beside it The README says a shared database gives transactional integrity and recommends against relying on it: a separate database for the queue, and transaction.on_commit for tasks that depend on data 63 "enqueue() writes immediately and returns a real persisted task result ID"; for a task that depends on rows in the current transaction the README provides enqueue_on_commit() or transaction.on_commit() 68 Not stated as a feature. The PostgreSQL broker section says pg_notify() runs inside the same transaction as the INSERT and is delivered on commit 71
Retries and backoff Exponential backoff by default; per-task max_attempts and backoff on Django 6.1 or Django 5.2 with django-tasks 0.12+. Backend defaults: MAX_ATTEMPTS, BACKOFF_INITIAL, BACKOFF_MAX. Every attempt's traceback kept. See Per-task policy Not stated in the README or the configuration page; failed tasks are retried from the Django admin 63 64 None built in: "dj_queue does not add an automatic retry policy or backoff engine". Failed jobs are retried from the admin or with retry_failed_job() 68 No automatic retry stated. A FAILED task is re-run from the admin action "Retry failed tasks" or with run_task_by_id(..., allow_retry=True) 71
Recurring schedules Cron or a fixed interval in settings, or rows edited in the Django admin; every worker dispatches, no scheduler process Cron-style, with the @recurring decorator in code; a scheduler process under the supervisor 63 Static recurring tasks in OPTIONS["recurring"] and dynamic recurring tasks managed at runtime; a scheduler under the supervisor 68 None built in. The worker is run from cron, a systemd timer, JP1, Hinemos or Cloud Scheduler 71
Unique tasks / deduplicated enqueue @unique or UNIQUE_TASKS returns the existing TaskResult while a match is READY or RUNNING; not an execution-exclusivity guarantee. All four features below require the optional oxpull app and OxpullBackend on Django 6.0+ Issue #24, open since 2026-02-10; owner: "For now ... I don't think this will get added to the package", but is "open to creating a contrib subpackage that could include this" 66 @concurrency(..., on_conflict="discard") provides singleton-style work through a concurrency semaphore with a TTL; the discarded enqueue's return value is not stated 70 Not stated; SELECT FOR UPDATE SKIP LOCKED prevents duplicate claims, not duplicate enqueues 73
Batch completion callback on_complete is enqueued once every member's row is settled or gone, whatever the outcome; on_success requires all to succeed. Callbacks run at least once. Requires a once-a-minute reconciler schedule Not stated 67 Not stated 70 Not stated 73
Rate limits shared across workers Fixed start windows coordinated through one database row per limit name; requires OxpullWorker. Bounded overshoot is possible. Throttled tasks stay READY without spending an attempt; this is not a concurrency cap Not stated; the README's concurrency controls limit simultaneous execution, not start rate 67 Not stated; shared semaphores provide concurrency limits, not rate limits 70 Not provided by the package; the README points to Google Cloud Tasks per-queue rate settings for CloudTasksDatabaseBackend 73
Dependency workflows Provisional. depends_on forms a DAG; a node runs only after every upstream succeeds. Requires WORKFLOWS: True, OxpullWorker and a reconciler schedule Not stated 67 Not stated 70 Not stated 73
Priorities -100 to 100 -100 to 100 63 priority on the task call; higher claimed first within a queue 68 -100 to 100 71
Concurrency controls No per-task concurrency limit. Rate limiting caps how often a task starts, not how many run at once "Limit how many instances of a specific task can run simultaneously" 63 @concurrency decorator with database-backed limits; semaphores shown in the admin dashboard 68 Not stated 71
Worker dies mid-task Lease renewed every LOCK_TIMEOUT / 3; a task whose lease goes stale for LOCK_TIMEOUT is put back to READY, or marked LOST when attempts are spent Marked failed with ProcessPrunedError (heartbeat expired) or ProcessExitError (killed), to be inspected 63 Kept "as failed work that operators can inspect and retry": ProcessExitError, ProcessPrunedError, ProcessMissingError 68 Requeued by requeue_stale_database_tasks --older-than, a separate command run from cron or a systemd timer; --max-attempts (default 3) marks the task FAILED instead 71
Health and metrics ox_health command, django_ox.stats, a Prometheus endpoint (/ox/metrics), structured log events, admin retry and discard Django admin: pause and resume queues, inspect tasks, retry or discard failed ones; operational signals from steady_queue.signals 63 Admin dashboard at /admin/dj_queue/dashboard/; dj_queue_health command; /dj_queue/metrics in Prometheus text format with the prometheus extra 68 Django admin; structured log fields; opt-in worker exit codes for job schedulers 71
Async tasks and results async def tasks run; get_result() supported "Async task enqueueing is not supported"; "Result fetching is not supported" 63 get_result() documented; async task functions not stated 68 "Supports async task functions"; get_result() documented 71
Licence BSD 3-Clause MIT 65 MIT 69 MIT 72

When not to use django-ox

  • You need to stop one chosen task while it runs. django-ox supports configured attempt deadlines through TASK_TIMEOUT, per-queue TASK_TIMEOUTS, or a task's declared timeout on Django 6.1 or Django 5.2 with django-tasks 0.12+. Attempts are unbounded by default. Operator actions discard queued tasks and retry failed ones. For on-demand interruption, Celery can revoke and terminate a running task, from Flower or the control API 32.
  • The queue must live on a different database from your models. Every django-ox table lives on the database OxTask routes to, and a queue on a different database from your models gives up the transactional enqueue.
  • Throughput beyond what one database comfortably serves. The benchmarks page gives measured numbers with the method. If your workload is above them, a broker-based queue is the right tool, and the cost is the second datastore.
  • Celery-style chains, groups and chords. django-ox does not implement these APIs. It provides batches for grouped work and provisional workflows for dependency DAGs. Check their semantics before replacing an existing composition.
  • CPU-bound tasks in one process. Worker concurrency is a thread pool. Run ox_worker --processes N --concurrency 1 for N interpreters, or pick a queue with a process pool.

Maintenance

Each footnote carries the date its row was last checked. If a cell is out of date, open an issue with the link that shows it.


  1. https://github.com/RealOrangeOne/django-tasks-db README, checked 2026-08-23. ↩↩↩↩↩↩

  2. https://github.com/RealOrangeOne/django-tasks-db/blob/master/django_tasks_db/management/commands/db_worker.py, add_arguments, checked 2026-08-23. ↩↩

  3. https://github.com/RealOrangeOne/django-tasks-db/blob/master/django_tasks_db/backend.py, supports_priority = True, checked 2026-08-23. ↩

  4. https://github.com/RealOrangeOne/django-tasks-db/issues/5, open, checked 2026-08-23. ↩

  5. https://github.com/RealOrangeOne/django-tasks-db/issues/44, open, checked 2026-08-23. ↩

  6. https://github.com/RealOrangeOne/django-tasks-db, licence field, checked 2026-08-23. ↩

  7. https://github.com/RealOrangeOne/django-tasks/issues/259, open, checked 2026-08-23. ↩

  8. https://github.com/RealOrangeOne/django-tasks/issues/164, closed as a duplicate of discussion #143; maintainer assigns deduplication to business logic, checked 2026-10-11. ↩

  9. https://github.com/RealOrangeOne/django-tasks/discussions/58, "Task Groups", open since 2024-06-20 with 0 comments, checked 2026-10-11. ↩↩

  10. https://github.com/RealOrangeOne/django-tasks-db, README at 8f79c7f; no rate-limit option documented, checked 2026-10-11. ↩

  11. https://github.com/RealOrangeOne/django-tasks-db/blob/master/django_tasks_db/management/commands/db_worker.py, add_arguments; no rate-limit flag documented, checked 2026-10-11. ↩

  12. https://huey.readthedocs.io/en/latest/guide.html, checked 2026-08-23. ↩↩↩↩↩↩↩↩↩

  13. https://huey.readthedocs.io/en/latest/contrib.html, checked 2026-09-19. ↩

  14. https://github.com/coleifer/huey, licence field, checked 2026-08-23. ↩

  15. https://huey.readthedocs.io/en/stable/guide.html, huey 3.4.0; huey's own task locks, chords, fixed-window rate limits and pipelines, checked 2026-10-11. ↩↩↩↩

  16. https://github.com/coleifer/huey/blob/3.4.0/huey/api.py, class RateLimit; counter key includes huey name and limit name and uses storage.incr(), checked 2026-10-11. ↩

  17. https://docs.celeryq.dev/en/stable/userguide/tasks.html, checked 2026-08-23. ↩↩

  18. https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/index.html, checked 2026-08-23. ↩↩

  19. https://docs.celeryq.dev/en/stable/userguide/workers.html, Time Limits, checked 2026-08-23. ↩

  20. https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html, checked 2026-08-23. ↩

  21. django-celery-beat 2.9.0. The documentation says periodic tasks "can be managed from the Django Admin interface", and django_celery_beat/models.py declares PeriodicTask.task as models.CharField(max_length=200) with no choices and no validators. https://django-celery-beat.readthedocs.io/en/latest/, checked 2026-09-12. ↩

  22. https://docs.celeryq.dev/en/stable/userguide/calling.html, checked 2026-08-23. ↩

  23. https://github.com/celery/celery/issues/5149, open, checked 2026-08-23. ↩

  24. https://github.com/celery/celery/issues/6552, open, checked 2026-08-23. ↩

  25. https://github.com/celery/celery/issues/10062, closed, checked 2026-08-23. ↩

  26. https://github.com/celery/celery/blob/main/LICENSE, checked 2026-08-23. ↩

  27. https://docs.celeryq.dev/en/stable/tutorials/task-cookbook.html, Celery 5.6.3; custom cache lock for execution exclusivity, checked 2026-10-11. ↩

  28. https://docs.celeryq.dev/en/stable/userguide/configuration.html, Celery 5.6.3; worker_deduplicate_successful_tasks applies to successful redeliveries with the same task identifier and late acknowledgment, checked 2026-10-11. ↩

  29. https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/sqs.html, Celery 5.6.3; SQS FIFO message properties including MessageDeduplicationId, checked 2026-10-11. ↩

  30. https://docs.celeryq.dev/en/stable/userguide/canvas.html, Celery 5.6.3; chord result-backend requirements and failure behavior, including an errback attached to the chord callback running instead on failure; chains and links, checked 2026-10-11. ↩↩

  31. https://docs.celeryq.dev/en/stable/userguide/tasks.html#Task.rate_limit, Celery 5.6.3; per-worker rate limits and queue restriction advice, checked 2026-10-11. ↩

  32. https://flower.readthedocs.io/en/latest/features.html, checked 2026-08-23. ↩↩

  33. https://docs.djangoproject.com/en/6.0/topics/db/transactions/#performing-actions-after-commit, checked 2026-08-23. ↩

  34. https://docs.djangoproject.com/en/6.0/ref/tasks/, Django 6.0 Tasks API; no rate-limit or dependency option, checked 2026-10-11. ↩↩

  35. https://django-q2.readthedocs.io/en/master/brokers.html, checked 2026-08-23. ↩↩↩

  36. https://django-q2.readthedocs.io/en/master/configure.html, checked 2026-08-23. ↩↩↩

  37. https://django-q2.readthedocs.io/en/master/schedules.html, checked 2026-08-23. ↩

  38. https://django-q2.readthedocs.io/en/master/monitor.html, checked 2026-08-23. ↩

  39. https://github.com/django-q2/django-q2/pull/315, open, checked 2026-08-23. ↩

  40. https://github.com/django-q2/django-q2/issues/327, open, checked 2026-08-23. ↩

  41. https://github.com/django-q2/django-q2/blob/master/pyproject.toml, checked 2026-08-23. ↩

  42. https://django-q2.readthedocs.io/en/master/tasks.html, Django Q2 1.11.1; async_task() options and per-task hook, checked 2026-10-11. ↩↩

  43. https://django-q2.readthedocs.io/en/master/group.html, result grouping and polling; no group completion hook documented, checked 2026-10-11. ↩

  44. https://django-q2.readthedocs.io/en/master/configure.html, Django Q2 1.11.1; no shared rate-limit option documented, checked 2026-10-11. ↩

  45. https://github.com/django-q2/django-q2/pull/350, proposed per-cluster throttle, open since 2026-09-16, checked 2026-10-11. ↩

  46. https://github.com/django-q2/django-q2/issues/327, open since 2026-05-05; Bug 2 reports chains continuing after failure, checked 2026-10-11. ↩

  47. https://github.com/django-q2/django-q2/blob/v1.11.1/django_q/monitor.py, save_task() has no success check before enqueueing the next chain link, checked 2026-10-11. ↩

  48. https://dramatiq.io/guide.html, checked 2026-08-23. ↩↩↩↩↩↩↩↩

  49. https://dramatiq.io/cookbook.html, checked 2026-08-23. ↩↩

  50. https://github.com/Bogdanp/dramatiq/blob/master/dramatiq/middleware/init.py, default_middleware, and middleware/prometheus.py, checked 2026-08-23. ↩

  51. https://github.com/Bogdanp/dramatiq, licence field, checked 2026-08-23. ↩

  52. https://dramatiq.io/reference.html, Dramatiq 2.2.0; composition and rate-limiter APIs; success gating also checked in dramatiq/middleware/group_callbacks.py and dramatiq/middleware/pipelines.py at 8534ffa, checked 2026-10-11. ↩↩↩↩

  53. https://dramatiq.io/cookbook.html, rate limiters used inside actor code with a shared backend, checked 2026-10-11. ↩

  54. https://procrastinate.readthedocs.io/en/stable/, checked 2026-08-23. ↩↩↩↩↩↩↩↩

  55. https://procrastinate.readthedocs.io/en/stable/howto/django/basic_usage.html, checked 2026-08-23. ↩

  56. https://procrastinate.readthedocs.io/en/stable/howto/production/retry_stalled_jobs.html, checked 2026-08-23. ↩

  57. https://github.com/procrastinate-org/procrastinate, licence field, checked 2026-08-23. ↩

  58. https://procrastinate.readthedocs.io/en/stable/howto/advanced/queueing_locks.html, queueing_lock, todo versus doing, and AlreadyEnqueued, checked 2026-10-11. ↩

  59. https://github.com/procrastinate-org/procrastinate/issues/1219, batch and workflow request closed in favour of #1198; a contributor, not a maintainer, says "there is nothing out of the box", checked 2026-10-11. ↩

  60. https://github.com/procrastinate-org/procrastinate/issues/1289, rate-limit request, open since 2025-01-14, checked 2026-10-11. ↩

  61. https://github.com/procrastinate-org/procrastinate/issues/1198, dependency workflow request, open since 2024-09-18, checked 2026-10-11. ↩

  62. https://procrastinate.readthedocs.io/en/stable/howto/advanced/locks.html, execution locks order jobs whether the preceding job succeeds or not, checked 2026-10-11. ↩

  63. https://github.com/knifecake/steady-queue README, checked 2026-09-19. ↩↩↩↩↩↩↩↩↩↩

  64. https://steady-queue.readthedocs.io/en/latest/configuration.html, checked 2026-09-19. ↩

  65. https://pypi.org/project/steady-queue/ 0.2.1, uploaded 2026-09-06; Requires-Dist: django<7,>=6.0; licence MIT. Checked 2026-10-11. ↩↩↩

  66. https://github.com/knifecake/steady-queue/issues/24, enqueue uniqueness request, open since 2026-02-10; owner: "For now ... I don't think this will get added to the package", but is "open to creating a contrib subpackage that could include this", checked 2026-10-11. ↩

  67. https://github.com/knifecake/steady-queue, README at db46170; concurrency controls and documented feature scope, checked 2026-10-11. ↩↩↩

  68. https://github.com/coriocactus/dj_queue README, checked 2026-09-19. ↩↩↩↩↩↩↩↩↩↩

  69. https://pypi.org/project/dj-queue/ 0.14.2, uploaded 2026-09-26; Requires-Dist: django<6.2,>=6.0.0; licence MIT. Checked 2026-10-11. ↩↩↩

  70. https://github.com/coriocactus/dj_queue, README at 086ce2a; singleton-style concurrency controls and documented feature scope, checked 2026-10-11. ↩↩↩↩

  71. https://github.com/tokibito/django-database-task README, checked 2026-09-19. ↩↩↩↩↩↩↩↩↩↩

  72. https://pypi.org/project/django-database-task/ 0.6.0, uploaded 2026-09-30; Requires-Dist: Django>=6.0; licence MIT. Checked 2026-10-11. ↩↩↩

  73. https://github.com/tokibito/django-database-task, README at 9f4db93; claim exclusivity, Google Cloud Tasks rate settings and documented feature scope, checked 2026-10-11. ↩↩↩↩