pgbus 0.6.7 → 0.6.8
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/app/controllers/pgbus/dashboard_controller.rb +4 -0
- data/app/controllers/pgbus/queues_controller.rb +1 -0
- data/app/views/pgbus/dashboard/_queue_health.html.erb +78 -0
- data/app/views/pgbus/dashboard/show.html.erb +2 -0
- data/app/views/pgbus/queues/show.html.erb +37 -0
- data/config/locales/da.yml +35 -4
- data/config/locales/de.yml +35 -4
- data/config/locales/en.yml +35 -4
- data/config/locales/es.yml +35 -4
- data/config/locales/fi.yml +35 -4
- data/config/locales/fr.yml +35 -4
- data/config/locales/it.yml +35 -4
- data/config/locales/ja.yml +35 -4
- data/config/locales/nb.yml +35 -4
- data/config/locales/nl.yml +35 -4
- data/config/locales/pt.yml +35 -4
- data/config/locales/sv.yml +35 -4
- data/lib/generators/pgbus/templates/migration.rb.erb +6 -0
- data/lib/generators/pgbus/templates/tune_autovacuum.rb.erb +38 -0
- data/lib/generators/pgbus/tune_autovacuum_generator.rb +55 -0
- data/lib/pgbus/autovacuum_tuning.rb +93 -0
- data/lib/pgbus/client.rb +13 -1
- data/lib/pgbus/generators/migration_detector.rb +38 -3
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +133 -1
- data/lib/pgbus/web/metrics_serializer.rb +71 -2
- metadata +5 -1
data/config/locales/nb.yml
CHANGED
|
@@ -10,6 +10,28 @@ nb:
|
|
|
10
10
|
pid: PID
|
|
11
11
|
status: Status
|
|
12
12
|
title: Aktive prosesser
|
|
13
|
+
queue_health:
|
|
14
|
+
dead_tuples: Døde tupler
|
|
15
|
+
headers:
|
|
16
|
+
bloat: Bloat
|
|
17
|
+
dead: Døde
|
|
18
|
+
kind: Type
|
|
19
|
+
last_vacuum: Siste Vacuum
|
|
20
|
+
live: Levende
|
|
21
|
+
table: Tabell
|
|
22
|
+
kinds:
|
|
23
|
+
archive: Arkiv
|
|
24
|
+
queue: Kø
|
|
25
|
+
last_vacuum: Siste Vacuum
|
|
26
|
+
live: Levende
|
|
27
|
+
mvcc_horizon: MVCC-horisont
|
|
28
|
+
oldest_open_txn: Eldste åpne transaksjon
|
|
29
|
+
oldest_vacuum_ago: Eldste tabell-vacuum
|
|
30
|
+
tables_need_vacuum:
|
|
31
|
+
one: "%{count} tabell trenger vacuum"
|
|
32
|
+
other: "%{count} tabeller trenger vacuum"
|
|
33
|
+
title: Køhelse
|
|
34
|
+
worst_bloat: Verste Bloat
|
|
13
35
|
queues_table:
|
|
14
36
|
empty: Ingen køer funnet
|
|
15
37
|
headers:
|
|
@@ -337,6 +359,15 @@ nb:
|
|
|
337
359
|
resume: Gjenoppta
|
|
338
360
|
retry: Prøv igjen
|
|
339
361
|
retry_confirm: Tilbakestill synlighetstidsavbrudd og prøv igjen?
|
|
362
|
+
table_health:
|
|
363
|
+
headers:
|
|
364
|
+
bloat: Bloat
|
|
365
|
+
dead: Døde
|
|
366
|
+
last_vacuum: Siste Vacuum
|
|
367
|
+
live: Levende
|
|
368
|
+
table: Tabell
|
|
369
|
+
oldest_txn: 'Eldste åpne transaksjon:'
|
|
370
|
+
title: Tabellhelse
|
|
340
371
|
total: 'Totalt:'
|
|
341
372
|
visible: 'Synlig:'
|
|
342
373
|
recurring_tasks:
|
|
@@ -345,10 +376,6 @@ nb:
|
|
|
345
376
|
one: "%{count} oppgave konfigurert"
|
|
346
377
|
other: "%{count} oppgaver konfigurert"
|
|
347
378
|
title: Gjentakende oppgaver
|
|
348
|
-
toggle:
|
|
349
|
-
disabled: Oppgave deaktivert
|
|
350
|
-
enabled: Oppgave aktivert
|
|
351
|
-
failed: Kunne ikke endre oppgave
|
|
352
379
|
show:
|
|
353
380
|
back: Tilbake
|
|
354
381
|
configuration: Konfigurasjon
|
|
@@ -392,3 +419,7 @@ nb:
|
|
|
392
419
|
task: Oppgave
|
|
393
420
|
never: Aldri
|
|
394
421
|
run_now: Kjør nå
|
|
422
|
+
toggle:
|
|
423
|
+
disabled: Oppgave deaktivert
|
|
424
|
+
enabled: Oppgave aktivert
|
|
425
|
+
failed: Kunne ikke endre oppgave
|
data/config/locales/nl.yml
CHANGED
|
@@ -10,6 +10,28 @@ nl:
|
|
|
10
10
|
pid: PID
|
|
11
11
|
status: Status
|
|
12
12
|
title: Actieve processen
|
|
13
|
+
queue_health:
|
|
14
|
+
dead_tuples: Dode tupels
|
|
15
|
+
headers:
|
|
16
|
+
bloat: Bloat
|
|
17
|
+
dead: Dood
|
|
18
|
+
kind: Type
|
|
19
|
+
last_vacuum: Laatste Vacuum
|
|
20
|
+
live: Actief
|
|
21
|
+
table: Tabel
|
|
22
|
+
kinds:
|
|
23
|
+
archive: Archief
|
|
24
|
+
queue: Wachtrij
|
|
25
|
+
last_vacuum: Laatste Vacuum
|
|
26
|
+
live: Actief
|
|
27
|
+
mvcc_horizon: MVCC-horizon
|
|
28
|
+
oldest_open_txn: Oudste open transactie
|
|
29
|
+
oldest_vacuum_ago: Oudste tabel-vacuum
|
|
30
|
+
tables_need_vacuum:
|
|
31
|
+
one: "%{count} tabel heeft vacuum nodig"
|
|
32
|
+
other: "%{count} tabellen hebben vacuum nodig"
|
|
33
|
+
title: Wachtrijgezondheid
|
|
34
|
+
worst_bloat: Ergste Bloat
|
|
13
35
|
queues_table:
|
|
14
36
|
empty: Geen wachtrijen gevonden
|
|
15
37
|
headers:
|
|
@@ -337,6 +359,15 @@ nl:
|
|
|
337
359
|
resume: Hervatten
|
|
338
360
|
retry: Opnieuw proberen
|
|
339
361
|
retry_confirm: Zichtbaarheidstimeout resetten en opnieuw proberen?
|
|
362
|
+
table_health:
|
|
363
|
+
headers:
|
|
364
|
+
bloat: Bloat
|
|
365
|
+
dead: Dood
|
|
366
|
+
last_vacuum: Laatste Vacuum
|
|
367
|
+
live: Actief
|
|
368
|
+
table: Tabel
|
|
369
|
+
oldest_txn: 'Oudste open transactie:'
|
|
370
|
+
title: Tabelgezondheid
|
|
340
371
|
total: 'Totaal:'
|
|
341
372
|
visible: 'Zichtbaar:'
|
|
342
373
|
recurring_tasks:
|
|
@@ -345,10 +376,6 @@ nl:
|
|
|
345
376
|
one: "%{count} taak geconfigureerd"
|
|
346
377
|
other: "%{count} taken geconfigureerd"
|
|
347
378
|
title: Terugkerende taken
|
|
348
|
-
toggle:
|
|
349
|
-
disabled: Taak uitgeschakeld
|
|
350
|
-
enabled: Taak ingeschakeld
|
|
351
|
-
failed: Kon taak niet omschakelen
|
|
352
379
|
show:
|
|
353
380
|
back: Terug
|
|
354
381
|
configuration: Configuratie
|
|
@@ -392,3 +419,7 @@ nl:
|
|
|
392
419
|
task: Taak
|
|
393
420
|
never: Nooit
|
|
394
421
|
run_now: Nu uitvoeren
|
|
422
|
+
toggle:
|
|
423
|
+
disabled: Taak uitgeschakeld
|
|
424
|
+
enabled: Taak ingeschakeld
|
|
425
|
+
failed: Kon taak niet omschakelen
|
data/config/locales/pt.yml
CHANGED
|
@@ -10,6 +10,28 @@ pt:
|
|
|
10
10
|
pid: PID
|
|
11
11
|
status: Status
|
|
12
12
|
title: Processos Ativos
|
|
13
|
+
queue_health:
|
|
14
|
+
dead_tuples: Tuplas mortas
|
|
15
|
+
headers:
|
|
16
|
+
bloat: Bloat
|
|
17
|
+
dead: Mortas
|
|
18
|
+
kind: Tipo
|
|
19
|
+
last_vacuum: Último Vacuum
|
|
20
|
+
live: Ativas
|
|
21
|
+
table: Tabela
|
|
22
|
+
kinds:
|
|
23
|
+
archive: Arquivo
|
|
24
|
+
queue: Fila
|
|
25
|
+
last_vacuum: Último Vacuum
|
|
26
|
+
live: Ativas
|
|
27
|
+
mvcc_horizon: Horizonte MVCC
|
|
28
|
+
oldest_open_txn: Transação aberta mais antiga
|
|
29
|
+
oldest_vacuum_ago: Vacuum de tabela mais antigo
|
|
30
|
+
tables_need_vacuum:
|
|
31
|
+
one: "%{count} tabela precisa de vacuum"
|
|
32
|
+
other: "%{count} tabelas precisam de vacuum"
|
|
33
|
+
title: Saúde das Filas
|
|
34
|
+
worst_bloat: Pior Bloat
|
|
13
35
|
queues_table:
|
|
14
36
|
empty: Nenhuma fila encontrada
|
|
15
37
|
headers:
|
|
@@ -337,6 +359,15 @@ pt:
|
|
|
337
359
|
resume: Retomar
|
|
338
360
|
retry: Tentar novamente
|
|
339
361
|
retry_confirm: Redefinir tempo de visibilidade e tentar novamente?
|
|
362
|
+
table_health:
|
|
363
|
+
headers:
|
|
364
|
+
bloat: Bloat
|
|
365
|
+
dead: Mortas
|
|
366
|
+
last_vacuum: Último Vacuum
|
|
367
|
+
live: Ativas
|
|
368
|
+
table: Tabela
|
|
369
|
+
oldest_txn: 'Transação aberta mais antiga:'
|
|
370
|
+
title: Saúde das Tabelas
|
|
340
371
|
total: 'Total:'
|
|
341
372
|
visible: 'Visível:'
|
|
342
373
|
recurring_tasks:
|
|
@@ -345,10 +376,6 @@ pt:
|
|
|
345
376
|
one: "%{count} tarefa configurada"
|
|
346
377
|
other: "%{count} tarefas configuradas"
|
|
347
378
|
title: Tarefas Recorrentes
|
|
348
|
-
toggle:
|
|
349
|
-
disabled: Tarefa desativada
|
|
350
|
-
enabled: Tarefa ativada
|
|
351
|
-
failed: Falha ao alternar tarefa
|
|
352
379
|
show:
|
|
353
380
|
back: Voltar
|
|
354
381
|
configuration: Configuração
|
|
@@ -392,3 +419,7 @@ pt:
|
|
|
392
419
|
task: Tarefa
|
|
393
420
|
never: Nunca
|
|
394
421
|
run_now: Executar Agora
|
|
422
|
+
toggle:
|
|
423
|
+
disabled: Tarefa desativada
|
|
424
|
+
enabled: Tarefa ativada
|
|
425
|
+
failed: Falha ao alternar tarefa
|
data/config/locales/sv.yml
CHANGED
|
@@ -10,6 +10,28 @@ sv:
|
|
|
10
10
|
pid: PID
|
|
11
11
|
status: Status
|
|
12
12
|
title: Aktiva processer
|
|
13
|
+
queue_health:
|
|
14
|
+
dead_tuples: Döda tuplar
|
|
15
|
+
headers:
|
|
16
|
+
bloat: Bloat
|
|
17
|
+
dead: Döda
|
|
18
|
+
kind: Typ
|
|
19
|
+
last_vacuum: Senaste Vacuum
|
|
20
|
+
live: Levande
|
|
21
|
+
table: Tabell
|
|
22
|
+
kinds:
|
|
23
|
+
archive: Arkiv
|
|
24
|
+
queue: Kö
|
|
25
|
+
last_vacuum: Senaste Vacuum
|
|
26
|
+
live: Levande
|
|
27
|
+
mvcc_horizon: MVCC-horisont
|
|
28
|
+
oldest_open_txn: Äldsta öppna transaktion
|
|
29
|
+
oldest_vacuum_ago: Äldsta tabell-vacuum
|
|
30
|
+
tables_need_vacuum:
|
|
31
|
+
one: "%{count} tabell behöver vacuum"
|
|
32
|
+
other: "%{count} tabeller behöver vacuum"
|
|
33
|
+
title: Köhälsa
|
|
34
|
+
worst_bloat: Värsta Bloat
|
|
13
35
|
queues_table:
|
|
14
36
|
empty: Inga köer hittades
|
|
15
37
|
headers:
|
|
@@ -337,6 +359,15 @@ sv:
|
|
|
337
359
|
resume: Återuppta
|
|
338
360
|
retry: Försök igen
|
|
339
361
|
retry_confirm: Återställ synlighetstimeout och försök igen?
|
|
362
|
+
table_health:
|
|
363
|
+
headers:
|
|
364
|
+
bloat: Bloat
|
|
365
|
+
dead: Döda
|
|
366
|
+
last_vacuum: Senaste Vacuum
|
|
367
|
+
live: Levande
|
|
368
|
+
table: Tabell
|
|
369
|
+
oldest_txn: 'Äldsta öppna transaktion:'
|
|
370
|
+
title: Tabellhälsa
|
|
340
371
|
total: 'Totalt:'
|
|
341
372
|
visible: 'Synliga:'
|
|
342
373
|
recurring_tasks:
|
|
@@ -345,10 +376,6 @@ sv:
|
|
|
345
376
|
one: "%{count} uppgift konfigurerad"
|
|
346
377
|
other: "%{count} uppgifter konfigurerade"
|
|
347
378
|
title: Återkommande uppgifter
|
|
348
|
-
toggle:
|
|
349
|
-
disabled: Uppgift inaktiverad
|
|
350
|
-
enabled: Uppgift aktiverad
|
|
351
|
-
failed: Kunde inte ändra uppgift
|
|
352
379
|
show:
|
|
353
380
|
back: Tillbaka
|
|
354
381
|
configuration: Konfiguration
|
|
@@ -392,3 +419,7 @@ sv:
|
|
|
392
419
|
task: Uppgift
|
|
393
420
|
never: Aldrig
|
|
394
421
|
run_now: Kör nu
|
|
422
|
+
toggle:
|
|
423
|
+
disabled: Uppgift inaktiverad
|
|
424
|
+
enabled: Uppgift aktiverad
|
|
425
|
+
failed: Kunde inte ändra uppgift
|
|
@@ -150,6 +150,12 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
150
150
|
# Create default queues via PGMQ
|
|
151
151
|
execute "SELECT pgmq.create('pgbus_default')"
|
|
152
152
|
execute "SELECT pgmq.create('pgbus_default_dlq')"
|
|
153
|
+
|
|
154
|
+
# Tune autovacuum for queue/archive tables and high-churn pgbus tables.
|
|
155
|
+
# Default settings are too conservative for the insert/delete churn of
|
|
156
|
+
# queue processing and concurrency lock management.
|
|
157
|
+
execute Pgbus::AutovacuumTuning.sql_for_all_queues
|
|
158
|
+
execute Pgbus::AutovacuumTuning.sql_for_high_churn_tables
|
|
153
159
|
end
|
|
154
160
|
|
|
155
161
|
def down
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class TunePgbusAutovacuum < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
+
def up
|
|
3
|
+
# Apply aggressive autovacuum settings to all existing PGMQ queue and
|
|
4
|
+
# archive tables. Queue tables have very high insert/delete churn from
|
|
5
|
+
# the read→process→archive cycle; default autovacuum settings (vacuum
|
|
6
|
+
# at 20% dead tuples) allow dead tuple accumulation that bloats indexes
|
|
7
|
+
# and degrades lock acquisition times.
|
|
8
|
+
#
|
|
9
|
+
# New queues created after this migration automatically receive these
|
|
10
|
+
# settings via Pgbus::Client at queue creation time.
|
|
11
|
+
execute Pgbus::AutovacuumTuning.sql_for_all_queues
|
|
12
|
+
|
|
13
|
+
# Also tune pgbus-owned tables with high write churn:
|
|
14
|
+
# - pgbus_semaphores: rapid upsert+increment per job, periodic expiry
|
|
15
|
+
# - pgbus_uniqueness_keys: INSERT on enqueue, DELETE on completion
|
|
16
|
+
# - pgbus_processed_events: INSERT per event, bulk DELETE on TTL expiry
|
|
17
|
+
execute Pgbus::AutovacuumTuning.sql_for_high_churn_tables
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def down
|
|
21
|
+
# Reset to PostgreSQL defaults
|
|
22
|
+
execute <<~SQL
|
|
23
|
+
DO $$
|
|
24
|
+
DECLARE
|
|
25
|
+
q RECORD;
|
|
26
|
+
BEGIN
|
|
27
|
+
FOR q IN SELECT queue_name FROM pgmq.meta LOOP
|
|
28
|
+
EXECUTE format('ALTER TABLE pgmq.q_%I RESET (autovacuum_vacuum_scale_factor, autovacuum_vacuum_cost_delay, autovacuum_analyze_scale_factor)', q.queue_name);
|
|
29
|
+
EXECUTE format('ALTER TABLE pgmq.a_%I RESET (autovacuum_vacuum_scale_factor, autovacuum_vacuum_cost_delay, autovacuum_analyze_scale_factor)', q.queue_name);
|
|
30
|
+
END LOOP;
|
|
31
|
+
END $$;
|
|
32
|
+
SQL
|
|
33
|
+
|
|
34
|
+
Pgbus::AutovacuumTuning::HIGH_CHURN_TABLES.each do |table|
|
|
35
|
+
execute "ALTER TABLE #{table} RESET (autovacuum_vacuum_scale_factor, autovacuum_vacuum_cost_delay, autovacuum_analyze_scale_factor)"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module Pgbus
|
|
7
|
+
module Generators
|
|
8
|
+
class TuneAutovacuumGenerator < Rails::Generators::Base
|
|
9
|
+
include ActiveRecord::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
desc "Tune autovacuum settings on PGMQ queue and archive tables for optimal queue health"
|
|
14
|
+
|
|
15
|
+
class_option :database,
|
|
16
|
+
type: :string,
|
|
17
|
+
default: nil,
|
|
18
|
+
desc: "Use a separate database for pgbus tables (e.g. --database=pgbus)"
|
|
19
|
+
|
|
20
|
+
def create_migration_file
|
|
21
|
+
if separate_database?
|
|
22
|
+
migration_template "tune_autovacuum.rb.erb",
|
|
23
|
+
"db/pgbus_migrate/tune_pgbus_autovacuum.rb"
|
|
24
|
+
else
|
|
25
|
+
migration_template "tune_autovacuum.rb.erb",
|
|
26
|
+
"db/migrate/tune_pgbus_autovacuum.rb"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def display_post_install
|
|
31
|
+
say ""
|
|
32
|
+
say "Pgbus autovacuum tuning migration created!", :green
|
|
33
|
+
say ""
|
|
34
|
+
say "This migration applies aggressive autovacuum settings to all existing"
|
|
35
|
+
say "PGMQ queue and archive tables. New queues created at runtime will"
|
|
36
|
+
say "automatically receive these settings."
|
|
37
|
+
say ""
|
|
38
|
+
say "Next steps:"
|
|
39
|
+
say " 1. Run: rails db:migrate#{":#{options[:database]}" if separate_database?}"
|
|
40
|
+
say " 2. Restart pgbus: bin/pgbus start"
|
|
41
|
+
say ""
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def migration_version
|
|
47
|
+
"[#{ActiveRecord::Migration.current_version}]"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def separate_database?
|
|
51
|
+
options[:database].present?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgbus
|
|
4
|
+
# Shared autovacuum storage parameters for tables with high write churn.
|
|
5
|
+
#
|
|
6
|
+
# Queue tables (q_*) have high insert/delete churn: every read + archive
|
|
7
|
+
# cycle deletes from q_ and inserts into a_. Default autovacuum settings
|
|
8
|
+
# (vacuum at 20% dead tuples) are far too conservative — dead tuples
|
|
9
|
+
# accumulate, bloat B-tree indexes, and eventually degrade lock acquisition
|
|
10
|
+
# times. See: https://planetscale.com/blog/keeping-a-postgres-queue-healthy
|
|
11
|
+
#
|
|
12
|
+
# Several pgbus-owned tables share similar churn patterns:
|
|
13
|
+
# - pgbus_semaphores: rapid upsert+increment per job, periodic expiry
|
|
14
|
+
# - pgbus_uniqueness_keys: INSERT on enqueue, DELETE on completion
|
|
15
|
+
# - pgbus_processed_events: INSERT per event, bulk DELETE on TTL expiry
|
|
16
|
+
#
|
|
17
|
+
# Used by:
|
|
18
|
+
# - Client#ensure_single_queue (runtime, on queue creation)
|
|
19
|
+
# - CreatePgbusTables migration (fresh install)
|
|
20
|
+
# - TunePgbusAutovacuum migration (upgrade for existing installations)
|
|
21
|
+
module AutovacuumTuning
|
|
22
|
+
# Queue tables: very aggressive — high delete rate from read+archive.
|
|
23
|
+
QUEUE_SETTINGS = {
|
|
24
|
+
"autovacuum_vacuum_scale_factor" => "0.01",
|
|
25
|
+
"autovacuum_vacuum_cost_delay" => "2",
|
|
26
|
+
"autovacuum_analyze_scale_factor" => "0.05"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Archive tables: moderately aggressive — append-heavy with periodic purge.
|
|
30
|
+
ARCHIVE_SETTINGS = {
|
|
31
|
+
"autovacuum_vacuum_scale_factor" => "0.05",
|
|
32
|
+
"autovacuum_vacuum_cost_delay" => "5",
|
|
33
|
+
"autovacuum_analyze_scale_factor" => "0.05"
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
# High-churn pgbus tables: rapid INSERT/DELETE or upsert cycles.
|
|
37
|
+
# - semaphores: upsert + increment per job acquire, decrement on release, periodic expiry
|
|
38
|
+
# - uniqueness_keys: INSERT on enqueue, DELETE on job completion (fast lifecycle)
|
|
39
|
+
# - processed_events: INSERT per event handler, bulk DELETE on idempotency TTL expiry
|
|
40
|
+
HIGH_CHURN_SETTINGS = {
|
|
41
|
+
"autovacuum_vacuum_scale_factor" => "0.02",
|
|
42
|
+
"autovacuum_vacuum_cost_delay" => "2",
|
|
43
|
+
"autovacuum_analyze_scale_factor" => "0.05"
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
HIGH_CHURN_TABLES = %w[
|
|
47
|
+
pgbus_semaphores
|
|
48
|
+
pgbus_uniqueness_keys
|
|
49
|
+
pgbus_processed_events
|
|
50
|
+
].freeze
|
|
51
|
+
|
|
52
|
+
class << self
|
|
53
|
+
# Generate ALTER TABLE SQL for a single queue's tables.
|
|
54
|
+
def sql_for_queue(queue_name)
|
|
55
|
+
[
|
|
56
|
+
alter_table_sql("pgmq.q_#{queue_name}", QUEUE_SETTINGS),
|
|
57
|
+
alter_table_sql("pgmq.a_#{queue_name}", ARCHIVE_SETTINGS)
|
|
58
|
+
].join("\n")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Generate ALTER TABLE SQL for all queues discovered via pgmq.meta.
|
|
62
|
+
def sql_for_all_queues
|
|
63
|
+
<<~SQL
|
|
64
|
+
DO $$
|
|
65
|
+
DECLARE
|
|
66
|
+
q RECORD;
|
|
67
|
+
BEGIN
|
|
68
|
+
FOR q IN SELECT queue_name FROM pgmq.meta LOOP
|
|
69
|
+
EXECUTE format('ALTER TABLE pgmq.q_%I SET (#{settings_clause(QUEUE_SETTINGS)})', q.queue_name);
|
|
70
|
+
EXECUTE format('ALTER TABLE pgmq.a_%I SET (#{settings_clause(ARCHIVE_SETTINGS)})', q.queue_name);
|
|
71
|
+
END LOOP;
|
|
72
|
+
END $$;
|
|
73
|
+
SQL
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Generate ALTER TABLE SQL for pgbus-owned high-churn tables.
|
|
77
|
+
def sql_for_high_churn_tables
|
|
78
|
+
HIGH_CHURN_TABLES.map { |table| alter_table_sql(table, HIGH_CHURN_SETTINGS, if_exists: true) }.join("\n")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def alter_table_sql(table, settings, if_exists: false)
|
|
84
|
+
prefix = if_exists ? "ALTER TABLE IF EXISTS" : "ALTER TABLE"
|
|
85
|
+
"#{prefix} #{table} SET (#{settings_clause(settings)});"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def settings_clause(settings)
|
|
89
|
+
settings.map { |k, v| "#{k} = #{v}" }.join(", ")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/pgbus/client.rb
CHANGED
|
@@ -75,7 +75,10 @@ module Pgbus
|
|
|
75
75
|
return if @queues_created[dlq_name]
|
|
76
76
|
|
|
77
77
|
@queues_created.compute_if_absent(dlq_name) do
|
|
78
|
-
synchronized
|
|
78
|
+
synchronized do
|
|
79
|
+
@pgmq.create(dlq_name)
|
|
80
|
+
tune_autovacuum(dlq_name)
|
|
81
|
+
end
|
|
79
82
|
true
|
|
80
83
|
end
|
|
81
84
|
end
|
|
@@ -446,12 +449,21 @@ module Pgbus
|
|
|
446
449
|
@queues_created.compute_if_absent(full_name) do
|
|
447
450
|
synchronized do
|
|
448
451
|
@pgmq.create(full_name)
|
|
452
|
+
tune_autovacuum(full_name)
|
|
449
453
|
@pgmq.enable_notify_insert(full_name, throttle_interval_ms: NOTIFY_THROTTLE_MS) if config.listen_notify
|
|
450
454
|
end
|
|
451
455
|
true
|
|
452
456
|
end
|
|
453
457
|
end
|
|
454
458
|
|
|
459
|
+
def tune_autovacuum(queue_name)
|
|
460
|
+
with_raw_connection do |conn|
|
|
461
|
+
conn.exec(AutovacuumTuning.sql_for_queue(queue_name))
|
|
462
|
+
end
|
|
463
|
+
rescue StandardError => e
|
|
464
|
+
Pgbus.logger.debug { "[Pgbus::Client] Autovacuum tuning failed for #{queue_name}: #{e.message}" }
|
|
465
|
+
end
|
|
466
|
+
|
|
455
467
|
# Serialize PGMQ operations through a mutex when sharing a connection
|
|
456
468
|
# with ActiveRecord (Proc path). When pgmq-ruby owns its own connections
|
|
457
469
|
# (String/Hash path), the internal connection_pool handles concurrency.
|
|
@@ -73,7 +73,8 @@ module Pgbus
|
|
|
73
73
|
add_queue_states: "pgbus:add_queue_states",
|
|
74
74
|
add_outbox: "pgbus:add_outbox",
|
|
75
75
|
add_recurring: "pgbus:add_recurring",
|
|
76
|
-
add_failed_events_index: "pgbus:add_failed_events_index"
|
|
76
|
+
add_failed_events_index: "pgbus:add_failed_events_index",
|
|
77
|
+
tune_autovacuum: "pgbus:tune_autovacuum"
|
|
77
78
|
}.freeze
|
|
78
79
|
|
|
79
80
|
# Human-friendly description of each migration for the generator
|
|
@@ -88,7 +89,8 @@ module Pgbus
|
|
|
88
89
|
add_queue_states: "queue states table (pause/resume)",
|
|
89
90
|
add_outbox: "outbox entries table (transactional outbox)",
|
|
90
91
|
add_recurring: "recurring tasks + executions tables",
|
|
91
|
-
add_failed_events_index: "unique index on pgbus_failed_events (queue_name, msg_id)"
|
|
92
|
+
add_failed_events_index: "unique index on pgbus_failed_events (queue_name, msg_id)",
|
|
93
|
+
tune_autovacuum: "autovacuum tuning for PGMQ queue and archive tables"
|
|
92
94
|
}.freeze
|
|
93
95
|
|
|
94
96
|
def initialize(connection)
|
|
@@ -110,7 +112,8 @@ module Pgbus
|
|
|
110
112
|
*queue_states_migrations,
|
|
111
113
|
*outbox_migrations,
|
|
112
114
|
*recurring_migrations,
|
|
113
|
-
*failed_events_index_migrations
|
|
115
|
+
*failed_events_index_migrations,
|
|
116
|
+
*autovacuum_migrations
|
|
114
117
|
]
|
|
115
118
|
end
|
|
116
119
|
|
|
@@ -193,6 +196,15 @@ module Pgbus
|
|
|
193
196
|
[:add_failed_events_index]
|
|
194
197
|
end
|
|
195
198
|
|
|
199
|
+
# Autovacuum tuning: check if any PGMQ queue table already has
|
|
200
|
+
# custom autovacuum settings applied. If not, queue the migration.
|
|
201
|
+
def autovacuum_migrations
|
|
202
|
+
return [] unless pgmq_schema_exists?
|
|
203
|
+
return [] if autovacuum_already_tuned?
|
|
204
|
+
|
|
205
|
+
[:tune_autovacuum]
|
|
206
|
+
end
|
|
207
|
+
|
|
196
208
|
# --- schema probes -------------------------------------------------
|
|
197
209
|
|
|
198
210
|
def table_exists?(name)
|
|
@@ -212,6 +224,29 @@ module Pgbus
|
|
|
212
224
|
rescue StandardError
|
|
213
225
|
false
|
|
214
226
|
end
|
|
227
|
+
|
|
228
|
+
def pgmq_schema_exists?
|
|
229
|
+
result = connection.select_value("SELECT 1 FROM information_schema.schemata WHERE schema_name = 'pgmq'")
|
|
230
|
+
result.present?
|
|
231
|
+
rescue StandardError
|
|
232
|
+
false
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def autovacuum_already_tuned?
|
|
236
|
+
queue_name = connection.select_value("SELECT queue_name FROM pgmq.meta ORDER BY queue_name LIMIT 1")
|
|
237
|
+
return true unless queue_name # no queues = nothing to tune, skip
|
|
238
|
+
|
|
239
|
+
result = connection.select_value(<<~SQL)
|
|
240
|
+
SELECT reloptions::text LIKE '%autovacuum_vacuum_scale_factor%'
|
|
241
|
+
FROM pg_class
|
|
242
|
+
WHERE relname = 'q_#{queue_name}'
|
|
243
|
+
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'pgmq')
|
|
244
|
+
SQL
|
|
245
|
+
|
|
246
|
+
[true, "t"].include?(result)
|
|
247
|
+
rescue StandardError
|
|
248
|
+
true # if we can't tell, assume already tuned (safe default)
|
|
249
|
+
end
|
|
215
250
|
end
|
|
216
251
|
end
|
|
217
252
|
end
|
data/lib/pgbus/version.rb
CHANGED