pgbus 0.14.1 → 0.15.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +33 -0
  4. data/Rakefile +6 -1
  5. data/app/helpers/pgbus/application_helper.rb +7 -0
  6. data/app/models/pgbus/batch_entry.rb +78 -6
  7. data/app/models/pgbus/batch_execution.rb +28 -0
  8. data/app/models/pgbus/blocked_execution.rb +2 -2
  9. data/app/models/pgbus/uniqueness_key.rb +50 -4
  10. data/app/views/pgbus/batches/_batches_table.html.erb +3 -3
  11. data/app/views/pgbus/batches/show.html.erb +7 -7
  12. data/app/views/pgbus/dead_letter/show.html.erb +17 -0
  13. data/app/views/pgbus/events/_pending_table.html.erb +21 -0
  14. data/app/views/pgbus/jobs/show.html.erb +17 -0
  15. data/config/locales/da.yml +5 -2
  16. data/config/locales/de.yml +5 -2
  17. data/config/locales/en.yml +5 -2
  18. data/config/locales/es.yml +5 -2
  19. data/config/locales/fi.yml +5 -2
  20. data/config/locales/fr.yml +5 -2
  21. data/config/locales/it.yml +5 -2
  22. data/config/locales/ja.yml +5 -2
  23. data/config/locales/nb.yml +5 -2
  24. data/config/locales/nl.yml +5 -2
  25. data/config/locales/pt.yml +5 -2
  26. data/config/locales/sv.yml +5 -2
  27. data/lib/generators/pgbus/add_batch_callback_jobs_generator.rb +47 -0
  28. data/lib/generators/pgbus/add_batch_executions_generator.rb +44 -0
  29. data/lib/generators/pgbus/templates/add_batch_callback_jobs.rb.erb +13 -0
  30. data/lib/generators/pgbus/templates/add_batch_executions.rb.erb +50 -0
  31. data/lib/generators/pgbus/templates/initializer.rb.erb +2 -0
  32. data/lib/generators/pgbus/templates/migration.rb.erb +23 -2
  33. data/lib/pgbus/active_job/adapter.rb +146 -28
  34. data/lib/pgbus/active_job/batch_id.rb +48 -0
  35. data/lib/pgbus/active_job/current_attributes.rb +52 -0
  36. data/lib/pgbus/active_job/executor.rb +45 -9
  37. data/lib/pgbus/batch/sweep.rb +163 -0
  38. data/lib/pgbus/batch.rb +448 -63
  39. data/lib/pgbus/client/fair_read.rb +187 -0
  40. data/lib/pgbus/client.rb +221 -18
  41. data/lib/pgbus/concurrency/blocked_execution.rb +14 -1
  42. data/lib/pgbus/configuration.rb +81 -1
  43. data/lib/pgbus/current_attributes.rb +156 -0
  44. data/lib/pgbus/engine.rb +2 -0
  45. data/lib/pgbus/event.rb +7 -2
  46. data/lib/pgbus/event_bus/handler.rb +12 -2
  47. data/lib/pgbus/event_bus/publisher.rb +37 -2
  48. data/lib/pgbus/event_bus/subscriber.rb +4 -0
  49. data/lib/pgbus/fair_share.rb +110 -0
  50. data/lib/pgbus/generators/migration_detector.rb +30 -0
  51. data/lib/pgbus/instrumentation.rb +5 -0
  52. data/lib/pgbus/outbox/poller.rb +6 -7
  53. data/lib/pgbus/outbox.rb +8 -1
  54. data/lib/pgbus/process/consumer.rb +38 -1
  55. data/lib/pgbus/process/dispatcher.rb +41 -17
  56. data/lib/pgbus/process/worker.rb +41 -0
  57. data/lib/pgbus/recurring/schedule.rb +12 -1
  58. data/lib/pgbus/testing.rb +2 -1
  59. data/lib/pgbus/uniqueness.rb +35 -7
  60. data/lib/pgbus/version.rb +1 -1
  61. data/lib/pgbus/web/data_source.rb +47 -9
  62. data/lib/pgbus/web/job_context.rb +80 -0
  63. data/lib/pgbus.rb +2 -0
  64. metadata +13 -1
@@ -19,10 +19,10 @@ en:
19
19
  completed: Completed
20
20
  created_at: Created At
21
21
  details: Details
22
- discarded: Discarded
22
+ failed: Failed
23
23
  finished_at: Finished At
24
24
  not_found: Batch not found
25
- on_discard: On Discard
25
+ on_failure: On Failure
26
26
  on_finish: On Finish
27
27
  on_success: On Success
28
28
  progress: Progress
@@ -131,6 +131,7 @@ en:
131
131
  total_label: messages in dead letter queue
132
132
  show:
133
133
  back: Back to DLQ
134
+ context: Context
134
135
  discard: Discard
135
136
  discard_confirm: Permanently discard?
136
137
  headers: Headers
@@ -194,6 +195,7 @@ en:
194
195
  title: Events
195
196
  pending_table:
196
197
  arguments: Payload
198
+ context: Context
197
199
  discard: Discard
198
200
  discard_confirm: Discard this event? It will be archived.
199
201
  edit_payload: Edit & Retry
@@ -372,6 +374,7 @@ en:
372
374
  show:
373
375
  back: Back to Jobs
374
376
  backtrace: Backtrace
377
+ context: Context
375
378
  discard: Discard
376
379
  discard_confirm: Discard this job permanently?
377
380
  error_message: Error Message
@@ -19,10 +19,10 @@ es:
19
19
  completed: Completados
20
20
  created_at: Creado el
21
21
  details: Detalles
22
- discarded: Descartados
22
+ failed: Fallados
23
23
  finished_at: Finalizado el
24
24
  not_found: Lote no encontrado
25
- on_discard: Al descartar
25
+ on_failure: Al fallar
26
26
  on_finish: Al finalizar
27
27
  on_success: Al tener éxito
28
28
  progress: Progreso
@@ -131,6 +131,7 @@ es:
131
131
  total_label: mensajes en la cola de mensajes fallidos
132
132
  show:
133
133
  back: Volver a DLQ
134
+ context: Contexto
134
135
  discard: Descartar
135
136
  discard_confirm: "¿Descartar permanentemente?"
136
137
  headers: Encabezados
@@ -194,6 +195,7 @@ es:
194
195
  title: Eventos
195
196
  pending_table:
196
197
  arguments: Carga útil
198
+ context: Contexto
197
199
  discard: Descartar
198
200
  discard_confirm: "¿Descartar este evento? Será archivado."
199
201
  edit_payload: Editar y reintentar
@@ -372,6 +374,7 @@ es:
372
374
  show:
373
375
  back: Volver a Trabajos
374
376
  backtrace: Rastreo de pila
377
+ context: Contexto
375
378
  discard: Descartar
376
379
  discard_confirm: "¿Descartar este trabajo permanentemente?"
377
380
  error_message: Mensaje de error
@@ -19,10 +19,10 @@ fi:
19
19
  completed: Valmiit
20
20
  created_at: Luotu
21
21
  details: Tiedot
22
- discarded: Hylätyt
22
+ failed: Epäonnistuneet
23
23
  finished_at: Valmistunut
24
24
  not_found: Erää ei löytynyt
25
- on_discard: Hylkäyksessä
25
+ on_failure: Epäonnistumisessa
26
26
  on_finish: Valmistumisessa
27
27
  on_success: Onnistumisessa
28
28
  progress: Edistyminen
@@ -131,6 +131,7 @@ fi:
131
131
  total_label: viestiä dead letter -jonossa
132
132
  show:
133
133
  back: Takaisin DLQ:hon
134
+ context: Konteksti
134
135
  discard: Hylkää
135
136
  discard_confirm: Hylätään pysyvästi?
136
137
  headers: Otsikot
@@ -194,6 +195,7 @@ fi:
194
195
  title: Tapahtumat
195
196
  pending_table:
196
197
  arguments: Kuorma
198
+ context: Konteksti
197
199
  discard: Hylkää
198
200
  discard_confirm: Hylätäänkö tämä tapahtuma? Se arkistoidaan.
199
201
  edit_payload: Muokkaa ja yritä uudelleen
@@ -372,6 +374,7 @@ fi:
372
374
  show:
373
375
  back: Takaisin töihin
374
376
  backtrace: Takautumisketju
377
+ context: Konteksti
375
378
  discard: Hylkää
376
379
  discard_confirm: Hylkää tämä työ pysyvästi?
377
380
  error_message: Virheilmoitus
@@ -19,10 +19,10 @@ fr:
19
19
  completed: Terminées
20
20
  created_at: Créé le
21
21
  details: Détails
22
- discarded: Rejetées
22
+ failed: Échouées
23
23
  finished_at: Terminé le
24
24
  not_found: Lot non trouvé
25
- on_discard: Au rejet
25
+ on_failure: En cas d'échec
26
26
  on_finish: À la fin
27
27
  on_success: Au succès
28
28
  progress: Progression
@@ -131,6 +131,7 @@ fr:
131
131
  total_label: messages dans la file de lettres mortes
132
132
  show:
133
133
  back: Retour à la DLQ
134
+ context: Contexte
134
135
  discard: Ignorer
135
136
  discard_confirm: Ignorer définitivement ?
136
137
  headers: En-têtes
@@ -194,6 +195,7 @@ fr:
194
195
  title: Événements
195
196
  pending_table:
196
197
  arguments: Charge utile
198
+ context: Contexte
197
199
  discard: Jeter
198
200
  discard_confirm: Jeter cet événement ? Il sera archivé.
199
201
  edit_payload: Modifier & Réessayer
@@ -372,6 +374,7 @@ fr:
372
374
  show:
373
375
  back: Retour aux travaux
374
376
  backtrace: Trace de la pile
377
+ context: Contexte
375
378
  discard: Ignorer
376
379
  discard_confirm: Supprimer ce travail définitivement ?
377
380
  error_message: Message d'erreur
@@ -19,10 +19,10 @@ it:
19
19
  completed: Completati
20
20
  created_at: Creato il
21
21
  details: Dettagli
22
- discarded: Scartati
22
+ failed: Falliti
23
23
  finished_at: Terminato il
24
24
  not_found: Lotto non trovato
25
- on_discard: Allo scarto
25
+ on_failure: Al fallimento
26
26
  on_finish: Al termine
27
27
  on_success: Al successo
28
28
  progress: Progresso
@@ -131,6 +131,7 @@ it:
131
131
  total_label: messaggi nella coda dei messaggi non recapitati
132
132
  show:
133
133
  back: Torna a DLQ
134
+ context: Contesto
134
135
  discard: Scarta
135
136
  discard_confirm: Scartare definitivamente?
136
137
  headers: Intestazioni
@@ -194,6 +195,7 @@ it:
194
195
  title: Eventi
195
196
  pending_table:
196
197
  arguments: Payload
198
+ context: Contesto
197
199
  discard: Scarta
198
200
  discard_confirm: Scartare questo evento? Verrà archiviato.
199
201
  edit_payload: Modifica e riprova
@@ -372,6 +374,7 @@ it:
372
374
  show:
373
375
  back: Torna ai Lavori
374
376
  backtrace: Traccia dello stack
377
+ context: Contesto
375
378
  discard: Scarta
376
379
  discard_confirm: Scartare definitivamente questo lavoro?
377
380
  error_message: Messaggio di errore
@@ -19,10 +19,10 @@ ja:
19
19
  completed: 完了
20
20
  created_at: 作成日時
21
21
  details: 詳細
22
- discarded: 破棄済み
22
+ failed: 失敗
23
23
  finished_at: 完了日時
24
24
  not_found: バッチが見つかりません
25
- on_discard: 破棄時
25
+ on_failure: 失敗時
26
26
  on_finish: 完了時
27
27
  on_success: 成功時
28
28
  progress: 進捗
@@ -131,6 +131,7 @@ ja:
131
131
  total_label: デッドレターキュー内のメッセージ
132
132
  show:
133
133
  back: DLQに戻る
134
+ context: コンテキスト
134
135
  discard: 破棄
135
136
  discard_confirm: 完全に破棄しますか?
136
137
  headers: ヘッダー
@@ -194,6 +195,7 @@ ja:
194
195
  title: イベント
195
196
  pending_table:
196
197
  arguments: ペイロード
198
+ context: コンテキスト
197
199
  discard: 破棄
198
200
  discard_confirm: このイベントを破棄しますか?アーカイブされます。
199
201
  edit_payload: 編集して再試行
@@ -372,6 +374,7 @@ ja:
372
374
  show:
373
375
  back: ジョブに戻る
374
376
  backtrace: バックトレース
377
+ context: コンテキスト
375
378
  discard: 破棄
376
379
  discard_confirm: このジョブを完全に破棄しますか?
377
380
  error_message: エラーメッセージ
@@ -19,10 +19,10 @@ nb:
19
19
  completed: Fullført
20
20
  created_at: Opprettet den
21
21
  details: Detaljer
22
- discarded: Forkastet
22
+ failed: Mislyktes
23
23
  finished_at: Avsluttet den
24
24
  not_found: Gruppe ikke funnet
25
- on_discard: Ved forkastning
25
+ on_failure: Ved feil
26
26
  on_finish: Ved avslutning
27
27
  on_success: Ved suksess
28
28
  progress: Fremdrift
@@ -131,6 +131,7 @@ nb:
131
131
  total_label: meldinger i dead letter-kø
132
132
  show:
133
133
  back: Tilbake til DLQ
134
+ context: Kontekst
134
135
  discard: Forkast
135
136
  discard_confirm: Forkaste permanent?
136
137
  headers: Overskrifter
@@ -194,6 +195,7 @@ nb:
194
195
  title: Hendelser
195
196
  pending_table:
196
197
  arguments: Nyttelast
198
+ context: Kontekst
197
199
  discard: Forkast
198
200
  discard_confirm: Forkast denne hendelsen? Den vil bli arkivert.
199
201
  edit_payload: Rediger & prøv på nytt
@@ -372,6 +374,7 @@ nb:
372
374
  show:
373
375
  back: Tilbake til jobber
374
376
  backtrace: Tilbakekalling
377
+ context: Kontekst
375
378
  discard: Forkast
376
379
  discard_confirm: Slett denne jobben permanent?
377
380
  error_message: Feilmelding
@@ -19,10 +19,10 @@ nl:
19
19
  completed: Voltooid
20
20
  created_at: Aangemaakt op
21
21
  details: Details
22
- discarded: Verworpen
22
+ failed: Mislukt
23
23
  finished_at: Voltooid op
24
24
  not_found: Groep niet gevonden
25
- on_discard: Bij verwerping
25
+ on_failure: Bij mislukking
26
26
  on_finish: Bij voltooiing
27
27
  on_success: Bij succes
28
28
  progress: Voortgang
@@ -131,6 +131,7 @@ nl:
131
131
  total_label: berichten in dead letter-wachtrij
132
132
  show:
133
133
  back: Terug naar DLQ
134
+ context: Context
134
135
  discard: Verwerpen
135
136
  discard_confirm: Permanent verwerpen?
136
137
  headers: Headers
@@ -194,6 +195,7 @@ nl:
194
195
  title: Gebeurtenissen
195
196
  pending_table:
196
197
  arguments: Payload
198
+ context: Context
197
199
  discard: Verwijderen
198
200
  discard_confirm: Deze gebeurtenis verwijderen? Deze wordt gearchiveerd.
199
201
  edit_payload: Bewerken & Opnieuw Proberen
@@ -372,6 +374,7 @@ nl:
372
374
  show:
373
375
  back: Terug naar Taken
374
376
  backtrace: Stacktrace
377
+ context: Context
375
378
  discard: Verwijderen
376
379
  discard_confirm: Deze taak permanent verwijderen?
377
380
  error_message: Foutmelding
@@ -19,10 +19,10 @@ pt:
19
19
  completed: Concluídos
20
20
  created_at: Criado em
21
21
  details: Detalhes
22
- discarded: Descartados
22
+ failed: Falhados
23
23
  finished_at: Finalizado em
24
24
  not_found: Lote não encontrado
25
- on_discard: Ao descartar
25
+ on_failure: Ao falhar
26
26
  on_finish: Ao finalizar
27
27
  on_success: Ao ter sucesso
28
28
  progress: Progresso
@@ -131,6 +131,7 @@ pt:
131
131
  total_label: mensagens na fila de mensagens mortas
132
132
  show:
133
133
  back: Voltar para DLQ
134
+ context: Contexto
134
135
  discard: Descartar
135
136
  discard_confirm: Descartar permanentemente?
136
137
  headers: Cabeçalhos
@@ -194,6 +195,7 @@ pt:
194
195
  title: Eventos
195
196
  pending_table:
196
197
  arguments: Payload
198
+ context: Contexto
197
199
  discard: Descartar
198
200
  discard_confirm: Descartar este evento? Ele será arquivado.
199
201
  edit_payload: Editar e Tentar Novamente
@@ -372,6 +374,7 @@ pt:
372
374
  show:
373
375
  back: Voltar para Trabalhos
374
376
  backtrace: Rastreamento de Pilha
377
+ context: Contexto
375
378
  discard: Descartar
376
379
  discard_confirm: Descartar este trabalho permanentemente?
377
380
  error_message: Mensagem de Erro
@@ -19,10 +19,10 @@ sv:
19
19
  completed: Slutförda
20
20
  created_at: Skapad den
21
21
  details: Detaljer
22
- discarded: Kasserade
22
+ failed: Misslyckades
23
23
  finished_at: Avslutad den
24
24
  not_found: Grupp hittades inte
25
- on_discard: Vid kassering
25
+ on_failure: Vid misslyckande
26
26
  on_finish: Vid avslut
27
27
  on_success: Vid framgång
28
28
  progress: Framsteg
@@ -131,6 +131,7 @@ sv:
131
131
  total_label: meddelanden i dead letter-kö
132
132
  show:
133
133
  back: Tillbaka till DLQ
134
+ context: Kontext
134
135
  discard: Kassera
135
136
  discard_confirm: Kassera permanent?
136
137
  headers: Rubriker
@@ -194,6 +195,7 @@ sv:
194
195
  title: Händelser
195
196
  pending_table:
196
197
  arguments: Payload
198
+ context: Kontext
197
199
  discard: Kassera
198
200
  discard_confirm: Kassera denna händelse? Den kommer att arkiveras.
199
201
  edit_payload: Redigera & Försök igen
@@ -372,6 +374,7 @@ sv:
372
374
  show:
373
375
  back: Tillbaka till jobb
374
376
  backtrace: Backtrace
377
+ context: Kontext
375
378
  discard: Kassera
376
379
  discard_confirm: Kassera detta jobb permanent?
377
380
  error_message: Felmeddelande
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+ require_relative "migration_path"
6
+
7
+ module Pgbus
8
+ module Generators
9
+ class AddBatchCallbackJobsGenerator < Rails::Generators::Base
10
+ include ActiveRecord::Generators::Migration
11
+ include MigrationPath
12
+
13
+ source_root File.expand_path("templates", __dir__)
14
+
15
+ desc "Add jsonb callback-job columns to pgbus_batches (issue #415)"
16
+
17
+ class_option :database,
18
+ type: :string,
19
+ default: nil,
20
+ desc: "Use a separate database for pgbus tables (e.g. --database=pgbus)"
21
+
22
+ def create_migration_file
23
+ migration_template "add_batch_callback_jobs.rb.erb",
24
+ File.join(pgbus_migrate_path, "add_pgbus_batch_callback_jobs.rb")
25
+ end
26
+
27
+ def display_post_install
28
+ say ""
29
+ say "Pgbus configured batch callbacks installed!", :green
30
+ say ""
31
+ say "Next steps:"
32
+ say " 1. Run: rails db:migrate#{migrate_command_suffix}"
33
+ say " 2. Restart pgbus: bin/pgbus start"
34
+ say ""
35
+ say "You can now pass a configured ActiveJob instance to a batch:"
36
+ say " Pgbus::Batch.new(on_finish: ReportJob.new.set(queue: :critical, wait: 5.minutes))"
37
+ say ""
38
+ end
39
+
40
+ private
41
+
42
+ def migration_version
43
+ "[#{ActiveRecord::Migration.current_version}]"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+ require_relative "migration_path"
6
+
7
+ module Pgbus
8
+ module Generators
9
+ class AddBatchExecutionsGenerator < Rails::Generators::Base
10
+ include ActiveRecord::Generators::Migration
11
+ include MigrationPath
12
+
13
+ source_root File.expand_path("templates", __dir__)
14
+
15
+ desc "Add pgbus_batch_executions and rename batch failure columns (issue #414)"
16
+
17
+ class_option :database,
18
+ type: :string,
19
+ default: nil,
20
+ desc: "Use a separate database for pgbus tables (e.g. --database=pgbus)"
21
+
22
+ def create_migration_file
23
+ migration_template "add_batch_executions.rb.erb",
24
+ File.join(pgbus_migrate_path, "add_pgbus_batch_executions.rb")
25
+ end
26
+
27
+ def display_post_install
28
+ say ""
29
+ say "Pgbus batch execution-row tracking installed!", :green
30
+ say ""
31
+ say "Next steps:"
32
+ say " 1. Run: rails db:migrate#{migrate_command_suffix}"
33
+ say " 2. Restart pgbus: bin/pgbus start"
34
+ say ""
35
+ end
36
+
37
+ private
38
+
39
+ def migration_version
40
+ "[#{ActiveRecord::Migration.current_version}]"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ class AddPgbusBatchCallbackJobs < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ add_column :pgbus_batches, :on_finish_job, :jsonb unless column_exists?(:pgbus_batches, :on_finish_job)
4
+ add_column :pgbus_batches, :on_success_job, :jsonb unless column_exists?(:pgbus_batches, :on_success_job)
5
+ add_column :pgbus_batches, :on_failure_job, :jsonb unless column_exists?(:pgbus_batches, :on_failure_job)
6
+ end
7
+
8
+ def down
9
+ remove_column :pgbus_batches, :on_finish_job if column_exists?(:pgbus_batches, :on_finish_job)
10
+ remove_column :pgbus_batches, :on_success_job if column_exists?(:pgbus_batches, :on_success_job)
11
+ remove_column :pgbus_batches, :on_failure_job if column_exists?(:pgbus_batches, :on_failure_job)
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ class AddPgbusBatchExecutions < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ create_table :pgbus_batch_executions do |t|
4
+ t.string :batch_id, null: false
5
+ t.string :job_id, null: false
6
+ t.bigint :msg_id
7
+ t.string :queue_name
8
+ t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
9
+ end
10
+
11
+ add_index :pgbus_batch_executions, :job_id,
12
+ unique: true, name: "idx_pgbus_batch_executions_job_id"
13
+ add_index :pgbus_batch_executions, :batch_id,
14
+ name: "idx_pgbus_batch_executions_batch_id"
15
+ add_index :pgbus_batch_executions, :created_at,
16
+ where: "msg_id IS NULL",
17
+ name: "idx_pgbus_batch_executions_orphans"
18
+ add_foreign_key :pgbus_batch_executions, :pgbus_batches,
19
+ column: :batch_id, primary_key: :batch_id, on_delete: :cascade
20
+
21
+ if column_exists?(:pgbus_batches, :on_discard_class) && !column_exists?(:pgbus_batches, :on_failure_class)
22
+ rename_column :pgbus_batches, :on_discard_class, :on_failure_class
23
+ end
24
+
25
+ # Drain/restart pgbus workers after this migration: a process that still
26
+ # writes discarded_jobs will error once the column is gone.
27
+ if column_exists?(:pgbus_batches, :discarded_jobs)
28
+ if column_exists?(:pgbus_batches, :failed_jobs)
29
+ execute "UPDATE pgbus_batches SET failed_jobs = failed_jobs + discarded_jobs"
30
+ remove_column :pgbus_batches, :discarded_jobs
31
+ else
32
+ rename_column :pgbus_batches, :discarded_jobs, :failed_jobs
33
+ end
34
+ end
35
+ end
36
+
37
+ def down
38
+ unless column_exists?(:pgbus_batches, :discarded_jobs)
39
+ add_column :pgbus_batches, :discarded_jobs, :integer, null: false, default: 0
40
+ if column_exists?(:pgbus_batches, :failed_jobs)
41
+ execute "UPDATE pgbus_batches SET discarded_jobs = failed_jobs"
42
+ execute "UPDATE pgbus_batches SET failed_jobs = 0"
43
+ end
44
+ end
45
+ if column_exists?(:pgbus_batches, :on_failure_class) && !column_exists?(:pgbus_batches, :on_discard_class)
46
+ rename_column :pgbus_batches, :on_failure_class, :on_discard_class
47
+ end
48
+ drop_table :pgbus_batch_executions if table_exists?(:pgbus_batch_executions)
49
+ end
50
+ end
@@ -44,6 +44,8 @@ Pgbus.configure do |c|
44
44
 
45
45
  # --- Dispatcher (maintenance tasks) ------------------------------------
46
46
  # c.dispatch_interval = 1.0
47
+ # c.batch_retention = 7.days # finished-batch cleanup; nil disables
48
+ # c.batch_sweep_interval = 5.minutes # stalled-batch repair sweep
47
49
 
48
50
  # --- Realtime broadcast isolation (turbo-rails) ------------------------
49
51
  # Route turbo-rails' async render+broadcast jobs to a dedicated queue so a
@@ -104,12 +104,14 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
104
104
  t.string :description
105
105
  t.string :on_finish_class
106
106
  t.string :on_success_class
107
- t.string :on_discard_class
107
+ t.string :on_failure_class
108
+ t.jsonb :on_finish_job
109
+ t.jsonb :on_success_job
110
+ t.jsonb :on_failure_job
108
111
  t.jsonb :properties, default: {}
109
112
  t.integer :total_jobs, null: false, default: 0
110
113
  t.integer :completed_jobs, null: false, default: 0
111
114
  t.integer :failed_jobs, null: false, default: 0
112
- t.integer :discarded_jobs, null: false, default: 0
113
115
  t.string :status, null: false, default: "pending"
114
116
  t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
115
117
  t.datetime :finished_at
@@ -120,6 +122,24 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
120
122
  add_index :pgbus_batches, :status,
121
123
  name: "idx_pgbus_batches_status"
122
124
 
125
+ create_table :pgbus_batch_executions do |t|
126
+ t.string :batch_id, null: false
127
+ t.string :job_id, null: false
128
+ t.bigint :msg_id
129
+ t.string :queue_name
130
+ t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
131
+ end
132
+
133
+ add_index :pgbus_batch_executions, :job_id,
134
+ unique: true, name: "idx_pgbus_batch_executions_job_id"
135
+ add_index :pgbus_batch_executions, :batch_id,
136
+ name: "idx_pgbus_batch_executions_batch_id"
137
+ add_index :pgbus_batch_executions, :created_at,
138
+ where: "msg_id IS NULL",
139
+ name: "idx_pgbus_batch_executions_orphans"
140
+ add_foreign_key :pgbus_batch_executions, :pgbus_batches,
141
+ column: :batch_id, primary_key: :batch_id, on_delete: :cascade
142
+
123
143
  # Recurring task definitions (synced from config/recurring.yml)
124
144
  create_table :pgbus_recurring_tasks do |t|
125
145
  t.string :key, null: false
@@ -186,6 +206,7 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
186
206
  drop_table :pgbus_stream_queues
187
207
  drop_table :pgbus_recurring_executions
188
208
  drop_table :pgbus_recurring_tasks
209
+ drop_table :pgbus_batch_executions
189
210
  drop_table :pgbus_batches
190
211
  drop_table :pgbus_blocked_executions
191
212
  drop_table :pgbus_semaphores