pgbus 0.19.3 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +104 -9
  4. data/Rakefile +6 -1
  5. data/app/controllers/pgbus/dashboard_controller.rb +2 -0
  6. data/app/controllers/pgbus/workflow_definitions_controller.rb +28 -0
  7. data/app/controllers/pgbus/workflows_controller.rb +197 -0
  8. data/app/frontend/pgbus/modules/charts.js +130 -4
  9. data/app/frontend/pgbus/modules/charts.min.js +1 -1
  10. data/app/frontend/pgbus/modules/charts.min.js.map +1 -1
  11. data/app/frontend/pgbus/style.css +1 -1
  12. data/app/helpers/pgbus/button_helper.rb +4 -2
  13. data/app/helpers/pgbus/workflow_insights_helper.rb +129 -0
  14. data/app/helpers/pgbus/workflows_helper.rb +165 -0
  15. data/app/models/pgbus/workflow_entry.rb +172 -0
  16. data/app/models/pgbus/workflow_event.rb +43 -0
  17. data/app/models/pgbus/workflow_signal.rb +77 -0
  18. data/app/models/pgbus/workflow_step.rb +142 -0
  19. data/app/views/layouts/pgbus/application.html.erb +2 -0
  20. data/app/views/pgbus/dashboard/_stats_cards.html.erb +17 -0
  21. data/app/views/pgbus/workflows/_actions.html.erb +59 -0
  22. data/app/views/pgbus/workflows/_events_table.html.erb +24 -0
  23. data/app/views/pgbus/workflows/_filters.html.erb +29 -0
  24. data/app/views/pgbus/workflows/_signal_form.html.erb +31 -0
  25. data/app/views/pgbus/workflows/_signals_table.html.erb +40 -0
  26. data/app/views/pgbus/workflows/_status_filter.html.erb +12 -0
  27. data/app/views/pgbus/workflows/_steps_table.html.erb +79 -0
  28. data/app/views/pgbus/workflows/_timeline.html.erb +39 -0
  29. data/app/views/pgbus/workflows/_workflows_table.html.erb +59 -0
  30. data/app/views/pgbus/workflows/attention.html.erb +88 -0
  31. data/app/views/pgbus/workflows/definitions/_by_revision.html.erb +52 -0
  32. data/app/views/pgbus/workflows/definitions/_duration_table.html.erb +18 -0
  33. data/app/views/pgbus/workflows/definitions/_funnel.html.erb +49 -0
  34. data/app/views/pgbus/workflows/definitions/_pipeline.html.erb +16 -0
  35. data/app/views/pgbus/workflows/definitions/_step_card.html.erb +31 -0
  36. data/app/views/pgbus/workflows/definitions/_throughput_table.html.erb +32 -0
  37. data/app/views/pgbus/workflows/definitions/_window.html.erb +17 -0
  38. data/app/views/pgbus/workflows/definitions/index.html.erb +71 -0
  39. data/app/views/pgbus/workflows/definitions/not_found.html.erb +5 -0
  40. data/app/views/pgbus/workflows/definitions/show.html.erb +80 -0
  41. data/app/views/pgbus/workflows/index.html.erb +15 -0
  42. data/app/views/pgbus/workflows/not_found.html.erb +5 -0
  43. data/app/views/pgbus/workflows/show.html.erb +93 -0
  44. data/config/i18n-tasks.yml +6 -0
  45. data/config/locales/da.yml +442 -0
  46. data/config/locales/de.yml +442 -0
  47. data/config/locales/en.yml +442 -0
  48. data/config/locales/es.yml +442 -0
  49. data/config/locales/fi.yml +442 -0
  50. data/config/locales/fr.yml +442 -0
  51. data/config/locales/it.yml +442 -0
  52. data/config/locales/ja.yml +442 -0
  53. data/config/locales/nb.yml +442 -0
  54. data/config/locales/nl.yml +442 -0
  55. data/config/locales/pt.yml +442 -0
  56. data/config/locales/sv.yml +442 -0
  57. data/config/routes.rb +26 -0
  58. data/lib/generators/pgbus/add_workflow_signals_generator.rb +56 -0
  59. data/lib/generators/pgbus/add_workflows_generator.rb +54 -0
  60. data/lib/generators/pgbus/templates/add_workflow_signals.rb.erb +51 -0
  61. data/lib/generators/pgbus/templates/add_workflows.rb.erb +150 -0
  62. data/lib/generators/pgbus/templates/migration.rb.erb +127 -0
  63. data/lib/pgbus/active_job/adapter.rb +67 -26
  64. data/lib/pgbus/active_job/batch_id.rb +23 -1
  65. data/lib/pgbus/active_job/delivery.rb +49 -0
  66. data/lib/pgbus/active_job/executor.rb +3 -1
  67. data/lib/pgbus/batch/builder.rb +1 -0
  68. data/lib/pgbus/batch.rb +35 -3
  69. data/lib/pgbus/configuration/workflow_settings.rb +99 -0
  70. data/lib/pgbus/configuration.rb +21 -3
  71. data/lib/pgbus/generators/migration_detector.rb +40 -0
  72. data/lib/pgbus/instrumentation.rb +32 -0
  73. data/lib/pgbus/integrations/appsignal/dashboard.json +128 -1
  74. data/lib/pgbus/integrations/appsignal/event_span.rb +43 -0
  75. data/lib/pgbus/integrations/appsignal/job_span.rb +47 -0
  76. data/lib/pgbus/integrations/appsignal/span_subscriber.rb +128 -0
  77. data/lib/pgbus/integrations/appsignal/subscriber.rb +50 -100
  78. data/lib/pgbus/mcp/rack_app.rb +7 -2
  79. data/lib/pgbus/mcp/runner.rb +2 -1
  80. data/lib/pgbus/mcp/serialization.rb +25 -0
  81. data/lib/pgbus/mcp/server.rb +53 -10
  82. data/lib/pgbus/mcp/tools/deploy_tasks_tool.rb +1 -10
  83. data/lib/pgbus/mcp/tools/workflow_cancel_tool.rb +23 -0
  84. data/lib/pgbus/mcp/tools/workflow_definitions_tool.rb +36 -0
  85. data/lib/pgbus/mcp/tools/workflow_detail_tool.rb +38 -0
  86. data/lib/pgbus/mcp/tools/workflow_resume_at_tool.rb +32 -0
  87. data/lib/pgbus/mcp/tools/workflow_resume_tool.rb +24 -0
  88. data/lib/pgbus/mcp/tools/workflow_run_now_tool.rb +23 -0
  89. data/lib/pgbus/mcp/tools/workflow_signal_tool.rb +77 -0
  90. data/lib/pgbus/mcp/tools/workflow_skip_step_tool.rb +23 -0
  91. data/lib/pgbus/mcp/tools/workflow_stats_tool.rb +42 -0
  92. data/lib/pgbus/mcp/tools/workflows_attention_tool.rb +42 -0
  93. data/lib/pgbus/mcp/tools/workflows_tool.rb +60 -0
  94. data/lib/pgbus/mcp/workflow_insights_payload.rb +15 -0
  95. data/lib/pgbus/mcp/workflow_payloads.rb +53 -0
  96. data/lib/pgbus/mcp/write_tool.rb +134 -0
  97. data/lib/pgbus/mcp.rb +15 -0
  98. data/lib/pgbus/metrics/backends/statsd.rb +6 -0
  99. data/lib/pgbus/metrics/subscriber.rb +14 -1
  100. data/lib/pgbus/metrics/workflow_metrics.rb +65 -0
  101. data/lib/pgbus/process/dispatcher.rb +26 -0
  102. data/lib/pgbus/version.rb +1 -1
  103. data/lib/pgbus/web/data_source/workflow_actions.rb +109 -0
  104. data/lib/pgbus/web/data_source/workflow_insights.rb +99 -0
  105. data/lib/pgbus/web/data_source/workflow_list.rb +248 -0
  106. data/lib/pgbus/web/data_source.rb +3 -0
  107. data/lib/pgbus/web/workflow_action_plan.rb +62 -0
  108. data/lib/pgbus/web/workflow_insights_queries.rb +250 -0
  109. data/lib/pgbus/web/workflow_insights_report.rb +171 -0
  110. data/lib/pgbus/web/workflow_payload.rb +36 -0
  111. data/lib/pgbus/web/workflow_query.rb +82 -0
  112. data/lib/pgbus/web/workflow_state.rb +76 -0
  113. data/lib/pgbus/web/workflow_timeline.rb +113 -0
  114. data/lib/pgbus/web/workflow_window.rb +51 -0
  115. data/lib/pgbus/workflow/batch_finished_job.rb +21 -0
  116. data/lib/pgbus/workflow/dsl.rb +105 -0
  117. data/lib/pgbus/workflow/error_capture.rb +63 -0
  118. data/lib/pgbus/workflow/exception_policy.rb +170 -0
  119. data/lib/pgbus/workflow/fan_out.rb +157 -0
  120. data/lib/pgbus/workflow/fan_out_definition.rb +41 -0
  121. data/lib/pgbus/workflow/flow_control.rb +94 -0
  122. data/lib/pgbus/workflow/instance.rb +183 -0
  123. data/lib/pgbus/workflow/outside_verbs.rb +138 -0
  124. data/lib/pgbus/workflow/payload.rb +53 -0
  125. data/lib/pgbus/workflow/registry.rb +53 -0
  126. data/lib/pgbus/workflow/removed_step.rb +14 -0
  127. data/lib/pgbus/workflow/runner.rb +218 -0
  128. data/lib/pgbus/workflow/signal_matcher.rb +53 -0
  129. data/lib/pgbus/workflow/signals.rb +132 -0
  130. data/lib/pgbus/workflow/states.rb +119 -0
  131. data/lib/pgbus/workflow/step_context.rb +58 -0
  132. data/lib/pgbus/workflow/step_definition.rb +131 -0
  133. data/lib/pgbus/workflow/step_execution.rb +242 -0
  134. data/lib/pgbus/workflow/step_job.rb +28 -0
  135. data/lib/pgbus/workflow/step_outcome.rb +151 -0
  136. data/lib/pgbus/workflow/steps.rb +103 -0
  137. data/lib/pgbus/workflow/sweep.rb +156 -0
  138. data/lib/pgbus/workflow/test_helpers.rb +152 -0
  139. data/lib/pgbus/workflow/wait_timeout.rb +73 -0
  140. data/lib/pgbus/workflow/waiting.rb +82 -0
  141. data/lib/pgbus/workflow.rb +183 -0
  142. data/sig/pgbus/configuration.rbs +22 -0
  143. metadata +95 -1
@@ -140,6 +140,11 @@ sv:
140
140
  throughput: Genomströmning
141
141
  throughput_detail: jobb/min under de senaste %{minutes} min
142
142
  throughput_unavailable: kräver jobbstatistik (rails g pgbus:add_job_stats)
143
+ workflows: Arbetsflöden
144
+ workflows_attention:
145
+ one: "%{count} behöver uppmärksamhet"
146
+ other: "%{count} behöver uppmärksamhet"
147
+ workflows_detail: pågående (%{paused} pausade)
143
148
  verdict:
144
149
  links:
145
150
  dlq: Dead letters
@@ -723,6 +728,7 @@ sv:
723
728
  processes: Processer
724
729
  queues: Köer
725
730
  recurring: Återkommande
731
+ workflows: Arbetsflöden
726
732
  return_to_app: Tillbaka till appen
727
733
  title: Pgbus-instrumentpanel
728
734
  toggle_dark_mode: Växla mörkt läge
@@ -1023,3 +1029,439 @@ sv:
1023
1029
  disabled: Uppgift inaktiverad
1024
1030
  enabled: Uppgift aktiverad
1025
1031
  failed: Kunde inte ändra uppgift
1032
+ workflow_insights:
1033
+ back_to_classes: Alla arbetsflödesklasser
1034
+ back_to_runs: Alla arbetsflödeskörningar
1035
+ bad_request: 'Felaktig begäran: %{reason}'
1036
+ capped: 'Begränsat till workflow_retention (%{retention}): slutförda och avbrutna körningar äldre än så raderas.'
1037
+ few_samples:
1038
+ one: n=%{count}, få mätvärden
1039
+ other: n=%{count}, få mätvärden
1040
+ index:
1041
+ description: 'Hur varje arbetsflödesklass gick under %{window}: körningarna som pågår nu, körningarna som avslutades och hur lång tid en körning tar från start till slut.'
1042
+ empty_hint: 'Definiera en underklass till Pgbus::Workflow och starta en körning: YourWorkflow.start!(subject, **arguments).'
1043
+ empty_title: Ingen arbetsflödesklass har körts än.
1044
+ headers:
1045
+ attention: Behöver åtgärd
1046
+ cancelled: Avbrutna
1047
+ duration_p50: Varaktighet p50
1048
+ duration_p95: Varaktighet p95
1049
+ finished: Slutförda
1050
+ ongoing: Pågående
1051
+ paused: Pausade
1052
+ workflow_class: Arbetsflödesklass
1053
+ title: Arbetsflödesinsikter
1054
+ links:
1055
+ at_step: "%{status} körningar vid %{step}"
1056
+ attention: körningar av %{workflow} som behöver åtgärd
1057
+ cancelled: körningar av %{workflow} som avbröts i fönstret
1058
+ cancelled_bucket: körningar som avbröts under perioden
1059
+ finished: körningar av %{workflow} som slutfördes i fönstret
1060
+ finished_bucket: körningar som slutfördes under perioden
1061
+ ongoing: pågående körningar av %{workflow}
1062
+ paused: pausade körningar av %{workflow}
1063
+ started: körningar av %{workflow} som startade i fönstret
1064
+ started_bucket: körningar som startade under perioden
1065
+ not_found:
1066
+ hint: Ingen underklass till Pgbus::Workflow och ingen körning heter %{workflow} (eller så misslyckades läsningen, se loggen).
1067
+ title: Okänd arbetsflödesklass
1068
+ not_loaded: Definitionen är inte laddad
1069
+ not_loaded_hint: 'Klassen laddas inte längre i appen: stegen kommer från körningarnas rader, i den ordning körningarna nådde dem.'
1070
+ percentile: "%{name} %{value}"
1071
+ removed_in_flight:
1072
+ one: "%{count} körning på borttaget steg %{step}"
1073
+ other: "%{count} körningar på borttaget steg %{step}"
1074
+ show:
1075
+ by_revision:
1076
+ all_steps: Alla steg
1077
+ caption: 'Samma siffror för de fem senaste appversionerna som avslutade ett försök i fönstret, nyast först: en försämring efter en driftsättning syns som en rad.'
1078
+ empty: Inget försök i det här fönstret registrerade en appversion (config.app_revision).
1079
+ headers:
1080
+ attempts: Försök
1081
+ failed: Misslyckade
1082
+ reattempted: Nya försök
1083
+ revision: Version
1084
+ run_time: Körtid
1085
+ skipped: Överhoppade
1086
+ step: Steg
1087
+ seen_html: sedd %{from} till %{to}
1088
+ title: Per appversion
1089
+ worse: upp från %{rate} på %{revision}
1090
+ charts:
1091
+ durations:
1092
+ between: "%{from}–%{to}"
1093
+ caption:
1094
+ one: Hur lång tid den %{count} körning som slutfördes i fönstret tog, från start till slut.
1095
+ other: Hur lång tid de %{count} körningar som slutfördes i fönstret tog, från start till slut.
1096
+ headers:
1097
+ range: Varaktighet
1098
+ runs: Körningar
1099
+ over: "%{from} eller mer"
1100
+ title: Varaktighet från start till slut
1101
+ under: under %{to}
1102
+ series:
1103
+ cancelled: Avbrutna
1104
+ finished: Slutförda
1105
+ runs: Körningar
1106
+ started: Startade
1107
+ throughput:
1108
+ caption: 'Körningar som startade, slutfördes och avbröts per %{bucket} under %{window}: %{started} startade, %{finished} slutförda, %{cancelled} avbrutna.'
1109
+ headers:
1110
+ cancelled: Avbrutna
1111
+ finished: Slutförda
1112
+ from: Från
1113
+ started: Startade
1114
+ title: Körningar per %{bucket}
1115
+ description: 'Under %{window}: var körningarna är nu, hur varje steg presterar och var körningar stannar.'
1116
+ funnel:
1117
+ caption:
1118
+ one: 'Av den %{count} körning som startade i fönstret: hur många som nådde varje steg och var den är nu.'
1119
+ other: 'Av de %{count} körningar som startade i fönstret: hur många som nådde varje steg och var var och en är nu.'
1120
+ empty: Ingen körning startade i det här fönstret.
1121
+ headers:
1122
+ now_here: Nu vid steget
1123
+ reached: Nådde
1124
+ share: Av startade
1125
+ step: Steg
1126
+ here:
1127
+ cancelled:
1128
+ one: "%{count} avbruten"
1129
+ other: "%{count} avbrutna"
1130
+ finished:
1131
+ one: "%{count} slutförd"
1132
+ other: "%{count} slutförda"
1133
+ paused:
1134
+ one: "%{count} pausad"
1135
+ other: "%{count} pausade"
1136
+ performing:
1137
+ one: "%{count} körs"
1138
+ other: "%{count} körs"
1139
+ ready:
1140
+ one: "%{count} schemalagd"
1141
+ other: "%{count} schemalagda"
1142
+ waiting:
1143
+ one: "%{count} väntar"
1144
+ other: "%{count} väntar"
1145
+ title: Tratt
1146
+ pipeline:
1147
+ attempts:
1148
+ one: "%{count} försök"
1149
+ other: "%{count} försök"
1150
+ caption: 'Varje steg i ordning: körningarna som står vid det nu, och hur dess försök som avslutades i fönstret gick.'
1151
+ empty: 'Inga steg än: ingen körning av klassen har ett steg i fönstret och ingen pågår.'
1152
+ failed: Misslyckade
1153
+ kinds:
1154
+ fan_out: Fan-out
1155
+ removed: Borttaget
1156
+ signal: Väntar på en signal
1157
+ undeclared: Inte i definitionen
1158
+ live:
1159
+ paused: Pausade
1160
+ performing: Körs
1161
+ ready: Schemalagda
1162
+ waiting: Väntar
1163
+ now_label: Körningar vid steget nu
1164
+ pickup: Upphämtning
1165
+ reattempted: Nya försök
1166
+ run_time: Körtid
1167
+ skipped: Överhoppade
1168
+ step_link: Pågående körningar vid %{step}
1169
+ title: Pipeline
1170
+ window_label: Försök som avslutades i fönstret
1171
+ summary:
1172
+ attention: Behöver åtgärd
1173
+ cancelled: Avbrutna
1174
+ duration: Varaktighet p50
1175
+ finished: Slutförda
1176
+ ongoing: Pågående
1177
+ started: Startade
1178
+ window_label: Tidsfönster
1179
+ windows:
1180
+ 1h: den senaste timmen
1181
+ 24h: de senaste 24 timmarna
1182
+ 30d: de senaste 30 dagarna
1183
+ 7d: de senaste 7 dagarna
1184
+ workflows:
1185
+ actions:
1186
+ bulk_cancel: Avbryt alla
1187
+ bulk_resume: Återuppta alla
1188
+ cancel: Avbryt
1189
+ rerun: Kör om
1190
+ resume: Återuppta
1191
+ resume_at: Återuppta vid steg
1192
+ run_now: Kör nu
1193
+ signal: Skicka signal
1194
+ skip_step: Hoppa över steg
1195
+ attempt: försök %{attempt}
1196
+ attention:
1197
+ at_step_html: vid %{step}
1198
+ back: "← Arbetsflöden"
1199
+ cancel_all:
1200
+ one: Avbryt alla %{count}
1201
+ other: Avbryt alla %{count}
1202
+ confirm:
1203
+ cancel:
1204
+ one: Avbryta körningen som har fastnat vid %{step}?
1205
+ other: Avbryta alla %{count} körningar som har fastnat vid %{step}?
1206
+ cancel_error:
1207
+ one: Avbryta körningen som har fastnat vid %{step} på grund av %{error}?
1208
+ other: Avbryta alla %{count} körningar som har fastnat vid %{step} på grund av %{error}?
1209
+ resume:
1210
+ one: Återuppta körningen som är pausad vid %{step}?
1211
+ other: Återuppta alla %{count} körningar som är pausade vid %{step}?
1212
+ resume_error:
1213
+ one: Återuppta körningen som är pausad vid %{step} på grund av %{error}?
1214
+ other: Återuppta alla %{count} körningar som är pausade vid %{step} på grund av %{error}?
1215
+ description: Pågående körningar som väntar på en person, grupperade efter vad som stoppade dem. När orsaken är åtgärdad, agera på hela gruppen.
1216
+ empty_hint: Ingen pågående körning är pausad, har fastnat, är försenad eller har väntat längre än workflow_waiting_visibility_threshold.
1217
+ empty_title: Inget behöver uppmärksamhet.
1218
+ first_seen_html: första gången %{time}
1219
+ headers:
1220
+ actions: Åtgärder
1221
+ reason: Orsak
1222
+ runs: Körningar
1223
+ seen: Sedd
1224
+ workflow: Arbetsflöde
1225
+ last_seen_html: senast %{time}
1226
+ only_class_html: Endast %{workflow}.
1227
+ reasons:
1228
+ overdue: 'Försenad: förfallen men inte hämtad'
1229
+ paused_error: Pausad på grund av ett fel
1230
+ paused_manual: Pausad för hand
1231
+ stalled: 'Har fastnat: dess worker svarar inte längre'
1232
+ waiting_overdue: Väntar för länge
1233
+ resume_all:
1234
+ one: Återuppta alla %{count}
1235
+ other: Återuppta alla %{count}
1236
+ show_all: Visa alla klasser
1237
+ show_runs: Visa körningar
1238
+ title: Behöver uppmärksamhet
1239
+ audit:
1240
+ by: "%{event} av %{actor}"
1241
+ empty: Inga händelser registrerade.
1242
+ headers:
1243
+ what: Vad hände
1244
+ when: När
1245
+ title: Granskningslogg
1246
+ with_reason: "%{sentence}: %{reason}"
1247
+ with_step: "%{sentence} (%{step} #%{attempt})"
1248
+ bad_request: 'Ogiltig förfrågan: %{reason}'
1249
+ confirm:
1250
+ cancel: Avbryta det här arbetsflödet? Dess öppna steg avbryts och det körs inte igen.
1251
+ rerun: Starta det här arbetsflödet igen som en ny körning, med samma ämne och argument?
1252
+ resume: Återuppta det här arbetsflödet?
1253
+ resume_at: Återuppta det här arbetsflödet vid det valda steget? Dess öppna steg avbryts först.
1254
+ run_now: Köra det schemalagda steget nu i stället för att vänta?
1255
+ signal: Skicka den här signalen till arbetsflödet?
1256
+ skip_step: Hoppa över steget som det här arbetsflödet väntar på och gå till nästa?
1257
+ events:
1258
+ cancelled: Avbruten
1259
+ fan_out_finished: Fan-out slutförd
1260
+ fan_out_opened: Fan-out öppnad
1261
+ finished: Slutförd
1262
+ late_completion_ignored: Sen slutförning ignorerad
1263
+ paused: Pausad
1264
+ rerun: Omkörd
1265
+ rescheduled: Omschemalagd
1266
+ resumed: Återupptagen
1267
+ run_now: Kör nu
1268
+ signal_claimed: Signal upptagen
1269
+ signal_received: Signal mottagen
1270
+ stall_detected: Stopp upptäckt
1271
+ started: Startad
1272
+ step_completed: Steg slutfört
1273
+ step_failed: Steg misslyckades
1274
+ step_reattempted: Steg försökt igen
1275
+ step_scheduled: Steg schemalagt
1276
+ step_skipped: Steg överhoppat
1277
+ step_started: Steg startat
1278
+ step_waiting: Steg väntar
1279
+ wait_timed_out: Väntan tog slut
1280
+ filters:
1281
+ apply: Använd filter
1282
+ clear: Rensa
1283
+ created_from: Skapad från
1284
+ created_to: Skapad till
1285
+ ended_from: Avslutad från
1286
+ ended_to: Avslutad till
1287
+ error_class: Felklass
1288
+ step: Aktuellt steg
1289
+ subject_id: Ämnes-ID
1290
+ subject_type: Ämnestyp
1291
+ workflow_class: Arbetsflödesklass
1292
+ workflow_id: Arbetsflödes-ID
1293
+ flash:
1294
+ bad_group: Den gruppen är inte giltig.
1295
+ bulk_cancel:
1296
+ one: Avbröt %{count}.
1297
+ other: Avbröt %{count}.
1298
+ bulk_cancel_skipped: Avbröt %{count}; %{skipped} hade redan gått vidare.
1299
+ bulk_failed: "%{count} kunde inte ändras: %{reason}"
1300
+ bulk_more: 'Fler körningar kan vänta: tryck igen för nästa %{cap}.'
1301
+ bulk_resume:
1302
+ one: Återupptog %{count}.
1303
+ other: Återupptog %{count}.
1304
+ bulk_resume_skipped: Återupptog %{count}; %{skipped} hade redan gått vidare.
1305
+ cancel: Arbetsflödet avbröts.
1306
+ choose_step: Välj ett steg att återuppta vid.
1307
+ failed:
1308
+ not_allowed: 'Inte utfört: körningen är nu %{status}.'
1309
+ not_found: Det arbetsflödet finns inte längre.
1310
+ other: "%{action} misslyckades: %{reason}"
1311
+ payload_not_object: 'Inte skickad: payload är inte ett JSON-objekt.'
1312
+ payload_too_large: 'Inte skickad: payload är över %{limit} byte.'
1313
+ rerun: En ny körning startades.
1314
+ resume: Arbetsflödet återupptogs.
1315
+ resume_at: Arbetsflödet återupptogs vid %{step}.
1316
+ run_now: Det schemalagda steget körs nu.
1317
+ signal: Signalen %{name} skickades.
1318
+ signal_duplicate: Signalen %{name} har redan levererats med den här nyckeln; inget nytt skickades.
1319
+ signal_name_missing: 'Inte skickad: signalen behöver ett namn.'
1320
+ skip_step: Steget hoppades över.
1321
+ headers:
1322
+ current_step: Aktuellt steg
1323
+ last_activity: Senaste aktivitet
1324
+ started: Startad
1325
+ status: Status
1326
+ subject: Ämne
1327
+ workflow: Arbetsflöde
1328
+ index:
1329
+ attention_link:
1330
+ one: Behöver uppmärksamhet (%{count})
1331
+ other: Behöver uppmärksamhet (%{count})
1332
+ description: 'Varaktiga körningar i flera steg: vad var och en gör och vad som händer härnäst.'
1333
+ empty_hint: Välj en annan status eller rensa filtren. En körning startar med YourWorkflow.start!(subject, **arguments).
1334
+ empty_title: Inga arbetsflöden matchar.
1335
+ insights_link: Insikter per klass
1336
+ status_label: Filtrera på status
1337
+ title: Arbetsflöden
1338
+ next:
1339
+ cancelled_html: Avbruten %{ago}.
1340
+ due_html: 'Redo: %{step} skulle ha körts %{ago} och väntar på en worker.'
1341
+ finished_html: Slutförd %{ago}.
1342
+ overdue_html: 'Försenad: %{step} skulle ha körts %{ago}; städningen skickar det igen.'
1343
+ paused_error_html: 'Pausad vid %{step}: %{error} — återuppta eller avbryt.'
1344
+ paused_manual_html: Pausad vid %{step} — återuppta eller avbryt.
1345
+ performing_attempt_html: Kör %{step} (försök %{attempt}).
1346
+ performing_html: Kör %{step}.
1347
+ ready_html: 'Redo: %{step} körs härnäst.'
1348
+ scheduled_html: 'Schemalagd: %{step} körs %{time}.'
1349
+ unknown_html: 'Status: %{status}.'
1350
+ waiting_batch_html: Väntar på gruppen %{batch} (%{done} av %{total} jobb klara).
1351
+ waiting_signal_html: Väntar på signalen %{signal} sedan %{ago}.
1352
+ waiting_signal_timeout_html: Väntar på signalen %{signal} sedan %{ago}. Väntan tar slut %{time}.
1353
+ waiting_step_html: Väntar på %{step}.
1354
+ not_found:
1355
+ hint: Det kan ha avslutats och tagits bort efter workflow_retention, eller så kunde databasen inte läsas (se loggen).
1356
+ title: Arbetsflödet hittades inte
1357
+ outcomes:
1358
+ cancelled: avbrutet
1359
+ completed: slutfört
1360
+ finished_early: avslutade körningen i förtid
1361
+ paused: pausade körningen
1362
+ reattempted: försökt igen
1363
+ skipped: hoppat över
1364
+ resume:
1365
+ carry_on: 'Återuppta: fortsätt med %{step}'
1366
+ continue: 'Återuppta: fortsätt med %{step}'
1367
+ finish: 'Återuppta: avsluta körningen'
1368
+ resend: 'Återuppta: skicka %{step} igen'
1369
+ retry: 'Återuppta: försök %{step} igen'
1370
+ rewait: 'Återuppta: vänta på %{step} igen'
1371
+ show:
1372
+ actions: Åtgärder
1373
+ arguments: Argument
1374
+ back: "← Arbetsflöden"
1375
+ cancelled_at: Avbruten
1376
+ class_missing: "%{workflow_class} är inte laddad i den här processen: körningen kan fortfarande avbrytas, men inte återupptas."
1377
+ definition_outdated: 'Den här körningen startade på en äldre definition av %{workflow_class}: dess steg har ändrats sedan start.'
1378
+ details: Detaljer
1379
+ finished_at: Slutförd
1380
+ no_arguments: Den här körningen har inga argument.
1381
+ paused_at: Pausad
1382
+ reason_hint: Sparas i granskningsloggen, bredvid vem som gjorde det.
1383
+ reason_label: Orsak (valfri)
1384
+ rerun_of: Omkörning av
1385
+ reruns: Omkörd som
1386
+ resume_at_label: Steg att återuppta vid
1387
+ signal:
1388
+ hint: Ett steg som väntar på den vaknar direkt; annars ligger den väntande tills ett steg tar den.
1389
+ name: Signalnamn
1390
+ payload: Payload (JSON)
1391
+ send: Skicka signal
1392
+ title: Skicka en signal
1393
+ signal_statuses:
1394
+ claimed: Upptagen
1395
+ consumed: Förbrukad
1396
+ pending: Väntande
1397
+ signals:
1398
+ empty: Inga signaler har levererats till den här körningen.
1399
+ headers:
1400
+ claimed: Upptagen
1401
+ consumed: Förbrukad
1402
+ idempotency_key: Idempotensnyckel
1403
+ name: Signal
1404
+ received: Mottagen
1405
+ status: Status
1406
+ payload: Payload
1407
+ title: Signaler
1408
+ statuses:
1409
+ cancelled: Avbruten
1410
+ finished: Slutförd
1411
+ gone: borta
1412
+ paused: Pausad
1413
+ performing: Körs
1414
+ ready: Redo
1415
+ scheduled: Schemalagd
1416
+ waiting: Väntar
1417
+ step_statuses:
1418
+ cancelled: Avbrutet
1419
+ completed: Slutfört
1420
+ failed: Misslyckat
1421
+ in_progress: Körs
1422
+ scheduled: Schemalagt
1423
+ skipped: Hoppat över
1424
+ waiting: Väntar
1425
+ steps:
1426
+ batch: grupp %{batch}
1427
+ caused_by: Orsakad av %{error}
1428
+ empty: Inga försök ännu.
1429
+ headers:
1430
+ finished: Slutförd
1431
+ queued: Köad
1432
+ ran: Körde
1433
+ scheduled: Schemalagd
1434
+ started: Startad
1435
+ status: Status
1436
+ step: Steg
1437
+ waited: Väntade
1438
+ worker: Worker
1439
+ revision: revision %{revision}
1440
+ show_backtrace: Visa backtrace
1441
+ title: Försök
1442
+ waiting_for: väntar sedan %{duration}
1443
+ waits_for_html: väntar på %{signal}
1444
+ tabs:
1445
+ all: Alla
1446
+ cancelled: Avbrutna
1447
+ finished: Slutförda
1448
+ ongoing: Pågående
1449
+ paused: Pausade
1450
+ performing: Körs
1451
+ ready: Redo
1452
+ waiting: Väntar
1453
+ 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.'
1455
+ empty: Inga försök ännu.
1456
+ paused_period_html: Pausad %{from} i %{duration}
1457
+ paused_row: Pausad
1458
+ series:
1459
+ paused: Pausad
1460
+ queued: Köad
1461
+ ran_cancelled: Körde, avbröts
1462
+ ran_failed: Körde, misslyckades
1463
+ ran_ok: Körde
1464
+ ran_open: Körs
1465
+ waited: Väntade
1466
+ waiting: Väntar
1467
+ title: Tidslinje
data/config/routes.rb CHANGED
@@ -53,6 +53,32 @@ Pgbus::Engine.routes.draw do
53
53
  end
54
54
 
55
55
  resources :batches, only: %i[index show]
56
+
57
+ # The workflow insights (issue #549), declared before the workflows
58
+ # resource so /workflows/definitions is never read as a workflow_id. The
59
+ # class name is a constant path: OrderWorkflow or Billing::OrderWorkflow.
60
+ get "workflows/definitions", to: "workflow_definitions#index", as: :workflow_definitions
61
+ get "workflows/definitions/:workflow_class", to: "workflow_definitions#show", as: :workflow_definition,
62
+ constraints: { workflow_class: /[A-Z][A-Za-z0-9_]*(::[A-Z][A-Za-z0-9_]*)*/ }
63
+
64
+ # attention is a collection route, declared before the member routes so
65
+ # /workflows/attention is never read as a workflow_id.
66
+ resources :workflows, only: %i[index show], param: :workflow_id do
67
+ collection do
68
+ get :attention
69
+ post :bulk_resume
70
+ post :bulk_cancel
71
+ end
72
+ member do
73
+ post :resume
74
+ post :resume_at
75
+ post :run_now
76
+ post :signal
77
+ post :rerun
78
+ post :cancel
79
+ post :skip_step
80
+ end
81
+ end
56
82
  resources :processes, only: [:index]
57
83
 
58
84
  resources :events, only: %i[index show] do
@@ -0,0 +1,56 @@
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 AddWorkflowSignalsGenerator < Rails::Generators::Base
10
+ include ActiveRecord::Generators::Migration
11
+ include MigrationPath
12
+
13
+ source_root File.expand_path("templates", __dir__)
14
+
15
+ desc "Add waiting workflow steps (fan_out, wait_for: signals, wait timeouts) to an installation " \
16
+ "that ran pgbus:add_workflows before them: the pgbus_workflow_signals table and five step columns"
17
+
18
+ class_option :database,
19
+ type: :string,
20
+ default: nil,
21
+ desc: "Use a separate database for pgbus tables (e.g. --database=pgbus)"
22
+
23
+ def create_migration_file
24
+ migration_template "add_workflow_signals.rb.erb",
25
+ File.join(pgbus_migrate_path, "add_pgbus_workflow_signals.rb")
26
+ end
27
+
28
+ def display_post_install
29
+ say ""
30
+ say "Pgbus workflow signals installed!", :green
31
+ say ""
32
+ say "Next steps:"
33
+ say " 1. Run: rails db:migrate#{migrate_command_suffix}"
34
+ say " 2. Restart pgbus: bin/pgbus start"
35
+ say ""
36
+ say "Wait for an outside event, or for a group of jobs:"
37
+ say " step :capture, wait_for: :payment_confirmed, wait_timeout: 2.days do"
38
+ say " PaymentGateway.capture(subject, received_signal.payload[\"amount\"])"
39
+ say " end"
40
+ say ""
41
+ say " fan_out :notify_vendors do |b|"
42
+ say " b.add(*subject.vendors.map { |vendor| NotifyVendorJob.new(vendor.id) })"
43
+ say " end"
44
+ say ""
45
+ say " workflow.signal!(:payment_confirmed, payload: { amount: 42 }, idempotency_key: event_id)"
46
+ say ""
47
+ end
48
+
49
+ private
50
+
51
+ def migration_version
52
+ "[#{ActiveRecord::Migration.current_version}]"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,54 @@
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 AddWorkflowsGenerator < Rails::Generators::Base
10
+ include ActiveRecord::Generators::Migration
11
+ include MigrationPath
12
+
13
+ source_root File.expand_path("templates", __dir__)
14
+
15
+ desc "Add the workflow tables (pgbus_workflows, pgbus_workflow_steps, pgbus_workflow_events) " \
16
+ "to an existing Pgbus installation"
17
+
18
+ class_option :database,
19
+ type: :string,
20
+ default: nil,
21
+ desc: "Use a separate database for pgbus tables (e.g. --database=pgbus)"
22
+
23
+ def create_migration_file
24
+ migration_template "add_workflows.rb.erb",
25
+ File.join(pgbus_migrate_path, "add_pgbus_workflows.rb")
26
+ end
27
+
28
+ def display_post_install
29
+ say ""
30
+ say "Pgbus workflows installed!", :green
31
+ say ""
32
+ say "Next steps:"
33
+ say " 1. Run: rails db:migrate#{migrate_command_suffix}"
34
+ say " 2. Restart pgbus: bin/pgbus start"
35
+ say ""
36
+ say "Define a workflow:"
37
+ say " class SignupWorkflow < Pgbus::Workflow"
38
+ say " step :welcome do"
39
+ say " WelcomeMailer.with(user: subject).welcome.deliver_later"
40
+ say " end"
41
+ say " end"
42
+ say ""
43
+ say " SignupWorkflow.start!(user)"
44
+ say ""
45
+ end
46
+
47
+ private
48
+
49
+ def migration_version
50
+ "[#{ActiveRecord::Migration.current_version}]"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,51 @@
1
+ class AddPgbusWorkflowSignals < ActiveRecord::Migration<%= migration_version %>
2
+ # Waiting workflow steps (fan_out, wait_for: signals, wait timeouts) for an
3
+ # installation that ran pgbus:add_workflows before them. if_not_exists
4
+ # throughout, so pgbus:update can queue it on a half-migrated schema.
5
+ def up
6
+ add_column :pgbus_workflow_steps, :batch_id, :string, if_not_exists: true # fan_out: the batch this step waits on
7
+ add_column :pgbus_workflow_steps, :wait_for, :string, if_not_exists: true # the signal a parked step waits for
8
+ add_column :pgbus_workflow_steps, :signal_id, :bigint, if_not_exists: true # the signal it claimed
9
+ add_column :pgbus_workflow_steps, :waiting_since, :datetime, if_not_exists: true
10
+ add_column :pgbus_workflow_steps, :timeout_at, :datetime, if_not_exists: true # waiting_since + wait_timeout:
11
+ add_index :pgbus_workflow_steps, :batch_id,
12
+ name: "idx_pgbus_workflow_steps_batch", if_not_exists: true
13
+ add_index :pgbus_workflow_steps, [:status, :timeout_at],
14
+ name: "idx_pgbus_workflow_steps_timeout", if_not_exists: true
15
+
16
+ # One row per signal delivered to a run (Pgbus::WorkflowSignal):
17
+ # pending → claimed → consumed.
18
+ create_table :pgbus_workflow_signals, if_not_exists: true do |t|
19
+ t.string :workflow_id, null: false
20
+ t.string :name, null: false
21
+ t.jsonb :payload, null: false, default: {}
22
+ t.string :idempotency_key
23
+ t.string :status, null: false, default: "pending"
24
+ t.bigint :claimed_by_step_id
25
+ t.datetime :created_at, null: false
26
+ t.datetime :claimed_at
27
+ t.datetime :consumed_at
28
+ end
29
+
30
+ add_index :pgbus_workflow_signals, [:workflow_id, :idempotency_key],
31
+ unique: true, where: "idempotency_key IS NOT NULL",
32
+ name: "idx_pgbus_workflow_signals_idempotency", if_not_exists: true
33
+ add_index :pgbus_workflow_signals, [:workflow_id, :name, :status],
34
+ name: "idx_pgbus_workflow_signals_pending", if_not_exists: true
35
+ add_index :pgbus_workflow_signals, [:workflow_id, :status, :claimed_by_step_id],
36
+ name: "idx_pgbus_workflow_signals_claimed", if_not_exists: true
37
+ add_foreign_key :pgbus_workflow_signals, :pgbus_workflows,
38
+ column: :workflow_id, primary_key: :workflow_id, on_delete: :cascade, if_not_exists: true
39
+ end
40
+
41
+ def down
42
+ drop_table :pgbus_workflow_signals, if_exists: true
43
+ remove_index :pgbus_workflow_steps, name: "idx_pgbus_workflow_steps_timeout", if_exists: true
44
+ remove_index :pgbus_workflow_steps, name: "idx_pgbus_workflow_steps_batch", if_exists: true
45
+ remove_column :pgbus_workflow_steps, :timeout_at, if_exists: true
46
+ remove_column :pgbus_workflow_steps, :waiting_since, if_exists: true
47
+ remove_column :pgbus_workflow_steps, :signal_id, if_exists: true
48
+ remove_column :pgbus_workflow_steps, :wait_for, if_exists: true
49
+ remove_column :pgbus_workflow_steps, :batch_id, if_exists: true
50
+ end
51
+ end