pgbus 0.6.7 → 0.6.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/pgbus/dashboard_controller.rb +4 -0
  3. data/app/controllers/pgbus/queues_controller.rb +1 -0
  4. data/app/views/pgbus/dashboard/_queue_health.html.erb +78 -0
  5. data/app/views/pgbus/dashboard/show.html.erb +2 -0
  6. data/app/views/pgbus/queues/show.html.erb +37 -0
  7. data/config/locales/da.yml +35 -4
  8. data/config/locales/de.yml +35 -4
  9. data/config/locales/en.yml +35 -4
  10. data/config/locales/es.yml +35 -4
  11. data/config/locales/fi.yml +35 -4
  12. data/config/locales/fr.yml +35 -4
  13. data/config/locales/it.yml +35 -4
  14. data/config/locales/ja.yml +35 -4
  15. data/config/locales/nb.yml +35 -4
  16. data/config/locales/nl.yml +35 -4
  17. data/config/locales/pt.yml +35 -4
  18. data/config/locales/sv.yml +35 -4
  19. data/lib/generators/pgbus/add_failed_events_index_generator.rb +4 -11
  20. data/lib/generators/pgbus/add_job_locks_generator.rb +4 -11
  21. data/lib/generators/pgbus/add_job_stats_generator.rb +4 -11
  22. data/lib/generators/pgbus/add_job_stats_latency_generator.rb +4 -11
  23. data/lib/generators/pgbus/add_job_stats_queue_index_generator.rb +4 -11
  24. data/lib/generators/pgbus/add_outbox_generator.rb +4 -11
  25. data/lib/generators/pgbus/add_presence_generator.rb +4 -11
  26. data/lib/generators/pgbus/add_queue_states_generator.rb +4 -11
  27. data/lib/generators/pgbus/add_recurring_generator.rb +4 -11
  28. data/lib/generators/pgbus/add_stream_stats_generator.rb +4 -11
  29. data/lib/generators/pgbus/install_generator.rb +4 -11
  30. data/lib/generators/pgbus/migrate_job_locks_generator.rb +4 -11
  31. data/lib/generators/pgbus/migration_path.rb +28 -0
  32. data/lib/generators/pgbus/templates/migration.rb.erb +6 -0
  33. data/lib/generators/pgbus/templates/tune_autovacuum.rb.erb +38 -0
  34. data/lib/generators/pgbus/tune_autovacuum_generator.rb +48 -0
  35. data/lib/generators/pgbus/upgrade_pgmq_generator.rb +4 -11
  36. data/lib/pgbus/autovacuum_tuning.rb +93 -0
  37. data/lib/pgbus/client.rb +13 -1
  38. data/lib/pgbus/engine.rb +1 -0
  39. data/lib/pgbus/generators/migration_detector.rb +38 -3
  40. data/lib/pgbus/version.rb +1 -1
  41. data/lib/pgbus/web/data_source.rb +133 -1
  42. data/lib/pgbus/web/metrics_serializer.rb +71 -2
  43. data/lib/tasks/pgbus_autovacuum.rake +40 -0
  44. metadata +7 -1
@@ -24,6 +24,7 @@ module Pgbus
24
24
  append_process_metrics(lines)
25
25
  append_summary_metrics(lines)
26
26
  append_stream_metrics(lines)
27
+ append_health_metrics(lines)
27
28
  "#{lines.join("\n")}\n"
28
29
  end
29
30
 
@@ -97,9 +98,41 @@ module Pgbus
97
98
  end
98
99
 
99
100
  def append_process_metrics(lines)
100
- count = @data_source.processes.count
101
+ procs = @data_source.processes
101
102
  gauge(lines, "pgbus_active_processes", "Number of active pgbus worker processes") do
102
- [[count]]
103
+ [[procs.count]]
104
+ end
105
+
106
+ workers = procs.select { |p| p[:kind] == "worker" && p[:metadata].is_a?(Hash) }
107
+ unless workers.empty?
108
+ gauge(lines, "pgbus_worker_pool_capacity", "Total thread/async pool capacity per worker") do
109
+ workers.filter_map do |w|
110
+ capacity = w[:metadata]["capacity"]
111
+ next unless capacity
112
+
113
+ [capacity, { pid: w[:pid], hostname: w[:hostname] }]
114
+ end
115
+ end
116
+
117
+ gauge(lines, "pgbus_worker_pool_busy", "Number of busy threads/slots per worker") do
118
+ workers.filter_map do |w|
119
+ busy = w[:metadata]["busy"]
120
+ next unless busy
121
+
122
+ [busy, { pid: w[:pid], hostname: w[:hostname] }]
123
+ end
124
+ end
125
+
126
+ gauge(lines, "pgbus_worker_pool_utilization", "Pool utilization ratio (busy / capacity)") do
127
+ workers.filter_map do |w|
128
+ capacity = w[:metadata]["capacity"].to_i
129
+ busy = w[:metadata]["busy"].to_i
130
+ next unless capacity.positive?
131
+
132
+ ratio = (busy.to_f / capacity).round(4)
133
+ [ratio, { pid: w[:pid], hostname: w[:hostname] }]
134
+ end
135
+ end
103
136
  end
104
137
  rescue StandardError => e
105
138
  Pgbus.logger.debug { "[Pgbus::Metrics] Error serializing process metrics: #{e.message}" }
@@ -141,6 +174,42 @@ module Pgbus
141
174
  Pgbus.logger.debug { "[Pgbus::Metrics] Error serializing stream metrics: #{e.message}" }
142
175
  end
143
176
 
177
+ def append_health_metrics(lines)
178
+ health = @data_source.queue_health_stats
179
+ return if health[:tables].empty? && health[:oldest_transaction_age_sec].nil?
180
+
181
+ tables = health[:tables]
182
+ unless tables.empty?
183
+ gauge(lines, "pgbus_table_dead_tuples", "Number of dead tuples in queue/archive table") do
184
+ tables.map { |t| [t[:dead_tuples], { table: t[:table], kind: t[:kind] }] }
185
+ end
186
+
187
+ gauge(lines, "pgbus_table_live_tuples", "Number of live tuples in queue/archive table") do
188
+ tables.map { |t| [t[:live_tuples], { table: t[:table], kind: t[:kind] }] }
189
+ end
190
+
191
+ gauge(lines, "pgbus_table_bloat_ratio", "Dead tuple ratio (dead / total) per table") do
192
+ tables.map { |t| [t[:bloat_ratio], { table: t[:table], kind: t[:kind] }] }
193
+ end
194
+
195
+ vacuum_tables = tables.select { |t| t[:last_vacuum_ago_sec] }
196
+ unless vacuum_tables.empty?
197
+ gauge(lines, "pgbus_table_last_vacuum_age_seconds", "Seconds since last vacuum") do
198
+ vacuum_tables.map { |t| [t[:last_vacuum_ago_sec], { table: t[:table], kind: t[:kind] }] }
199
+ end
200
+ end
201
+ end
202
+
203
+ if health[:oldest_transaction_age_sec]
204
+ gauge(lines, "pgbus_oldest_transaction_age_seconds",
205
+ "Age of the oldest open transaction (MVCC horizon pin risk)") do
206
+ [[health[:oldest_transaction_age_sec]]]
207
+ end
208
+ end
209
+ rescue StandardError => e
210
+ Pgbus.logger.debug { "[Pgbus::Metrics] Error serializing health metrics: #{e.message}" }
211
+ end
212
+
144
213
  # Emits a Prometheus gauge metric family. The block must return an array
145
214
  # of [value] or [value, { label: "val" }] pairs.
146
215
  def gauge(lines, name, help)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :pgbus do
4
+ desc "Apply autovacuum tuning to PGMQ queue/archive tables and high-churn pgbus tables"
5
+ task tune_autovacuum: :environment do
6
+ require "pgbus/autovacuum_tuning"
7
+
8
+ conn = Pgbus.configuration.connects_to ? Pgbus::BusRecord.connection : ActiveRecord::Base.connection
9
+
10
+ # Only run if pgmq schema exists (tables may not be created yet during
11
+ # initial setup — the install migration handles tuning itself).
12
+ pgmq_exists = conn.select_value(
13
+ "SELECT 1 FROM information_schema.schemata WHERE schema_name = 'pgmq'"
14
+ )
15
+
16
+ unless pgmq_exists
17
+ puts "[pgbus] PGMQ schema not found — skipping autovacuum tuning."
18
+ next
19
+ end
20
+
21
+ puts "[pgbus] Applying autovacuum tuning to PGMQ queue/archive tables..."
22
+ conn.execute(Pgbus::AutovacuumTuning.sql_for_all_queues)
23
+
24
+ puts "[pgbus] Applying autovacuum tuning to high-churn pgbus tables..."
25
+ conn.execute(Pgbus::AutovacuumTuning.sql_for_high_churn_tables)
26
+
27
+ puts "[pgbus] Autovacuum tuning complete."
28
+ end
29
+ end
30
+
31
+ # Reapply autovacuum settings after schema:load since Ruby-format schema.rb
32
+ # does not preserve ALTER TABLE ... SET (reloptions). This is a no-op if the
33
+ # tables don't exist yet (IF EXISTS guards in the SQL).
34
+ %w[db:schema:load db:schema:load:pgbus].each do |task_name|
35
+ next unless Rake::Task.task_defined?(task_name)
36
+
37
+ Rake::Task[task_name].enhance do
38
+ Rake::Task["pgbus:tune_autovacuum"].invoke if Rake::Task.task_defined?("pgbus:tune_autovacuum")
39
+ end
40
+ 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.6.7
4
+ version: 0.6.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -162,6 +162,7 @@ files:
162
162
  - app/models/pgbus/uniqueness_key.rb
163
163
  - app/views/layouts/pgbus/application.html.erb
164
164
  - app/views/pgbus/dashboard/_processes_table.html.erb
165
+ - app/views/pgbus/dashboard/_queue_health.html.erb
165
166
  - app/views/pgbus/dashboard/_queues_table.html.erb
166
167
  - app/views/pgbus/dashboard/_recent_failures.html.erb
167
168
  - app/views/pgbus/dashboard/_stats_cards.html.erb
@@ -214,6 +215,7 @@ files:
214
215
  - lib/generators/pgbus/add_stream_stats_generator.rb
215
216
  - lib/generators/pgbus/install_generator.rb
216
217
  - lib/generators/pgbus/migrate_job_locks_generator.rb
218
+ - lib/generators/pgbus/migration_path.rb
217
219
  - lib/generators/pgbus/templates/add_failed_events_unique_index.rb.erb
218
220
  - lib/generators/pgbus/templates/add_job_locks.rb.erb
219
221
  - lib/generators/pgbus/templates/add_job_stats.rb.erb
@@ -230,12 +232,15 @@ files:
230
232
  - lib/generators/pgbus/templates/pgbus.yml.erb
231
233
  - lib/generators/pgbus/templates/pgbus_binstub.erb
232
234
  - lib/generators/pgbus/templates/recurring.yml.erb
235
+ - lib/generators/pgbus/templates/tune_autovacuum.rb.erb
233
236
  - lib/generators/pgbus/templates/upgrade_pgmq.rb.erb
237
+ - lib/generators/pgbus/tune_autovacuum_generator.rb
234
238
  - lib/generators/pgbus/update_generator.rb
235
239
  - lib/generators/pgbus/upgrade_pgmq_generator.rb
236
240
  - lib/pgbus.rb
237
241
  - lib/pgbus/active_job/adapter.rb
238
242
  - lib/pgbus/active_job/executor.rb
243
+ - lib/pgbus/autovacuum_tuning.rb
239
244
  - lib/pgbus/batch.rb
240
245
  - lib/pgbus/bus_record.rb
241
246
  - lib/pgbus/circuit_breaker.rb
@@ -315,6 +320,7 @@ files:
315
320
  - lib/pgbus/web/streamer/registry.rb
316
321
  - lib/pgbus/web/streamer/stream_event_dispatcher.rb
317
322
  - lib/puma/plugin/pgbus_streams.rb
323
+ - lib/tasks/pgbus_autovacuum.rake
318
324
  - lib/tasks/pgbus_pgmq.rake
319
325
  - lib/tasks/pgbus_streams.rake
320
326
  homepage: https://github.com/mhenrixon/pgbus