Debugging queues¶
RC4 includes a ready-made metrics kit and domain gauges. Monitoring stays optional; no exporter or middleware is started by the broker automatically.
Enable processing and storage metrics¶
Install with uv pip install "iddqueue[binary,monitoring]==0.13.0".
The exporter/dashboard examples are available in the repository.
Add native processing middleware in the worker bootstrap before starting workers:
from dramatiq.middleware.prometheus import Prometheus
broker.add_middleware(Prometheus())
Dramatiq exposes processing metrics on port 9191 by default. Configure
dramatiq_prom_host, dramatiq_prom_port, and a separate dramatiq_prom_db
directory per worker service. Multiple spawned processes within that service
share its multiprocess metrics directory. Do not share that directory between
independent replicas or unrelated jobs. Scrape each replica's endpoint once.
Use a fresh directory for each replica lifetime, including a restart after
SIGKILL. A hard kill skips Dramatiq's shutdown cleanup, so reusing its directory
can preserve stale inprogress gauges. For example, create a unique directory
before each launch and export it as dramatiq_prom_db. Remove an old directory
only after every process using it has exited; never clean an active replica's
files. A fresh directory resets process-local counters and loses unsampled
history. Prometheus rate handles counter resets, but these metrics cannot
reconstruct work lost in the crash window.
Start the separate storage exporter with the same database/schema/prefix:
# DATABASE_URL or libpq PG* settings identify the database.
uv run python examples/monitoring/exporter.py --domain billing --domain notifications
# Optional namespace:
uv run python examples/monitoring/exporter.py --schema app --prefix jobs_ --domain billing
This uses an existing Prometheus client HTTP server, outside the broker. Default
address is 127.0.0.1:9192; choose --host and --port for your deployment.
SIGINT/SIGTERM stop the example and close its pool. It imports no actor modules.
An explicit domain filter keeps empty-domain gauges present. Use one exporter
per storage namespace; scraping several copies of the same snapshot must not
multiply backlog. The collector queries retained rows, so choose scrape interval
and retention appropriate to your database workload.
Example targets in your existing Prometheus configuration (replace addresses):
scrape_configs:
- job_name: iddqueue-workers
static_configs:
- targets: ['worker-a:9191', 'worker-b:9191']
- job_name: iddqueue-storage
static_configs:
- targets: ['storage-exporter:9192']
A host/service address must be reachable from Prometheus. A localhost-bound exporter is only reachable on its own host; choose the deployment interface when scraping across hosts. Give unrelated databases/namespaces separate storage jobs.
Import the dashboard¶
Download dashboard.json from the checkout. In Grafana select Dashboards → New → Import, upload/paste the JSON, then choose your Prometheus datasource, worker/storage job names and domains. The file uses the Classic dashboard JSON format. No Grafana dependency is installed in IDDQueue.
| Panel | Source | Meaning |
|---|---|---|
| Ready backlog / oldest ready age | iddqueue_domain_ready / iddqueue_domain_oldest_ready_seconds |
Eligible queued tasks and time waiting since eligibility |
| Scheduled tasks | iddqueue_domain_scheduled |
Future tasks, including prefetched ones |
| Executing actors | dramatiq_messages_inprogress |
Native processing gauge; SQL consumed is not this count |
| Retained rejected rows | iddqueue_domain_messages{state="rejected"} |
Stored terminal tasks; decreases with purge |
| Errors / retries / terminal rejects in selected range | Native *_total counters with increase |
Separate attempt errors, rescheduled retries, dead letters |
| Processed attempts per second | dramatiq_messages_total with rate |
Completed processing attempts, including errors; not unique successful jobs |
| Duration p50 / p95 | Native duration histogram buckets | Estimated percentiles in seconds across the selected actors/replicas |
| Errors per actor per second | Native errors with rate |
Find which qualified actor is failing |
One task may generate several errors and retries. Counter increases are estimates
for the selected interval; resets are handled by Prometheus. The dashboard
aggregates histogram buckets before computing percentiles and converts Dramatiq's
milliseconds to seconds. With no observations a percentile may be NaN/no data,
which does not mean zero latency. For SQL snapshots it uses max, not summing
identical exporter replicas; a single job must represent one storage namespace.
Dotted domain names are escaped in regex filters; main and .DQ transport queues
are selected together. The .DQ suffix remains reserved for delayed transport.
Read symptoms and find the task¶
- Backlog age keeps rising: compare attempt throughput and execution duration; inspect worker availability and selected queue filters.
- Errors and retries rise: look at the actor breakdown, then logs and failed tasks.
- Terminal rejects rise: retries may be exhausted or an actor was explicitly rejected; inspect the exception rather than assuming every reject is a retry.
- Scheduled tasks rise without ready backlog: tasks may be waiting for ETA.
- No data: check Prometheus target
up, scrape errors, job names, middleware and domain selection. Missing metrics are not a healthy zero backlog.
For the individual failed message use iddqueue failed --help and the optional
attempt-history APIs/CLI described in recipes. IDs, arguments and
exception text belong in task inspection/logs, not metric labels. This kit adds
no automatic alert thresholds: choose thresholds from your workload's latency
and retry expectations.
References: Grafana JSON model, Prometheus histogram aggregation, domain metric semantics.