pgbus 0.9.8 → 0.9.9
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 +4 -4
- data/CHANGELOG.md +329 -0
- data/README.md +336 -25
- data/lib/generators/pgbus/{add_job_locks_generator.rb → add_uniqueness_keys_generator.rb} +5 -5
- data/lib/pgbus/active_job/adapter.rb +1 -1
- data/lib/pgbus/config_loader.rb +29 -2
- data/lib/pgbus/configuration.rb +202 -73
- data/lib/pgbus/doctor.rb +28 -3
- data/lib/pgbus/execution_pools/async_pool.rb +2 -2
- data/lib/pgbus/generators/config_converter.rb +7 -5
- data/lib/pgbus/generators/migration_detector.rb +20 -16
- data/lib/pgbus/instrumentation.rb +2 -1
- data/lib/pgbus/integrations/appsignal/subscriber.rb +17 -2
- data/lib/pgbus/metrics/subscriber.rb +26 -2
- data/lib/pgbus/metrics.rb +3 -2
- data/lib/pgbus/pgmq_schema/pgmq_v1.11.1.sql +2126 -0
- data/lib/pgbus/pgmq_schema.rb +7 -2
- data/lib/pgbus/process/consumer.rb +141 -12
- data/lib/pgbus/process/supervisor.rb +33 -10
- data/lib/pgbus/process/worker.rb +6 -16
- data/lib/pgbus/recurring/schedule.rb +1 -2
- data/lib/pgbus/serializer.rb +4 -4
- data/lib/pgbus/streams/broadcastable_override.rb +0 -8
- data/lib/pgbus/streams/signed_name.rb +2 -2
- data/lib/pgbus/uniqueness.rb +11 -12
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +20 -4
- data/lib/pgbus/web/payload_filter.rb +3 -3
- data/lib/pgbus/web/streamer/instance.rb +6 -4
- data/lib/pgbus/web/streamer/listener.rb +21 -36
- data/lib/pgbus.rb +46 -6
- metadata +4 -6
- data/app/models/pgbus/job_lock.rb +0 -98
- data/lib/generators/pgbus/templates/add_job_locks.rb.erb +0 -21
- data/lib/rubocop/cop/pgbus/no_ruby_timeout.rb +0 -42
- data/lib/rubocop/pgbus.rb +0 -5
|
@@ -11,7 +11,9 @@ module Pgbus
|
|
|
11
11
|
#
|
|
12
12
|
# Drops settings that:
|
|
13
13
|
# - match the gem default (no point restating it)
|
|
14
|
-
# - are deprecated (
|
|
14
|
+
# - are deprecated (settings that no longer exist in the public API —
|
|
15
|
+
# see DEPRECATED_SETTINGS; pool_size is NOT one of these, it's a
|
|
16
|
+
# live accessor that overrides auto-tuning and is preserved)
|
|
15
17
|
#
|
|
16
18
|
# Converts seconds to durations when they evenly divide into a clean
|
|
17
19
|
# unit (7 days, 30 days, 10 minutes). Falls back to the raw integer
|
|
@@ -28,7 +30,7 @@ module Pgbus
|
|
|
28
30
|
# generator's CLI wrapper writes the new initializer and tells the
|
|
29
31
|
# user to delete the YAML when ready.
|
|
30
32
|
class ConfigConverter
|
|
31
|
-
class Error <
|
|
33
|
+
class Error < Pgbus::Error; end
|
|
32
34
|
|
|
33
35
|
# Setters that accept ActiveSupport::Duration (PR 5).
|
|
34
36
|
DURATION_SETTINGS = %w[
|
|
@@ -38,15 +40,15 @@ module Pgbus
|
|
|
38
40
|
|
|
39
41
|
# Settings that no longer exist in the public API. The converter
|
|
40
42
|
# silently drops these from the generated initializer so users on
|
|
41
|
-
# legacy YAML get a clean migration.
|
|
43
|
+
# legacy YAML get a clean migration. NOTE: pool_size is intentionally
|
|
44
|
+
# NOT here — it's a live Configuration#pool_size accessor users set to
|
|
45
|
+
# override auto-tuning, so it must survive the conversion unchanged.
|
|
42
46
|
#
|
|
43
|
-
# - pool_size -> auto-tuned from worker thread counts
|
|
44
47
|
# - notify_throttle_ms -> Pgbus::Client::NOTIFY_THROTTLE_MS
|
|
45
48
|
# - circuit_breaker_* -> Pgbus::CircuitBreaker constants
|
|
46
49
|
# - archive_compaction_* -> Pgbus::Process::Dispatcher constants
|
|
47
50
|
# - dead_letter_queue_suffix -> Pgbus::DEAD_LETTER_SUFFIX (frozen)
|
|
48
51
|
DEPRECATED_SETTINGS = %w[
|
|
49
|
-
pool_size
|
|
50
52
|
notify_throttle_ms
|
|
51
53
|
circuit_breaker_threshold circuit_breaker_base_backoff circuit_breaker_max_backoff
|
|
52
54
|
archive_compaction_interval archive_compaction_batch_size
|
|
@@ -35,9 +35,10 @@ module Pgbus
|
|
|
35
35
|
#
|
|
36
36
|
# 5. Indexes missing on existing tables → queued. Additive, safe.
|
|
37
37
|
#
|
|
38
|
-
# 6. Modern
|
|
39
|
-
#
|
|
40
|
-
#
|
|
38
|
+
# 6. Modern uniqueness table (pgbus_uniqueness_keys) missing → queue
|
|
39
|
+
# add_uniqueness_keys unconditionally. A legacy pgbus_job_locks
|
|
40
|
+
# table left over from an older install is not dropped by this
|
|
41
|
+
# detector — run `pgbus:migrate_job_locks` by hand to retire it.
|
|
41
42
|
class MigrationDetector
|
|
42
43
|
# Sentinel returned when the database looks empty of pgbus tables.
|
|
43
44
|
# The caller (pgbus:update generator) should redirect the user to
|
|
@@ -58,13 +59,14 @@ module Pgbus
|
|
|
58
59
|
|
|
59
60
|
# generator_key → Rails generator name. Passed to Thor's invoke.
|
|
60
61
|
#
|
|
61
|
-
# Note: uniqueness_keys
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
62
|
+
# Note: uniqueness_keys invokes add_uniqueness_keys, which only
|
|
63
|
+
# creates the modern pgbus_uniqueness_keys table. If the legacy
|
|
64
|
+
# pgbus_job_locks table is also present (an install mid-migration),
|
|
65
|
+
# run `pgbus:migrate_job_locks` separately to drop it — that
|
|
66
|
+
# generator is not part of this detector's default flow since it
|
|
67
|
+
# requires the legacy table to already exist.
|
|
66
68
|
GENERATOR_MAP = {
|
|
67
|
-
uniqueness_keys: "pgbus:
|
|
69
|
+
uniqueness_keys: "pgbus:add_uniqueness_keys",
|
|
68
70
|
add_job_stats: "pgbus:add_job_stats",
|
|
69
71
|
add_job_stats_latency: "pgbus:add_job_stats_latency",
|
|
70
72
|
add_job_stats_queue_index: "pgbus:add_job_stats_queue_index",
|
|
@@ -81,7 +83,7 @@ module Pgbus
|
|
|
81
83
|
# Human-friendly description of each migration for the generator
|
|
82
84
|
# output. Keeps the update generator's run log readable.
|
|
83
85
|
DESCRIPTIONS = {
|
|
84
|
-
uniqueness_keys: "uniqueness keys table (job deduplication
|
|
86
|
+
uniqueness_keys: "uniqueness keys table (job deduplication)",
|
|
85
87
|
add_job_stats: "job stats table (Insights dashboard)",
|
|
86
88
|
add_job_stats_latency: "job stats latency columns (enqueue_latency_ms, retry_count)",
|
|
87
89
|
add_job_stats_queue_index: "job stats (queue_name, created_at) index",
|
|
@@ -128,10 +130,9 @@ module Pgbus
|
|
|
128
130
|
CORE_INSTALL_TABLES.none? { |t| table_exists?(t) }
|
|
129
131
|
end
|
|
130
132
|
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
# One symbol covers both cases; see GENERATOR_MAP.
|
|
133
|
+
# Queue add_uniqueness_keys whenever the modern table is missing.
|
|
134
|
+
# This does not touch a legacy pgbus_job_locks table — that is
|
|
135
|
+
# handled separately by `pgbus:migrate_job_locks`, see GENERATOR_MAP.
|
|
135
136
|
def uniqueness_key_migrations
|
|
136
137
|
return [] if table_exists?("pgbus_uniqueness_keys")
|
|
137
138
|
|
|
@@ -180,8 +181,11 @@ module Pgbus
|
|
|
180
181
|
def recurring_migrations
|
|
181
182
|
# pgbus_recurring_tasks + pgbus_recurring_executions are the two
|
|
182
183
|
# tables the recurring generator creates. If BOTH exist, nothing
|
|
183
|
-
# to do. If either is missing, we
|
|
184
|
-
#
|
|
184
|
+
# to do. If either is missing, we queue the generator. The
|
|
185
|
+
# add_recurring template uses plain create_table (no if_not_exists),
|
|
186
|
+
# so idempotency is provided by this detector, not the template:
|
|
187
|
+
# the migration is only queued while a table is absent, and never
|
|
188
|
+
# re-invoked once both tables are present.
|
|
185
189
|
return [] if table_exists?("pgbus_recurring_tasks") && table_exists?("pgbus_recurring_executions")
|
|
186
190
|
|
|
187
191
|
[:add_recurring]
|
|
@@ -21,7 +21,8 @@ module Pgbus
|
|
|
21
21
|
# pgbus.stream.broadcast — stream broadcast (sync or deferred)
|
|
22
22
|
# pgbus.outbox.publish — outbox row created
|
|
23
23
|
# pgbus.recurring.enqueue — scheduler enqueued a due recurring task
|
|
24
|
-
# pgbus.worker.recycle — worker hit a recycle threshold
|
|
24
|
+
# pgbus.worker.recycle — worker hit a recycle threshold (reason/jobs_processed/memory_mb/lifetime_seconds)
|
|
25
|
+
# pgbus.consumer.recycle — consumer hit a recycle threshold (same payload shape as pgbus.worker.recycle)
|
|
25
26
|
# pgbus.serializer.serialize — job/event serialization
|
|
26
27
|
# pgbus.serializer.deserialize — job/event deserialization
|
|
27
28
|
#
|
|
@@ -41,7 +41,13 @@ module Pgbus
|
|
|
41
41
|
subscribe("pgbus.stream.broadcast") { |event| on_stream_broadcast(event) },
|
|
42
42
|
subscribe("pgbus.outbox.publish") { |event| on_outbox_publish(event) },
|
|
43
43
|
subscribe("pgbus.recurring.enqueue") { |event| on_recurring_enqueue(event) },
|
|
44
|
-
subscribe("pgbus.worker.recycle") { |event| on_worker_recycle(event) }
|
|
44
|
+
subscribe("pgbus.worker.recycle") { |event| on_worker_recycle(event) },
|
|
45
|
+
subscribe("pgbus.consumer.recycle") { |event| on_consumer_recycle(event) }
|
|
46
|
+
# pgbus.client.pool is deliberately NOT subscribed here — the
|
|
47
|
+
# minutely Probe#track_pool already reports pgbus_pool_size /
|
|
48
|
+
# pgbus_pool_available gauges for AppSignal (see probe.rb).
|
|
49
|
+
# Subscribing here too would double-report on every heartbeat
|
|
50
|
+
# and skew the gauges.
|
|
45
51
|
]
|
|
46
52
|
@installed = true
|
|
47
53
|
end
|
|
@@ -250,7 +256,16 @@ module Pgbus
|
|
|
250
256
|
::Appsignal.increment_counter(
|
|
251
257
|
"#{METRIC_PREFIX}worker_recycled",
|
|
252
258
|
1,
|
|
253
|
-
{ reason: payload[:reason] }
|
|
259
|
+
{ reason: payload[:reason], kind: "worker" }
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def on_consumer_recycle(event)
|
|
264
|
+
payload = event.payload
|
|
265
|
+
::Appsignal.increment_counter(
|
|
266
|
+
"#{METRIC_PREFIX}worker_recycled",
|
|
267
|
+
1,
|
|
268
|
+
{ reason: payload[:reason], kind: "consumer" }
|
|
254
269
|
)
|
|
255
270
|
end
|
|
256
271
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "socket"
|
|
4
|
+
|
|
3
5
|
module Pgbus
|
|
4
6
|
module Metrics
|
|
5
7
|
# Consumes Pgbus::Instrumentation events and forwards them to the configured
|
|
@@ -36,7 +38,9 @@ module Pgbus
|
|
|
36
38
|
subscribe("pgbus.stream.broadcast") { |event| on_stream_broadcast(event) },
|
|
37
39
|
subscribe("pgbus.outbox.publish") { |event| on_outbox_publish(event) },
|
|
38
40
|
subscribe("pgbus.recurring.enqueue") { |event| on_recurring_enqueue(event) },
|
|
39
|
-
subscribe("pgbus.worker.recycle") { |event| on_worker_recycle(event) }
|
|
41
|
+
subscribe("pgbus.worker.recycle") { |event| on_worker_recycle(event) },
|
|
42
|
+
subscribe("pgbus.consumer.recycle") { |event| on_consumer_recycle(event) },
|
|
43
|
+
subscribe("pgbus.client.pool") { |event| on_client_pool(event) }
|
|
40
44
|
]
|
|
41
45
|
@installed = true
|
|
42
46
|
end
|
|
@@ -176,10 +180,30 @@ module Pgbus
|
|
|
176
180
|
def on_worker_recycle(event)
|
|
177
181
|
backend.increment(
|
|
178
182
|
"#{METRIC_PREFIX}worker_recycled", 1,
|
|
179
|
-
compact(reason: event.payload[:reason])
|
|
183
|
+
compact(reason: event.payload[:reason], kind: "worker")
|
|
184
|
+
)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def on_consumer_recycle(event)
|
|
188
|
+
backend.increment(
|
|
189
|
+
"#{METRIC_PREFIX}worker_recycled", 1,
|
|
190
|
+
compact(reason: event.payload[:reason], kind: "consumer")
|
|
180
191
|
)
|
|
181
192
|
end
|
|
182
193
|
|
|
194
|
+
# ── Connection pool ───────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
def on_client_pool(event)
|
|
197
|
+
payload = event.payload
|
|
198
|
+
tags = { hostname: hostname }
|
|
199
|
+
backend.gauge("#{METRIC_PREFIX}pool_size", payload[:size], tags)
|
|
200
|
+
backend.gauge("#{METRIC_PREFIX}pool_available", payload[:available], tags)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def hostname
|
|
204
|
+
Socket.gethostname
|
|
205
|
+
end
|
|
206
|
+
|
|
183
207
|
# Drop nil-valued tags so backends don't emit empty labels.
|
|
184
208
|
def compact(tags)
|
|
185
209
|
tags.compact
|
data/lib/pgbus/metrics.rb
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
module Pgbus
|
|
4
4
|
# Backend-agnostic metrics adapter.
|
|
5
5
|
#
|
|
6
|
-
# Pgbus emits
|
|
7
|
-
# Pgbus::Instrumentation
|
|
6
|
+
# Pgbus emits a family of `pgbus.*` ActiveSupport::Notifications events;
|
|
7
|
+
# `Pgbus::Instrumentation` is the single source of truth for the catalog. The
|
|
8
|
+
# AppSignal integration is one consumer of those
|
|
8
9
|
# events; this namespace is a second, vendor-neutral consumer that forwards the
|
|
9
10
|
# same event→metric mapping to a configurable backend so teams on Prometheus,
|
|
10
11
|
# Datadog, or plain StatsD get metrics without hand-writing subscribers.
|