pgbus 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a966ed340b2cd097f82b926f90647a0d481df35da76bb4ef06f4e3c13e3d5692
4
- data.tar.gz: 779124d08216918b02a162f2a1263c4115cd7a7fb9404dc0055dc940da28793f
3
+ metadata.gz: 3393ad40ec489306eff74a6504c6694aa27747e5e0d2ce533ba4b7d7e989fe93
4
+ data.tar.gz: b1e7244a064bfa8fe1776a5eaa9dd7317682e098bf98133759eadc0c6402e4c8
5
5
  SHA512:
6
- metadata.gz: b25519a1d9ea9e1e2700b959a5ebcf03adf8efbcb130001f1e962067e0def2546b1644e199d24d2b849b29ba67f15bc83eef5f41506ffde3d6ea5d79da752af7
7
- data.tar.gz: f0ec03ccc2b075b90f6b87cc5eb1315d5d3834b2cdd7c9c19fc851f350196f6860b941ec0566aac0315f531bf29356f9a1f94b68e852aa497eb642833ddc3800
6
+ metadata.gz: 440cfbc362912153a1cfb694033e667b32559a965b5b2b8c99214d11bc06a09d717127e8616a49dc59858f6a8048619a0d4a0267398f5cccf78019ca2f0dedf3
7
+ data.tar.gz: 45163ef47567b3ff8be5e4c415311f6619d11a4712ecbc970aafe7e949cc09ebd75632727ebf6b7817c124cfbb9dec0cf3c9d29c41061d37704048c9522d6851
data/CHANGELOG.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  ### Fixed
4
4
 
5
+ - **`ensure_stream_queue` recovers when a process-local `@queues_created` memo is stale after another process drops the physical queue.** Stream queues are created lazily and memoized per process. The dispatcher orphan stream sweep (and dashboard / manual `drop_queue`) only clears the *local* process memo when it drops an empty or aged stream queue. Peer processes — typically long-lived web workers that already published or subscribed once — keep the memo, skip `pgmq.create` on the next durable broadcast, and fail in `pgmq.enable_notify_insert` with `Queue "…" does not exist. Create it first using pgmq.create()`. That exception often surfaces from an `after_commit` Turbo/stream broadcast and turns a successful write into a 500. On that specific missing-queue `PGMQ::Errors::ConnectionError`, `ensure_stream_queue` now forgets the local queue + archive-index memo and retries create/notify once. Unrelated connection errors still raise immediately. Happy path: no extra round-trips (recovery is error-path only).
5
6
  - **`streams_pool_database_url` / `streams_pool_host` / `streams_pool_port` — route the streams PGMQ pool independently of the streamer's LISTEN connection (issue #358).** 0.12.0 started building the dedicated streams pool from `streams_connection_options` (correct for a separate streams database), but that method is also how pooler-bypass installs pin the LISTEN connection to the **direct** Postgres port (`streams_port = 5432`, the documented "workers go through PgBouncer, streamer goes direct" pattern) — so upgrading silently moved up to `streams_pool_size` connections **per process** onto the direct port, whose `max_connections` ceiling on managed Postgres is typically low. A modest fleet exhausts it (`FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute`): `StreamApp` then 500s every SSE connect and durable broadcasts fail at publish, intermittently, because the pools are lazy. Only LISTEN actually needs the direct port (it dies at transaction-pool COMMIT boundaries); the pool's broadcast INSERTs and replay reads are plain pooler-safe SQL. The new `streams_pool_*` triple routes the POOL independently — applied to the base options exactly like the `streams_*` and `worker_notify_*` groups: set `streams_pool_port` back to the pooled port and only the LISTEN pins remain direct. Default `nil` = follows `streams_connection_options`, byte-identical to 0.12.0, so separate-streams-DB installs are unchanged. Refs #358.
6
7
  - **The health verdict no longer reads durable stream queues as a wedged fleet (issue #359).** The Process-liveness signal (`pgbus doctor`, `pgbus_health`, the MCP health tool) treats *visible messages with `read_ct=0` while workers are alive* as the silent-worker-wedge signature — but durable stream delivery is a non-consuming peek, so **every** stream queue matches it permanently by design. On a streams-heavy install the verdict screamed STALLED listing hundreds of healthy stream queues, burying real wedges in noise. `HealthAnalyzer` now excludes queues registered in the `pgbus_stream_queues` registry from the operational set (exactly like DLQs) — out of the STALLED/DEGRADED reasons and the backlog totals — via a new `Web::DataSource#stream_queue_names` (loaded fresh per verdict; degrades to an empty set on pre-registry installs). Same bug class as #308/#309, same registry cure. Refs #359.
7
8
  - **The `upgrade_pgmq` migration no longer silently kills NOTIFY-gated wakeups fleet-wide (issue #360).** The generated migration drops every pgmq function **with CASCADE** — which also drops the `trigger_notify_queue_insert_listeners` trigger from every existing queue table (it depends on the dropped `pgmq.notify_queue_listeners()`), and `install_sql` re-created the functions but never the per-queue triggers. Result: after every PGMQ schema upgrade, NOTIFY wakeups (#174) silently died and workers fell back to `polling_interval` polling — nothing errored; latency degraded and DB poll load rose. Trigger re-install only happened in `ensure_single_queue` (per-process memoized), so on the common deploy ordering where the job supervisor boots *before* migrations run, the supervisor installed triggers at boot and the migration wiped them minutes later — with nothing left to re-install them until a full process restart. New `PgmqSchema.reinstall_notify_triggers_sql` replays `pgmq.enable_notify_insert` for every row in `pgmq.notify_insert_throttle` (a table, preserved by the upgrade, recording exactly which queues had notify and at what throttle; the function is idempotent), and the migration template runs it after `install_sql` — restoring every trigger at its original interval. No-op on vendored versions without the notify feature. **Already ran an upgrade migration?** Run `Pgbus::PgmqSchema.reinstall_notify_triggers_sql` once (e.g. from a console: `ActiveRecord::Base.connection.execute(...)`) or restart the fleet; `pgbus doctor`'s LISTEN/NOTIFY check confirms the repair. Refs #360.
@@ -29,16 +29,7 @@ module Pgbus
29
29
  # raise on the missing bare table. Streams never use priority
30
30
  # sub-queues (issue #310).
31
31
  ensure_pgmq_schema
32
- ensure_single_queue(full_name)
33
-
34
- # PGMQ's default NOTIFY throttle is 250ms — meant to coalesce
35
- # high-frequency worker queue inserts. Streams are latency-
36
- # sensitive and need every broadcast to fire a NOTIFY, even
37
- # when several are batched within a single millisecond.
38
- # Override the throttle to 0 specifically for stream queues.
39
- # Use the idempotent path to avoid deadlocks when multiple
40
- # processes race to set up the same stream queue.
41
- synchronized { enable_notify_if_needed(full_name, 0) }
32
+ ensure_stream_queue_tables(full_name, stream_name)
42
33
  end
43
34
 
44
35
  # CREATE INDEX IF NOT EXISTS is idempotent in Postgres but still
@@ -68,6 +59,43 @@ module Pgbus
68
59
 
69
60
  @stream_indexes_created[stream_name] = true
70
61
  end
62
+
63
+ private
64
+
65
+ # Creates the bare stream queue + forces NOTIFY throttle 0. Retries once
66
+ # when a process-local `@queues_created` memo is stale relative to the
67
+ # database: another process can drop the physical queue (orphan stream
68
+ # sweep, dashboard drop, manual `drop_queue`) without invalidating every
69
+ # peer process's memo. The memoized path then skips `pgmq.create` and
70
+ # `enable_notify_insert` raises "Queue does not exist".
71
+ def ensure_stream_queue_tables(full_name, stream_name)
72
+ ensure_single_queue(full_name)
73
+
74
+ # PGMQ's default NOTIFY throttle is 250ms — meant to coalesce
75
+ # high-frequency worker queue inserts. Streams are latency-
76
+ # sensitive and need every broadcast to fire a NOTIFY, even
77
+ # when several are batched within a single millisecond.
78
+ # Override the throttle to 0 specifically for stream queues.
79
+ # Use the idempotent path to avoid deadlocks when multiple
80
+ # processes race to set up the same stream queue.
81
+ synchronized { enable_notify_if_needed(full_name, 0) }
82
+ rescue PGMQ::Errors::ConnectionError => e
83
+ raise unless missing_pgmq_queue_error?(e)
84
+
85
+ forget_stream_queue_memo!(full_name, stream_name)
86
+ ensure_single_queue(full_name)
87
+ synchronized { enable_notify_if_needed(full_name, 0) }
88
+ end
89
+
90
+ def forget_stream_queue_memo!(full_name, stream_name)
91
+ @queues_created.delete(full_name)
92
+ @stream_indexes_created.delete(stream_name)
93
+ end
94
+
95
+ def missing_pgmq_queue_error?(error)
96
+ message = error.message.to_s
97
+ message.include?("does not exist") && message.include?("pgmq.create")
98
+ end
71
99
  end
72
100
  end
73
101
  end
data/lib/pgbus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgbus
4
- VERSION = "0.12.1"
4
+ VERSION = "0.12.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson