Skip to content

Repository files navigation

Polyqueue

Async-first Python job queue that keeps Postgres as the authoritative job state while supporting Redis, SQS, Azure Service Bus, PGMQ, and an in-process broker. Handlers are typed via Pydantic. Return values are persisted to the same Postgres DB — no separate result backend required.


DimensionPolyqueueCeleryTaskiq
Primary modelSmall queue runtime with Postgres as source of truth plus pluggable broker adaptersMature distributed task queue with broad ecosystem/toolingAsync-first distributed task runner with modular brokers/extensions
Async supportYes, async-first. Worker, handlers, DB access, and adapters are designed around asyncioNot primarily async-first in overall designYes, async-first
Built-in broker/backend supportRedis, SQS, Azure Service Bus, PGMQ, none (in-process)Commonly used with Redis/RabbitMQ and other broker/result-backend combinationsBroker-based, typically via pluggable integrations
Redis supportYesYesYes, depending on broker plugin/config
SQS supportYesYesPossible via broker integration/plugin, but not the default mental model
Azure Service Bus supportYesNot a standard first-class default choice in the same way as Redis/RabbitMQPossible via ecosystem extension/custom broker, but not a core default pairing
No-broker / in-process modeYes via queue_backend="none"Not the usual modelNot the main model
Source of truth for job statePostgresBroker/backend-centric task system, optionally paired with result backendsBroker/message-centric
Pydantic supportYesenqueue() accepts BaseModel, dispatch() validates annotated payload models via model_validate()Available via explicit task-side supportWorks naturally with typed task signatures and model parsing patterns
Pydantic behaviorStrict: model serialised to dict on enqueue, model_validate() on dispatch, ValidationError => TerminalError (no retry)Usually task-side validation/conversionMore ergonomic/best-effort style than strict contract-first behavior
Result persistencePostgres jobs.result JSONB — same DB already tracking job state, no extra infrastructureRequires a separate result backend (Redis, DB, RPC, etc.) configured explicitly; off by defaultRequires a separate result backend plugin (e.g. taskiq-redis); pluggable but extra infrastructure
Multi-worker distributionYes. Workers compete on claim; one worker owns the in-flight deliveryYesYes
Worker identity trackingYespolyqueue_workers registry: worker_id, hostname, pid, current job, heartbeat, status (running/stopped/dead); stale workers reaped by maintenance loopStronger built-in worker identity/inspection modelWorkers exist, but less built-in cluster observability than Celery
In-flight ownership modelRedis lease / SQS receipt handle + visibility timeout / Azure SB lock token + lock renewalBroker/worker-managedBroker/worker-managed
Recovery when worker crashesYes, via lease expiry / visibility timeout / lock expiry and redelivery/reclaimYesYes
Detecting "alive but wedged" jobsBasicprogress_heartbeat_at + timeout_at in DB; maintenance backstop reconciles timed-out jobsBetter operational story; mature controls existTypically needs app/broker-level design
Time limitsYes — per-job max_run_seconds, cooperative timeout via asyncio.wait_for, DB backstop, three strategies (retry/fail/ignore)More mature built-in supportLess central than in Celery's ops model
Operational maturityFocused, understandable, smaller surface areaMost matureLighter-weight, async-friendly
Best fitInternal service jobs where you want simplicity, explicit semantics, and controlLarge-scale general background job platformAsync Python apps that want typed/ergonomic task execution

Summary

  • Brokers: Redis, SQS, Azure Service Bus, PGMQ, or in-process (none). Postgres always holds the authoritative job state regardless of broker.
  • Pydantic-first: handlers declare typed payloads via @register; ValidationError on dispatch becomes TerminalError (no retry).
  • Result persistence in Postgres: handler return values (BaseModel, dict, or primitive) are saved to jobs.result JSONB — no separate result backend needed. Fetch via get_job_result, get_job_result_typed, or get_job_result_typed_via_registry. Not available with the none/in-process backend.
  • Time limits: per-job max_run_seconds enforced via asyncio.wait_for, with three strategies (retry / fail / ignore) and a maintenance-loop DB backstop.
  • Worker registry: polyqueue_workers table tracks id, hostname, pid, current job, and heartbeat. Stale workers are reaped to dead by the maintenance loop.
  • Metrics hook: pluggable MetricsHook for Prometheus, CloudWatch, or OpenTelemetry. Pass to main(metrics=...).
  • Admin CLI: polyqueue inspect stats|workers|jobs and polyqueue admin requeue|fail.
  • Smaller surface area than Celery — no admin UI or auto-scaling built-in.

About

Pydantic-typed async-first Python job queue supporting Redis, SQS, Azure Service Bus, and PGMQ

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages