pgbus 0.8.3 → 0.9.0

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.
@@ -590,6 +590,43 @@ module Pgbus
590
590
  []
591
591
  end
592
592
 
593
+ # Batches
594
+ def batches(limit: 100)
595
+ BatchEntry.order(created_at: :desc).limit(limit).map { |r| format_batch(r) }
596
+ rescue StandardError => e
597
+ Pgbus.logger.debug { "[Pgbus::Web] Error fetching batches: #{e.message}" }
598
+ []
599
+ end
600
+
601
+ def batch_detail(batch_id)
602
+ record = BatchEntry.find_by(batch_id: batch_id)
603
+ return nil unless record
604
+
605
+ format_batch(record).merge(
606
+ properties: record.properties,
607
+ on_finish_class: record.on_finish_class,
608
+ on_success_class: record.on_success_class,
609
+ on_discard_class: record.on_discard_class
610
+ )
611
+ rescue StandardError => e
612
+ Pgbus.logger.debug { "[Pgbus::Web] Error fetching batch #{batch_id}: #{e.message}" }
613
+ nil
614
+ end
615
+
616
+ def batches_count
617
+ BatchEntry.count
618
+ rescue StandardError => e
619
+ Pgbus.logger.debug { "[Pgbus::Web] Error counting batches: #{e.message}" }
620
+ 0
621
+ end
622
+
623
+ def active_batches_count
624
+ BatchEntry.where.not(status: "finished").count
625
+ rescue StandardError => e
626
+ Pgbus.logger.debug { "[Pgbus::Web] Error counting active batches: #{e.message}" }
627
+ 0
628
+ end
629
+
593
630
  # Job stats
594
631
  def job_stats_summary(minutes: 60)
595
632
  JobStat.summary(minutes: minutes)
@@ -874,6 +911,33 @@ module Pgbus
874
911
  []
875
912
  end
876
913
 
914
+ # NOTIFY throttle status for all queues with notifications enabled.
915
+ # Returns an array of hashes: { queue_name:, throttle_interval_ms:, last_notified_at: }
916
+ def notify_throttles
917
+ @client.list_notify_insert_throttles.map do |throttle|
918
+ {
919
+ queue_name: throttle.queue_name,
920
+ throttle_interval_ms: throttle.throttle_interval_ms,
921
+ last_notified_at: throttle.last_notified_at
922
+ }
923
+ end
924
+ rescue StandardError => e
925
+ Pgbus.logger.debug { "[Pgbus::Web] Error fetching notify throttles: #{e.message}" }
926
+ []
927
+ end
928
+
929
+ # FIFO group head sampling for a specific queue.
930
+ # Returns the oldest visible message from each distinct group (up to qty).
931
+ # Useful for detecting head-of-line stalls in multi-tenant queues.
932
+ def queue_group_heads(queue_name, qty: 20)
933
+ logical = logical_queue_name(queue_name)
934
+ messages = @client.read_grouped_head(logical, qty: qty) || []
935
+ messages.map { |m| format_pgmq_message(m, queue_name) }
936
+ rescue StandardError => e
937
+ Pgbus.logger.debug { "[Pgbus::Web] Error fetching group heads for #{queue_name}: #{e.message}" }
938
+ []
939
+ end
940
+
877
941
  private
878
942
 
879
943
  def connection
@@ -1114,6 +1178,19 @@ module Pgbus
1114
1178
  }
1115
1179
  end
1116
1180
 
1181
+ def format_pgmq_message(msg, queue_name)
1182
+ {
1183
+ msg_id: msg.msg_id.to_i,
1184
+ read_ct: msg.read_ct.to_i,
1185
+ enqueued_at: msg.enqueued_at,
1186
+ last_read_at: msg.respond_to?(:last_read_at) ? msg.last_read_at : nil,
1187
+ vt: msg.respond_to?(:vt) ? msg.vt : nil,
1188
+ message: msg.message,
1189
+ headers: msg.headers,
1190
+ queue_name: queue_name
1191
+ }
1192
+ end
1193
+
1117
1194
  def format_process(row)
1118
1195
  heartbeat = row["last_heartbeat_at"]
1119
1196
  heartbeat_time = heartbeat.is_a?(String) ? Time.parse(heartbeat) : heartbeat
@@ -1131,6 +1208,25 @@ module Pgbus
1131
1208
  }
1132
1209
  end
1133
1210
 
1211
+ def format_batch(record)
1212
+ total = record.total_jobs
1213
+ done = record.completed_jobs + record.discarded_jobs
1214
+ pct = total.positive? ? ((done * 100) / total) : 100
1215
+
1216
+ {
1217
+ batch_id: record.batch_id,
1218
+ description: record.description,
1219
+ status: record.status,
1220
+ total_jobs: total,
1221
+ completed_jobs: record.completed_jobs,
1222
+ discarded_jobs: record.discarded_jobs,
1223
+ failed_jobs: record.failed_jobs,
1224
+ progress_pct: pct,
1225
+ created_at: record.created_at,
1226
+ finished_at: record.finished_at
1227
+ }
1228
+ end
1229
+
1134
1230
  def sanitize_name(name)
1135
1231
  QueueNameValidator.sanitize!(name)
1136
1232
  end
@@ -145,7 +145,7 @@ module Pgbus
145
145
 
146
146
  def build_pg_connection
147
147
  require "pg" unless defined?(::PG::Connection)
148
- opts = @config.connection_options
148
+ opts = @config.streams_connection_options
149
149
  case opts
150
150
  when String then ::PG.connect(opts)
151
151
  when Hash then ::PG.connect(**opts)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :pgbus do
4
+ namespace :queues do
5
+ desc "Create FIFO indexes on all pgbus queues (required for grouped reads)"
6
+ task fifo_indexes: :environment do
7
+ puts "Creating FIFO indexes on all pgbus queues..."
8
+ Pgbus.client.create_fifo_indexes_all
9
+ puts "Done. FIFO indexes created on all queues."
10
+ end
11
+
12
+ desc "Create FIFO index on a specific queue (QUEUE=name)"
13
+ task fifo_index: :environment do
14
+ queue = ENV.fetch("QUEUE") do
15
+ abort "Usage: rake pgbus:queues:fifo_index QUEUE=<queue_name>"
16
+ end
17
+ puts "Creating FIFO index on queue '#{queue}'..."
18
+ Pgbus.client.create_fifo_index(queue)
19
+ puts "Done."
20
+ end
21
+ end
22
+
23
+ namespace :archives do
24
+ desc "Convert a queue's archive table to pg_partman-managed partitions (QUEUE=name)"
25
+ task partition: :environment do
26
+ queue = ENV.fetch("QUEUE") do
27
+ abort "Usage: rake pgbus:archives:partition QUEUE=<queue_name> " \
28
+ "[INTERVAL=10000] [RETENTION=100000] [LEADING_PARTITION=10]"
29
+ end
30
+ interval = ENV.fetch("INTERVAL", "10000")
31
+ retention = ENV.fetch("RETENTION", "100000")
32
+ leading_raw = ENV.fetch("LEADING_PARTITION", "10")
33
+ leading = begin
34
+ Integer(leading_raw, 10)
35
+ rescue ArgumentError, TypeError
36
+ abort "LEADING_PARTITION must be a positive integer, got #{leading_raw.inspect}"
37
+ end
38
+ abort "LEADING_PARTITION must be a positive integer" if leading <= 0
39
+
40
+ puts "Converting archive table for queue '#{queue}' to partitioned..."
41
+ puts " Partition interval: #{interval}"
42
+ puts " Retention interval: #{retention}"
43
+ puts " Leading partitions: #{leading}"
44
+
45
+ Pgbus.client.convert_archive_partitioned(
46
+ queue,
47
+ partition_interval: interval,
48
+ retention_interval: retention,
49
+ leading_partition: leading
50
+ )
51
+ puts "Done. Archive table is now partitioned."
52
+ end
53
+ end
54
+ 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.8.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -63,14 +63,14 @@ dependencies:
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: '0.5'
66
+ version: 0.7.0
67
67
  type: :runtime
68
68
  prerelease: false
69
69
  version_requirements: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '0.5'
73
+ version: 0.7.0
74
74
  - !ruby/object:Gem::Dependency
75
75
  name: railties
76
76
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +126,7 @@ files:
126
126
  - app/controllers/pgbus/api/metrics_controller.rb
127
127
  - app/controllers/pgbus/api/stats_controller.rb
128
128
  - app/controllers/pgbus/application_controller.rb
129
+ - app/controllers/pgbus/batches_controller.rb
129
130
  - app/controllers/pgbus/dashboard_controller.rb
130
131
  - app/controllers/pgbus/dead_letter_controller.rb
131
132
  - app/controllers/pgbus/events_controller.rb
@@ -161,6 +162,9 @@ files:
161
162
  - app/models/pgbus/stream_stat.rb
162
163
  - app/models/pgbus/uniqueness_key.rb
163
164
  - app/views/layouts/pgbus/application.html.erb
165
+ - app/views/pgbus/batches/_batches_table.html.erb
166
+ - app/views/pgbus/batches/index.html.erb
167
+ - app/views/pgbus/batches/show.html.erb
164
168
  - app/views/pgbus/dashboard/_processes_table.html.erb
165
169
  - app/views/pgbus/dashboard/_queue_health.html.erb
166
170
  - app/views/pgbus/dashboard/_queues_table.html.erb
@@ -343,6 +347,7 @@ files:
343
347
  - lib/puma/plugin/pgbus_streams.rb
344
348
  - lib/tasks/pgbus_autovacuum.rake
345
349
  - lib/tasks/pgbus_pgmq.rake
350
+ - lib/tasks/pgbus_queues.rake
346
351
  - lib/tasks/pgbus_streams.rake
347
352
  homepage: https://github.com/mhenrixon/pgbus
348
353
  licenses: