pgbus 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e16e68e83d8c1963b60a9c2137d3fbd737512b7e0fed1566b5f1320ef33c470
4
- data.tar.gz: 03d54ebcc0357a61b71bbb3f11fcafa662114245c3fedc33b58a83d40718ffc6
3
+ metadata.gz: 6674a7ff09a30f7bc774f46d1f43754e3371b8831422251cbe97b6f660ce6d78
4
+ data.tar.gz: 5993390e26fb669327a037a7e6885fad88f7d9ab6985f6fbdbdd88d94175608d
5
5
  SHA512:
6
- metadata.gz: 5579b34e30ee64cfff273975174d4023b921f8f64a98c1baa09df0673ccb5ba6a5db73566452a35feead0bb4584d8a969b5513efb34ada4614e2945fbc85790d
7
- data.tar.gz: ea716b54c14023d91d440fb37db87358330ced5671365fd808e0e092a86963d5c2587519d7493e185c51c106a070eb5e49e36d79d441af37c5f2098528aea26e
6
+ metadata.gz: f434f3ab52040cf1ece892472a0b61f0b6af83073e7bab45f467a15649a576870ccec972d6c908be60dd560bfec67ce87acdd6f973ac07e4b51ffee5705e6e34
7
+ data.tar.gz: 11f903c2ad03c2a2f1ae4333bf2ada15a34933e4712ab161e209b519a032d8d90aa163dbf40a101481084f37234f3b5acccf3487844fc06730426f924688d3a2
@@ -33,5 +33,21 @@ module Pgbus
33
33
  data_source.resume_queue(params[:name])
34
34
  redirect_to queue_path(name: params[:name]), notice: "Queue resumed."
35
35
  end
36
+
37
+ def retry_message
38
+ if data_source.retry_job(params[:name], params[:msg_id])
39
+ redirect_back fallback_location: queue_path(name: params[:name]), notice: t("pgbus.queues.show.message_retried")
40
+ else
41
+ redirect_back fallback_location: queue_path(name: params[:name]), alert: t("pgbus.queues.show.message_retry_failed")
42
+ end
43
+ end
44
+
45
+ def discard_message
46
+ if data_source.discard_job(params[:name], params[:msg_id])
47
+ redirect_back fallback_location: queue_path(name: params[:name]), notice: t("pgbus.queues.show.message_discarded")
48
+ else
49
+ redirect_back fallback_location: queue_path(name: params[:name]), alert: t("pgbus.queues.show.message_discard_failed")
50
+ end
51
+ end
36
52
  end
37
53
  end
@@ -140,11 +140,17 @@
140
140
  }, 5000);
141
141
  }
142
142
 
143
- // Render flash toasts from <template> tags
144
- document.querySelectorAll("template[data-pgbus-toast]").forEach(tpl => {
145
- showToast(tpl.content.textContent.trim(), tpl.dataset.pgbusToast);
146
- tpl.remove();
147
- });
143
+ // Render flash toasts from <template> tags.
144
+ // Must run on turbo:load as well — module scripts only execute once,
145
+ // but Turbo Drive replaces the body on navigation.
146
+ function renderFlashToasts() {
147
+ document.querySelectorAll("template[data-pgbus-toast]").forEach(tpl => {
148
+ showToast(tpl.content.textContent.trim(), tpl.dataset.pgbusToast);
149
+ tpl.remove();
150
+ });
151
+ }
152
+ renderFlashToasts();
153
+ document.addEventListener("turbo:load", renderFlashToasts);
148
154
 
149
155
  <% if Pgbus.configuration.web_live_updates %>
150
156
  const interval = <%= Pgbus.configuration.web_refresh_interval %>;
@@ -291,10 +297,10 @@
291
297
  </div>
292
298
  </div>
293
299
  </div>
294
- <div class="flex justify-end space-x-3 px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-b-lg">
300
+ <form method="dialog" class="flex justify-end space-x-3 px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-b-lg">
295
301
  <button value="cancel" class="rounded-md px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-indigo-500"><%= t("pgbus.dialogs.cancel", default: "Cancel") %></button>
296
302
  <button value="confirm" id="pgbus-confirm-btn" class="rounded-md px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-500 focus:outline-none focus:ring-2 focus:ring-red-500"><%= t("pgbus.dialogs.confirm", default: "Confirm") %></button>
297
- </div>
303
+ </form>
298
304
  </dialog>
299
305
 
300
306
  <!-- Alert dialog -->
@@ -309,9 +315,9 @@
309
315
  <p class="text-sm text-gray-700 dark:text-gray-300" id="pgbus-alert-message"></p>
310
316
  </div>
311
317
  </div>
312
- <div class="flex justify-end px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-b-lg">
318
+ <form method="dialog" class="flex justify-end px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-b-lg">
313
319
  <button value="ok" class="rounded-md px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500"><%= t("pgbus.dialogs.ok", default: "OK") %></button>
314
- </div>
320
+ </form>
315
321
  </dialog>
316
322
 
317
323
  <!-- Content -->
@@ -57,6 +57,16 @@
57
57
  <pre class="text-xs text-gray-600 bg-white dark:bg-gray-800 rounded p-2 mt-1 overflow-x-auto"><%= JSON.pretty_generate(JSON.parse(j[:headers])) rescue j[:headers] %></pre>
58
58
  </details>
59
59
  <% end %>
60
+ <div class="flex space-x-2 mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
61
+ <%= button_to t("pgbus.jobs.enqueued_table.retry"), pgbus.retry_message_queue_path(name: j[:queue_name], msg_id: j[:msg_id]),
62
+ method: :post,
63
+ class: "rounded-md bg-indigo-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-indigo-500",
64
+ data: { turbo_confirm: t("pgbus.jobs.enqueued_table.retry_confirm"), turbo_frame: "_top" } %>
65
+ <%= button_to t("pgbus.jobs.enqueued_table.discard"), pgbus.discard_message_queue_path(name: j[:queue_name], msg_id: j[:msg_id]),
66
+ method: :post,
67
+ class: "rounded-md bg-red-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-red-500",
68
+ data: { turbo_confirm: t("pgbus.jobs.enqueued_table.discard_confirm"), turbo_frame: "_top" } %>
69
+ </div>
60
70
  </div>
61
71
  </details>
62
72
  </td>
@@ -46,6 +46,7 @@
46
46
  <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.show.headers.reads") %></th>
47
47
  <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.show.headers.vt") %></th>
48
48
  <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.show.headers.payload") %></th>
49
+ <th class="px-4 py-3 text-right text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.show.headers.actions") %></th>
49
50
  </tr>
50
51
  </thead>
51
52
  <tbody class="divide-y divide-gray-100 dark:divide-gray-700">
@@ -58,10 +59,20 @@
58
59
  <td class="px-4 py-3 text-sm text-gray-600 font-mono text-xs max-w-md truncate">
59
60
  <%= pgbus_json_preview(m[:message]) %>
60
61
  </td>
62
+ <td class="px-4 py-3 text-sm text-right space-x-1 whitespace-nowrap">
63
+ <%= button_to t("pgbus.queues.show.retry"), pgbus.retry_message_queue_path(name: params[:name], msg_id: m[:msg_id]),
64
+ method: :post,
65
+ class: "text-xs text-indigo-600 hover:text-indigo-800 font-medium",
66
+ data: { turbo_confirm: t("pgbus.queues.show.retry_confirm") } %>
67
+ <%= button_to t("pgbus.queues.show.discard"), pgbus.discard_message_queue_path(name: params[:name], msg_id: m[:msg_id]),
68
+ method: :post,
69
+ class: "text-xs text-red-600 hover:text-red-800 font-medium",
70
+ data: { turbo_confirm: t("pgbus.queues.show.discard_confirm") } %>
71
+ </td>
61
72
  </tr>
62
73
  <% end %>
63
74
  <% if @messages.empty? %>
64
- <tr><td colspan="5" class="px-4 py-8 text-center text-sm text-gray-400"><%= t("pgbus.queues.show.empty") %></td></tr>
75
+ <tr><td colspan="6" class="px-4 py-8 text-center text-sm text-gray-400"><%= t("pgbus.queues.show.empty") %></td></tr>
65
76
  <% end %>
66
77
  </tbody>
67
78
  </table>
@@ -177,6 +177,10 @@ da:
177
177
  timezone: 'Tidszone:'
178
178
  visible_at: 'Synlig fra:'
179
179
  title: Job i kø
180
+ discard: Kassér
181
+ discard_confirm: Kassér denne besked?
182
+ retry: Prøv igen
183
+ retry_confirm: Nulstil synlighedstimeout og prøv igen?
180
184
  failed_table:
181
185
  discard: Kassér
182
186
  discard_confirm: Kassér dette job?
@@ -301,18 +305,27 @@ da:
301
305
  delete_confirm: Slet denne kø permanent? Dette kan ikke fortrydes.
302
306
  delete_queue: Slet kø
303
307
  depth: 'Dybde:'
308
+ discard: Kassér
309
+ discard_confirm: Kassér denne besked?
304
310
  empty: Køen er tom
305
311
  headers:
312
+ actions: Handlinger
306
313
  enqueued: Sat i kø
307
314
  id: ID
308
315
  payload: Indhold
309
316
  reads: Læsninger
310
317
  vt: VT
318
+ message_discard_failed: Kunne ikke kassere besked.
319
+ message_discarded: Besked kasseret.
320
+ message_retried: Beskedens synlighed nulstillet.
321
+ message_retry_failed: Kunne ikke prøve besked igen.
311
322
  pause: Pause
312
323
  pause_confirm: Pause behandling?
313
324
  purge_confirm: Rens alle beskeder?
314
325
  purge_queue: Rens kø
315
326
  resume: Genoptag
327
+ retry: Prøv igen
328
+ retry_confirm: Nulstil synlighedstimeout og prøv igen?
316
329
  total: 'Total:'
317
330
  visible: 'Synlig:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ de:
177
177
  timezone: 'Zeitzone:'
178
178
  visible_at: 'Sichtbar ab:'
179
179
  title: Eingereihte Jobs
180
+ discard: Verwerfen
181
+ discard_confirm: Diese Nachricht verwerfen?
182
+ retry: Wiederholen
183
+ retry_confirm: Sichtbarkeits-Timeout zurücksetzen und erneut versuchen?
180
184
  failed_table:
181
185
  discard: Verwerfen
182
186
  discard_confirm: Diesen Job verwerfen?
@@ -301,18 +305,27 @@ de:
301
305
  delete_confirm: Diese Warteschlange dauerhaft löschen? Dies kann nicht rückgängig gemacht werden.
302
306
  delete_queue: Warteschlange löschen
303
307
  depth: 'Tiefe:'
308
+ discard: Verwerfen
309
+ discard_confirm: Diese Nachricht verwerfen?
304
310
  empty: Warteschlange ist leer
305
311
  headers:
312
+ actions: Aktionen
306
313
  enqueued: Eingereiht
307
314
  id: ID
308
315
  payload: Nutzlast
309
316
  reads: Lesevorgänge
310
317
  vt: VT
318
+ message_discard_failed: Nachricht konnte nicht verworfen werden.
319
+ message_discarded: Nachricht verworfen.
320
+ message_retried: Sichtbarkeit der Nachricht zurückgesetzt.
321
+ message_retry_failed: Nachricht konnte nicht wiederholt werden.
311
322
  pause: Pause
312
323
  pause_confirm: Verarbeitung pausieren?
313
324
  purge_confirm: Alle Nachrichten löschen?
314
325
  purge_queue: Warteschlange bereinigen
315
326
  resume: Fortsetzen
327
+ retry: Wiederholen
328
+ retry_confirm: Sichtbarkeits-Timeout zurücksetzen und erneut versuchen?
316
329
  total: 'Insgesamt:'
317
330
  visible: 'Sichtbar:'
318
331
  recurring_tasks:
@@ -176,6 +176,10 @@ en:
176
176
  scheduled: 'Scheduled:'
177
177
  timezone: 'Timezone:'
178
178
  visible_at: 'Visible at:'
179
+ discard: Discard
180
+ discard_confirm: Discard this message?
181
+ retry: Retry
182
+ retry_confirm: Reset visibility timeout and retry?
179
183
  title: Enqueued Jobs
180
184
  failed_table:
181
185
  discard: Discard
@@ -301,18 +305,27 @@ en:
301
305
  delete_confirm: Permanently delete this queue? This cannot be undone.
302
306
  delete_queue: Delete Queue
303
307
  depth: 'Depth:'
308
+ discard: Discard
309
+ discard_confirm: Discard this message?
304
310
  empty: Queue is empty
305
311
  headers:
312
+ actions: Actions
306
313
  enqueued: Enqueued
307
314
  id: ID
308
315
  payload: Payload
309
316
  reads: Reads
310
317
  vt: VT
318
+ message_discard_failed: Could not discard message.
319
+ message_discarded: Message discarded.
320
+ message_retried: Message visibility reset.
321
+ message_retry_failed: Could not retry message.
311
322
  pause: Pause
312
323
  pause_confirm: Pause processing?
313
324
  purge_confirm: Purge all messages?
314
325
  purge_queue: Purge Queue
315
326
  resume: Resume
327
+ retry: Retry
328
+ retry_confirm: Reset visibility timeout and retry?
316
329
  total: 'Total:'
317
330
  visible: 'Visible:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ es:
177
177
  timezone: 'Zona horaria:'
178
178
  visible_at: 'Visible en:'
179
179
  title: Trabajos en Cola
180
+ discard: Descartar
181
+ discard_confirm: "¿Descartar este mensaje?"
182
+ retry: Reintentar
183
+ retry_confirm: "¿Restablecer tiempo de visibilidad y reintentar?"
180
184
  failed_table:
181
185
  discard: Descartar
182
186
  discard_confirm: "¿Descartar este trabajo?"
@@ -301,18 +305,27 @@ es:
301
305
  delete_confirm: "¿Eliminar permanentemente esta cola? Esto no se puede deshacer."
302
306
  delete_queue: Eliminar cola
303
307
  depth: 'Profundidad:'
308
+ discard: Descartar
309
+ discard_confirm: "¿Descartar este mensaje?"
304
310
  empty: La cola está vacía
305
311
  headers:
312
+ actions: Acciones
306
313
  enqueued: Encolado
307
314
  id: ID
308
315
  payload: Carga útil
309
316
  reads: Lecturas
310
317
  vt: VT
318
+ message_discard_failed: No se pudo descartar el mensaje.
319
+ message_discarded: Mensaje descartado.
320
+ message_retried: Visibilidad del mensaje restablecida.
321
+ message_retry_failed: No se pudo reintentar el mensaje.
311
322
  pause: Pausar
312
323
  pause_confirm: "¿Pausar el procesamiento?"
313
324
  purge_confirm: "¿Purgar todos los mensajes?"
314
325
  purge_queue: Purgar cola
315
326
  resume: Reanudar
327
+ retry: Reintentar
328
+ retry_confirm: "¿Restablecer tiempo de visibilidad y reintentar?"
316
329
  total: 'Total:'
317
330
  visible: 'Visible:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ fi:
177
177
  timezone: 'Aikavyöhyke:'
178
178
  visible_at: 'Näkyvissä:'
179
179
  title: Jonotetut työt
180
+ discard: Hylkää
181
+ discard_confirm: Hylätä tämä viesti?
182
+ retry: Yritä uudelleen
183
+ retry_confirm: Nollaa näkyvyysaika ja yritä uudelleen?
180
184
  failed_table:
181
185
  discard: Hylkää
182
186
  discard_confirm: Hylätäänkö tämä työ?
@@ -301,18 +305,27 @@ fi:
301
305
  delete_confirm: Poista tämä jono pysyvästi? Tätä ei voi kumota.
302
306
  delete_queue: Poista jono
303
307
  depth: 'Syvyys:'
308
+ discard: Hylkää
309
+ discard_confirm: Hylätä tämä viesti?
304
310
  empty: Jono on tyhjä
305
311
  headers:
312
+ actions: Toiminnot
306
313
  enqueued: Jonoon lisätty
307
314
  id: ID
308
315
  payload: Kuorma
309
316
  reads: Lukukerrat
310
317
  vt: VT
318
+ message_discard_failed: Viestiä ei voitu hylätä.
319
+ message_discarded: Viesti hylätty.
320
+ message_retried: Viestin näkyvyys nollattu.
321
+ message_retry_failed: Viestiä ei voitu yrittää uudelleen.
311
322
  pause: Tauko
312
323
  pause_confirm: Keskeytetäänkö käsittely?
313
324
  purge_confirm: Tyhjennetäänkö kaikki viestit?
314
325
  purge_queue: Tyhjennä jono
315
326
  resume: Jatka
327
+ retry: Yritä uudelleen
328
+ retry_confirm: Nollaa näkyvyysaika ja yritä uudelleen?
316
329
  total: 'Yhteensä:'
317
330
  visible: 'Näkyvissä:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ fr:
177
177
  timezone: 'Fuseau horaire :'
178
178
  visible_at: 'Visible à :'
179
179
  title: Travaux en file d'attente
180
+ discard: Rejeter
181
+ discard_confirm: Rejeter ce message ?
182
+ retry: Réessayer
183
+ retry_confirm: Réinitialiser le délai de visibilité et réessayer ?
180
184
  failed_table:
181
185
  discard: Ignorer
182
186
  discard_confirm: Ignorer ce travail ?
@@ -301,18 +305,27 @@ fr:
301
305
  delete_confirm: Supprimer définitivement cette file d'attente ? Cette action est irréversible.
302
306
  delete_queue: Supprimer la file d'attente
303
307
  depth: 'Profondeur :'
308
+ discard: Rejeter
309
+ discard_confirm: Rejeter ce message ?
304
310
  empty: La file d'attente est vide
305
311
  headers:
312
+ actions: Actions
306
313
  enqueued: Enregistré
307
314
  id: ID
308
315
  payload: Charge utile
309
316
  reads: Lectures
310
317
  vt: VT
318
+ message_discard_failed: Impossible de rejeter le message.
319
+ message_discarded: Message rejeté.
320
+ message_retried: Visibilité du message réinitialisée.
321
+ message_retry_failed: Impossible de réessayer le message.
311
322
  pause: Pause
312
323
  pause_confirm: Mettre en pause le traitement ?
313
324
  purge_confirm: Purger tous les messages ?
314
325
  purge_queue: Purger la file d'attente
315
326
  resume: Reprendre
327
+ retry: Réessayer
328
+ retry_confirm: Réinitialiser le délai de visibilité et réessayer ?
316
329
  total: 'Total :'
317
330
  visible: 'Visible :'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ it:
177
177
  timezone: 'Fuso orario:'
178
178
  visible_at: 'Visibile alle:'
179
179
  title: Lavori in coda
180
+ discard: Scarta
181
+ discard_confirm: Scartare questo messaggio?
182
+ retry: Riprova
183
+ retry_confirm: Reimpostare il timeout di visibilità e riprovare?
180
184
  failed_table:
181
185
  discard: Scarta
182
186
  discard_confirm: Scartare questo lavoro?
@@ -301,18 +305,27 @@ it:
301
305
  delete_confirm: Eliminare permanentemente questa coda? Questa azione non può essere annullata.
302
306
  delete_queue: Elimina coda
303
307
  depth: 'Profondità:'
308
+ discard: Scarta
309
+ discard_confirm: Scartare questo messaggio?
304
310
  empty: La coda è vuota
305
311
  headers:
312
+ actions: Azioni
306
313
  enqueued: In coda
307
314
  id: ID
308
315
  payload: Carico utile
309
316
  reads: Letture
310
317
  vt: VT
318
+ message_discard_failed: Impossibile scartare il messaggio.
319
+ message_discarded: Messaggio scartato.
320
+ message_retried: Visibilità del messaggio reimpostata.
321
+ message_retry_failed: Impossibile riprovare il messaggio.
311
322
  pause: Pausa
312
323
  pause_confirm: Mettere in pausa l'elaborazione?
313
324
  purge_confirm: Eliminare tutti i messaggi?
314
325
  purge_queue: Pulisci coda
315
326
  resume: Riprendi
327
+ retry: Riprova
328
+ retry_confirm: Reimpostare il timeout di visibilità e riprovare?
316
329
  total: 'Totale:'
317
330
  visible: 'Visibile:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ ja:
177
177
  timezone: タイムゾーン:
178
178
  visible_at: 表示可能日時:
179
179
  title: キューに入れられたジョブ
180
+ discard: 破棄
181
+ discard_confirm: このメッセージを破棄しますか?
182
+ retry: リトライ
183
+ retry_confirm: 可視性タイムアウトをリセットしてリトライしますか?
180
184
  failed_table:
181
185
  discard: 破棄
182
186
  discard_confirm: このジョブを破棄しますか?
@@ -301,18 +305,27 @@ ja:
301
305
  delete_confirm: このキューを完全に削除しますか?この操作は取り消せません。
302
306
  delete_queue: キューを削除
303
307
  depth: 深さ:
308
+ discard: 破棄
309
+ discard_confirm: このメッセージを破棄しますか?
304
310
  empty: キューは空です
305
311
  headers:
312
+ actions: アクション
306
313
  enqueued: エンキュー済み
307
314
  id: ID
308
315
  payload: ペイロード
309
316
  reads: 読み取り回数
310
317
  vt: VT
318
+ message_discard_failed: メッセージを破棄できませんでした。
319
+ message_discarded: メッセージを破棄しました。
320
+ message_retried: メッセージの可視性をリセットしました。
321
+ message_retry_failed: メッセージをリトライできませんでした。
311
322
  pause: 一時停止
312
323
  pause_confirm: 処理を一時停止しますか?
313
324
  purge_confirm: すべてのメッセージを削除しますか?
314
325
  purge_queue: キューをパージ
315
326
  resume: 再開
327
+ retry: リトライ
328
+ retry_confirm: 可視性タイムアウトをリセットしてリトライしますか?
316
329
  total: 合計:
317
330
  visible: 表示中:
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ nb:
177
177
  timezone: 'Tidssone:'
178
178
  visible_at: 'Synlig fra:'
179
179
  title: Kølagte jobber
180
+ discard: Forkast
181
+ discard_confirm: Forkaste denne meldingen?
182
+ retry: Prøv igjen
183
+ retry_confirm: Tilbakestill synlighetstidsavbrudd og prøv igjen?
180
184
  failed_table:
181
185
  discard: Forkast
182
186
  discard_confirm: Forkast denne jobben?
@@ -301,18 +305,27 @@ nb:
301
305
  delete_confirm: Slette denne køen permanent? Dette kan ikke angres.
302
306
  delete_queue: Slett kø
303
307
  depth: 'Dybde:'
308
+ discard: Forkast
309
+ discard_confirm: Forkaste denne meldingen?
304
310
  empty: Køen er tom
305
311
  headers:
312
+ actions: Handlinger
306
313
  enqueued: I kø
307
314
  id: ID
308
315
  payload: Innhold
309
316
  reads: Lesninger
310
317
  vt: VT
318
+ message_discard_failed: Kunne ikke forkaste meldingen.
319
+ message_discarded: Melding forkastet.
320
+ message_retried: Meldingens synlighet tilbakestilt.
321
+ message_retry_failed: Kunne ikke prøve meldingen igjen.
311
322
  pause: Pause
312
323
  pause_confirm: Pause behandling?
313
324
  purge_confirm: Rens alle meldinger?
314
325
  purge_queue: Rens kø
315
326
  resume: Gjenoppta
327
+ retry: Prøv igjen
328
+ retry_confirm: Tilbakestill synlighetstidsavbrudd og prøv igjen?
316
329
  total: 'Totalt:'
317
330
  visible: 'Synlig:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ nl:
177
177
  timezone: 'Tijdzone:'
178
178
  visible_at: 'Zichtbaar op:'
179
179
  title: Taken in de wachtrij
180
+ discard: Verwerpen
181
+ discard_confirm: Dit bericht verwerpen?
182
+ retry: Opnieuw proberen
183
+ retry_confirm: Zichtbaarheidstimeout resetten en opnieuw proberen?
180
184
  failed_table:
181
185
  discard: Verwijderen
182
186
  discard_confirm: Deze taak verwijderen?
@@ -301,18 +305,27 @@ nl:
301
305
  delete_confirm: Deze wachtrij permanent verwijderen? Dit kan niet ongedaan worden gemaakt.
302
306
  delete_queue: Wachtrij verwijderen
303
307
  depth: 'Diepte:'
308
+ discard: Verwerpen
309
+ discard_confirm: Dit bericht verwerpen?
304
310
  empty: Wachtrij is leeg
305
311
  headers:
312
+ actions: Acties
306
313
  enqueued: In de wachtrij geplaatst
307
314
  id: ID
308
315
  payload: Inhoud
309
316
  reads: Lezingen
310
317
  vt: VT
318
+ message_discard_failed: Kon bericht niet verwerpen.
319
+ message_discarded: Bericht verworpen.
320
+ message_retried: Zichtbaarheid van bericht gereset.
321
+ message_retry_failed: Kon bericht niet opnieuw proberen.
311
322
  pause: Pauzeren
312
323
  pause_confirm: Verwerking pauzeren?
313
324
  purge_confirm: Alle berichten verwijderen?
314
325
  purge_queue: Wachtrij opschonen
315
326
  resume: Hervatten
327
+ retry: Opnieuw proberen
328
+ retry_confirm: Zichtbaarheidstimeout resetten en opnieuw proberen?
316
329
  total: 'Totaal:'
317
330
  visible: 'Zichtbaar:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ pt:
177
177
  timezone: 'Fuso horário:'
178
178
  visible_at: 'Visível em:'
179
179
  title: Trabalhos Enfileirados
180
+ discard: Descartar
181
+ discard_confirm: Descartar esta mensagem?
182
+ retry: Tentar novamente
183
+ retry_confirm: Redefinir tempo de visibilidade e tentar novamente?
180
184
  failed_table:
181
185
  discard: Descartar
182
186
  discard_confirm: Descartar este trabalho?
@@ -301,18 +305,27 @@ pt:
301
305
  delete_confirm: Excluir permanentemente esta fila? Esta ação não pode ser desfeita.
302
306
  delete_queue: Excluir fila
303
307
  depth: 'Profundidade:'
308
+ discard: Descartar
309
+ discard_confirm: Descartar esta mensagem?
304
310
  empty: Fila está vazia
305
311
  headers:
312
+ actions: Ações
306
313
  enqueued: Enfileirado
307
314
  id: ID
308
315
  payload: Carga útil
309
316
  reads: Leituras
310
317
  vt: VT
318
+ message_discard_failed: Não foi possível descartar a mensagem.
319
+ message_discarded: Mensagem descartada.
320
+ message_retried: Visibilidade da mensagem redefinida.
321
+ message_retry_failed: Não foi possível tentar novamente a mensagem.
311
322
  pause: Pausar
312
323
  pause_confirm: Pausar processamento?
313
324
  purge_confirm: Limpar todas as mensagens?
314
325
  purge_queue: Limpar fila
315
326
  resume: Retomar
327
+ retry: Tentar novamente
328
+ retry_confirm: Redefinir tempo de visibilidade e tentar novamente?
316
329
  total: 'Total:'
317
330
  visible: 'Visível:'
318
331
  recurring_tasks:
@@ -177,6 +177,10 @@ sv:
177
177
  timezone: 'Tidszon:'
178
178
  visible_at: 'Synlig vid:'
179
179
  title: Köade jobb
180
+ discard: Kassera
181
+ discard_confirm: Kassera detta meddelande?
182
+ retry: Försök igen
183
+ retry_confirm: Återställ synlighetstimeout och försök igen?
180
184
  failed_table:
181
185
  discard: Kassera
182
186
  discard_confirm: Kassera detta jobb?
@@ -301,18 +305,27 @@ sv:
301
305
  delete_confirm: Ta bort denna kö permanent? Detta kan inte ångras.
302
306
  delete_queue: Ta bort kö
303
307
  depth: 'Djup:'
308
+ discard: Kassera
309
+ discard_confirm: Kassera detta meddelande?
304
310
  empty: Kön är tom
305
311
  headers:
312
+ actions: Åtgärder
306
313
  enqueued: Inlagd
307
314
  id: ID
308
315
  payload: Innehåll
309
316
  reads: Läsningar
310
317
  vt: VT
318
+ message_discard_failed: Kunde inte kassera meddelandet.
319
+ message_discarded: Meddelande kasserat.
320
+ message_retried: Meddelandets synlighet återställd.
321
+ message_retry_failed: Kunde inte försöka igen med meddelandet.
311
322
  pause: Pausa
312
323
  pause_confirm: Pausa bearbetning?
313
324
  purge_confirm: Rensa alla meddelanden?
314
325
  purge_queue: Rensa kö
315
326
  resume: Återuppta
327
+ retry: Försök igen
328
+ retry_confirm: Återställ synlighetstimeout och försök igen?
316
329
  total: 'Totalt:'
317
330
  visible: 'Synliga:'
318
331
  recurring_tasks:
data/config/routes.rb CHANGED
@@ -8,6 +8,8 @@ Pgbus::Engine.routes.draw do
8
8
  post :purge
9
9
  post :pause
10
10
  post :resume
11
+ post :retry_message
12
+ post :discard_message
11
13
  end
12
14
  end
13
15
 
@@ -12,11 +12,13 @@ module Pgbus
12
12
  def initialize(queues:, threads: 5, config: Pgbus.configuration,
13
13
  single_active_consumer: false, consumer_priority: 0)
14
14
  @queues = Array(queues)
15
+ @wildcard = @queues.include?("*")
15
16
  @threads = threads
16
17
  @config = config
17
18
  @single_active_consumer = single_active_consumer
18
19
  @consumer_priority = consumer_priority
19
20
  @lifecycle = Lifecycle.new
21
+ @last_wildcard_resolve = nil
20
22
  @jobs_processed = Concurrent::AtomicFixnum.new(0)
21
23
  @jobs_failed = Concurrent::AtomicFixnum.new(0)
22
24
  @in_flight = Concurrent::AtomicFixnum.new(0)
@@ -53,6 +55,7 @@ module Pgbus
53
55
  loop do
54
56
  process_signals
55
57
  check_recycle
58
+ refresh_wildcard_queues
56
59
 
57
60
  break if @lifecycle.stopped?
58
61
  break if @lifecycle.draining? && @pool.queue_length.zero?
@@ -79,6 +82,8 @@ module Pgbus
79
82
  @pool.kill
80
83
  end
81
84
 
85
+ WILDCARD_REFRESH_INTERVAL = 30 # seconds
86
+
82
87
  private
83
88
 
84
89
  def claim_and_execute
@@ -125,7 +130,11 @@ module Pgbus
125
130
  fetch_multi(active_queues, qty)
126
131
  end
127
132
  rescue StandardError => e
128
- Pgbus.logger.error { "[Pgbus] Error fetching messages: #{e.message}" }
133
+ if e.message.include?("does not exist") && e.message.include?("pgmq.q_")
134
+ evict_missing_queues(e)
135
+ else
136
+ Pgbus.logger.error { "[Pgbus] Error fetching messages: #{e.message}" }
137
+ end
129
138
  []
130
139
  end
131
140
 
@@ -184,9 +193,8 @@ module Pgbus
184
193
  end
185
194
 
186
195
  # Resolve "*" to all non-DLQ queues from pgmq.meta, stripping the prefix.
187
- # Called once at startup. If no wildcard, this is a no-op.
188
196
  def resolve_wildcard_queues
189
- return unless @queues.include?("*")
197
+ return unless @wildcard
190
198
 
191
199
  dlq_suffix = config.dead_letter_queue_suffix
192
200
  prefix = "#{config.queue_prefix}_"
@@ -201,12 +209,39 @@ module Pgbus
201
209
  Pgbus.logger.warn { "[Pgbus] Wildcard queue '*' resolved to no queues — falling back to default" }
202
210
  @queues = [config.default_queue]
203
211
  else
212
+ if @last_wildcard_resolve && resolved != @queues
213
+ Pgbus.logger.info { "[Pgbus] Wildcard queues changed: #{@queues.join(", ")} → #{resolved.join(", ")}" }
214
+ end
204
215
  @queues = resolved
205
- Pgbus.logger.info { "[Pgbus] Wildcard queue '*' resolved to: #{@queues.join(", ")}" }
216
+ Pgbus.logger.info { "[Pgbus] Wildcard queue '*' resolved to: #{@queues.join(", ")}" } unless @last_wildcard_resolve
206
217
  end
218
+ @last_wildcard_resolve = monotonic_now
207
219
  rescue StandardError => e
208
220
  Pgbus.logger.error { "[Pgbus] Failed to resolve wildcard queues: #{e.message} — falling back to default" }
209
- @queues = [config.default_queue]
221
+ @queues = [config.default_queue] unless @last_wildcard_resolve
222
+ end
223
+
224
+ # Periodically re-resolve wildcard queues to pick up new queues and
225
+ # drop deleted ones without requiring a worker restart.
226
+ def refresh_wildcard_queues
227
+ return unless @wildcard
228
+ return if @last_wildcard_resolve && (monotonic_now - @last_wildcard_resolve) < WILDCARD_REFRESH_INTERVAL
229
+
230
+ resolve_wildcard_queues
231
+ end
232
+
233
+ # When a "relation does not exist" error occurs, the queue was deleted.
234
+ # Extract the queue name from the error and remove it from the active list.
235
+ def evict_missing_queues(error)
236
+ prefix = "#{config.queue_prefix}_"
237
+ if error.message =~ /pgmq\.q_(\w+)/
238
+ physical_name = Regexp.last_match(1)
239
+ logical_name = physical_name.delete_prefix(prefix)
240
+ if @queues.delete(logical_name)
241
+ Pgbus.logger.warn { "[Pgbus] Evicted deleted queue '#{logical_name}' (#{physical_name}) from worker" }
242
+ end
243
+ end
244
+ Pgbus.logger.error { "[Pgbus] Queue table missing: #{error.message}" }
210
245
  end
211
246
 
212
247
  def check_recycle
@@ -287,6 +322,10 @@ module Pgbus
287
322
  restore_signals
288
323
  Pgbus.logger.info { "[Pgbus] Worker stopped. Processed: #{@jobs_processed.value}, Failed: #{@jobs_failed.value}" }
289
324
  end
325
+
326
+ def monotonic_now
327
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
328
+ end
290
329
  end
291
330
  end
292
331
  end
data/lib/pgbus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgbus
4
- VERSION = "0.2.7"
4
+ VERSION = "0.2.9"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson