pgbus 0.18.2 → 0.19.1
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/CHANGELOG.md +345 -142
- data/README.md +49 -0
- data/app/controllers/pgbus/deploy_tasks_controller.rb +38 -0
- data/app/controllers/pgbus/frontends_controller.rb +10 -8
- data/app/helpers/pgbus/application_helper.rb +4 -3
- data/app/helpers/pgbus/deploy_tasks_helper.rb +77 -0
- data/app/helpers/pgbus/processes_helper.rb +1 -1
- data/app/models/pgbus/deploy_task.rb +104 -0
- data/app/models/pgbus/deploy_task_run.rb +45 -0
- data/app/models/pgbus/job_stat.rb +4 -0
- data/app/models/pgbus/processed_event.rb +3 -1
- data/app/models/pgbus/stream_queue.rb +34 -3
- data/app/models/pgbus/stream_stat.rb +3 -0
- data/app/views/layouts/pgbus/application.html.erb +2 -0
- data/app/views/pgbus/deploy_tasks/_rollover_banner.html.erb +25 -0
- data/app/views/pgbus/deploy_tasks/_tasks_table.html.erb +71 -0
- data/app/views/pgbus/deploy_tasks/index.html.erb +6 -0
- data/app/views/pgbus/deploy_tasks/show.html.erb +134 -0
- data/config/i18n-tasks.yml +2 -0
- data/config/locales/da.yml +97 -0
- data/config/locales/de.yml +97 -0
- data/config/locales/en.yml +97 -0
- data/config/locales/es.yml +97 -0
- data/config/locales/fi.yml +97 -0
- data/config/locales/fr.yml +97 -0
- data/config/locales/it.yml +97 -0
- data/config/locales/ja.yml +97 -0
- data/config/locales/nb.yml +97 -0
- data/config/locales/nl.yml +97 -0
- data/config/locales/pt.yml +97 -0
- data/config/locales/sv.yml +97 -0
- data/config/routes.rb +7 -0
- data/lib/generators/pgbus/add_deploy_tasks_generator.rb +51 -0
- data/lib/generators/pgbus/templates/add_deploy_tasks_tables.rb.erb +43 -0
- data/lib/generators/pgbus/templates/deploy_tasks.yml.erb +42 -0
- data/lib/generators/pgbus/templates/migration.rb.erb +43 -0
- data/lib/pgbus/active_job/current_attributes.rb +3 -1
- data/lib/pgbus/batch.rb +17 -9
- data/lib/pgbus/cli.rb +3 -0
- data/lib/pgbus/client/read_after.rb +10 -1
- data/lib/pgbus/client.rb +54 -9
- data/lib/pgbus/concurrency.rb +3 -0
- data/lib/pgbus/configuration/deploy_settings.rb +46 -0
- data/lib/pgbus/configuration.rb +87 -9
- data/lib/pgbus/current_attributes.rb +3 -1
- data/lib/pgbus/dedup_cache.rb +15 -7
- data/lib/pgbus/deploy/revision.rb +59 -0
- data/lib/pgbus/deploy/rollover.rb +65 -0
- data/lib/pgbus/deploy/runner.rb +259 -0
- data/lib/pgbus/deploy/task.rb +105 -0
- data/lib/pgbus/engine.rb +28 -0
- data/lib/pgbus/event_bus/handler.rb +16 -4
- data/lib/pgbus/execution_pools/async_pool.rb +23 -2
- data/lib/pgbus/execution_pools/thread_pool.rb +22 -0
- data/lib/pgbus/execution_pools.rb +26 -0
- data/lib/pgbus/generators/migration_detector.rb +11 -0
- data/lib/pgbus/instrumentation.rb +3 -0
- data/lib/pgbus/integrations/appsignal/probe.rb +17 -9
- data/lib/pgbus/integrations/appsignal/subscriber.rb +30 -18
- data/lib/pgbus/integrations/appsignal.rb +26 -16
- data/lib/pgbus/mcp/rack_app.rb +1 -1
- data/lib/pgbus/mcp/server.rb +2 -1
- data/lib/pgbus/mcp/tools/deploy_tasks_tool.rb +86 -0
- data/lib/pgbus/mcp.rb +1 -0
- data/lib/pgbus/metrics/subscriber.rb +35 -21
- data/lib/pgbus/outbox/poller.rb +1 -0
- data/lib/pgbus/process/consumer.rb +2 -1
- data/lib/pgbus/process/consumer_priority.rb +1 -1
- data/lib/pgbus/process/dispatcher.rb +18 -1
- data/lib/pgbus/process/heartbeat.rb +7 -2
- data/lib/pgbus/process/lifecycle.rb +5 -5
- data/lib/pgbus/process/notify_listener.rb +32 -18
- data/lib/pgbus/process/supervisor.rb +35 -3
- data/lib/pgbus/process/wake_pipe.rb +4 -1
- data/lib/pgbus/process/worker.rb +5 -2
- data/lib/pgbus/recurring/scheduler.rb +54 -20
- data/lib/pgbus/retry_backoff.rb +3 -0
- data/lib/pgbus/shareable.rb +70 -0
- data/lib/pgbus/streams/broadcastable_override.rb +3 -0
- data/lib/pgbus/streams/presence.rb +6 -7
- data/lib/pgbus/streams.rb +38 -27
- data/lib/pgbus/testing.rb +19 -3
- data/lib/pgbus/uniqueness.rb +3 -0
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/visibility_heartbeat.rb +51 -34
- data/lib/pgbus/web/authentication.rb +19 -2
- data/lib/pgbus/web/data_source/deploy_task_list.rb +114 -0
- data/lib/pgbus/web/data_source/list_counts.rb +10 -7
- data/lib/pgbus/web/data_source.rb +1 -0
- data/lib/pgbus/web/deploy_task_state.rb +51 -0
- data/lib/pgbus/web/health_server.rb +4 -1
- data/lib/pgbus/web/process_activity.rb +1 -1
- data/lib/pgbus/web/streamer/heartbeat.rb +4 -1
- data/lib/pgbus/web/streamer/hub_client.rb +4 -1
- data/lib/pgbus/web/streamer/listener.rb +10 -1
- data/lib/pgbus/web/streamer/master_hub.rb +11 -5
- data/lib/pgbus/web/streamer/master_hub_boot.rb +3 -1
- data/lib/pgbus/web/streamer/outbound_pump.rb +4 -1
- data/lib/pgbus/web/streamer/peer_close_watcher.rb +4 -1
- data/lib/pgbus/web/streamer/stream_event_dispatcher.rb +13 -1
- data/lib/pgbus.rb +92 -30
- data/sig/pgbus/active_job/adapter.rbs +57 -0
- data/sig/pgbus/client.rbs +78 -0
- data/sig/pgbus/configuration.rbs +326 -0
- data/sig/pgbus/streams/stream.rbs +25 -0
- data/sig/pgbus/streams.rbs +22 -0
- data/sig/pgbus.rbs +73 -0
- metadata +27 -1
data/config/locales/nl.yml
CHANGED
|
@@ -236,6 +236,102 @@ nl:
|
|
|
236
236
|
payload: Inhoud
|
|
237
237
|
retry: Opnieuw proberen
|
|
238
238
|
title: 'DLQ Bericht #%{id}'
|
|
239
|
+
deploy_tasks:
|
|
240
|
+
banner:
|
|
241
|
+
heading: Uitrol
|
|
242
|
+
lagging_html:
|
|
243
|
+
one: 'Wacht op uitrol: 1 proces draait nog niet op <code>%{revision}</code>.'
|
|
244
|
+
other: 'Wacht op uitrol: %{count} processen draaien nog niet op <code>%{revision}</code>.'
|
|
245
|
+
lagging_more: "…en nog %{count}"
|
|
246
|
+
lagging_process: "%{kind}@%{hostname} pid %{pid} op %{revision}"
|
|
247
|
+
live_count: "%{count} op %{revision}"
|
|
248
|
+
live_heading: 'Actieve processen:'
|
|
249
|
+
no_revision_hint_html: Stel <code>config.app_revision</code> in, of een van <code>PGBUS_REVISION</code>, <code>DASH_VERSION</code> of <code>KAMAL_VERSION</code>, of lever een <code>REVISION</code>-bestand mee. Nu uitvoeren werkt nog steeds.
|
|
250
|
+
no_revision_html: 'App-revisie niet geconfigureerd: deploy-taken wachten tot die er is.'
|
|
251
|
+
no_revision_label: geen revisie
|
|
252
|
+
no_scheduler_html: 'Er draait geen scheduler: deploy-taken worden alleen door de scheduler gestart. Dit oordeel gebruikt de eigen revisie van het dashboard.'
|
|
253
|
+
no_workers_html: 'Wacht op uitrol: nog geen worker op <code>%{revision}</code>.'
|
|
254
|
+
ready_html: 'Uitgerold: elk actief proces draait <code>%{revision}</code>. Klaargezette taken draaien bij de volgende tick van de scheduler.'
|
|
255
|
+
unavailable_html: De uitrolstatus kon niet worden gelezen.
|
|
256
|
+
default_queue: default
|
|
257
|
+
disable: Uitschakelen
|
|
258
|
+
enable: Inschakelen
|
|
259
|
+
enqueue:
|
|
260
|
+
enqueued: Deploy-taak in de wachtrij gezet
|
|
261
|
+
failed: 'De deploy-taak is niet in de wachtrij gezet: zie het log voor de reden'
|
|
262
|
+
every_deploy: Bij elke deploy
|
|
263
|
+
index:
|
|
264
|
+
task_count:
|
|
265
|
+
one: "%{count} taak gedeclareerd"
|
|
266
|
+
other: "%{count} taken gedeclareerd"
|
|
267
|
+
title: Deploy-taken
|
|
268
|
+
not_found: Deploy-taak niet gevonden
|
|
269
|
+
reasons:
|
|
270
|
+
disabled_armed_html: 'Uitgeschakeld met een uitrolrun in afwachting: schakel hem in om hem te laten draaien.'
|
|
271
|
+
disabled_html: 'Uitgeschakeld: de scheduler slaat hem over.'
|
|
272
|
+
due_html: 'Klaar: draait bij de volgende tick van de scheduler.'
|
|
273
|
+
manual_html: "· draaide %{ago} (handmatig)"
|
|
274
|
+
never_html: Nooit gedraaid na een uitrol.
|
|
275
|
+
ran_html: Draaide %{ago} op <code>%{revision}</code> (uitrol).
|
|
276
|
+
waiting_lagging_html:
|
|
277
|
+
one: 'Wacht op uitrol: 1 proces draait nog niet op <code>%{revision}</code>.'
|
|
278
|
+
other: 'Wacht op uitrol: %{count} processen draaien nog niet op <code>%{revision}</code>.'
|
|
279
|
+
waiting_no_revision_html: 'Wacht: de app-revisie is niet geconfigureerd.'
|
|
280
|
+
waiting_no_scheduler_html: 'Wacht: er draait geen scheduler.'
|
|
281
|
+
waiting_no_workers_html: Wacht op een worker op <code>%{revision}</code>.
|
|
282
|
+
waiting_unknown_html: 'Wacht: de uitrolstatus kon niet worden gelezen.'
|
|
283
|
+
run_now: Nu uitvoeren
|
|
284
|
+
show:
|
|
285
|
+
armed_html: "%{armed_for}, klaargezet %{ago}"
|
|
286
|
+
back: Terug
|
|
287
|
+
configuration: Configuratie
|
|
288
|
+
labels:
|
|
289
|
+
arguments: Argumenten
|
|
290
|
+
armed: Openstaande run
|
|
291
|
+
command: Commando
|
|
292
|
+
description: Beschrijving
|
|
293
|
+
job_class: Jobklasse
|
|
294
|
+
priority: Prioriteit
|
|
295
|
+
queue: Wachtrij
|
|
296
|
+
source: Bron
|
|
297
|
+
last_run: Laatste run
|
|
298
|
+
no_runs: Nog geen runs vastgelegd
|
|
299
|
+
recent_runs: Recente runs
|
|
300
|
+
run_headers:
|
|
301
|
+
enqueued_at: In wachtrij gezet
|
|
302
|
+
queue: Wachtrij
|
|
303
|
+
revision: Revisie
|
|
304
|
+
triggered_by: Gestart door
|
|
305
|
+
version: Versie
|
|
306
|
+
source_config: Configuratiebestand
|
|
307
|
+
source_dynamic: Dynamisch
|
|
308
|
+
state: Status
|
|
309
|
+
version: Versie
|
|
310
|
+
states:
|
|
311
|
+
disabled: Uitgeschakeld
|
|
312
|
+
due: Klaar
|
|
313
|
+
never: Nooit gedraaid
|
|
314
|
+
ran: Gedraaid
|
|
315
|
+
waiting: Wacht
|
|
316
|
+
tasks_table:
|
|
317
|
+
empty_hint_html: Declareer eenmalige taken in <code>config/deploy_tasks.yml</code>; <code>rails generate pgbus:add_deploy_tasks</code> schrijft een startbestand.
|
|
318
|
+
empty_title: Geen deploy-taken gedeclareerd
|
|
319
|
+
headers:
|
|
320
|
+
actions: Acties
|
|
321
|
+
last_run: Laatste run
|
|
322
|
+
queue: Wachtrij
|
|
323
|
+
state: Status
|
|
324
|
+
task: Taak
|
|
325
|
+
version: Versie
|
|
326
|
+
never: Nooit
|
|
327
|
+
toggle:
|
|
328
|
+
disabled: Deploy-taak uitgeschakeld
|
|
329
|
+
enabled: Deploy-taak ingeschakeld
|
|
330
|
+
failed: Omschakelen van de deploy-taak mislukt
|
|
331
|
+
triggered_by:
|
|
332
|
+
manual: Handmatig
|
|
333
|
+
rollover: Uitrol
|
|
334
|
+
version: v%{version}
|
|
239
335
|
dialogs:
|
|
240
336
|
cancel: Annuleren
|
|
241
337
|
confirm: Bevestigen
|
|
@@ -617,6 +713,7 @@ nl:
|
|
|
617
713
|
nav:
|
|
618
714
|
batches: Groepen
|
|
619
715
|
dashboard: Dashboard
|
|
716
|
+
deploy_tasks: Deploy
|
|
620
717
|
dlq: DLQ
|
|
621
718
|
events: Evenementen
|
|
622
719
|
insights: Inzichten
|
data/config/locales/pt.yml
CHANGED
|
@@ -236,6 +236,102 @@ pt:
|
|
|
236
236
|
payload: Carga útil
|
|
237
237
|
retry: Tentar novamente
|
|
238
238
|
title: 'Mensagem DLQ #%{id}'
|
|
239
|
+
deploy_tasks:
|
|
240
|
+
banner:
|
|
241
|
+
heading: Transição
|
|
242
|
+
lagging_html:
|
|
243
|
+
one: 'Aguardando a transição: 1 processo ainda não executa <code>%{revision}</code>.'
|
|
244
|
+
other: 'Aguardando a transição: %{count} processos ainda não executam <code>%{revision}</code>.'
|
|
245
|
+
lagging_more: "…e mais %{count}"
|
|
246
|
+
lagging_process: "%{kind}@%{hostname} pid %{pid} em %{revision}"
|
|
247
|
+
live_count: "%{count} em %{revision}"
|
|
248
|
+
live_heading: 'Processos ativos:'
|
|
249
|
+
no_revision_hint_html: Defina <code>config.app_revision</code>, ou uma de <code>PGBUS_REVISION</code>, <code>DASH_VERSION</code> ou <code>KAMAL_VERSION</code>, ou inclua um arquivo <code>REVISION</code>. Executar agora continua funcionando.
|
|
250
|
+
no_revision_html: 'Revisão da aplicação não configurada: as tarefas de deploy aguardam até que esteja.'
|
|
251
|
+
no_revision_label: sem revisão
|
|
252
|
+
no_scheduler_html: 'Nenhum agendador em execução: as tarefas de deploy só são disparadas pelo agendador. Este veredito usa a revisão do próprio painel.'
|
|
253
|
+
no_workers_html: 'Aguardando a transição: ainda nenhum worker em <code>%{revision}</code>.'
|
|
254
|
+
ready_html: 'Transição concluída: cada processo ativo executa <code>%{revision}</code>. Tarefas armadas executam no próximo tick do agendador.'
|
|
255
|
+
unavailable_html: Não foi possível ler o estado da transição.
|
|
256
|
+
default_queue: default
|
|
257
|
+
disable: Desativar
|
|
258
|
+
enable: Ativar
|
|
259
|
+
enqueue:
|
|
260
|
+
enqueued: Tarefa de deploy enfileirada
|
|
261
|
+
failed: 'A tarefa de deploy não foi enfileirada: veja o log para o motivo'
|
|
262
|
+
every_deploy: Em todo deploy
|
|
263
|
+
index:
|
|
264
|
+
task_count:
|
|
265
|
+
one: "%{count} tarefa declarada"
|
|
266
|
+
other: "%{count} tarefas declaradas"
|
|
267
|
+
title: Tarefas de deploy
|
|
268
|
+
not_found: Tarefa de deploy não encontrada
|
|
269
|
+
reasons:
|
|
270
|
+
disabled_armed_html: 'Desativada com uma execução de transição pendente: ative-a para deixá-la executar.'
|
|
271
|
+
disabled_html: 'Desativada: o agendador a ignora.'
|
|
272
|
+
due_html: 'Pronta: executa no próximo tick do agendador.'
|
|
273
|
+
manual_html: "· executada %{ago} (manual)"
|
|
274
|
+
never_html: Nunca executada após uma transição.
|
|
275
|
+
ran_html: Executada %{ago} em <code>%{revision}</code> (transição).
|
|
276
|
+
waiting_lagging_html:
|
|
277
|
+
one: 'Aguardando a transição: 1 processo ainda não executa <code>%{revision}</code>.'
|
|
278
|
+
other: 'Aguardando a transição: %{count} processos ainda não executam <code>%{revision}</code>.'
|
|
279
|
+
waiting_no_revision_html: 'Aguardando: a revisão da aplicação não está configurada.'
|
|
280
|
+
waiting_no_scheduler_html: 'Aguardando: nenhum agendador em execução.'
|
|
281
|
+
waiting_no_workers_html: Aguardando um worker em <code>%{revision}</code>.
|
|
282
|
+
waiting_unknown_html: 'Aguardando: não foi possível ler o estado da transição.'
|
|
283
|
+
run_now: Executar agora
|
|
284
|
+
show:
|
|
285
|
+
armed_html: "%{armed_for}, armada %{ago}"
|
|
286
|
+
back: Voltar
|
|
287
|
+
configuration: Configuração
|
|
288
|
+
labels:
|
|
289
|
+
arguments: Argumentos
|
|
290
|
+
armed: Execução pendente
|
|
291
|
+
command: Comando
|
|
292
|
+
description: Descrição
|
|
293
|
+
job_class: Classe do job
|
|
294
|
+
priority: Prioridade
|
|
295
|
+
queue: Fila
|
|
296
|
+
source: Origem
|
|
297
|
+
last_run: Última execução
|
|
298
|
+
no_runs: Nenhuma execução registrada ainda
|
|
299
|
+
recent_runs: Execuções recentes
|
|
300
|
+
run_headers:
|
|
301
|
+
enqueued_at: Enfileirada em
|
|
302
|
+
queue: Fila
|
|
303
|
+
revision: Revisão
|
|
304
|
+
triggered_by: Disparada por
|
|
305
|
+
version: Versão
|
|
306
|
+
source_config: Arquivo de configuração
|
|
307
|
+
source_dynamic: Dinâmica
|
|
308
|
+
state: Estado
|
|
309
|
+
version: Versão
|
|
310
|
+
states:
|
|
311
|
+
disabled: Desativada
|
|
312
|
+
due: Pronta
|
|
313
|
+
never: Nunca executada
|
|
314
|
+
ran: Executada
|
|
315
|
+
waiting: Aguardando
|
|
316
|
+
tasks_table:
|
|
317
|
+
empty_hint_html: Declare tarefas pontuais em <code>config/deploy_tasks.yml</code>; <code>rails generate pgbus:add_deploy_tasks</code> cria um arquivo inicial.
|
|
318
|
+
empty_title: Nenhuma tarefa de deploy declarada
|
|
319
|
+
headers:
|
|
320
|
+
actions: Ações
|
|
321
|
+
last_run: Última execução
|
|
322
|
+
queue: Fila
|
|
323
|
+
state: Estado
|
|
324
|
+
task: Tarefa
|
|
325
|
+
version: Versão
|
|
326
|
+
never: Nunca
|
|
327
|
+
toggle:
|
|
328
|
+
disabled: Tarefa de deploy desativada
|
|
329
|
+
enabled: Tarefa de deploy ativada
|
|
330
|
+
failed: Falha ao alternar a tarefa de deploy
|
|
331
|
+
triggered_by:
|
|
332
|
+
manual: Manual
|
|
333
|
+
rollover: Transição
|
|
334
|
+
version: v%{version}
|
|
239
335
|
dialogs:
|
|
240
336
|
cancel: Cancelar
|
|
241
337
|
confirm: Confirmar
|
|
@@ -617,6 +713,7 @@ pt:
|
|
|
617
713
|
nav:
|
|
618
714
|
batches: Lotes
|
|
619
715
|
dashboard: Painel
|
|
716
|
+
deploy_tasks: Deploy
|
|
620
717
|
dlq: DLQ
|
|
621
718
|
events: Eventos
|
|
622
719
|
insights: Insights
|
data/config/locales/sv.yml
CHANGED
|
@@ -236,6 +236,102 @@ sv:
|
|
|
236
236
|
payload: Innehåll
|
|
237
237
|
retry: Försök igen
|
|
238
238
|
title: 'DLQ-meddelande #%{id}'
|
|
239
|
+
deploy_tasks:
|
|
240
|
+
banner:
|
|
241
|
+
heading: Utrullning
|
|
242
|
+
lagging_html:
|
|
243
|
+
one: 'Väntar på utrullning: 1 process kör ännu inte <code>%{revision}</code>.'
|
|
244
|
+
other: 'Väntar på utrullning: %{count} processer kör ännu inte <code>%{revision}</code>.'
|
|
245
|
+
lagging_more: "…och %{count} till"
|
|
246
|
+
lagging_process: "%{kind}@%{hostname} pid %{pid} på %{revision}"
|
|
247
|
+
live_count: "%{count} på %{revision}"
|
|
248
|
+
live_heading: 'Aktiva processer:'
|
|
249
|
+
no_revision_hint_html: Sätt <code>config.app_revision</code>, eller någon av <code>PGBUS_REVISION</code>, <code>DASH_VERSION</code> eller <code>KAMAL_VERSION</code>, eller leverera en <code>REVISION</code>-fil. Kör nu fungerar fortfarande.
|
|
250
|
+
no_revision_html: 'Appens revision är inte konfigurerad: deploy-uppgifter väntar tills den är det.'
|
|
251
|
+
no_revision_label: ingen revision
|
|
252
|
+
no_scheduler_html: 'Ingen schemaläggare körs: deploy-uppgifter startas bara av schemaläggaren. Bedömningen använder instrumentpanelens egen revision.'
|
|
253
|
+
no_workers_html: 'Väntar på utrullning: ingen worker på <code>%{revision}</code> ännu.'
|
|
254
|
+
ready_html: 'Utrullad: varje aktiv process kör <code>%{revision}</code>. Väntande uppgifter körs vid schemaläggarens nästa tick.'
|
|
255
|
+
unavailable_html: Utrullningsstatusen kunde inte läsas.
|
|
256
|
+
default_queue: default
|
|
257
|
+
disable: Inaktivera
|
|
258
|
+
enable: Aktivera
|
|
259
|
+
enqueue:
|
|
260
|
+
enqueued: Deploy-uppgiften köad
|
|
261
|
+
failed: 'Deploy-uppgiften köades inte: se loggen för orsaken'
|
|
262
|
+
every_deploy: Vid varje deploy
|
|
263
|
+
index:
|
|
264
|
+
task_count:
|
|
265
|
+
one: "%{count} uppgift deklarerad"
|
|
266
|
+
other: "%{count} uppgifter deklarerade"
|
|
267
|
+
title: Deploy-uppgifter
|
|
268
|
+
not_found: Deploy-uppgiften hittades inte
|
|
269
|
+
reasons:
|
|
270
|
+
disabled_armed_html: 'Inaktiverad med en väntande utrullningskörning: aktivera den för att låta den köras.'
|
|
271
|
+
disabled_html: 'Inaktiverad: schemaläggaren hoppar över den.'
|
|
272
|
+
due_html: 'Redo: körs vid schemaläggarens nästa tick.'
|
|
273
|
+
manual_html: "· kördes %{ago} (manuellt)"
|
|
274
|
+
never_html: Har aldrig körts efter en utrullning.
|
|
275
|
+
ran_html: Kördes %{ago} på <code>%{revision}</code> (utrullning).
|
|
276
|
+
waiting_lagging_html:
|
|
277
|
+
one: 'Väntar på utrullning: 1 process kör ännu inte <code>%{revision}</code>.'
|
|
278
|
+
other: 'Väntar på utrullning: %{count} processer kör ännu inte <code>%{revision}</code>.'
|
|
279
|
+
waiting_no_revision_html: 'Väntar: appens revision är inte konfigurerad.'
|
|
280
|
+
waiting_no_scheduler_html: 'Väntar: ingen schemaläggare körs.'
|
|
281
|
+
waiting_no_workers_html: Väntar på en worker på <code>%{revision}</code>.
|
|
282
|
+
waiting_unknown_html: 'Väntar: utrullningsstatusen kunde inte läsas.'
|
|
283
|
+
run_now: Kör nu
|
|
284
|
+
show:
|
|
285
|
+
armed_html: "%{armed_for}, förberedd %{ago}"
|
|
286
|
+
back: Tillbaka
|
|
287
|
+
configuration: Konfiguration
|
|
288
|
+
labels:
|
|
289
|
+
arguments: Argument
|
|
290
|
+
armed: Väntande körning
|
|
291
|
+
command: Kommando
|
|
292
|
+
description: Beskrivning
|
|
293
|
+
job_class: Jobbklass
|
|
294
|
+
priority: Prioritet
|
|
295
|
+
queue: Kö
|
|
296
|
+
source: Källa
|
|
297
|
+
last_run: Senaste körning
|
|
298
|
+
no_runs: Inga körningar registrerade ännu
|
|
299
|
+
recent_runs: Senaste körningar
|
|
300
|
+
run_headers:
|
|
301
|
+
enqueued_at: Köad
|
|
302
|
+
queue: Kö
|
|
303
|
+
revision: Revision
|
|
304
|
+
triggered_by: Startad av
|
|
305
|
+
version: Version
|
|
306
|
+
source_config: Konfigurationsfil
|
|
307
|
+
source_dynamic: Dynamisk
|
|
308
|
+
state: Tillstånd
|
|
309
|
+
version: Version
|
|
310
|
+
states:
|
|
311
|
+
disabled: Inaktiverad
|
|
312
|
+
due: Redo
|
|
313
|
+
never: Aldrig körd
|
|
314
|
+
ran: Körd
|
|
315
|
+
waiting: Väntar
|
|
316
|
+
tasks_table:
|
|
317
|
+
empty_hint_html: Deklarera engångsuppgifter i <code>config/deploy_tasks.yml</code>; <code>rails generate pgbus:add_deploy_tasks</code> skapar en startfil.
|
|
318
|
+
empty_title: Inga deploy-uppgifter deklarerade
|
|
319
|
+
headers:
|
|
320
|
+
actions: Åtgärder
|
|
321
|
+
last_run: Senaste körning
|
|
322
|
+
queue: Kö
|
|
323
|
+
state: Tillstånd
|
|
324
|
+
task: Uppgift
|
|
325
|
+
version: Version
|
|
326
|
+
never: Aldrig
|
|
327
|
+
toggle:
|
|
328
|
+
disabled: Deploy-uppgiften inaktiverad
|
|
329
|
+
enabled: Deploy-uppgiften aktiverad
|
|
330
|
+
failed: Det gick inte att växla deploy-uppgiften
|
|
331
|
+
triggered_by:
|
|
332
|
+
manual: Manuellt
|
|
333
|
+
rollover: Utrullning
|
|
334
|
+
version: v%{version}
|
|
239
335
|
dialogs:
|
|
240
336
|
cancel: Avbryt
|
|
241
337
|
confirm: Bekräfta
|
|
@@ -617,6 +713,7 @@ sv:
|
|
|
617
713
|
nav:
|
|
618
714
|
batches: Grupper
|
|
619
715
|
dashboard: Instrumentpanel
|
|
716
|
+
deploy_tasks: Deploy
|
|
620
717
|
dlq: DLQ
|
|
621
718
|
events: Händelser
|
|
622
719
|
insights: Insikter
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
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 AddDeployTasksGenerator < Rails::Generators::Base
|
|
10
|
+
include ActiveRecord::Generators::Migration
|
|
11
|
+
include MigrationPath
|
|
12
|
+
|
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
|
14
|
+
|
|
15
|
+
desc "Add deploy tasks tables (one-time jobs run after a rollover) to an existing Pgbus installation"
|
|
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_deploy_tasks_tables.rb.erb",
|
|
24
|
+
File.join(pgbus_migrate_path, "add_pgbus_deploy_tasks_tables.rb")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_deploy_tasks_config
|
|
28
|
+
template "deploy_tasks.yml.erb", "config/deploy_tasks.yml"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def display_post_install
|
|
32
|
+
say ""
|
|
33
|
+
say "Pgbus deploy tasks installed!", :green
|
|
34
|
+
say ""
|
|
35
|
+
say "Next steps:"
|
|
36
|
+
say " 1. Run: rails db:migrate#{migrate_command_suffix}"
|
|
37
|
+
say " 2. Edit config/deploy_tasks.yml to declare your one-time tasks"
|
|
38
|
+
say " 3. Make sure every pgbus process knows the app revision (config.app_revision,"
|
|
39
|
+
say " PGBUS_REVISION, DASH_VERSION, KAMAL_VERSION or a REVISION file)"
|
|
40
|
+
say " 4. 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
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class AddPgbusDeployTasksTables < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
+
# if_not_exists throughout: pgbus:update queues this migration when either
|
|
3
|
+
# table is missing, so a half-present schema must not fail it.
|
|
4
|
+
def change
|
|
5
|
+
create_table :pgbus_deploy_tasks, if_not_exists: true do |t|
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.string :class_name
|
|
8
|
+
t.string :command, limit: 2048
|
|
9
|
+
t.text :arguments
|
|
10
|
+
t.string :queue_name
|
|
11
|
+
t.integer :priority, default: 0
|
|
12
|
+
t.string :version, null: false, default: "1"
|
|
13
|
+
t.boolean :every_deploy, null: false, default: false
|
|
14
|
+
t.boolean :static, null: false, default: true
|
|
15
|
+
t.boolean :enabled, null: false, default: true
|
|
16
|
+
t.text :description
|
|
17
|
+
t.datetime :armed_at
|
|
18
|
+
t.string :armed_for
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
add_index :pgbus_deploy_tasks, :key,
|
|
23
|
+
unique: true, name: "idx_pgbus_deploy_tasks_key", if_not_exists: true
|
|
24
|
+
add_index :pgbus_deploy_tasks, :armed_at,
|
|
25
|
+
where: "armed_at IS NOT NULL", name: "idx_pgbus_deploy_tasks_armed", if_not_exists: true
|
|
26
|
+
|
|
27
|
+
create_table :pgbus_deploy_task_runs, if_not_exists: true do |t|
|
|
28
|
+
t.string :task_key, null: false
|
|
29
|
+
t.string :version, null: false
|
|
30
|
+
t.string :revision
|
|
31
|
+
t.string :triggered_by, null: false
|
|
32
|
+
t.string :queue_name
|
|
33
|
+
t.bigint :msg_id
|
|
34
|
+
t.datetime :run_at, null: false
|
|
35
|
+
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
add_index :pgbus_deploy_task_runs, [:task_key, :run_at],
|
|
39
|
+
name: "idx_pgbus_deploy_task_runs_task", if_not_exists: true
|
|
40
|
+
add_index :pgbus_deploy_task_runs, :run_at,
|
|
41
|
+
name: "idx_pgbus_deploy_task_runs_cleanup", if_not_exists: true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Pgbus Deploy Tasks Configuration
|
|
2
|
+
#
|
|
3
|
+
# One-time jobs that run once the whole fleet has rolled over to a new app
|
|
4
|
+
# revision: a backfill, a data fix, a cache warm. Same shape as
|
|
5
|
+
# config/recurring.yml, without schedule:. Supports environment-scoped or
|
|
6
|
+
# flat configuration.
|
|
7
|
+
#
|
|
8
|
+
# The scheduler enqueues a task only when every live pgbus process reports
|
|
9
|
+
# the scheduler's own app revision and at least one worker on it is alive,
|
|
10
|
+
# so a task never runs on old code during the deploy overlap window. The
|
|
11
|
+
# revision comes from config.app_revision, then PGBUS_REVISION, DASH_VERSION,
|
|
12
|
+
# KAMAL_VERSION, then the REVISION file at Rails.root. Without one, deploy
|
|
13
|
+
# tasks wait (the dashboard says so) and only "Run now" enqueues them.
|
|
14
|
+
#
|
|
15
|
+
# Format:
|
|
16
|
+
# task_key:
|
|
17
|
+
# class: JobClassName # ActiveJob class to run (or use command:)
|
|
18
|
+
# version: 1 # Runs once per version; bump it to run again after the next deploy
|
|
19
|
+
# every_deploy: false # true: run after every rollover instead (version is ignored)
|
|
20
|
+
# queue: default # Optional: queue name (default: pgbus default queue)
|
|
21
|
+
# args: [arg1, arg2] # Optional: job arguments
|
|
22
|
+
# priority: 0 # Optional: numeric priority
|
|
23
|
+
# description: "text" # Optional: human-readable description
|
|
24
|
+
#
|
|
25
|
+
# Command-based tasks (no job class needed):
|
|
26
|
+
# warm_fragment_caches:
|
|
27
|
+
# command: "FragmentCacheWarmer.run"
|
|
28
|
+
# every_deploy: true
|
|
29
|
+
#
|
|
30
|
+
# Examples:
|
|
31
|
+
#
|
|
32
|
+
# production:
|
|
33
|
+
# backfill_order_totals:
|
|
34
|
+
# class: BackfillOrderTotalsJob
|
|
35
|
+
# version: 2
|
|
36
|
+
# queue: maintenance
|
|
37
|
+
# args: [{ batch_size: 500 }]
|
|
38
|
+
# description: Recompute totals after the currency migration
|
|
39
|
+
#
|
|
40
|
+
# warm_fragment_caches:
|
|
41
|
+
# command: "FragmentCacheWarmer.run"
|
|
42
|
+
# every_deploy: true
|
|
@@ -170,6 +170,47 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
170
170
|
add_index :pgbus_recurring_executions, :run_at,
|
|
171
171
|
name: "idx_pgbus_recurring_executions_cleanup"
|
|
172
172
|
|
|
173
|
+
# Deploy tasks (synced from config/deploy_tasks.yml): one-time jobs the
|
|
174
|
+
# scheduler enqueues once the fleet has rolled over to a new revision.
|
|
175
|
+
# armed_at/armed_for is the compare-and-set claim; runs are the history.
|
|
176
|
+
create_table :pgbus_deploy_tasks do |t|
|
|
177
|
+
t.string :key, null: false
|
|
178
|
+
t.string :class_name
|
|
179
|
+
t.string :command, limit: 2048
|
|
180
|
+
t.text :arguments
|
|
181
|
+
t.string :queue_name
|
|
182
|
+
t.integer :priority, default: 0
|
|
183
|
+
t.string :version, null: false, default: "1"
|
|
184
|
+
t.boolean :every_deploy, null: false, default: false
|
|
185
|
+
t.boolean :static, null: false, default: true
|
|
186
|
+
t.boolean :enabled, null: false, default: true
|
|
187
|
+
t.text :description
|
|
188
|
+
t.datetime :armed_at
|
|
189
|
+
t.string :armed_for
|
|
190
|
+
t.timestamps
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
add_index :pgbus_deploy_tasks, :key,
|
|
194
|
+
unique: true, name: "idx_pgbus_deploy_tasks_key"
|
|
195
|
+
add_index :pgbus_deploy_tasks, :armed_at,
|
|
196
|
+
where: "armed_at IS NOT NULL", name: "idx_pgbus_deploy_tasks_armed"
|
|
197
|
+
|
|
198
|
+
create_table :pgbus_deploy_task_runs do |t|
|
|
199
|
+
t.string :task_key, null: false
|
|
200
|
+
t.string :version, null: false
|
|
201
|
+
t.string :revision
|
|
202
|
+
t.string :triggered_by, null: false
|
|
203
|
+
t.string :queue_name
|
|
204
|
+
t.bigint :msg_id
|
|
205
|
+
t.datetime :run_at, null: false
|
|
206
|
+
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
add_index :pgbus_deploy_task_runs, [:task_key, :run_at],
|
|
210
|
+
name: "idx_pgbus_deploy_task_runs_task"
|
|
211
|
+
add_index :pgbus_deploy_task_runs, :run_at,
|
|
212
|
+
name: "idx_pgbus_deploy_task_runs_cleanup"
|
|
213
|
+
|
|
173
214
|
# Stream queue registry — records which PGMQ queues back streams (vs jobs).
|
|
174
215
|
# Stream and job queues share the pgbus_ namespace, so maintenance
|
|
175
216
|
# (per-stream retention, orphan sweep) and wildcard workers rely on this
|
|
@@ -204,6 +245,8 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
204
245
|
execute "SELECT pgmq.drop_queue('pgbus_default')"
|
|
205
246
|
|
|
206
247
|
drop_table :pgbus_stream_queues
|
|
248
|
+
drop_table :pgbus_deploy_task_runs
|
|
249
|
+
drop_table :pgbus_deploy_tasks
|
|
207
250
|
drop_table :pgbus_recurring_executions
|
|
208
251
|
drop_table :pgbus_recurring_tasks
|
|
209
252
|
drop_table :pgbus_batch_executions
|
|
@@ -28,7 +28,9 @@ module Pgbus
|
|
|
28
28
|
included do
|
|
29
29
|
attr_accessor :pgbus_current_attributes
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# A per-class setting the job's own class body writes at load; jobs
|
|
32
|
+
# only read it while they serialize.
|
|
33
|
+
class_attribute :pgbus_persist_current_attributes, instance_writer: false, default: nil # rubocop:disable ThreadSafety/ClassAndModuleAttributes
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
def serialize
|
data/lib/pgbus/batch.rb
CHANGED
|
@@ -209,22 +209,28 @@ module Pgbus
|
|
|
209
209
|
0
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
+
# Schema-probe memos (a true answer is kept for the life of the process)
|
|
213
|
+
# and the warn-once flag, read and written under @schema_lock (issue
|
|
214
|
+
# #527). The probes run outside it: a duplicate probe on a race is
|
|
215
|
+
# harmless, a lock held across a query is not.
|
|
216
|
+
@schema_lock = Mutex.new
|
|
217
|
+
|
|
212
218
|
def self.executions_migrated?
|
|
213
|
-
return true if @executions_migrated
|
|
219
|
+
return true if @schema_lock.synchronize { @executions_migrated }
|
|
214
220
|
|
|
215
221
|
result = begin
|
|
216
222
|
BatchExecution.table_exists?
|
|
217
223
|
rescue StandardError
|
|
218
224
|
false
|
|
219
225
|
end
|
|
220
|
-
@executions_migrated = true if result
|
|
226
|
+
@schema_lock.synchronize { @executions_migrated = true } if result
|
|
221
227
|
result
|
|
222
228
|
end
|
|
223
229
|
|
|
224
230
|
def self.warn_callback_jobs_unmigrated
|
|
225
|
-
|
|
231
|
+
first = @schema_lock.synchronize { @warned_callback_jobs_unmigrated ? false : (@warned_callback_jobs_unmigrated = true) }
|
|
232
|
+
return unless first
|
|
226
233
|
|
|
227
|
-
@warned_callback_jobs_unmigrated = true
|
|
228
234
|
Pgbus.logger.warn do
|
|
229
235
|
"[Pgbus] Batch callback configured as an ActiveJob instance, but pgbus_batches has no " \
|
|
230
236
|
"on_*_job columns yet — .set options (queue, wait, priority) are ignored until " \
|
|
@@ -233,23 +239,25 @@ module Pgbus
|
|
|
233
239
|
end
|
|
234
240
|
|
|
235
241
|
def self.reset_executions_migrated_cache!
|
|
236
|
-
@
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
@schema_lock.synchronize do
|
|
243
|
+
@executions_migrated = nil
|
|
244
|
+
@callback_jobs_migrated = nil
|
|
245
|
+
@warned_callback_jobs_unmigrated = nil
|
|
246
|
+
end
|
|
239
247
|
end
|
|
240
248
|
|
|
241
249
|
# True once the on_finish_job / on_success_job / on_failure_job jsonb
|
|
242
250
|
# columns exist (issue #415). Until then, configured callback instances
|
|
243
251
|
# have nowhere to live and only bare classes are stored.
|
|
244
252
|
def self.callback_jobs_migrated?
|
|
245
|
-
return true if @callback_jobs_migrated
|
|
253
|
+
return true if @schema_lock.synchronize { @callback_jobs_migrated }
|
|
246
254
|
|
|
247
255
|
result = begin
|
|
248
256
|
BatchEntry.column_names.include?("on_finish_job")
|
|
249
257
|
rescue StandardError
|
|
250
258
|
false
|
|
251
259
|
end
|
|
252
|
-
@callback_jobs_migrated = true if result
|
|
260
|
+
@schema_lock.synchronize { @callback_jobs_migrated = true } if result
|
|
253
261
|
result
|
|
254
262
|
end
|
|
255
263
|
|
data/lib/pgbus/cli.rb
CHANGED
|
@@ -37,6 +37,9 @@ module Pgbus
|
|
|
37
37
|
|
|
38
38
|
def start_supervisor(args = [])
|
|
39
39
|
apply_start_options(args)
|
|
40
|
+
# The flags go through setters, after boot finalized the configuration:
|
|
41
|
+
# finalize their values too before the supervisor forks.
|
|
42
|
+
Pgbus.finalize!
|
|
40
43
|
Pgbus.logger.info { "[Pgbus] Starting Pgbus #{Pgbus::VERSION}..." }
|
|
41
44
|
|
|
42
45
|
supervisor = Process::Supervisor.new
|
|
@@ -9,7 +9,16 @@ module Pgbus
|
|
|
9
9
|
# The cursor is the highest msg_id the client has already seen. Replay returns
|
|
10
10
|
# everything strictly greater, ordered by msg_id ASC, capped by `limit`.
|
|
11
11
|
module ReadAfter
|
|
12
|
-
|
|
12
|
+
# Ractor-shareable like the streamer's other dispatch values (issue
|
|
13
|
+
# #527): the Strings are frozen in place, they are the read's own.
|
|
14
|
+
Envelope = Data.define(:msg_id, :enqueued_at, :payload, :source) do
|
|
15
|
+
def initialize(msg_id:, enqueued_at:, payload:, source:)
|
|
16
|
+
enqueued_at&.freeze
|
|
17
|
+
payload&.freeze
|
|
18
|
+
source&.freeze
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
13
22
|
|
|
14
23
|
DEFAULT_LIMIT = 500
|
|
15
24
|
|