pgbus 0.20.0 → 0.21.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -6
- data/README.md +3 -2
- data/app/frontend/pgbus/modules/charts.js +18 -0
- data/app/frontend/pgbus/modules/charts.min.js +1 -1
- data/app/frontend/pgbus/modules/charts.min.js.map +1 -1
- data/app/helpers/pgbus/workflow_insights_helper.rb +8 -0
- data/app/helpers/pgbus/workflows_helper.rb +2 -2
- data/app/models/pgbus/blocked_execution.rb +12 -0
- data/app/models/pgbus/uniqueness_key.rb +88 -3
- data/app/models/pgbus/workflow_entry.rb +3 -2
- data/app/models/pgbus/workflow_step.rb +7 -0
- data/app/views/pgbus/workflows/_timeline.html.erb +11 -1
- data/app/views/pgbus/workflows/definitions/_step_card.html.erb +3 -0
- data/config/locales/da.yml +7 -1
- data/config/locales/de.yml +7 -1
- data/config/locales/en.yml +7 -1
- data/config/locales/es.yml +7 -1
- data/config/locales/fi.yml +7 -1
- data/config/locales/fr.yml +7 -1
- data/config/locales/it.yml +7 -1
- data/config/locales/ja.yml +7 -1
- data/config/locales/nb.yml +7 -1
- data/config/locales/nl.yml +7 -1
- data/config/locales/pt.yml +7 -1
- data/config/locales/sv.yml +7 -1
- data/lib/generators/pgbus/templates/add_workflows.rb.erb +3 -1
- data/lib/generators/pgbus/templates/migration.rb.erb +3 -1
- data/lib/pgbus/active_job/adapter.rb +81 -3
- data/lib/pgbus/active_job/delivery.rb +20 -6
- data/lib/pgbus/active_job/executor.rb +18 -8
- data/lib/pgbus/concurrency/blocked_execution.rb +14 -6
- data/lib/pgbus/configuration/workflow_settings.rb +1 -1
- data/lib/pgbus/configuration.rb +43 -1
- data/lib/pgbus/dead_letter_header.rb +9 -1
- data/lib/pgbus/instrumentation.rb +3 -2
- data/lib/pgbus/integrations/appsignal/dashboard.json +30 -1
- data/lib/pgbus/mcp/tools/workflow_stats_tool.rb +1 -1
- data/lib/pgbus/mcp/tools/workflows_attention_tool.rb +1 -1
- data/lib/pgbus/metrics/workflow_metrics.rb +11 -1
- data/lib/pgbus/process/consumer.rb +2 -1
- data/lib/pgbus/process/dispatcher.rb +13 -3
- data/lib/pgbus/process/worker.rb +10 -1
- data/lib/pgbus/recurring/schedule.rb +1 -1
- data/lib/pgbus/streams/presence.rb +65 -36
- data/lib/pgbus/uniqueness.rb +23 -6
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source/workflow_list.rb +52 -7
- data/lib/pgbus/web/data_source.rb +123 -15
- data/lib/pgbus/web/workflow_insights_report.rb +11 -3
- data/lib/pgbus/web/workflow_state.rb +19 -3
- data/lib/pgbus/web/workflow_timeline.rb +23 -6
- data/lib/pgbus/workflow/instance.rb +36 -3
- data/lib/pgbus/workflow/runner.rb +2 -2
- data/lib/pgbus/workflow/signal_peek.rb +47 -0
- data/lib/pgbus/workflow/signals.rb +31 -10
- data/lib/pgbus/workflow/step_definition.rb +49 -5
- data/lib/pgbus/workflow/step_execution.rb +27 -10
- data/lib/pgbus/workflow/wait_timeout.rb +56 -4
- data/lib/pgbus/workflow.rb +11 -5
- data/sig/pgbus/configuration.rbs +1 -0
- data/sig/pgbus.rbs +43 -0
- metadata +2 -1
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
</h3>
|
|
11
11
|
<%= pgbus_insight_kind(step) %>
|
|
12
12
|
</div>
|
|
13
|
+
<% if step[:wait_timeout] %>
|
|
14
|
+
<p class="mt-2 text-sm"><span class="text-gray-600 dark:text-gray-300"><%= t("pgbus.workflow_insights.show.pipeline.wait_timeout") %></span> <span class="font-mono text-gray-900 dark:text-white"><%= pgbus_insight_wait_timeout(step[:wait_timeout]) %></span></p>
|
|
15
|
+
<% end %>
|
|
13
16
|
<ul class="mt-3 grid grid-cols-2 gap-x-3 gap-y-1 text-sm" aria-label="<%= t("pgbus.workflow_insights.show.pipeline.now_label") %>">
|
|
14
17
|
<% Pgbus::WorkflowInsightsHelper::LIVE_STATUSES.each do |status| %>
|
|
15
18
|
<% count = step[:counts][status.to_sym] %>
|
data/config/locales/da.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ da:
|
|
|
1167
1167
|
skipped: Sprunget over
|
|
1168
1168
|
step_link: Igangværende kørsler ved %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Ventetidsgrænse
|
|
1171
|
+
wait_timeout_dynamic: dynamisk (pr. kørsel)
|
|
1170
1172
|
window_label: Forsøg der sluttede i vinduet
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Kræver handling
|
|
@@ -1349,6 +1351,9 @@ da:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Venter på batch %{batch} (%{done} af %{total} jobs færdige).
|
|
1351
1353
|
waiting_signal_html: Venter på signalet %{signal} siden %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Venter på signalet %{signal} siden %{ago} (forsinket siden %{late}). Næste forsinkelsesbesked sendes %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Venter på signalet %{signal} siden %{ago} (forsinket siden %{late}). Der sendes ikke flere forsinkelsesbeskeder.
|
|
1356
|
+
waiting_signal_notify_html: Venter på signalet %{signal} siden %{ago}. En forsinkelsesbesked sendes %{time}, og ventetiden fortsætter.
|
|
1352
1357
|
waiting_signal_timeout_html: Venter på signalet %{signal} siden %{ago}. Ventetiden udløber %{time}.
|
|
1353
1358
|
waiting_step_html: Venter på %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ da:
|
|
|
1451
1456
|
ready: Klar
|
|
1452
1457
|
waiting: Venter
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Hvert forsøg, ældste øverst: ventede (dets ventetid), i kø (forfalden, endnu ikke hentet), kørte (farvet efter udfald) og venter (på et signal eller sin batch), med pauserne i en egen række. Forsøgstabellen nedenfor viser de samme varigheder.'
|
|
1459
|
+
caption: 'Hvert forsøg, ældste øverst: ventede (dets ventetid), i kø (forfalden, endnu ikke hentet), kørte (farvet efter udfald) og venter (på et signal eller sin batch), med pauserne i en egen række. Forsøgstabellen nedenfor viser de samme varigheder. En stiplet linje markerer hver gang en ventetid passerede sin frist.'
|
|
1455
1460
|
empty: Ingen forsøg endnu.
|
|
1456
1461
|
paused_period_html: Sat på pause %{from} i %{duration}
|
|
1457
1462
|
paused_row: På pause
|
|
@@ -1465,3 +1470,4 @@ da:
|
|
|
1465
1470
|
waited: Ventede
|
|
1466
1471
|
waiting: Venter
|
|
1467
1472
|
title: Tidslinje
|
|
1473
|
+
wait_timeout_html: 'Ventetiden for %{step} passerede sin frist %{time} (on_timeout: %{action}).'
|
data/config/locales/de.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ de:
|
|
|
1167
1167
|
skipped: Übersprungen
|
|
1168
1168
|
step_link: Laufende Läufe bei %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Warte-Timeout
|
|
1171
|
+
wait_timeout_dynamic: dynamisch (pro Lauf)
|
|
1170
1172
|
window_label: Im Zeitfenster beendete Versuche
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Brauchen Aufmerksamkeit
|
|
@@ -1349,6 +1351,9 @@ de:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Wartet auf Stapel %{batch} (%{done} von %{total} Jobs erledigt).
|
|
1351
1353
|
waiting_signal_html: Wartet auf Signal %{signal} seit %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Wartet auf Signal %{signal} seit %{ago} (verspätet seit %{late}). Die nächste Verspätungsmeldung folgt %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Wartet auf Signal %{signal} seit %{ago} (verspätet seit %{late}). Es folgt keine weitere Verspätungsmeldung.
|
|
1356
|
+
waiting_signal_notify_html: Wartet auf Signal %{signal} seit %{ago}. Eine Verspätungsmeldung folgt %{time}, und das Warten geht weiter.
|
|
1352
1357
|
waiting_signal_timeout_html: Wartet auf Signal %{signal} seit %{ago}. Das Warten läuft ab %{time}.
|
|
1353
1358
|
waiting_step_html: Wartet auf %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ de:
|
|
|
1451
1456
|
ready: Bereit
|
|
1452
1457
|
waiting: Wartend
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Jeder Versuch, der älteste oben: gewartet (seine Wartezeit), eingereiht (fällig, noch nicht abgeholt), gelaufen (nach Ausgang eingefärbt) und wartend (auf ein Signal oder seinen Stapel), die Pausen in einer eigenen Zeile. Die Versuchstabelle darunter listet dieselben Dauern.'
|
|
1459
|
+
caption: 'Jeder Versuch, der älteste oben: gewartet (seine Wartezeit), eingereiht (fällig, noch nicht abgeholt), gelaufen (nach Ausgang eingefärbt) und wartend (auf ein Signal oder seinen Stapel), die Pausen in einer eigenen Zeile. Die Versuchstabelle darunter listet dieselben Dauern. Eine gestrichelte Linie markiert jedes Mal, wenn ein Warten seine Frist überschritt.'
|
|
1455
1460
|
empty: Noch keine Versuche.
|
|
1456
1461
|
paused_period_html: Pausiert ab %{from} für %{duration}
|
|
1457
1462
|
paused_row: Pausiert
|
|
@@ -1465,3 +1470,4 @@ de:
|
|
|
1465
1470
|
waited: Gewartet
|
|
1466
1471
|
waiting: Wartend
|
|
1467
1472
|
title: Zeitachse
|
|
1473
|
+
wait_timeout_html: 'Das Warten von %{step} überschritt um %{time} seine Frist (on_timeout: %{action}).'
|
data/config/locales/en.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ en:
|
|
|
1167
1167
|
skipped: Skipped
|
|
1168
1168
|
step_link: Ongoing runs at %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Wait timeout
|
|
1171
|
+
wait_timeout_dynamic: dynamic (per run)
|
|
1170
1172
|
window_label: Attempts that finished in the window
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Need attention
|
|
@@ -1349,6 +1351,9 @@ en:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Waiting for batch %{batch} (%{done} of %{total} jobs done).
|
|
1351
1353
|
waiting_signal_html: Waiting for signal %{signal} since %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Waiting for signal %{signal} since %{ago} (late since %{late}). The next late notice goes out %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Waiting for signal %{signal} since %{ago} (late since %{late}). No further late notice is due.
|
|
1356
|
+
waiting_signal_notify_html: Waiting for signal %{signal} since %{ago}. A late notice goes out %{time}, and the wait goes on.
|
|
1352
1357
|
waiting_signal_timeout_html: Waiting for signal %{signal} since %{ago}. The wait times out %{time}.
|
|
1353
1358
|
waiting_step_html: Waiting on %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ en:
|
|
|
1451
1456
|
ready: Ready
|
|
1452
1457
|
waiting: Waiting
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Each attempt, oldest at the top: waited (its wait), queued (due, not yet picked up), ran (coloured by how it ended) and waiting (for a signal or its batch), with the pauses on their own row. The attempts table below lists the same durations.'
|
|
1459
|
+
caption: 'Each attempt, oldest at the top: waited (its wait), queued (due, not yet picked up), ran (coloured by how it ended) and waiting (for a signal or its batch), with the pauses on their own row. The attempts table below lists the same durations. A dashed line marks each time a wait passed its deadline.'
|
|
1455
1460
|
empty: No attempts yet.
|
|
1456
1461
|
paused_period_html: Paused at %{from} for %{duration}
|
|
1457
1462
|
paused_row: Paused
|
|
@@ -1465,3 +1470,4 @@ en:
|
|
|
1465
1470
|
waited: Waited
|
|
1466
1471
|
waiting: Waiting
|
|
1467
1472
|
title: Timeline
|
|
1473
|
+
wait_timeout_html: "%{step}'s wait passed its deadline at %{time} (on_timeout: %{action})."
|
data/config/locales/es.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ es:
|
|
|
1167
1167
|
skipped: Omitidos
|
|
1168
1168
|
step_link: Ejecuciones en curso en %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Tiempo de espera
|
|
1171
|
+
wait_timeout_dynamic: dinámico (por ejecución)
|
|
1170
1172
|
window_label: Intentos terminados en la ventana
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Requieren atención
|
|
@@ -1349,6 +1351,9 @@ es:
|
|
|
1349
1351
|
unknown_html: 'Estado: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Esperando el lote %{batch} (%{done} de %{total} trabajos terminados).
|
|
1351
1353
|
waiting_signal_html: Esperando la señal %{signal} desde %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Esperando la señal %{signal} desde %{ago} (con retraso desde %{late}). El próximo aviso de retraso sale %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Esperando la señal %{signal} desde %{ago} (con retraso desde %{late}). No sale ningún otro aviso de retraso.
|
|
1356
|
+
waiting_signal_notify_html: Esperando la señal %{signal} desde %{ago}. Un aviso de retraso sale %{time} y la espera continúa.
|
|
1352
1357
|
waiting_signal_timeout_html: Esperando la señal %{signal} desde %{ago}. La espera caduca %{time}.
|
|
1353
1358
|
waiting_step_html: Esperando a %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ es:
|
|
|
1451
1456
|
ready: Listos
|
|
1452
1457
|
waiting: En espera
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Cada intento, el más antiguo arriba: esperó (su espera), en cola (vencido, aún sin tomar), se ejecutó (coloreado según cómo terminó) y en espera (de una señal o de su lote), con las pausas en su propia fila. La tabla de intentos de abajo muestra las mismas duraciones.'
|
|
1459
|
+
caption: 'Cada intento, el más antiguo arriba: esperó (su espera), en cola (vencido, aún sin tomar), se ejecutó (coloreado según cómo terminó) y en espera (de una señal o de su lote), con las pausas en su propia fila. La tabla de intentos de abajo muestra las mismas duraciones. Una línea discontinua marca cada vez que una espera superó su plazo.'
|
|
1455
1460
|
empty: Aún no hay intentos.
|
|
1456
1461
|
paused_period_html: En pausa desde %{from} durante %{duration}
|
|
1457
1462
|
paused_row: En pausa
|
|
@@ -1465,3 +1470,4 @@ es:
|
|
|
1465
1470
|
waited: Esperó
|
|
1466
1471
|
waiting: En espera
|
|
1467
1472
|
title: Cronología
|
|
1473
|
+
wait_timeout_html: 'La espera de %{step} superó su plazo en %{time} (on_timeout: %{action}).'
|
data/config/locales/fi.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ fi:
|
|
|
1167
1167
|
skipped: Ohitettu
|
|
1168
1168
|
step_link: Käynnissä olevat ajot vaiheessa %{step}
|
|
1169
1169
|
title: Putki
|
|
1170
|
+
wait_timeout: Odotuksen aikaraja
|
|
1171
|
+
wait_timeout_dynamic: dynaaminen (ajokohtainen)
|
|
1170
1172
|
window_label: Ikkunassa päättyneet yritykset
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Vaatii toimia
|
|
@@ -1349,6 +1351,9 @@ fi:
|
|
|
1349
1351
|
unknown_html: 'Tila: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Odottaa erää %{batch} (%{done} / %{total} tehtävää valmis).
|
|
1351
1353
|
waiting_signal_html: Odottaa signaalia %{signal}, alkoi %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Odottaa signaalia %{signal}, alkoi %{ago} (myöhässä alkaen %{late}). Seuraava myöhästymisilmoitus lähtee %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Odottaa signaalia %{signal}, alkoi %{ago} (myöhässä alkaen %{late}). Uusia myöhästymisilmoituksia ei lähde.
|
|
1356
|
+
waiting_signal_notify_html: Odottaa signaalia %{signal}, alkoi %{ago}. Myöhästymisilmoitus lähtee %{time}, ja odotus jatkuu.
|
|
1352
1357
|
waiting_signal_timeout_html: Odottaa signaalia %{signal}, alkoi %{ago}. Odotus aikakatkaistaan %{time}.
|
|
1353
1358
|
waiting_step_html: Odottaa vaihetta %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ fi:
|
|
|
1451
1456
|
ready: Valmiina
|
|
1452
1457
|
waiting: Odottaa
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Jokainen yritys, vanhin ylimpänä: odotettu (sen odotus), jonossa (erääntynyt, ei vielä otettu), ajettu (väri kertoo lopputuloksen) ja odottaa (signaalia tai erää), keskeytykset omalla rivillään. Alla oleva yritystaulukko listaa samat kestot.'
|
|
1459
|
+
caption: 'Jokainen yritys, vanhin ylimpänä: odotettu (sen odotus), jonossa (erääntynyt, ei vielä otettu), ajettu (väri kertoo lopputuloksen) ja odottaa (signaalia tai erää), keskeytykset omalla rivillään. Alla oleva yritystaulukko listaa samat kestot. Katkoviiva merkitsee jokaista kertaa, kun odotus ylitti määräaikansa.'
|
|
1455
1460
|
empty: Ei vielä yrityksiä.
|
|
1456
1461
|
paused_period_html: Keskeytetty %{from} ajaksi %{duration}
|
|
1457
1462
|
paused_row: Keskeytetty
|
|
@@ -1465,3 +1470,4 @@ fi:
|
|
|
1465
1470
|
waited: Odotettu
|
|
1466
1471
|
waiting: Odottaa
|
|
1467
1472
|
title: Aikajana
|
|
1473
|
+
wait_timeout_html: 'Vaiheen %{step} odotus ylitti määräaikansa %{time} (on_timeout: %{action}).'
|
data/config/locales/fr.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ fr:
|
|
|
1167
1167
|
skipped: Ignorées
|
|
1168
1168
|
step_link: Exécutions en cours à %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Délai d'attente
|
|
1171
|
+
wait_timeout_dynamic: dynamique (par exécution)
|
|
1170
1172
|
window_label: Tentatives terminées dans la fenêtre
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: À traiter
|
|
@@ -1349,6 +1351,9 @@ fr:
|
|
|
1349
1351
|
unknown_html: 'Statut : %{status}.'
|
|
1350
1352
|
waiting_batch_html: En attente du lot %{batch} (%{done} travaux sur %{total} terminés).
|
|
1351
1353
|
waiting_signal_html: En attente du signal %{signal} depuis %{ago}.
|
|
1354
|
+
waiting_signal_late_html: En attente du signal %{signal} depuis %{ago} (en retard depuis %{late}). Le prochain avis de retard part %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: En attente du signal %{signal} depuis %{ago} (en retard depuis %{late}). Aucun autre avis de retard ne part.
|
|
1356
|
+
waiting_signal_notify_html: En attente du signal %{signal} depuis %{ago}. Un avis de retard part %{time} et l'attente continue.
|
|
1352
1357
|
waiting_signal_timeout_html: En attente du signal %{signal} depuis %{ago}. L'attente expire %{time}.
|
|
1353
1358
|
waiting_step_html: En attente de %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ fr:
|
|
|
1451
1456
|
ready: Prêts
|
|
1452
1457
|
waiting: En attente
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Chaque tentative, la plus ancienne en haut : attente (son délai), en file (due, pas encore prise), exécution (colorée selon son issue) et en attente (d''un signal ou de son lot), avec les pauses sur leur propre ligne. Le tableau des tentatives ci-dessous donne les mêmes durées.'
|
|
1459
|
+
caption: 'Chaque tentative, la plus ancienne en haut : attente (son délai), en file (due, pas encore prise), exécution (colorée selon son issue) et en attente (d''un signal ou de son lot), avec les pauses sur leur propre ligne. Le tableau des tentatives ci-dessous donne les mêmes durées. Une ligne pointillée marque chaque fois qu''une attente a dépassé son échéance.'
|
|
1455
1460
|
empty: Aucune tentative pour l'instant.
|
|
1456
1461
|
paused_period_html: En pause à %{from} pendant %{duration}
|
|
1457
1462
|
paused_row: En pause
|
|
@@ -1465,3 +1470,4 @@ fr:
|
|
|
1465
1470
|
waited: Attendu
|
|
1466
1471
|
waiting: En attente
|
|
1467
1472
|
title: Chronologie
|
|
1473
|
+
wait_timeout_html: 'L''attente de %{step} a dépassé son échéance à %{time} (on_timeout: %{action}).'
|
data/config/locales/it.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ it:
|
|
|
1167
1167
|
skipped: Saltati
|
|
1168
1168
|
step_link: Esecuzioni in corso a %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Timeout di attesa
|
|
1171
|
+
wait_timeout_dynamic: dinamico (per esecuzione)
|
|
1170
1172
|
window_label: Tentativi terminati nella finestra
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Richiedono attenzione
|
|
@@ -1349,6 +1351,9 @@ it:
|
|
|
1349
1351
|
unknown_html: 'Stato: %{status}.'
|
|
1350
1352
|
waiting_batch_html: In attesa del lotto %{batch} (%{done} lavori su %{total} completati).
|
|
1351
1353
|
waiting_signal_html: In attesa del segnale %{signal} da %{ago}.
|
|
1354
|
+
waiting_signal_late_html: In attesa del segnale %{signal} da %{ago} (in ritardo da %{late}). Il prossimo avviso di ritardo parte %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: In attesa del segnale %{signal} da %{ago} (in ritardo da %{late}). Non parte nessun altro avviso di ritardo.
|
|
1356
|
+
waiting_signal_notify_html: In attesa del segnale %{signal} da %{ago}. Un avviso di ritardo parte %{time} e l'attesa continua.
|
|
1352
1357
|
waiting_signal_timeout_html: In attesa del segnale %{signal} da %{ago}. L'attesa scade %{time}.
|
|
1353
1358
|
waiting_step_html: In attesa di %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ it:
|
|
|
1451
1456
|
ready: Pronti
|
|
1452
1457
|
waiting: In attesa
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Ogni tentativo, il più vecchio in alto: atteso (la sua attesa), in coda (previsto, non ancora preso), eseguito (colorato in base a come è finito) e in attesa (di un segnale o del suo lotto), con le pause su una riga a parte. La tabella dei tentativi qui sotto riporta le stesse durate.'
|
|
1459
|
+
caption: 'Ogni tentativo, il più vecchio in alto: atteso (la sua attesa), in coda (previsto, non ancora preso), eseguito (colorato in base a come è finito) e in attesa (di un segnale o del suo lotto), con le pause su una riga a parte. La tabella dei tentativi qui sotto riporta le stesse durate. Una linea tratteggiata segna ogni volta che un''attesa ha superato la scadenza.'
|
|
1455
1460
|
empty: Ancora nessun tentativo.
|
|
1456
1461
|
paused_period_html: In pausa dalle %{from} per %{duration}
|
|
1457
1462
|
paused_row: In pausa
|
|
@@ -1465,3 +1470,4 @@ it:
|
|
|
1465
1470
|
waited: Atteso
|
|
1466
1471
|
waiting: In attesa
|
|
1467
1472
|
title: Cronologia
|
|
1473
|
+
wait_timeout_html: 'L''attesa di %{step} ha superato la scadenza alle %{time} (on_timeout: %{action}).'
|
data/config/locales/ja.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ ja:
|
|
|
1167
1167
|
skipped: スキップ
|
|
1168
1168
|
step_link: "%{step} の進行中の実行"
|
|
1169
1169
|
title: パイプライン
|
|
1170
|
+
wait_timeout: 待機タイムアウト
|
|
1171
|
+
wait_timeout_dynamic: 動的(実行ごと)
|
|
1170
1172
|
window_label: 期間内に終了した試行
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: 要対応
|
|
@@ -1349,6 +1351,9 @@ ja:
|
|
|
1349
1351
|
unknown_html: 'ステータス: %{status}。'
|
|
1350
1352
|
waiting_batch_html: バッチ %{batch} を待機中です(%{total} 件中 %{done} 件のジョブが完了)。
|
|
1351
1353
|
waiting_signal_html: シグナル %{signal} を %{ago} から待機中です。
|
|
1354
|
+
waiting_signal_late_html: シグナル %{signal} を %{ago} から待機中です(%{late} から遅延)。次の遅延通知は %{time} に送られます。
|
|
1355
|
+
waiting_signal_late_no_deadline_html: シグナル %{signal} を %{ago} から待機中です(%{late} から遅延)。これ以上の遅延通知は送られません。
|
|
1356
|
+
waiting_signal_notify_html: シグナル %{signal} を %{ago} から待機中です。遅延通知は %{time} に送られ、待機は続きます。
|
|
1352
1357
|
waiting_signal_timeout_html: シグナル %{signal} を %{ago} から待機中です。待機は %{time} にタイムアウトします。
|
|
1353
1358
|
waiting_step_html: "%{step} を待機中です。"
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ ja:
|
|
|
1451
1456
|
ready: 実行待ち
|
|
1452
1457
|
waiting: 待機中
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption:
|
|
1459
|
+
caption: 各試行を古い順に表示します。待機(待ち時間)、キュー(期限到来、未取得)、実行(終了状態で色分け)、待機中(シグナルまたはバッチ待ち)で、一時停止は別の行に表示します。下の試行テーブルにも同じ時間が載っています。破線は待機が期限を過ぎた時点を示します。
|
|
1455
1460
|
empty: 試行はまだありません。
|
|
1456
1461
|
paused_period_html: "%{from} から %{duration} 一時停止"
|
|
1457
1462
|
paused_row: 一時停止
|
|
@@ -1465,3 +1470,4 @@ ja:
|
|
|
1465
1470
|
waited: 待機済み
|
|
1466
1471
|
waiting: 待機中
|
|
1467
1472
|
title: タイムライン
|
|
1473
|
+
wait_timeout_html: "%{step} の待機が %{time} に期限を過ぎました(on_timeout: %{action})。"
|
data/config/locales/nb.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ nb:
|
|
|
1167
1167
|
skipped: Hoppet over
|
|
1168
1168
|
step_link: Pågående kjøringer ved %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Tidsgrense for venting
|
|
1171
|
+
wait_timeout_dynamic: dynamisk (per kjøring)
|
|
1170
1172
|
window_label: Forsøk som ble avsluttet i vinduet
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Trenger tiltak
|
|
@@ -1349,6 +1351,9 @@ nb:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Venter på gruppen %{batch} (%{done} av %{total} jobber ferdige).
|
|
1351
1353
|
waiting_signal_html: Venter på signalet %{signal} siden %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Venter på signalet %{signal} siden %{ago} (forsinket siden %{late}). Neste forsinkelsesvarsel sendes %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Venter på signalet %{signal} siden %{ago} (forsinket siden %{late}). Det sendes ingen flere forsinkelsesvarsler.
|
|
1356
|
+
waiting_signal_notify_html: Venter på signalet %{signal} siden %{ago}. Et forsinkelsesvarsel sendes %{time}, og ventingen fortsetter.
|
|
1352
1357
|
waiting_signal_timeout_html: Venter på signalet %{signal} siden %{ago}. Ventetiden utløper %{time}.
|
|
1353
1358
|
waiting_step_html: Venter på %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ nb:
|
|
|
1451
1456
|
ready: Klare
|
|
1452
1457
|
waiting: Venter
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Hvert forsøk, eldst øverst: ventet (ventetiden), i kø (forfalt, ikke hentet ennå), kjørte (farget etter utfall) og venter (på et signal eller sin gruppe), med pausene på en egen rad. Forsøkstabellen under viser de samme varighetene.'
|
|
1459
|
+
caption: 'Hvert forsøk, eldst øverst: ventet (ventetiden), i kø (forfalt, ikke hentet ennå), kjørte (farget etter utfall) og venter (på et signal eller sin gruppe), med pausene på en egen rad. Forsøkstabellen under viser de samme varighetene. En stiplet linje markerer hver gang en ventetid passerte fristen.'
|
|
1455
1460
|
empty: Ingen forsøk ennå.
|
|
1456
1461
|
paused_period_html: Satt på pause %{from} i %{duration}
|
|
1457
1462
|
paused_row: På pause
|
|
@@ -1465,3 +1470,4 @@ nb:
|
|
|
1465
1470
|
waited: Ventet
|
|
1466
1471
|
waiting: Venter
|
|
1467
1472
|
title: Tidslinje
|
|
1473
|
+
wait_timeout_html: 'Ventetiden for %{step} passerte fristen %{time} (on_timeout: %{action}).'
|
data/config/locales/nl.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ nl:
|
|
|
1167
1167
|
skipped: Overgeslagen
|
|
1168
1168
|
step_link: Lopende runs bij %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Wachttime-out
|
|
1171
|
+
wait_timeout_dynamic: dynamisch (per run)
|
|
1170
1172
|
window_label: Pogingen die in het venster eindigden
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Aandacht nodig
|
|
@@ -1349,6 +1351,9 @@ nl:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Wacht op groep %{batch} (%{done} van %{total} taken klaar).
|
|
1351
1353
|
waiting_signal_html: Wacht op signaal %{signal}, gestart %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Wacht op signaal %{signal}, gestart %{ago} (te laat sinds %{late}). De volgende melding over de vertraging gaat %{time} uit.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Wacht op signaal %{signal}, gestart %{ago} (te laat sinds %{late}). Er gaat geen nieuwe melding over de vertraging meer uit.
|
|
1356
|
+
waiting_signal_notify_html: Wacht op signaal %{signal}, gestart %{ago}. Een melding over de vertraging gaat %{time} uit, en het wachten gaat door.
|
|
1352
1357
|
waiting_signal_timeout_html: Wacht op signaal %{signal}, gestart %{ago}. De wachttijd verloopt %{time}.
|
|
1353
1358
|
waiting_step_html: Wacht op %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ nl:
|
|
|
1451
1456
|
ready: Klaar
|
|
1452
1457
|
waiting: Wachtend
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Elke poging, oudste bovenaan: gewacht (de wachttijd), in wachtrij (aan de beurt, nog niet opgepakt), uitgevoerd (gekleurd naar hoe het afliep) en wachtend (op een signaal of de groep), met de pauzes op een eigen rij. De pogingentabel hieronder toont dezelfde duren.'
|
|
1459
|
+
caption: 'Elke poging, oudste bovenaan: gewacht (de wachttijd), in wachtrij (aan de beurt, nog niet opgepakt), uitgevoerd (gekleurd naar hoe het afliep) en wachtend (op een signaal of de groep), met de pauzes op een eigen rij. De pogingentabel hieronder toont dezelfde duren. Een stippellijn markeert elke keer dat een wachttijd zijn deadline passeerde.'
|
|
1455
1460
|
empty: Nog geen pogingen.
|
|
1456
1461
|
paused_period_html: Gepauzeerd op %{from} gedurende %{duration}
|
|
1457
1462
|
paused_row: Gepauzeerd
|
|
@@ -1465,3 +1470,4 @@ nl:
|
|
|
1465
1470
|
waited: Gewacht
|
|
1466
1471
|
waiting: Wachtend
|
|
1467
1472
|
title: Tijdlijn
|
|
1473
|
+
wait_timeout_html: 'De wachttijd van %{step} verstreek op %{time} (on_timeout: %{action}).'
|
data/config/locales/pt.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ pt:
|
|
|
1167
1167
|
skipped: Ignoradas
|
|
1168
1168
|
step_link: Execuções em curso em %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Tempo limite de espera
|
|
1171
|
+
wait_timeout_dynamic: dinâmico (por execução)
|
|
1170
1172
|
window_label: Tentativas terminadas na janela
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Precisam de atenção
|
|
@@ -1349,6 +1351,9 @@ pt:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Aguardando o lote %{batch} (%{done} de %{total} trabalhos concluídos).
|
|
1351
1353
|
waiting_signal_html: Aguardando o sinal %{signal} desde %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Aguardando o sinal %{signal} desde %{ago} (atrasado desde %{late}). O próximo aviso de atraso sai %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Aguardando o sinal %{signal} desde %{ago} (atrasado desde %{late}). Não sai mais nenhum aviso de atraso.
|
|
1356
|
+
waiting_signal_notify_html: Aguardando o sinal %{signal} desde %{ago}. Um aviso de atraso sai %{time} e a espera continua.
|
|
1352
1357
|
waiting_signal_timeout_html: Aguardando o sinal %{signal} desde %{ago}. A espera expira %{time}.
|
|
1353
1358
|
waiting_step_html: Aguardando %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ pt:
|
|
|
1451
1456
|
ready: Prontos
|
|
1452
1457
|
waiting: Aguardando
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Cada tentativa, a mais antiga no topo: esperou (sua espera), na fila (vencida, ainda não pega), executou (colorida conforme terminou) e aguardando (um sinal ou seu lote), com as pausas em linha própria. A tabela de tentativas abaixo lista as mesmas durações.'
|
|
1459
|
+
caption: 'Cada tentativa, a mais antiga no topo: esperou (sua espera), na fila (vencida, ainda não pega), executou (colorida conforme terminou) e aguardando (um sinal ou seu lote), com as pausas em linha própria. A tabela de tentativas abaixo lista as mesmas durações. Uma linha tracejada marca cada vez que uma espera passou do prazo.'
|
|
1455
1460
|
empty: Ainda não há tentativas.
|
|
1456
1461
|
paused_period_html: Em pausa desde %{from} durante %{duration}
|
|
1457
1462
|
paused_row: Em pausa
|
|
@@ -1465,3 +1470,4 @@ pt:
|
|
|
1465
1470
|
waited: Esperou
|
|
1466
1471
|
waiting: Aguardando
|
|
1467
1472
|
title: Linha do tempo
|
|
1473
|
+
wait_timeout_html: 'A espera de %{step} passou do prazo em %{time} (on_timeout: %{action}).'
|
data/config/locales/sv.yml
CHANGED
|
@@ -1167,6 +1167,8 @@ sv:
|
|
|
1167
1167
|
skipped: Överhoppade
|
|
1168
1168
|
step_link: Pågående körningar vid %{step}
|
|
1169
1169
|
title: Pipeline
|
|
1170
|
+
wait_timeout: Väntetidsgräns
|
|
1171
|
+
wait_timeout_dynamic: dynamisk (per körning)
|
|
1170
1172
|
window_label: Försök som avslutades i fönstret
|
|
1171
1173
|
summary:
|
|
1172
1174
|
attention: Behöver åtgärd
|
|
@@ -1349,6 +1351,9 @@ sv:
|
|
|
1349
1351
|
unknown_html: 'Status: %{status}.'
|
|
1350
1352
|
waiting_batch_html: Väntar på gruppen %{batch} (%{done} av %{total} jobb klara).
|
|
1351
1353
|
waiting_signal_html: Väntar på signalen %{signal} sedan %{ago}.
|
|
1354
|
+
waiting_signal_late_html: Väntar på signalen %{signal} sedan %{ago} (sen sedan %{late}). Nästa förseningsavisering går ut %{time}.
|
|
1355
|
+
waiting_signal_late_no_deadline_html: Väntar på signalen %{signal} sedan %{ago} (sen sedan %{late}). Ingen fler förseningsavisering går ut.
|
|
1356
|
+
waiting_signal_notify_html: Väntar på signalen %{signal} sedan %{ago}. En förseningsavisering går ut %{time}, och väntan fortsätter.
|
|
1352
1357
|
waiting_signal_timeout_html: Väntar på signalen %{signal} sedan %{ago}. Väntan tar slut %{time}.
|
|
1353
1358
|
waiting_step_html: Väntar på %{step}.
|
|
1354
1359
|
not_found:
|
|
@@ -1451,7 +1456,7 @@ sv:
|
|
|
1451
1456
|
ready: Redo
|
|
1452
1457
|
waiting: Väntar
|
|
1453
1458
|
timeline:
|
|
1454
|
-
caption: 'Varje försök, äldst överst: väntade (dess väntetid), köad (förfallen, ännu inte hämtad), körde (färgad efter utfall) och väntar (på en signal eller sin grupp), med pauserna på en egen rad. Försökstabellen nedan visar samma tider.'
|
|
1459
|
+
caption: 'Varje försök, äldst överst: väntade (dess väntetid), köad (förfallen, ännu inte hämtad), körde (färgad efter utfall) och väntar (på en signal eller sin grupp), med pauserna på en egen rad. Försökstabellen nedan visar samma tider. En streckad linje markerar varje gång en väntan passerade sin tidsgräns.'
|
|
1455
1460
|
empty: Inga försök ännu.
|
|
1456
1461
|
paused_period_html: Pausad %{from} i %{duration}
|
|
1457
1462
|
paused_row: Pausad
|
|
@@ -1465,3 +1470,4 @@ sv:
|
|
|
1465
1470
|
waited: Väntade
|
|
1466
1471
|
waiting: Väntar
|
|
1467
1472
|
title: Tidslinje
|
|
1473
|
+
wait_timeout_html: 'Väntan för %{step} passerade sin tidsgräns %{time} (on_timeout: %{action}).'
|
|
@@ -32,8 +32,10 @@ class AddPgbusWorkflows < ActiveRecord::Migration<%= migration_version %>
|
|
|
32
32
|
|
|
33
33
|
add_index :pgbus_workflows, :workflow_id,
|
|
34
34
|
unique: true, name: "idx_pgbus_workflows_workflow_id", if_not_exists: true
|
|
35
|
+
# status::text keeps the predicate stable in schema.rb: without the cast, a
|
|
36
|
+
# migrated and a schema-loaded database dump it differently (issue #573).
|
|
35
37
|
add_index :pgbus_workflows, [:workflow_class, :subject_type, :subject_id],
|
|
36
|
-
unique: true, where: "status IN ('ready','performing','waiting','paused')",
|
|
38
|
+
unique: true, where: "status::text IN ('ready','performing','waiting','paused')",
|
|
37
39
|
name: "idx_pgbus_workflows_one_ongoing", if_not_exists: true
|
|
38
40
|
add_index :pgbus_workflows, [:workflow_class, :idempotency_key],
|
|
39
41
|
unique: true, where: "idempotency_key IS NOT NULL",
|
|
@@ -238,8 +238,10 @@ class CreatePgbusTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
238
238
|
|
|
239
239
|
add_index :pgbus_workflows, :workflow_id,
|
|
240
240
|
unique: true, name: "idx_pgbus_workflows_workflow_id"
|
|
241
|
+
# status::text keeps the predicate stable in schema.rb: without the cast, a
|
|
242
|
+
# migrated and a schema-loaded database dump it differently (issue #573).
|
|
241
243
|
add_index :pgbus_workflows, [:workflow_class, :subject_type, :subject_id],
|
|
242
|
-
unique: true, where: "status IN ('ready','performing','waiting','paused')",
|
|
244
|
+
unique: true, where: "status::text IN ('ready','performing','waiting','paused')",
|
|
243
245
|
name: "idx_pgbus_workflows_one_ongoing"
|
|
244
246
|
add_index :pgbus_workflows, [:workflow_class, :idempotency_key],
|
|
245
247
|
unique: true, where: "idempotency_key IS NOT NULL",
|
|
@@ -80,6 +80,7 @@ module Pgbus
|
|
|
80
80
|
# reset: once the send returns, msg_id is set and the rescue routes
|
|
81
81
|
# on that instead.
|
|
82
82
|
sending = false
|
|
83
|
+
retry_lock = hand_lock_to_retry(active_job, payload_hash, queue)
|
|
83
84
|
|
|
84
85
|
if key && concurrency
|
|
85
86
|
# The check and the park commit together, under the semaphore row
|
|
@@ -115,6 +116,9 @@ module Pgbus
|
|
|
115
116
|
# Bind before backfill so a live message is never left with an unbound
|
|
116
117
|
# uniqueness row if execution-row bookkeeping raises.
|
|
117
118
|
bind_acquired_uniqueness_lock(queue, msg_id) if msg_id
|
|
119
|
+
bind_retry_lock(retry_lock, queue, msg_id) if msg_id
|
|
120
|
+
# Dropped by its concurrency limit: no retry will run, the attempt does.
|
|
121
|
+
return_retry_lock(retry_lock) unless msg_id || blocked
|
|
118
122
|
Batch.backfill_execution(payload_hash, msg_id, physical_queue(queue, priority)) if msg_id
|
|
119
123
|
# A retry re-enqueue that is now live (sent, or parked as a blocked
|
|
120
124
|
# execution) must stop the original attempt from signalling completion.
|
|
@@ -135,6 +139,7 @@ module Pgbus
|
|
|
135
139
|
# database error from the slot upsert or the park is not ambiguous.
|
|
136
140
|
if msg_id.nil? && !(sending && ambiguous_delivery?(e))
|
|
137
141
|
rollback_acquired_uniqueness_lock
|
|
142
|
+
return_retry_lock(retry_lock)
|
|
138
143
|
uncount_batch_job(payload_hash, active_job)
|
|
139
144
|
else
|
|
140
145
|
# Drop the thread-local so a later discard on this thread cannot
|
|
@@ -247,9 +252,10 @@ module Pgbus
|
|
|
247
252
|
# the executor still holds the key (it releases only on success/DLQ). So
|
|
248
253
|
# a re-enqueue with executions > 0 would otherwise hit its own held key,
|
|
249
254
|
# be rejected as a duplicate (JobNotUnique), and dead-letter the original
|
|
250
|
-
# while losing the retry. Let the retry through; the
|
|
251
|
-
#
|
|
252
|
-
#
|
|
255
|
+
# while losing the retry. Let the retry through (issue #333); the key
|
|
256
|
+
# row is handed to it (hand_lock_to_retry, then bind_retry_lock once
|
|
257
|
+
# sent), so the attempt's release leaves it held until the job finally
|
|
258
|
+
# succeeds or dead-letters (issue #564).
|
|
253
259
|
return false if active_job.executions.to_i.positive?
|
|
254
260
|
|
|
255
261
|
result = Uniqueness.acquire_enqueue_lock(uniqueness_key, active_job, queue_name: queue)
|
|
@@ -285,6 +291,78 @@ module Pgbus
|
|
|
285
291
|
Pgbus.logger.warn { "[Pgbus] Uniqueness bind failed: #{e.message}" }
|
|
286
292
|
end
|
|
287
293
|
|
|
294
|
+
# The job's :until_executed key and the delivery of the attempt running
|
|
295
|
+
# it, when this enqueue is that attempt's retry: the retry is let
|
|
296
|
+
# through the key the attempt holds and never acquires one, so the key
|
|
297
|
+
# row has to follow it. nil for a first attempt (it acquired its own
|
|
298
|
+
# row), a retry enqueued outside the executor (no attempt to take it
|
|
299
|
+
# from), or a job run inline inside another job, whose delivery it sees.
|
|
300
|
+
def retry_uniqueness_lock(active_job, payload_hash)
|
|
301
|
+
key = Uniqueness.extract_key(payload_hash)
|
|
302
|
+
return unless key && Uniqueness.extract_strategy(payload_hash) == :until_executed
|
|
303
|
+
return unless active_job.executions.to_i.positive?
|
|
304
|
+
|
|
305
|
+
delivery = Delivery.current
|
|
306
|
+
[key, delivery] if delivery && delivery[:job_id] == active_job.job_id
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Take the attempt's row for its retry before the retry exists — sent or
|
|
310
|
+
# parked — the way a first enqueue acquires before its send (see
|
|
311
|
+
# UniquenessKey.unbind!). Returns [key, delivery, stamp] for
|
|
312
|
+
# bind_retry_lock / return_retry_lock, or nil when there is nothing to
|
|
313
|
+
# hand over. A failure is logged and the retry goes out without the row,
|
|
314
|
+
# which then stays with the attempt.
|
|
315
|
+
def hand_lock_to_retry(active_job, payload_hash, queue)
|
|
316
|
+
key, delivery = retry_uniqueness_lock(active_job, payload_hash)
|
|
317
|
+
return unless key
|
|
318
|
+
|
|
319
|
+
stamp = UniquenessKey.unbind!(key, from_queue_name: Uniqueness.lock_queue_name(delivery[:queue_name]),
|
|
320
|
+
from_msg_id: delivery[:msg_id], from_enqueued_at: delivery[:enqueued_at],
|
|
321
|
+
queue_name: Uniqueness.lock_queue_name(queue))
|
|
322
|
+
return [key, delivery, stamp] if stamp
|
|
323
|
+
|
|
324
|
+
warn_retry_lock_not_held(key)
|
|
325
|
+
nil
|
|
326
|
+
rescue StandardError => e
|
|
327
|
+
Pgbus.logger.warn { "[Pgbus] Uniqueness handoff to retry failed for #{key}: #{e.message}" }
|
|
328
|
+
nil
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Point the handed-over row at the sent retry. The retry is live, so a
|
|
332
|
+
# failure is logged, not raised: the row stays unbound and stamped
|
|
333
|
+
# before the send, and the retry's own release takes it.
|
|
334
|
+
def bind_retry_lock(retry_lock, queue, msg_id)
|
|
335
|
+
return unless retry_lock
|
|
336
|
+
|
|
337
|
+
key, _delivery, stamp = retry_lock
|
|
338
|
+
UniquenessKey.bind!(key, queue_name: Uniqueness.lock_queue_name(queue), msg_id: msg_id, acquired_at: stamp)
|
|
339
|
+
rescue StandardError => e
|
|
340
|
+
Pgbus.logger.warn { "[Pgbus] Uniqueness bind to retry failed for #{key}: #{e.message}" }
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# No retry came of it (dropped by its concurrency limit, or the send
|
|
344
|
+
# failed before any message existed): bind the row back to the attempt,
|
|
345
|
+
# which runs on and releases it when it finishes.
|
|
346
|
+
def return_retry_lock(retry_lock)
|
|
347
|
+
return unless retry_lock
|
|
348
|
+
|
|
349
|
+
key, delivery, stamp = retry_lock
|
|
350
|
+
UniquenessKey.bind!(key, queue_name: Uniqueness.lock_queue_name(delivery[:queue_name]),
|
|
351
|
+
msg_id: delivery[:msg_id], acquired_at: stamp)
|
|
352
|
+
rescue StandardError => e
|
|
353
|
+
Pgbus.logger.warn { "[Pgbus] Uniqueness return to the attempt failed for #{key}: #{e.message}" }
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# The row is gone (released by an operator, reaped) or a successor's:
|
|
357
|
+
# the retry runs without the key, and a fresh enqueue is not deduped
|
|
358
|
+
# against it.
|
|
359
|
+
def warn_retry_lock_not_held(key)
|
|
360
|
+
Pgbus.logger.warn do
|
|
361
|
+
"[Pgbus] Uniqueness key #{key} is no longer held by the attempt that enqueued its retry; " \
|
|
362
|
+
"the retry runs without it"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
288
366
|
# Reverses inject_batch_metadata for a job discarded at enqueue time
|
|
289
367
|
# (uniqueness duplicate or concurrency :discard conflict): the message is
|
|
290
368
|
# never sent, so it can never signal completion. Only applies to a job
|
|
@@ -4,23 +4,30 @@ module Pgbus
|
|
|
4
4
|
module ActiveJob
|
|
5
5
|
# The PGMQ delivery of the job running in this fiber:
|
|
6
6
|
#
|
|
7
|
-
# Pgbus::ActiveJob::Delivery.current
|
|
7
|
+
# Pgbus::ActiveJob::Delivery.current
|
|
8
|
+
# # => { msg_id: 42, read_ct: 2, queue_name: "default",
|
|
9
|
+
# # enqueued_at: "2026-10-18 10:30:00+00", job_id: "8f0c…" }
|
|
8
10
|
#
|
|
9
11
|
# Set by Executor#execute around perform_now and cleared after, also when
|
|
10
12
|
# the job raises; nil outside the executor (perform_now in a test, a
|
|
11
13
|
# top-level inline-adapter job). A job run inline from inside an executing
|
|
12
14
|
# job sees the enclosing job's delivery; StepJob only acts on it when the
|
|
13
|
-
# step row's msg_id matches,
|
|
15
|
+
# step row's msg_id matches, and the adapter only when job_id is its own,
|
|
16
|
+
# so that is harmless. Fiber-local (Thread.current[]), so concurrent jobs under
|
|
14
17
|
# execution_mode: :async each see their own. Pgbus::Workflow::StepJob reads
|
|
15
|
-
# read_ct to tell a redelivery after a worker died from a first delivery
|
|
18
|
+
# read_ct to tell a redelivery after a worker died from a first delivery;
|
|
19
|
+
# the adapter reads the message identity and enqueued_at to move the
|
|
20
|
+
# job's uniqueness lock onto a retry the job enqueues (issue #564).
|
|
16
21
|
#
|
|
17
|
-
# The
|
|
22
|
+
# The values sit in one fiber-local each and the Hash is built only
|
|
18
23
|
# when read: setting them allocates nothing, which keeps the executor's
|
|
19
24
|
# per-job allocation budget (spec/pgbus/active_job/executor_allocation_budget_spec.rb).
|
|
20
25
|
module Delivery
|
|
21
26
|
MSG_ID_KEY = :pgbus_delivery_msg_id
|
|
22
27
|
READ_CT_KEY = :pgbus_delivery_read_ct
|
|
23
28
|
QUEUE_NAME_KEY = :pgbus_delivery_queue_name
|
|
29
|
+
ENQUEUED_AT_KEY = :pgbus_delivery_enqueued_at
|
|
30
|
+
JOB_ID_KEY = :pgbus_delivery_job_id
|
|
24
31
|
|
|
25
32
|
module_function
|
|
26
33
|
|
|
@@ -28,21 +35,28 @@ module Pgbus
|
|
|
28
35
|
msg_id = Thread.current[MSG_ID_KEY]
|
|
29
36
|
return nil if msg_id.nil?
|
|
30
37
|
|
|
31
|
-
{ msg_id: msg_id, read_ct: Thread.current[READ_CT_KEY], queue_name: Thread.current[QUEUE_NAME_KEY]
|
|
38
|
+
{ msg_id: msg_id, read_ct: Thread.current[READ_CT_KEY], queue_name: Thread.current[QUEUE_NAME_KEY],
|
|
39
|
+
enqueued_at: Thread.current[ENQUEUED_AT_KEY], job_id: Thread.current[JOB_ID_KEY] }
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
def with(msg_id:, read_ct:, queue_name:)
|
|
42
|
+
def with(msg_id:, read_ct:, queue_name:, enqueued_at: nil, job_id: nil)
|
|
35
43
|
previous_msg_id = Thread.current[MSG_ID_KEY]
|
|
36
44
|
previous_read_ct = Thread.current[READ_CT_KEY]
|
|
37
45
|
previous_queue_name = Thread.current[QUEUE_NAME_KEY]
|
|
46
|
+
previous_enqueued_at = Thread.current[ENQUEUED_AT_KEY]
|
|
47
|
+
previous_job_id = Thread.current[JOB_ID_KEY]
|
|
38
48
|
Thread.current[MSG_ID_KEY] = msg_id
|
|
39
49
|
Thread.current[READ_CT_KEY] = read_ct
|
|
40
50
|
Thread.current[QUEUE_NAME_KEY] = queue_name
|
|
51
|
+
Thread.current[ENQUEUED_AT_KEY] = enqueued_at
|
|
52
|
+
Thread.current[JOB_ID_KEY] = job_id
|
|
41
53
|
yield
|
|
42
54
|
ensure
|
|
43
55
|
Thread.current[MSG_ID_KEY] = previous_msg_id
|
|
44
56
|
Thread.current[READ_CT_KEY] = previous_read_ct
|
|
45
57
|
Thread.current[QUEUE_NAME_KEY] = previous_queue_name
|
|
58
|
+
Thread.current[ENQUEUED_AT_KEY] = previous_enqueued_at
|
|
59
|
+
Thread.current[JOB_ID_KEY] = previous_job_id
|
|
46
60
|
end
|
|
47
61
|
end
|
|
48
62
|
end
|