pgbus 0.1.4 → 0.1.6
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/README.md +326 -11
- data/app/controllers/pgbus/api/insights_controller.rb +16 -0
- data/app/controllers/pgbus/insights_controller.rb +10 -0
- data/app/controllers/pgbus/locks_controller.rb +9 -0
- data/app/controllers/pgbus/outbox_controller.rb +10 -0
- data/app/controllers/pgbus/queues_controller.rb +10 -0
- data/app/helpers/pgbus/application_helper.rb +34 -0
- data/app/models/pgbus/job_lock.rb +82 -0
- data/app/models/pgbus/job_stat.rb +94 -0
- data/app/models/pgbus/outbox_entry.rb +10 -0
- data/app/models/pgbus/queue_state.rb +33 -0
- data/app/views/layouts/pgbus/application.html.erb +33 -8
- data/app/views/pgbus/dashboard/_stats_cards.html.erb +24 -18
- data/app/views/pgbus/insights/show.html.erb +161 -0
- data/app/views/pgbus/locks/index.html.erb +53 -0
- data/app/views/pgbus/outbox/index.html.erb +55 -0
- data/app/views/pgbus/queues/_queues_list.html.erb +15 -1
- data/config/routes.rb +7 -0
- data/lib/generators/pgbus/add_job_locks_generator.rb +52 -0
- data/lib/generators/pgbus/add_job_stats_generator.rb +52 -0
- data/lib/generators/pgbus/add_outbox_generator.rb +52 -0
- data/lib/generators/pgbus/add_queue_states_generator.rb +51 -0
- data/lib/generators/pgbus/add_recurring_generator.rb +1 -1
- data/lib/generators/pgbus/install_generator.rb +1 -1
- data/lib/generators/pgbus/templates/add_job_locks.rb.erb +21 -0
- data/lib/generators/pgbus/templates/add_job_stats.rb.erb +18 -0
- data/lib/generators/pgbus/templates/add_outbox.rb.erb +25 -0
- data/lib/generators/pgbus/templates/add_queue_states.rb.erb +16 -0
- data/lib/generators/pgbus/upgrade_pgmq_generator.rb +1 -1
- data/lib/pgbus/active_job/adapter.rb +64 -9
- data/lib/pgbus/active_job/executor.rb +67 -5
- data/lib/pgbus/circuit_breaker.rb +112 -0
- data/lib/pgbus/client.rb +127 -50
- data/lib/pgbus/configuration.rb +55 -1
- data/lib/pgbus/dedup_cache.rb +76 -0
- data/lib/pgbus/engine.rb +1 -0
- data/lib/pgbus/event_bus/handler.rb +13 -2
- data/lib/pgbus/outbox/poller.rb +117 -0
- data/lib/pgbus/outbox.rb +30 -0
- data/lib/pgbus/process/consumer_priority.rb +64 -0
- data/lib/pgbus/process/dispatcher.rb +75 -0
- data/lib/pgbus/process/heartbeat.rb +3 -1
- data/lib/pgbus/process/lifecycle.rb +111 -0
- data/lib/pgbus/process/queue_lock.rb +87 -0
- data/lib/pgbus/process/supervisor.rb +46 -6
- data/lib/pgbus/process/wake_signal.rb +53 -0
- data/lib/pgbus/process/worker.rb +117 -21
- data/lib/pgbus/queue_factory.rb +62 -0
- data/lib/pgbus/rate_counter.rb +81 -0
- data/lib/pgbus/recurring/schedule.rb +1 -1
- data/lib/pgbus/uniqueness.rb +169 -0
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +136 -2
- data/lib/pgbus.rb +9 -0
- metadata +31 -1
|
@@ -7,6 +7,8 @@ module Pgbus
|
|
|
7
7
|
class DataSource
|
|
8
8
|
def initialize(client: Pgbus.client)
|
|
9
9
|
@client = client
|
|
10
|
+
@last_throughput_snapshot = nil
|
|
11
|
+
@last_throughput_at = nil
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
# Dashboard summary
|
|
@@ -17,6 +19,8 @@ module Pgbus
|
|
|
17
19
|
dlq_suffix = Pgbus.configuration.dead_letter_queue_suffix
|
|
18
20
|
dlq_depth = queues.select { |q| q[:name].end_with?(dlq_suffix) }.sum { |q| q[:queue_length] }
|
|
19
21
|
|
|
22
|
+
throughput = compute_throughput(queues)
|
|
23
|
+
|
|
20
24
|
{
|
|
21
25
|
total_queues: queues.size,
|
|
22
26
|
total_depth: total_depth,
|
|
@@ -24,7 +28,8 @@ module Pgbus
|
|
|
24
28
|
active_processes: processes.count,
|
|
25
29
|
failed_count: failed_events_count,
|
|
26
30
|
dlq_depth: dlq_depth,
|
|
27
|
-
recurring_count: recurring_tasks_count
|
|
31
|
+
recurring_count: recurring_tasks_count,
|
|
32
|
+
throughput_rate: throughput
|
|
28
33
|
}
|
|
29
34
|
end
|
|
30
35
|
|
|
@@ -33,7 +38,10 @@ module Pgbus
|
|
|
33
38
|
# different connection lifecycle than the worker processes).
|
|
34
39
|
def queues_with_metrics
|
|
35
40
|
queue_names = connection.select_values("SELECT queue_name FROM pgmq.meta ORDER BY queue_name")
|
|
36
|
-
|
|
41
|
+
paused_queues = paused_queue_names
|
|
42
|
+
queue_names.map { |name| queue_metrics_via_sql(name) }.compact.map do |q|
|
|
43
|
+
q.merge(paused: paused_queues.include?(logical_queue_name(q[:name])))
|
|
44
|
+
end
|
|
37
45
|
rescue StandardError => e
|
|
38
46
|
Pgbus.logger.error { "[Pgbus::Web] Error fetching queue metrics: #{e.class}: #{e.message}" }
|
|
39
47
|
[]
|
|
@@ -52,6 +60,24 @@ module Pgbus
|
|
|
52
60
|
@client.purge_queue(name)
|
|
53
61
|
end
|
|
54
62
|
|
|
63
|
+
def pause_queue(name, reason: nil)
|
|
64
|
+
QueueState.pause!(logical_queue_name(name), reason: reason)
|
|
65
|
+
rescue StandardError => e
|
|
66
|
+
Pgbus.logger.error { "[Pgbus::Web] Error pausing queue #{name}: #{e.message}" }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def resume_queue(name)
|
|
70
|
+
QueueState.resume!(logical_queue_name(name))
|
|
71
|
+
rescue StandardError => e
|
|
72
|
+
Pgbus.logger.error { "[Pgbus::Web] Error resuming queue #{name}: #{e.message}" }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def queue_paused?(name)
|
|
76
|
+
QueueState.paused?(logical_queue_name(name))
|
|
77
|
+
rescue StandardError
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
55
81
|
# Jobs (messages in queue tables)
|
|
56
82
|
def jobs(queue_name: nil, page: 1, per_page: 25)
|
|
57
83
|
offset = (page - 1) * per_page
|
|
@@ -451,6 +477,75 @@ module Pgbus
|
|
|
451
477
|
0
|
|
452
478
|
end
|
|
453
479
|
|
|
480
|
+
# Outbox
|
|
481
|
+
def outbox_stats
|
|
482
|
+
{
|
|
483
|
+
unpublished: OutboxEntry.unpublished.count,
|
|
484
|
+
total: OutboxEntry.count,
|
|
485
|
+
oldest_unpublished_age: oldest_unpublished_age
|
|
486
|
+
}
|
|
487
|
+
rescue StandardError => e
|
|
488
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching outbox stats: #{e.message}" }
|
|
489
|
+
{ unpublished: 0, total: 0, oldest_unpublished_age: nil }
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def outbox_entries(page: 1, per_page: 25)
|
|
493
|
+
offset = (page - 1) * per_page
|
|
494
|
+
OutboxEntry.order(id: :desc).limit(per_page).offset(offset).to_a
|
|
495
|
+
rescue StandardError => e
|
|
496
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching outbox entries: #{e.message}" }
|
|
497
|
+
[]
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Job locks
|
|
501
|
+
def job_locks
|
|
502
|
+
JobLock.order(locked_at: :desc).limit(100).map do |lock|
|
|
503
|
+
{
|
|
504
|
+
lock_key: lock.lock_key,
|
|
505
|
+
job_class: lock.job_class,
|
|
506
|
+
job_id: lock.job_id,
|
|
507
|
+
state: lock.state,
|
|
508
|
+
owner_pid: lock.owner_pid,
|
|
509
|
+
owner_hostname: lock.owner_hostname,
|
|
510
|
+
locked_at: lock.locked_at,
|
|
511
|
+
expires_at: lock.expires_at,
|
|
512
|
+
age_seconds: lock.locked_at ? (Time.current - lock.locked_at).to_i : nil
|
|
513
|
+
}
|
|
514
|
+
end
|
|
515
|
+
rescue StandardError => e
|
|
516
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching job locks: #{e.message}" }
|
|
517
|
+
[]
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Job stats
|
|
521
|
+
def job_stats_summary(minutes: 60)
|
|
522
|
+
JobStat.summary(minutes: minutes)
|
|
523
|
+
rescue StandardError => e
|
|
524
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching job stats summary: #{e.message}" }
|
|
525
|
+
{ total: 0, success: 0, failed: 0, dead_lettered: 0, avg_duration_ms: 0, max_duration_ms: 0 }
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def job_throughput(minutes: 60)
|
|
529
|
+
JobStat.throughput(minutes: minutes).map { |time, count| { time: time, count: count } }
|
|
530
|
+
rescue StandardError => e
|
|
531
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching throughput: #{e.message}" }
|
|
532
|
+
[]
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def job_status_counts(minutes: 60)
|
|
536
|
+
JobStat.status_counts(minutes: minutes)
|
|
537
|
+
rescue StandardError => e
|
|
538
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching status counts: #{e.message}" }
|
|
539
|
+
{}
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def slowest_job_classes(limit: 10, minutes: 60)
|
|
543
|
+
JobStat.slowest_classes(limit: limit, minutes: minutes)
|
|
544
|
+
rescue StandardError => e
|
|
545
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching slowest classes: #{e.message}" }
|
|
546
|
+
[]
|
|
547
|
+
end
|
|
548
|
+
|
|
454
549
|
# Subscriber registry
|
|
455
550
|
def registered_subscribers
|
|
456
551
|
EventBus::Registry.instance.subscribers.map do |s|
|
|
@@ -571,6 +666,45 @@ module Pgbus
|
|
|
571
666
|
sanitized
|
|
572
667
|
end
|
|
573
668
|
|
|
669
|
+
def compute_throughput(queues)
|
|
670
|
+
current_totals = queues.sum { |q| q[:total_messages] }
|
|
671
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
672
|
+
|
|
673
|
+
if @last_throughput_snapshot && @last_throughput_at
|
|
674
|
+
elapsed = now - @last_throughput_at
|
|
675
|
+
delta = current_totals - @last_throughput_snapshot
|
|
676
|
+
rate = elapsed.positive? ? (delta / elapsed).round(1) : 0.0
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
@last_throughput_snapshot = current_totals
|
|
680
|
+
@last_throughput_at = now
|
|
681
|
+
|
|
682
|
+
rate || 0.0
|
|
683
|
+
rescue StandardError
|
|
684
|
+
0.0
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def logical_queue_name(name)
|
|
688
|
+
name
|
|
689
|
+
.delete_prefix("#{Pgbus.configuration.queue_prefix}_")
|
|
690
|
+
.sub(/_p\d+\z/, "")
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def paused_queue_names
|
|
694
|
+
QueueState.paused.pluck(:queue_name)
|
|
695
|
+
rescue StandardError
|
|
696
|
+
[]
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
def oldest_unpublished_age
|
|
700
|
+
oldest = OutboxEntry.unpublished.order(:id).pick(:created_at)
|
|
701
|
+
return nil unless oldest
|
|
702
|
+
|
|
703
|
+
(Time.now - oldest).to_i
|
|
704
|
+
rescue StandardError
|
|
705
|
+
nil
|
|
706
|
+
end
|
|
707
|
+
|
|
574
708
|
def parse_arguments(args)
|
|
575
709
|
case args
|
|
576
710
|
when Array then args
|
data/lib/pgbus.rb
CHANGED
|
@@ -9,6 +9,7 @@ module Pgbus
|
|
|
9
9
|
class QueueNotFoundError < Error; end
|
|
10
10
|
class DeadLetterError < Error; end
|
|
11
11
|
class ConcurrencyLimitExceeded < Error; end
|
|
12
|
+
class JobNotUnique < Error; end
|
|
12
13
|
class SchemaNotReady < Error; end
|
|
13
14
|
|
|
14
15
|
class << self
|
|
@@ -63,6 +64,14 @@ module Pgbus
|
|
|
63
64
|
@configuration = nil
|
|
64
65
|
end
|
|
65
66
|
|
|
67
|
+
# Discard the inherited PGMQ client after fork.
|
|
68
|
+
# Do NOT call close — the parent's @pgmq_mutex is in undefined
|
|
69
|
+
# state post-fork and attempting to acquire it can deadlock.
|
|
70
|
+
# The next call to Pgbus.client will lazily create a fresh one.
|
|
71
|
+
def reset_client!
|
|
72
|
+
@client = nil
|
|
73
|
+
end
|
|
74
|
+
|
|
66
75
|
def logger
|
|
67
76
|
configuration.logger
|
|
68
77
|
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.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -107,12 +107,16 @@ files:
|
|
|
107
107
|
- LICENSE.txt
|
|
108
108
|
- README.md
|
|
109
109
|
- Rakefile
|
|
110
|
+
- app/controllers/pgbus/api/insights_controller.rb
|
|
110
111
|
- app/controllers/pgbus/api/stats_controller.rb
|
|
111
112
|
- app/controllers/pgbus/application_controller.rb
|
|
112
113
|
- app/controllers/pgbus/dashboard_controller.rb
|
|
113
114
|
- app/controllers/pgbus/dead_letter_controller.rb
|
|
114
115
|
- app/controllers/pgbus/events_controller.rb
|
|
116
|
+
- app/controllers/pgbus/insights_controller.rb
|
|
115
117
|
- app/controllers/pgbus/jobs_controller.rb
|
|
118
|
+
- app/controllers/pgbus/locks_controller.rb
|
|
119
|
+
- app/controllers/pgbus/outbox_controller.rb
|
|
116
120
|
- app/controllers/pgbus/processes_controller.rb
|
|
117
121
|
- app/controllers/pgbus/queues_controller.rb
|
|
118
122
|
- app/controllers/pgbus/recurring_tasks_controller.rb
|
|
@@ -120,8 +124,12 @@ files:
|
|
|
120
124
|
- app/models/pgbus/application_record.rb
|
|
121
125
|
- app/models/pgbus/batch_entry.rb
|
|
122
126
|
- app/models/pgbus/blocked_execution.rb
|
|
127
|
+
- app/models/pgbus/job_lock.rb
|
|
128
|
+
- app/models/pgbus/job_stat.rb
|
|
129
|
+
- app/models/pgbus/outbox_entry.rb
|
|
123
130
|
- app/models/pgbus/process_entry.rb
|
|
124
131
|
- app/models/pgbus/processed_event.rb
|
|
132
|
+
- app/models/pgbus/queue_state.rb
|
|
125
133
|
- app/models/pgbus/recurring_execution.rb
|
|
126
134
|
- app/models/pgbus/recurring_task.rb
|
|
127
135
|
- app/models/pgbus/semaphore.rb
|
|
@@ -136,10 +144,13 @@ files:
|
|
|
136
144
|
- app/views/pgbus/dead_letter/show.html.erb
|
|
137
145
|
- app/views/pgbus/events/index.html.erb
|
|
138
146
|
- app/views/pgbus/events/show.html.erb
|
|
147
|
+
- app/views/pgbus/insights/show.html.erb
|
|
139
148
|
- app/views/pgbus/jobs/_enqueued_table.html.erb
|
|
140
149
|
- app/views/pgbus/jobs/_failed_table.html.erb
|
|
141
150
|
- app/views/pgbus/jobs/index.html.erb
|
|
142
151
|
- app/views/pgbus/jobs/show.html.erb
|
|
152
|
+
- app/views/pgbus/locks/index.html.erb
|
|
153
|
+
- app/views/pgbus/outbox/index.html.erb
|
|
143
154
|
- app/views/pgbus/processes/_processes_table.html.erb
|
|
144
155
|
- app/views/pgbus/processes/index.html.erb
|
|
145
156
|
- app/views/pgbus/queues/_queues_list.html.erb
|
|
@@ -151,8 +162,16 @@ files:
|
|
|
151
162
|
- config/routes.rb
|
|
152
163
|
- exe/pgbus
|
|
153
164
|
- lib/active_job/queue_adapters/pgbus_adapter.rb
|
|
165
|
+
- lib/generators/pgbus/add_job_locks_generator.rb
|
|
166
|
+
- lib/generators/pgbus/add_job_stats_generator.rb
|
|
167
|
+
- lib/generators/pgbus/add_outbox_generator.rb
|
|
168
|
+
- lib/generators/pgbus/add_queue_states_generator.rb
|
|
154
169
|
- lib/generators/pgbus/add_recurring_generator.rb
|
|
155
170
|
- lib/generators/pgbus/install_generator.rb
|
|
171
|
+
- lib/generators/pgbus/templates/add_job_locks.rb.erb
|
|
172
|
+
- lib/generators/pgbus/templates/add_job_stats.rb.erb
|
|
173
|
+
- lib/generators/pgbus/templates/add_outbox.rb.erb
|
|
174
|
+
- lib/generators/pgbus/templates/add_queue_states.rb.erb
|
|
156
175
|
- lib/generators/pgbus/templates/add_recurring_tables.rb.erb
|
|
157
176
|
- lib/generators/pgbus/templates/migration.rb.erb
|
|
158
177
|
- lib/generators/pgbus/templates/pgbus.yml.erb
|
|
@@ -164,6 +183,7 @@ files:
|
|
|
164
183
|
- lib/pgbus/active_job/adapter.rb
|
|
165
184
|
- lib/pgbus/active_job/executor.rb
|
|
166
185
|
- lib/pgbus/batch.rb
|
|
186
|
+
- lib/pgbus/circuit_breaker.rb
|
|
167
187
|
- lib/pgbus/cli.rb
|
|
168
188
|
- lib/pgbus/client.rb
|
|
169
189
|
- lib/pgbus/concurrency.rb
|
|
@@ -171,6 +191,7 @@ files:
|
|
|
171
191
|
- lib/pgbus/concurrency/semaphore.rb
|
|
172
192
|
- lib/pgbus/config_loader.rb
|
|
173
193
|
- lib/pgbus/configuration.rb
|
|
194
|
+
- lib/pgbus/dedup_cache.rb
|
|
174
195
|
- lib/pgbus/engine.rb
|
|
175
196
|
- lib/pgbus/event.rb
|
|
176
197
|
- lib/pgbus/event_bus/handler.rb
|
|
@@ -178,14 +199,22 @@ files:
|
|
|
178
199
|
- lib/pgbus/event_bus/registry.rb
|
|
179
200
|
- lib/pgbus/event_bus/subscriber.rb
|
|
180
201
|
- lib/pgbus/instrumentation.rb
|
|
202
|
+
- lib/pgbus/outbox.rb
|
|
203
|
+
- lib/pgbus/outbox/poller.rb
|
|
181
204
|
- lib/pgbus/pgmq_schema.rb
|
|
182
205
|
- lib/pgbus/pgmq_schema/pgmq_v1.11.0.sql
|
|
183
206
|
- lib/pgbus/process/consumer.rb
|
|
207
|
+
- lib/pgbus/process/consumer_priority.rb
|
|
184
208
|
- lib/pgbus/process/dispatcher.rb
|
|
185
209
|
- lib/pgbus/process/heartbeat.rb
|
|
210
|
+
- lib/pgbus/process/lifecycle.rb
|
|
211
|
+
- lib/pgbus/process/queue_lock.rb
|
|
186
212
|
- lib/pgbus/process/signal_handler.rb
|
|
187
213
|
- lib/pgbus/process/supervisor.rb
|
|
214
|
+
- lib/pgbus/process/wake_signal.rb
|
|
188
215
|
- lib/pgbus/process/worker.rb
|
|
216
|
+
- lib/pgbus/queue_factory.rb
|
|
217
|
+
- lib/pgbus/rate_counter.rb
|
|
189
218
|
- lib/pgbus/recurring/already_recorded.rb
|
|
190
219
|
- lib/pgbus/recurring/command_job.rb
|
|
191
220
|
- lib/pgbus/recurring/config_loader.rb
|
|
@@ -193,6 +222,7 @@ files:
|
|
|
193
222
|
- lib/pgbus/recurring/scheduler.rb
|
|
194
223
|
- lib/pgbus/recurring/task.rb
|
|
195
224
|
- lib/pgbus/serializer.rb
|
|
225
|
+
- lib/pgbus/uniqueness.rb
|
|
196
226
|
- lib/pgbus/version.rb
|
|
197
227
|
- lib/pgbus/web/authentication.rb
|
|
198
228
|
- lib/pgbus/web/data_source.rb
|