pgbus 0.9.7 → 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 +367 -0
- data/README.md +454 -25
- data/Rakefile +10 -1
- data/app/helpers/pgbus/application_helper.rb +37 -0
- data/app/views/pgbus/processes/_processes_table.html.erb +4 -1
- data/config/locales/da.yml +4 -0
- data/config/locales/de.yml +4 -0
- data/config/locales/en.yml +4 -0
- data/config/locales/es.yml +4 -0
- data/config/locales/fi.yml +4 -0
- data/config/locales/fr.yml +4 -0
- data/config/locales/it.yml +4 -0
- data/config/locales/ja.yml +4 -0
- data/config/locales/nb.yml +4 -0
- data/config/locales/nl.yml +4 -0
- data/config/locales/pt.yml +4 -0
- data/config/locales/sv.yml +4 -0
- 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/active_job/executor.rb +25 -4
- data/lib/pgbus/cli/dlq.rb +164 -0
- data/lib/pgbus/cli.rb +18 -1
- data/lib/pgbus/client/connection_health.rb +194 -0
- data/lib/pgbus/client.rb +592 -73
- data/lib/pgbus/config_loader.rb +50 -4
- data/lib/pgbus/configuration.rb +294 -79
- data/lib/pgbus/dedup_cache.rb +8 -0
- data/lib/pgbus/doctor.rb +275 -0
- data/lib/pgbus/engine.rb +15 -0
- data/lib/pgbus/execution_pools/async_pool.rb +9 -2
- data/lib/pgbus/execution_pools/thread_pool.rb +7 -0
- data/lib/pgbus/generators/config_converter.rb +7 -5
- data/lib/pgbus/generators/migration_detector.rb +20 -16
- data/lib/pgbus/instrumentation.rb +3 -1
- data/lib/pgbus/integrations/appsignal/probe.rb +23 -1
- data/lib/pgbus/integrations/appsignal/subscriber.rb +17 -2
- data/lib/pgbus/metrics/backend.rb +38 -0
- data/lib/pgbus/metrics/backends/prometheus.rb +123 -0
- data/lib/pgbus/metrics/backends/statsd.rb +64 -0
- data/lib/pgbus/metrics/prometheus_exporter.rb +34 -0
- data/lib/pgbus/metrics/subscriber.rb +214 -0
- data/lib/pgbus/metrics.rb +43 -0
- 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 +354 -18
- data/lib/pgbus/process/consumer_priority.rb +34 -0
- data/lib/pgbus/process/dispatcher.rb +265 -41
- data/lib/pgbus/process/heartbeat.rb +18 -5
- data/lib/pgbus/process/memory_usage.rb +48 -0
- data/lib/pgbus/process/notify_listener.rb +26 -7
- data/lib/pgbus/process/notify_probe.rb +96 -0
- data/lib/pgbus/process/primary_validator.rb +53 -0
- data/lib/pgbus/process/signal_handler.rb +6 -0
- data/lib/pgbus/process/supervisor.rb +423 -50
- data/lib/pgbus/process/worker.rb +288 -35
- data/lib/pgbus/recurring/schedule.rb +1 -2
- data/lib/pgbus/recurring/scheduler.rb +15 -1
- 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/streams/turbo_broadcastable.rb +7 -5
- data/lib/pgbus/table_maintenance.rb +13 -2
- data/lib/pgbus/uniqueness.rb +11 -12
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +36 -4
- data/lib/pgbus/web/health_app.rb +102 -0
- data/lib/pgbus/web/health_server.rb +144 -0
- data/lib/pgbus/web/payload_filter.rb +3 -3
- data/lib/pgbus/web/streamer/instance.rb +58 -2
- data/lib/pgbus/web/streamer/listener.rb +69 -21
- data/lib/pgbus.rb +77 -0
- data/lib/tasks/pgbus_doctor.rake +12 -0
- metadata +19 -4
- data/app/models/pgbus/job_lock.rb +0 -98
- data/lib/generators/pgbus/templates/add_job_locks.rb.erb +0 -21
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.9.
|
|
4
|
+
version: 0.9.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -150,7 +150,6 @@ files:
|
|
|
150
150
|
- app/models/pgbus/application_record.rb
|
|
151
151
|
- app/models/pgbus/batch_entry.rb
|
|
152
152
|
- app/models/pgbus/blocked_execution.rb
|
|
153
|
-
- app/models/pgbus/job_lock.rb
|
|
154
153
|
- app/models/pgbus/job_stat.rb
|
|
155
154
|
- app/models/pgbus/outbox_entry.rb
|
|
156
155
|
- app/models/pgbus/process_entry.rb
|
|
@@ -209,7 +208,6 @@ files:
|
|
|
209
208
|
- exe/pgbus
|
|
210
209
|
- lib/active_job/queue_adapters/pgbus_adapter.rb
|
|
211
210
|
- lib/generators/pgbus/add_failed_events_index_generator.rb
|
|
212
|
-
- lib/generators/pgbus/add_job_locks_generator.rb
|
|
213
211
|
- lib/generators/pgbus/add_job_stats_generator.rb
|
|
214
212
|
- lib/generators/pgbus/add_job_stats_latency_generator.rb
|
|
215
213
|
- lib/generators/pgbus/add_job_stats_queue_index_generator.rb
|
|
@@ -218,11 +216,11 @@ files:
|
|
|
218
216
|
- lib/generators/pgbus/add_queue_states_generator.rb
|
|
219
217
|
- lib/generators/pgbus/add_recurring_generator.rb
|
|
220
218
|
- lib/generators/pgbus/add_stream_stats_generator.rb
|
|
219
|
+
- lib/generators/pgbus/add_uniqueness_keys_generator.rb
|
|
221
220
|
- lib/generators/pgbus/install_generator.rb
|
|
222
221
|
- lib/generators/pgbus/migrate_job_locks_generator.rb
|
|
223
222
|
- lib/generators/pgbus/migration_path.rb
|
|
224
223
|
- lib/generators/pgbus/templates/add_failed_events_unique_index.rb.erb
|
|
225
|
-
- lib/generators/pgbus/templates/add_job_locks.rb.erb
|
|
226
224
|
- lib/generators/pgbus/templates/add_job_stats.rb.erb
|
|
227
225
|
- lib/generators/pgbus/templates/add_job_stats_latency.rb.erb
|
|
228
226
|
- lib/generators/pgbus/templates/add_job_stats_queue_index.rb.erb
|
|
@@ -252,7 +250,9 @@ files:
|
|
|
252
250
|
- lib/pgbus/bus_record.rb
|
|
253
251
|
- lib/pgbus/circuit_breaker.rb
|
|
254
252
|
- lib/pgbus/cli.rb
|
|
253
|
+
- lib/pgbus/cli/dlq.rb
|
|
255
254
|
- lib/pgbus/client.rb
|
|
255
|
+
- lib/pgbus/client/connection_health.rb
|
|
256
256
|
- lib/pgbus/client/ensure_stream_queue.rb
|
|
257
257
|
- lib/pgbus/client/notify_stream.rb
|
|
258
258
|
- lib/pgbus/client/read_after.rb
|
|
@@ -263,6 +263,7 @@ files:
|
|
|
263
263
|
- lib/pgbus/configuration.rb
|
|
264
264
|
- lib/pgbus/configuration/capsule_dsl.rb
|
|
265
265
|
- lib/pgbus/dedup_cache.rb
|
|
266
|
+
- lib/pgbus/doctor.rb
|
|
266
267
|
- lib/pgbus/engine.rb
|
|
267
268
|
- lib/pgbus/error_reporter.rb
|
|
268
269
|
- lib/pgbus/event.rb
|
|
@@ -305,16 +306,26 @@ files:
|
|
|
305
306
|
- lib/pgbus/mcp/tools/recurring_tool.rb
|
|
306
307
|
- lib/pgbus/mcp/tools/stats_tool.rb
|
|
307
308
|
- lib/pgbus/mcp/tools/throughput_tool.rb
|
|
309
|
+
- lib/pgbus/metrics.rb
|
|
310
|
+
- lib/pgbus/metrics/backend.rb
|
|
311
|
+
- lib/pgbus/metrics/backends/prometheus.rb
|
|
312
|
+
- lib/pgbus/metrics/backends/statsd.rb
|
|
313
|
+
- lib/pgbus/metrics/prometheus_exporter.rb
|
|
314
|
+
- lib/pgbus/metrics/subscriber.rb
|
|
308
315
|
- lib/pgbus/outbox.rb
|
|
309
316
|
- lib/pgbus/outbox/poller.rb
|
|
310
317
|
- lib/pgbus/pgmq_schema.rb
|
|
311
318
|
- lib/pgbus/pgmq_schema/pgmq_v1.11.0.sql
|
|
319
|
+
- lib/pgbus/pgmq_schema/pgmq_v1.11.1.sql
|
|
312
320
|
- lib/pgbus/process/consumer.rb
|
|
313
321
|
- lib/pgbus/process/consumer_priority.rb
|
|
314
322
|
- lib/pgbus/process/dispatcher.rb
|
|
315
323
|
- lib/pgbus/process/heartbeat.rb
|
|
316
324
|
- lib/pgbus/process/lifecycle.rb
|
|
325
|
+
- lib/pgbus/process/memory_usage.rb
|
|
317
326
|
- lib/pgbus/process/notify_listener.rb
|
|
327
|
+
- lib/pgbus/process/notify_probe.rb
|
|
328
|
+
- lib/pgbus/process/primary_validator.rb
|
|
318
329
|
- lib/pgbus/process/queue_lock.rb
|
|
319
330
|
- lib/pgbus/process/signal_handler.rb
|
|
320
331
|
- lib/pgbus/process/supervisor.rb
|
|
@@ -355,6 +366,8 @@ files:
|
|
|
355
366
|
- lib/pgbus/version.rb
|
|
356
367
|
- lib/pgbus/web/authentication.rb
|
|
357
368
|
- lib/pgbus/web/data_source.rb
|
|
369
|
+
- lib/pgbus/web/health_app.rb
|
|
370
|
+
- lib/pgbus/web/health_server.rb
|
|
358
371
|
- lib/pgbus/web/metrics_serializer.rb
|
|
359
372
|
- lib/pgbus/web/payload_filter.rb
|
|
360
373
|
- lib/pgbus/web/stream_app.rb
|
|
@@ -370,6 +383,7 @@ files:
|
|
|
370
383
|
- lib/pgbus/web/streamer/stream_event_dispatcher.rb
|
|
371
384
|
- lib/puma/plugin/pgbus_streams.rb
|
|
372
385
|
- lib/tasks/pgbus_autovacuum.rake
|
|
386
|
+
- lib/tasks/pgbus_doctor.rake
|
|
373
387
|
- lib/tasks/pgbus_pgmq.rake
|
|
374
388
|
- lib/tasks/pgbus_queues.rake
|
|
375
389
|
- lib/tasks/pgbus_streams.rake
|
|
@@ -380,6 +394,7 @@ metadata:
|
|
|
380
394
|
homepage_uri: https://github.com/mhenrixon/pgbus
|
|
381
395
|
source_code_uri: https://github.com/mhenrixon/pgbus/tree/main
|
|
382
396
|
changelog_uri: https://github.com/mhenrixon/pgbus/blob/main/CHANGELOG.md
|
|
397
|
+
documentation_uri: https://pgbus.zoolutions.llc
|
|
383
398
|
rubygems_mfa_required: 'true'
|
|
384
399
|
rdoc_options: []
|
|
385
400
|
require_paths:
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Pgbus
|
|
4
|
-
class JobLock < BusRecord
|
|
5
|
-
self.table_name = "pgbus_job_locks"
|
|
6
|
-
|
|
7
|
-
# States:
|
|
8
|
-
# queued — lock held from enqueue time (:until_executed), no worker yet
|
|
9
|
-
# executing — lock held by an active worker process
|
|
10
|
-
STATES = %w[queued executing].freeze
|
|
11
|
-
|
|
12
|
-
scope :executing, -> { where(state: "executing") }
|
|
13
|
-
scope :queued_locks, -> { where(state: "queued") }
|
|
14
|
-
scope :expired, ->(now = Time.current) { where("expires_at < ?", now) }
|
|
15
|
-
|
|
16
|
-
# Atomically try to acquire a lock.
|
|
17
|
-
# Cleans up expired locks for this key first (crash recovery at acquire time).
|
|
18
|
-
# Returns true if acquired, false if already locked.
|
|
19
|
-
#
|
|
20
|
-
# Uses raw SQL on the hot path to minimize ActiveRecord allocations
|
|
21
|
-
# (~29 objects vs ~304 per acquire+release cycle with AR query builder).
|
|
22
|
-
def self.acquire!(lock_key, job_class:, ttl:, job_id: nil, state: "queued", owner_pid: nil, owner_hostname: nil) # rubocop:disable Naming/PredicateMethod
|
|
23
|
-
expires_at = Time.current + ttl
|
|
24
|
-
|
|
25
|
-
# Remove any expired lock for this key inline (last-resort TTL recovery)
|
|
26
|
-
connection.exec_delete(
|
|
27
|
-
"DELETE FROM #{table_name} WHERE lock_key = $1 AND expires_at < $2",
|
|
28
|
-
"JobLock Expire", [lock_key, Time.current]
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
result = connection.exec_query(
|
|
32
|
-
"INSERT INTO #{table_name} (lock_key, job_class, job_id, state, owner_pid, owner_hostname, expires_at) " \
|
|
33
|
-
"VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (lock_key) DO NOTHING RETURNING id",
|
|
34
|
-
"JobLock Acquire", [lock_key, job_class, job_id, state, owner_pid, owner_hostname, expires_at]
|
|
35
|
-
)
|
|
36
|
-
result.rows.any?
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Transition a queued lock to executing state and claim ownership.
|
|
40
|
-
# Called when a worker starts executing a job that was locked at enqueue time.
|
|
41
|
-
def self.claim_for_execution!(lock_key, owner_pid:, owner_hostname:, ttl:)
|
|
42
|
-
connection.exec_update(
|
|
43
|
-
"UPDATE #{table_name} SET state = $1, owner_pid = $2, owner_hostname = $3, expires_at = $4 " \
|
|
44
|
-
"WHERE lock_key = $5",
|
|
45
|
-
"JobLock Claim", ["executing", owner_pid, owner_hostname, Time.current + ttl, lock_key]
|
|
46
|
-
)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Release a lock by key.
|
|
50
|
-
def self.release!(lock_key)
|
|
51
|
-
connection.exec_delete(
|
|
52
|
-
"DELETE FROM #{table_name} WHERE lock_key = $1",
|
|
53
|
-
"JobLock Release", [lock_key]
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Check if a lock is currently held (regardless of expiry — reaper handles orphans).
|
|
58
|
-
def self.locked?(lock_key)
|
|
59
|
-
result = connection.select_value(
|
|
60
|
-
"SELECT 1 FROM #{table_name} WHERE lock_key = $1 LIMIT 1", "JobLock Check", [lock_key]
|
|
61
|
-
)
|
|
62
|
-
!result.nil?
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Reap orphaned locks whose owner is no longer alive, plus stale queued
|
|
66
|
-
# locks that were never claimed by a worker.
|
|
67
|
-
# Returns the total number of orphaned locks released.
|
|
68
|
-
def self.reap_orphaned!
|
|
69
|
-
reaped = 0
|
|
70
|
-
|
|
71
|
-
# 1. Executing locks whose owner process has no healthy heartbeat
|
|
72
|
-
alive_workers = ProcessEntry
|
|
73
|
-
.where("last_heartbeat_at >= ?", Time.current - Process::Heartbeat::ALIVE_THRESHOLD)
|
|
74
|
-
.pluck(:pid, :hostname)
|
|
75
|
-
|
|
76
|
-
orphaned_executing = executing.select do |lock|
|
|
77
|
-
alive_workers.none? { |pid, hostname| pid == lock.owner_pid && hostname == lock.owner_hostname }
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
reaped += where(id: orphaned_executing.map(&:id)).delete_all if orphaned_executing.any?
|
|
81
|
-
|
|
82
|
-
# 2. Queued locks older than the visibility timeout that were never
|
|
83
|
-
# claimed. These are left behind when enqueue fails after lock
|
|
84
|
-
# acquisition (e.g. network error, process crash).
|
|
85
|
-
threshold = Pgbus.configuration.visibility_timeout
|
|
86
|
-
stale_queued = queued_locks.where("locked_at < ?", Time.current - threshold)
|
|
87
|
-
reaped += stale_queued.delete_all if stale_queued.exists?
|
|
88
|
-
|
|
89
|
-
reaped
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Last-resort cleanup: delete locks whose expires_at has passed.
|
|
93
|
-
# This only fires when the reaper itself can't run (e.g., entire supervisor dead).
|
|
94
|
-
def self.cleanup_expired!
|
|
95
|
-
expired.delete_all
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class AddPgbusJobLocks < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def change
|
|
3
|
-
create_table :pgbus_job_locks do |t|
|
|
4
|
-
t.string :lock_key, null: false
|
|
5
|
-
t.string :job_class, null: false
|
|
6
|
-
t.string :job_id
|
|
7
|
-
t.string :state, null: false, default: "queued"
|
|
8
|
-
t.integer :owner_pid
|
|
9
|
-
t.string :owner_hostname
|
|
10
|
-
t.datetime :locked_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
|
11
|
-
t.datetime :expires_at, null: false
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
add_index :pgbus_job_locks, :lock_key,
|
|
15
|
-
unique: true, name: "idx_pgbus_job_locks_key"
|
|
16
|
-
add_index :pgbus_job_locks, :expires_at,
|
|
17
|
-
name: "idx_pgbus_job_locks_expires"
|
|
18
|
-
add_index :pgbus_job_locks, [:state, :owner_pid],
|
|
19
|
-
name: "idx_pgbus_job_locks_reaper"
|
|
20
|
-
end
|
|
21
|
-
end
|