pgbus 0.19.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -147
  3. data/README.md +44 -0
  4. data/app/controllers/pgbus/deploy_tasks_controller.rb +38 -0
  5. data/app/helpers/pgbus/deploy_tasks_helper.rb +77 -0
  6. data/app/helpers/pgbus/processes_helper.rb +1 -1
  7. data/app/models/pgbus/deploy_task.rb +104 -0
  8. data/app/models/pgbus/deploy_task_run.rb +45 -0
  9. data/app/views/layouts/pgbus/application.html.erb +2 -0
  10. data/app/views/pgbus/deploy_tasks/_rollover_banner.html.erb +25 -0
  11. data/app/views/pgbus/deploy_tasks/_tasks_table.html.erb +71 -0
  12. data/app/views/pgbus/deploy_tasks/index.html.erb +6 -0
  13. data/app/views/pgbus/deploy_tasks/show.html.erb +134 -0
  14. data/config/i18n-tasks.yml +2 -0
  15. data/config/locales/da.yml +97 -0
  16. data/config/locales/de.yml +97 -0
  17. data/config/locales/en.yml +97 -0
  18. data/config/locales/es.yml +97 -0
  19. data/config/locales/fi.yml +97 -0
  20. data/config/locales/fr.yml +97 -0
  21. data/config/locales/it.yml +97 -0
  22. data/config/locales/ja.yml +97 -0
  23. data/config/locales/nb.yml +97 -0
  24. data/config/locales/nl.yml +97 -0
  25. data/config/locales/pt.yml +97 -0
  26. data/config/locales/sv.yml +97 -0
  27. data/config/routes.rb +7 -0
  28. data/lib/generators/pgbus/add_deploy_tasks_generator.rb +51 -0
  29. data/lib/generators/pgbus/templates/add_deploy_tasks_tables.rb.erb +43 -0
  30. data/lib/generators/pgbus/templates/deploy_tasks.yml.erb +42 -0
  31. data/lib/generators/pgbus/templates/migration.rb.erb +43 -0
  32. data/lib/pgbus/configuration/deploy_settings.rb +46 -0
  33. data/lib/pgbus/configuration.rb +3 -0
  34. data/lib/pgbus/deploy/revision.rb +59 -0
  35. data/lib/pgbus/deploy/rollover.rb +65 -0
  36. data/lib/pgbus/deploy/runner.rb +259 -0
  37. data/lib/pgbus/deploy/task.rb +105 -0
  38. data/lib/pgbus/engine.rb +17 -0
  39. data/lib/pgbus/generators/migration_detector.rb +11 -0
  40. data/lib/pgbus/instrumentation.rb +3 -0
  41. data/lib/pgbus/mcp/server.rb +2 -1
  42. data/lib/pgbus/mcp/tools/deploy_tasks_tool.rb +86 -0
  43. data/lib/pgbus/mcp.rb +1 -0
  44. data/lib/pgbus/outbox/poller.rb +1 -0
  45. data/lib/pgbus/process/consumer.rb +1 -0
  46. data/lib/pgbus/process/dispatcher.rb +18 -1
  47. data/lib/pgbus/process/heartbeat.rb +7 -2
  48. data/lib/pgbus/process/supervisor.rb +35 -3
  49. data/lib/pgbus/process/worker.rb +1 -0
  50. data/lib/pgbus/recurring/scheduler.rb +54 -20
  51. data/lib/pgbus/version.rb +1 -1
  52. data/lib/pgbus/web/data_source/deploy_task_list.rb +114 -0
  53. data/lib/pgbus/web/data_source/list_counts.rb +2 -1
  54. data/lib/pgbus/web/data_source.rb +1 -0
  55. data/lib/pgbus/web/deploy_task_state.rb +51 -0
  56. data/sig/pgbus/configuration.rbs +10 -0
  57. metadata +20 -1
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgbus
4
+ module Web
5
+ class DataSource
6
+ # The Deploy tasks page (issue #536): declared tasks with their last
7
+ # rollover and manual runs, one task's run history, the rollover the
8
+ # scheduler acts on, and the two actions (Run now, Enable/Disable).
9
+ module DeployTaskList
10
+ RUN_HISTORY = 25
11
+
12
+ # page/per_page nil returns every task (the MCP tool's default). An
13
+ # install without the tables (pgbus:add_deploy_tasks not run) gets the
14
+ # empty state, whose hint names the generator, not an error per refresh.
15
+ def deploy_tasks(page: nil, per_page: nil)
16
+ return [] unless DeployTask.table_exists?
17
+
18
+ scope = DeployTask.order(:key)
19
+ scope = scope.limit(per_page).offset(((page || 1) - 1) * per_page) if per_page
20
+ records = scope.to_a
21
+ runs = DeployTaskRun.latest_for(records.map(&:key)).group_by(&:task_key)
22
+ records.map { |record| deploy_task_row(record, runs.fetch(record.key, [])) }
23
+ rescue StandardError => e
24
+ Pgbus.logger.error { "[Pgbus::Web] Error fetching deploy tasks: #{e.class}: #{e.message}" }
25
+ []
26
+ end
27
+
28
+ def deploy_task(id)
29
+ return nil unless DeployTask.table_exists?
30
+
31
+ record = DeployTask.find_by(id: id)
32
+ return nil unless record
33
+
34
+ # The state reads the same unbounded latest-per-trigger runs as the
35
+ # list, so a run older than the 25 shown still counts.
36
+ runs = DeployTaskRun.for_task(record.key).recent(RUN_HISTORY).to_a
37
+ deploy_task_row(record, DeployTaskRun.latest_for([record.key]).to_a).merge(
38
+ arguments: record.parsed_arguments,
39
+ runs: runs.map { |run| deploy_run_hash(run).merge(queue_name: run.queue_name, msg_id: run.msg_id) }
40
+ )
41
+ rescue StandardError => e
42
+ Pgbus.logger.error { "[Pgbus::Web] Error fetching deploy task #{id}: #{e.class}: #{e.message}" }
43
+ nil
44
+ end
45
+
46
+ def toggle_deploy_task(id)
47
+ record = DeployTask.find_by(id: id)
48
+ return nil unless record
49
+
50
+ record.update!(enabled: !record.enabled)
51
+ record.enabled ? :enabled : :disabled
52
+ rescue StandardError => e
53
+ Pgbus.logger.error { "[Pgbus::Web] Error toggling deploy task #{id}: #{e.message}" }
54
+ nil
55
+ end
56
+
57
+ # Run now: the scheduler's enqueue path with no gate and no claim. It
58
+ # leaves a pending rollover arm alone and records a manual run.
59
+ # :enqueued or :failed, the real outcome.
60
+ def enqueue_deploy_task_now(id)
61
+ record = DeployTask.find_by(id: id)
62
+ return :failed unless record
63
+ # The hidden button is not the boundary: a disabled task is not run.
64
+ return disabled_enqueue(record) unless record.enabled
65
+
66
+ Deploy::Runner.new(config: Pgbus.configuration).enqueue(record.to_task, triggered_by: Deploy::Runner::MANUAL)
67
+ rescue StandardError => e
68
+ Pgbus.logger.error { "[Pgbus::Web] Error enqueuing deploy task #{id}: #{e.class}: #{e.message}" }
69
+ :failed
70
+ end
71
+
72
+ # The rollover judged against the newest live scheduler's revision,
73
+ # not this web process's; that one only when no scheduler is live
74
+ # (scheduler: false says so). nil when it cannot be read.
75
+ def deploy_rollover(now: Time.current)
76
+ scheduler, revision = Deploy::Rollover.scheduler_revision(now: now)
77
+ revision = Deploy::Revision.current unless scheduler
78
+ status = Deploy::Rollover.check(revision: revision, now: now)
79
+ { revision: revision, reason: status.reason, lagging: status.lagging, workers_live: status.workers_live,
80
+ live_by_revision: status.live_by_revision, scheduler: scheduler }
81
+ rescue StandardError => e
82
+ Pgbus.logger.error { "[Pgbus::Web] Error reading the deploy rollover: #{e.class}: #{e.message}" }
83
+ nil
84
+ end
85
+
86
+ private
87
+
88
+ def disabled_enqueue(record)
89
+ Pgbus.logger.warn { "[Pgbus::Web] Deploy task #{record.key} is disabled; Run now refused" }
90
+ :failed
91
+ end
92
+
93
+ def deploy_task_row(record, latest_runs)
94
+ by_trigger = latest_runs.index_by(&:triggered_by)
95
+ {
96
+ id: record.id, key: record.key, class_name: record.class_name, command: record.command,
97
+ version: record.version, every_deploy: record.every_deploy, queue_name: record.queue_name,
98
+ priority: record.priority, description: record.description, enabled: record.enabled,
99
+ static: record.static, armed_at: record.armed_at, armed_for: record.armed_for,
100
+ last_rollover_run: deploy_run_hash(by_trigger[Deploy::Runner::ROLLOVER]),
101
+ last_manual_run: deploy_run_hash(by_trigger[Deploy::Runner::MANUAL]),
102
+ created_at: record.created_at, updated_at: record.updated_at
103
+ }
104
+ end
105
+
106
+ def deploy_run_hash(run)
107
+ return nil unless run
108
+
109
+ { run_at: run.run_at, revision: run.revision, version: run.version, triggered_by: run.triggered_by }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -34,13 +34,14 @@ module Pgbus
34
34
  # names rather than lambdas, so the constant is plain frozen data.
35
35
  LIST_MODELS = {
36
36
  batches: :BatchEntry,
37
+ deploy_tasks: :DeployTask,
37
38
  job_locks: :UniquenessKey,
38
39
  outbox: :OutboxEntry,
39
40
  processed_events: :ProcessedEvent,
40
41
  recurring_tasks: :RecurringTask
41
42
  }.freeze
42
43
 
43
- # list – :batches, :job_locks, :concurrency_keys, :outbox, :processed_events or :recurring_tasks.
44
+ # list – :batches, :deploy_tasks, :job_locks, :concurrency_keys, :outbox, :processed_events or :recurring_tasks.
44
45
  def list_count(list)
45
46
  raise ArgumentError, "unknown list: #{list.inspect}" unless list == :concurrency_keys || LIST_MODELS.key?(list)
46
47
 
@@ -12,6 +12,7 @@ module Pgbus
12
12
  include EventList
13
13
  include ProcessList
14
14
  include DashboardSignals
15
+ include DeployTaskList
15
16
 
16
17
  # pgbus_failed_events rows outside the EventBus handler queues; binds
17
18
  # handler_queue_names_literal as $1.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgbus
4
+ module Web
5
+ # The Deploy tasks page's words for one task (issue #536), derived from
6
+ # its row and the rollover the scheduler acts on (DataSource#deploy_rollover),
7
+ # so the page prints the reason the scheduler waits for. Pure.
8
+ #
9
+ # States: disabled, waiting (armed, gate closed, with the gate's reason),
10
+ # due (armed, gate open), ran (not armed, has a rollover run), never. A
11
+ # manual run is appended to any state through manual_args.
12
+ module DeployTaskState
13
+ BADGE_TONES = { "disabled" => :gray, "waiting" => :yellow, "due" => :indigo, "ran" => :green,
14
+ "never" => :gray }.freeze
15
+
16
+ Result = Data.define(:state, :reason_key, :reason_args, :badge_tone, :manual_args)
17
+
18
+ module_function
19
+
20
+ # row – a DataSource#deploy_tasks row. rollover – DataSource#deploy_rollover, or nil when unreadable.
21
+ def present(row, rollover)
22
+ state, key, args = derive(row, rollover)
23
+ manual = row[:last_manual_run]
24
+ Result.new(state: state, reason_key: key, reason_args: args, badge_tone: BADGE_TONES.fetch(state),
25
+ manual_args: manual ? { ago: manual[:run_at] } : nil)
26
+ end
27
+
28
+ def derive(row, rollover)
29
+ return ["disabled", row[:armed_at] ? "disabled_armed" : "disabled", {}] unless row[:enabled]
30
+ return armed(rollover) if row[:armed_at]
31
+
32
+ run = row[:last_rollover_run]
33
+ return ["never", "never", {}] unless run
34
+
35
+ ["ran", "ran", { ago: run[:run_at], revision: run[:revision] }]
36
+ end
37
+
38
+ def armed(rollover)
39
+ return ["waiting", "waiting_unknown", {}] unless rollover
40
+ return ["waiting", "waiting_no_scheduler", {}] unless rollover[:scheduler]
41
+
42
+ case rollover[:reason]
43
+ when :ready then ["due", "due", {}]
44
+ when :lagging then ["waiting", "waiting_lagging", { count: rollover[:lagging].size, revision: rollover[:revision] }]
45
+ when :no_workers then ["waiting", "waiting_no_workers", { revision: rollover[:revision] }]
46
+ else ["waiting", "waiting_no_revision", {}]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,6 +7,8 @@ module Pgbus
7
7
  class Configuration
8
8
  def allowed_global_id_models: () -> untyped
9
9
  def allowed_global_id_models=: (untyped) -> untyped
10
+ def app_revision: () -> untyped
11
+ def app_revision=: (untyped) -> untyped
10
12
  def apply_log_format!: () -> void
11
13
  def appsignal_enabled: () -> bool
12
14
  def appsignal_enabled=: (untyped) -> untyped
@@ -46,6 +48,14 @@ module Pgbus
46
48
  def default_priority=: (untyped) -> untyped
47
49
  def default_queue: () -> String
48
50
  def default_queue=: (untyped) -> untyped
51
+ def deploy_task_run_retention: () -> Numeric?
52
+ def deploy_task_run_retention=: (untyped value) -> untyped
53
+ def deploy_tasks: () -> untyped
54
+ def deploy_tasks=: (untyped) -> untyped
55
+ def deploy_tasks_enabled: () -> bool
56
+ def deploy_tasks_enabled=: (untyped) -> untyped
57
+ def deploy_tasks_files: () -> untyped
58
+ def deploy_tasks_files=: (untyped) -> untyped
49
59
  def dispatch_interval: () -> Float
50
60
  def dispatch_interval=: (untyped) -> untyped
51
61
  def doctor_on_boot: () -> untyped
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.19.0
4
+ version: 0.19.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -134,6 +134,7 @@ files:
134
134
  - app/controllers/pgbus/batches_controller.rb
135
135
  - app/controllers/pgbus/dashboard_controller.rb
136
136
  - app/controllers/pgbus/dead_letter_controller.rb
137
+ - app/controllers/pgbus/deploy_tasks_controller.rb
137
138
  - app/controllers/pgbus/events_controller.rb
138
139
  - app/controllers/pgbus/frontends_controller.rb
139
140
  - app/controllers/pgbus/insights_controller.rb
@@ -159,6 +160,7 @@ files:
159
160
  - app/helpers/pgbus/application_helper.rb
160
161
  - app/helpers/pgbus/button_helper.rb
161
162
  - app/helpers/pgbus/dashboard_helper.rb
163
+ - app/helpers/pgbus/deploy_tasks_helper.rb
162
164
  - app/helpers/pgbus/events_helper.rb
163
165
  - app/helpers/pgbus/insights_helper.rb
164
166
  - app/helpers/pgbus/processes_helper.rb
@@ -167,6 +169,8 @@ files:
167
169
  - app/models/pgbus/batch_entry.rb
168
170
  - app/models/pgbus/batch_execution.rb
169
171
  - app/models/pgbus/blocked_execution.rb
172
+ - app/models/pgbus/deploy_task.rb
173
+ - app/models/pgbus/deploy_task_run.rb
170
174
  - app/models/pgbus/job_stat.rb
171
175
  - app/models/pgbus/outbox_entry.rb
172
176
  - app/models/pgbus/process_entry.rb
@@ -194,6 +198,10 @@ files:
194
198
  - app/views/pgbus/dead_letter/_row.html.erb
195
199
  - app/views/pgbus/dead_letter/index.html.erb
196
200
  - app/views/pgbus/dead_letter/show.html.erb
201
+ - app/views/pgbus/deploy_tasks/_rollover_banner.html.erb
202
+ - app/views/pgbus/deploy_tasks/_tasks_table.html.erb
203
+ - app/views/pgbus/deploy_tasks/index.html.erb
204
+ - app/views/pgbus/deploy_tasks/show.html.erb
197
205
  - app/views/pgbus/events/_list.html.erb
198
206
  - app/views/pgbus/events/_processed_table.html.erb
199
207
  - app/views/pgbus/events/_replay.html.erb
@@ -246,6 +254,7 @@ files:
246
254
  - lib/active_job/queue_adapters/pgbus_adapter.rb
247
255
  - lib/generators/pgbus/add_batch_callback_jobs_generator.rb
248
256
  - lib/generators/pgbus/add_batch_executions_generator.rb
257
+ - lib/generators/pgbus/add_deploy_tasks_generator.rb
249
258
  - lib/generators/pgbus/add_failed_events_index_generator.rb
250
259
  - lib/generators/pgbus/add_job_stats_generator.rb
251
260
  - lib/generators/pgbus/add_job_stats_latency_generator.rb
@@ -264,6 +273,7 @@ files:
264
273
  - lib/generators/pgbus/migration_path.rb
265
274
  - lib/generators/pgbus/templates/add_batch_callback_jobs.rb.erb
266
275
  - lib/generators/pgbus/templates/add_batch_executions.rb.erb
276
+ - lib/generators/pgbus/templates/add_deploy_tasks_tables.rb.erb
267
277
  - lib/generators/pgbus/templates/add_failed_events_unique_index.rb.erb
268
278
  - lib/generators/pgbus/templates/add_job_stats.rb.erb
269
279
  - lib/generators/pgbus/templates/add_job_stats_latency.rb.erb
@@ -277,6 +287,7 @@ files:
277
287
  - lib/generators/pgbus/templates/add_stream_queues.rb.erb
278
288
  - lib/generators/pgbus/templates/add_stream_stats.rb.erb
279
289
  - lib/generators/pgbus/templates/add_uniqueness_keys.rb.erb
290
+ - lib/generators/pgbus/templates/deploy_tasks.yml.erb
280
291
  - lib/generators/pgbus/templates/initializer.rb.erb
281
292
  - lib/generators/pgbus/templates/migrate_job_locks_to_uniqueness_keys.rb.erb
282
293
  - lib/generators/pgbus/templates/migration.rb.erb
@@ -315,6 +326,7 @@ files:
315
326
  - lib/pgbus/concurrency/semaphore.rb
316
327
  - lib/pgbus/configuration.rb
317
328
  - lib/pgbus/configuration/capsule_dsl.rb
329
+ - lib/pgbus/configuration/deploy_settings.rb
318
330
  - lib/pgbus/configuration/presence_settings.rb
319
331
  - lib/pgbus/configuration/stream_authorization.rb
320
332
  - lib/pgbus/current_attributes.rb
@@ -322,6 +334,10 @@ files:
322
334
  - lib/pgbus/dead_letter_header.rb
323
335
  - lib/pgbus/dedicated_connection.rb
324
336
  - lib/pgbus/dedup_cache.rb
337
+ - lib/pgbus/deploy/revision.rb
338
+ - lib/pgbus/deploy/rollover.rb
339
+ - lib/pgbus/deploy/runner.rb
340
+ - lib/pgbus/deploy/task.rb
325
341
  - lib/pgbus/doctor.rb
326
342
  - lib/pgbus/engine.rb
327
343
  - lib/pgbus/error_reporter.rb
@@ -357,6 +373,7 @@ files:
357
373
  - lib/pgbus/mcp/runner.rb
358
374
  - lib/pgbus/mcp/server.rb
359
375
  - lib/pgbus/mcp/tools/concurrency_tool.rb
376
+ - lib/pgbus/mcp/tools/deploy_tasks_tool.rb
360
377
  - lib/pgbus/mcp/tools/dlq_detail_tool.rb
361
378
  - lib/pgbus/mcp/tools/dlq_tool.rb
362
379
  - lib/pgbus/mcp/tools/health_tool.rb
@@ -447,12 +464,14 @@ files:
447
464
  - lib/pgbus/web/data_source.rb
448
465
  - lib/pgbus/web/data_source/dashboard_signals.rb
449
466
  - lib/pgbus/web/data_source/dead_letter.rb
467
+ - lib/pgbus/web/data_source/deploy_task_list.rb
450
468
  - lib/pgbus/web/data_source/event_list.rb
451
469
  - lib/pgbus/web/data_source/job_list.rb
452
470
  - lib/pgbus/web/data_source/list_counts.rb
453
471
  - lib/pgbus/web/data_source/process_list.rb
454
472
  - lib/pgbus/web/data_source/queue_summary.rb
455
473
  - lib/pgbus/web/dead_letter_reason.rb
474
+ - lib/pgbus/web/deploy_task_state.rb
456
475
  - lib/pgbus/web/event_state.rb
457
476
  - lib/pgbus/web/health_app.rb
458
477
  - lib/pgbus/web/health_server.rb