Documentation Index
Fetch the complete documentation index at: https://mintlify.com/NikolayS/PgQue/llms.txt
Use this file to discover all available pages before exploring further.
PgQue is tick-based: producers append events to a queue, but consumers only see events once a tick has been recorded. Without a running ticker, enqueues still work but nothing is ever delivered. This guide covers all three scheduling options — pg_cron, pg_timetable, and external schedulers — and explains how to tune the tick rate.
How ticking works
pgque.start() schedules a single 1-second pg_cron slot that calls CALL pgque.ticker_loop(). The ticker_loop procedure internally re-invokes pgque.ticker() every tick_period_ms milliseconds (default 100 ms = 10 ticks/sec), committing between iterations so each tick gets its own transaction and snapshot.
Four pg_cron jobs are scheduled by pgque.start():
| Job name | Schedule | Purpose |
|---|
pgque_ticker | every 1 s | Calls ticker_loop(), which ticks every 100 ms inside the slot |
pgque_retry_events | every 30 s | Moves nack’d events back into the main stream |
pgque_maint | every 30 s | Rotation step 1, queue maintenance hooks |
pgque_rotate_step2 | every 10 s | Rotation step 2 (must run in its own transaction) |
PgQue does not deliver messages without a working ticker. Enqueueing still works, but consumers see nothing new because no tick boundaries are created.
Scheduling options
pg_cron (recommended)
pg_timetable
External / manual
pg_cron is pre-installed or one command away on all major managed Postgres providers (RDS, Aurora, Cloud SQL, AlloyDB, Supabase, Neon). With it available in the same database as PgQue:Verify the jobs were scheduled:select * from pgque.status();
select * from cron.job where jobname like 'pgque%';
To stop the ticker and remove all pg_cron jobs:pg_cron in a different database. pg_cron runs jobs in one designated database (cron.database_name, typically postgres). If your PgQue schema lives in a different database, use the cross-database pattern to call pgque.ticker_loop(), pgque.maint_retry_events(), and pgque.maint() across databases.pg_cron log hygiene. Every pg_cron job execution writes a row to cron.job_run_details with no built-in purge. PgQue’s four jobs add roughly 5,000 rows per hour. Schedule a purge job:select cron.schedule(
'pgque_purge_cron_log',
'0 * * * *',
$$
delete from cron.job_run_details d
using cron.job j
where d.jobid = j.jobid
and j.jobname in (
'pgque_ticker', 'pgque_retry_events',
'pgque_maint', 'pgque_rotate_step2',
'pgque_purge_cron_log'
)
and d.end_time < now() - interval '1 day'
$$
);
Run the external pg_timetable worker against the database where PgQue is installed:pg_timetable --dbname=mydb --clientname=pgque
Then schedule PgQue inside that database:select pgque.start_timetable(); -- default: 10 ticks/sec
-- or explicitly set the rate:
select pgque.start_timetable(10); -- 10 ticks/sec
The --clientname=pgque flag is required — pg_timetable only executes chains whose job_client_name matches the running worker’s client name. Keep the worker running; unlike pg_cron, pg_timetable is an external process, not a background worker.To remove PgQue’s pg_timetable jobs:select pgque.stop_timetable();
pgque.start_timetable() automatically removes existing PgQue pg_cron jobs first. Similarly, pgque.start() removes PgQue pg_timetable jobs. The two schedulers are mutually exclusive for PgQue’s own jobs.
Without pg_cron or pg_timetable, PgQue still installs. Drive ticking and maintenance from your application or any external scheduler (system cron, systemd timer, a worker loop):# Tick at your chosen rate — each call is one transaction
psql -d mydb -c "select pgque.ticker()"
# Move nack'd events back into the main stream (every 30s)
psql -d mydb -c "select pgque.maint_retry_events()"
# Run rotation and maintenance (every 30s)
psql -d mydb -c "select pgque.maint()"
For sub-second ticking, loop pgque.ticker() at your target rate in application code. tick_period_ms is only consulted by ticker_loop() (the pg_cron path); outside that path, your driver controls the cadence.Skipping maint_retry_events() means nack’d events are never redelivered. All three commands are required for full functionality.
Tuning the tick rate
The default tick rate is 10 ticks/sec (100 ms period). Tune at runtime — no need to call start() again:
select pgque.set_tick_period_ms(50); -- 20 ticks/sec, ~25 ms median e2e latency
select pgque.set_tick_period_ms(100); -- 10 ticks/sec (default), ~50 ms median
select pgque.set_tick_period_ms(200); -- 5 ticks/sec, ~100 ms median
select pgque.set_tick_period_ms(1000); -- 1 tick/sec, ~500 ms median (pgqd-compatible)
Valid values are exact divisors of 1000 in the 1–1000 ms range. The change takes effect on the next pg_cron slot (within 1 second), without rescheduling.
| Tick period | Ticks/sec | Median e2e latency | WAL/day (per active queue) |
|---|
| 10 ms | 100 | ~5 ms | ~2.4 GiB |
| 50 ms | 20 | ~25 ms | ~480 MiB |
| 100 ms | 10 (default) | ~50 ms | ~240 MiB |
| 200 ms | 5 | ~100 ms | ~120 MiB |
| 1000 ms | 1 | ~500 ms | ~24 MiB |
WAL estimates apply only to queues that materialize ticks continuously (active producers). Idle queues back off toward ticker_idle_period (default 1 minute) and produce negligible WAL.
Inspect the current configuration:
select * from pgque.status();