You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v0.2.0 — release notes (draft, updated through rc.1 → GA)
This issue tracks the release notes for v0.2.0, growing from the rc.1 draft into the final GA copy. Open for comments/edits while we move through the rc.
SQL schema: install with \i sql/pgque.sql from this tag, or \i sql/pgque-tle.sql for the pg_tle wrapper.
Source layout: each client lives at clients/python/ (pgque-py), clients/typescript/ (pgque), clients/go/ (mirrored to github.com/NikolayS/pgque-go).
Headline
API parity across three client libraries. Python (pgque-py), TypeScript (pgque), and Go (pgque-go) all expose send / send_batch, receive, ack, nack (typed errors, retryAfter seconds, unknown-handler policy), force_next_tick/force_tick, subscribe/unsubscribe, plus a high-level Consumer and the experimental cooperative-consumer API.
Set-based send_batch() — 3-5× producer throughput.pgque.send_batch() is now a single set-based insert instead of a PL/pgSQL loop over insert_event(). Local-Postgres microbenchmarks at N=10 000 rows per batch (full numbers in #160):
send_batch(jsonb[]): 24,822 → 85,683 ev/s (~3.5× vs the legacy loop).
send_batch(text[]): 27,457 → 133,717 ev/s (~4.9× vs the legacy loop).
vs single-send() client loop (3,289 ev/s), set-based send_batch is ~26× the throughput on JSON.
Big batches benefit the most — the legacy loop's per-row PL/pgSQL overhead grew super-linearly past 50k.
Experimental cooperative consumers. A single logical consumer can now be split across multiple workers via subconsumers, with the SQL core handling batch allocation and lease tracking. SQL + all three clients.
Sub-second tick rate. Default ticker cadence drops from 1/s to 10/s; tunable via pgque.set_tick_period_ms(). Producer→consumer latency is now sub-second out of the box.
Scheduler choice. Alongside pg_cron, a new pg_timetable backend can drive the ticker on managed Postgres deployments where pg_cron is unavailable.
Optional pg_tle install path. For platforms that allow trusted-language extensions, \i sql/pgque-tle.sql registers pgque as a pg_tle extension; the default \i sql/pgque.sql install is unchanged.
Roles: pgq's producer/consumer split is restored. pgque_writer and pgque_reader are siblings — neither inherits the other. Apps that produce and consume must be granted both. (See upgrade notes.)
Security definer: every SECURITY DEFINER function pins search_path = pgque, pg_catalog. pgque.get_batch_cursor(extra_where) is now restricted to pgque_admin.
Atomic batch send + 3-5× throughput: pgque.send_batch is set-based (single SQL insert, all-or-nothing) instead of a PL/pgSQL loop over insert_event(). See the headline bullet for benchmark numbers and Benchmark: producer batch insert options and send_batch tradeoffs #160 for the full comparison vs single-send() loops, the legacy implementation, and direct INSERT/COPY.
High-level Consumer with handler dispatch, LISTEN/NOTIFY wakeup (Python), and a subconsumer option for the experimental coop API.
Stale-ack warning: ack() returning rowcount 0 logs a warning rather than failing silently.
Cross-driver tested against the same SQL core (audit summary: #211).
Tooling / install
\i sql/pgque.sql — single-file install, no extension, no shared_preload_libraries. Works on RDS / Aurora / Cloud SQL / AlloyDB / Supabase / Neon / Crunchy Bridge.
\i sql/pgque-tle.sql — optional pg_tle wrapper for platforms that allow trusted-language extensions.
pgque.start() schedules pgque_ticker (CALL pgque.ticker_loop() every 1s, internally re-tick at tick_period_ms), pgque_retry_events (30s), pgque_maint (30s), pgque_rotate_step2 (10s).
pgque.start_timetable() is the equivalent for pg_timetable deployments.
Behavior changes vs v0.1.0 (read before upgrading)
Roles: pgque_writer no longer inherits pgque_reader. Apps that both produce and consume must explicitly hold both roles. The install runs the role-split idempotently; tests/test_upgrade_grants.sql covers the regression. See docs/reference.md → Roles and grants.
Default tick cadence is 10/s (was 1/s). Producer→consumer latency improves; WAL/metadata churn rises proportionally. Tune with pgque.set_tick_period_ms() if needed.
pgque.maint() no longer issues VACUUM internally (PL/pgSQL cannot). Vacuum scheduling is the operator's responsibility — see docs.
Upgrade from v0.1.0
The supported upgrade path for v0.2.0-rc.1 is:
psql -d <db> -f sql/pgque.sql # re-run on top of the existing v0.1.0 install
The install is idempotent: every create table is if not exists, every function is create or replace, and new columns are added with guarded alter table … add column blocks. Existing data, queues, consumers, and pending batches are preserved.
A CI test that proves "install v0.1.0, then re-install HEAD on top, everything still works" is tracked at #226 and is a blocker for v0.2.0 GA (not for rc.1).
If you grant roles to your application user, double-check the role split (above) so produce-and-consume apps hold both pgque_reader and pgque_writer.
Known limitations / experimental APIs
These ship under v0.2.0 but are experimental and may change shape in subsequent releases:
Cooperative consumers (subconsumers) — function names and edge-case semantics may evolve.
v0.2.0 — release notes (draft, updated through rc.1 → GA)
This issue tracks the release notes for v0.2.0, growing from the rc.1 draft into the final GA copy. Open for comments/edits while we move through the rc.
The published rc.1 release notes mirror this body and live at the GitHub Release:
https://github.com/NikolayS/pgque/releases/tag/v0.2.0-rc.1.
Install
All three resolve to
0.2.0-rc.1. SQL schema:\i sql/pgque.sqlfrom this tag (or\i sql/pgque-tle.sqlfor the pg_tle wrapper).What rc.1 actually ships
pgque-py 0.2.0rc1(TestPyPI: test.pypi.org/project/pgque-py/0.2.0rc1)pgque@0.2.0-rc.1(@rcdist-tag)github.com/NikolayS/pgque-go@v0.2.0-rc.1via the public mirror atNikolayS/pgque-go\i sql/pgque.sqlfrom this tag, or\i sql/pgque-tle.sqlfor the pg_tle wrapper.clients/python/(pgque-py),clients/typescript/(pgque),clients/go/(mirrored togithub.com/NikolayS/pgque-go).Headline
pgque-py), TypeScript (pgque), and Go (pgque-go) all exposesend/send_batch,receive,ack,nack(typed errors,retryAfterseconds, unknown-handler policy),force_next_tick/force_tick,subscribe/unsubscribe, plus a high-levelConsumerand the experimental cooperative-consumer API.send_batch()— 3-5× producer throughput.pgque.send_batch()is now a single set-based insert instead of a PL/pgSQL loop overinsert_event(). Local-Postgres microbenchmarks at N=10 000 rows per batch (full numbers in #160):send_batch(jsonb[]): 24,822 → 85,683 ev/s (~3.5× vs the legacy loop).send_batch(text[]): 27,457 → 133,717 ev/s (~4.9× vs the legacy loop).send()client loop (3,289 ev/s), set-basedsend_batchis ~26× the throughput on JSON.pgque.set_tick_period_ms(). Producer→consumer latency is now sub-second out of the box.\i sql/pgque-tle.sqlregisterspgqueas a pg_tle extension; the default\i sql/pgque.sqlinstall is unchanged.SQL surface (additions)
pgque.send_at(queue, type, payload, deliver_at)— delayed delivery (experimental).pgque.subscribe_subconsumer,pgque.unsubscribe_subconsumer,pgque.receive_coop,pgque.touch_subconsumer(experimental).pgque.set_tick_period_ms(ms)andpgque.config.tick_period_mscolumn.pgque.config.schedulercolumn ('pg_cron'|'pg_timetable').pgque.force_next_tick(queue)as the canonical name;pgque.force_tick(queue)retained as compatibility alias.pgque.dlq_replay_all, harder validation ofpgque.dlq_purge, idempotentpgque.nackterminal handling.queue_stats,consumer_stats,queue_health,otel_metrics,stuck_consumers,in_flight,throughput,error_rate.SQL surface (hardenings, behavior)
pgque_writerandpgque_readerare siblings — neither inherits the other. Apps that produce and consume must be granted both. (See upgrade notes.)SECURITY DEFINERfunction pinssearch_path = pgque, pg_catalog.pgque.get_batch_cursor(extra_where)is now restricted topgque_admin.pgque.send_batchis set-based (single SQL insert, all-or-nothing) instead of a PL/pgSQL loop overinsert_event(). See the headline bullet for benchmark numbers and Benchmark: producer batch insert options and send_batch tradeoffs #160 for the full comparison vs single-send()loops, the legacy implementation, and directINSERT/COPY.pgque.receive(max_return)rejects values <1; queue creation hardened.Client libraries (Python, TypeScript, Go)
All three drivers reach parity:
send/sendBatch,receive,ack,nack(typed errors,retryAfterseconds, unknown-handler policy),forceNextTick/forceTick,subscribe/unsubscribe.Consumerwith handler dispatch, LISTEN/NOTIFY wakeup (Python), and asubconsumeroption for the experimental coop API.ack()returning rowcount 0 logs a warning rather than failing silently.Tooling / install
\i sql/pgque.sql— single-file install, no extension, noshared_preload_libraries. Works on RDS / Aurora / Cloud SQL / AlloyDB / Supabase / Neon / Crunchy Bridge.\i sql/pgque-tle.sql— optional pg_tle wrapper for platforms that allow trusted-language extensions.pgque.start()schedulespgque_ticker(CALL pgque.ticker_loop()every 1s, internally re-tick attick_period_ms),pgque_retry_events(30s),pgque_maint(30s),pgque_rotate_step2(10s).pgque.start_timetable()is the equivalent for pg_timetable deployments.Behavior changes vs v0.1.0 (read before upgrading)
pgque_writerno longer inheritspgque_reader. Apps that both produce and consume must explicitly hold both roles. The install runs the role-split idempotently;tests/test_upgrade_grants.sqlcovers the regression. See docs/reference.md → Roles and grants.pgque.create_queuerejects queue names longer than 57 bytes (was: silently truncated/broken).pgque.receive(max_return)rejects values < 1.pgque.set_tick_period_ms()if needed.pgque.maint()no longer issues VACUUM internally (PL/pgSQL cannot). Vacuum scheduling is the operator's responsibility — see docs.Upgrade from v0.1.0
The supported upgrade path for v0.2.0-rc.1 is:
The install is idempotent: every
create tableisif not exists, every function iscreate or replace, and new columns are added with guardedalter table … add columnblocks. Existing data, queues, consumers, and pending batches are preserved.A CI test that proves "install v0.1.0, then re-install HEAD on top, everything still works" is tracked at #226 and is a blocker for v0.2.0 GA (not for rc.1).
If you grant roles to your application user, double-check the role split (above) so produce-and-consume apps hold both
pgque_readerandpgque_writer.Known limitations / experimental APIs
These ship under v0.2.0 but are experimental and may change shape in subsequent releases:
pgque.send_at+maint_deliver_delayed).queue_stats,otel_metrics, etc.) are not loaded by default; opt in via\i sql/experimental/observability.sql.What's next (post-v0.2.0)
docs/upgrading.mdandCHANGELOG.md.Stamping milestones
v0.2.0-rc.1onmainafter #225 + #223 landed; clients distributed via #222, #225, #228, and #230.docs/upgrading.md+CHANGELOG.md.Full changelog (v0.1.0 → v0.2.0-rc.1)
77 commits since v0.1.0, grouped by type. Order within each group is chronological.
Features
Performance / benchmarks
Fixes
Refactors
Tests
CI / chore
Docs
Other
This draft will be updated in this issue body until GA. Comments welcome — anything missing, miscategorised, or worth highlighting?