pgbus 0.19.0 → 0.19.2

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +329 -147
  3. data/README.md +65 -27
  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 +98 -1
  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_batch_callback_jobs_generator.rb +4 -1
  29. data/lib/generators/pgbus/add_deploy_tasks_generator.rb +51 -0
  30. data/lib/generators/pgbus/templates/add_deploy_tasks_tables.rb.erb +43 -0
  31. data/lib/generators/pgbus/templates/deploy_tasks.yml.erb +42 -0
  32. data/lib/generators/pgbus/templates/migration.rb.erb +43 -0
  33. data/lib/pgbus/active_job/batch_id.rb +10 -4
  34. data/lib/pgbus/batch/builder.rb +92 -0
  35. data/lib/pgbus/batch.rb +97 -22
  36. data/lib/pgbus/configuration/deploy_settings.rb +46 -0
  37. data/lib/pgbus/configuration.rb +3 -0
  38. data/lib/pgbus/deploy/revision.rb +59 -0
  39. data/lib/pgbus/deploy/rollover.rb +65 -0
  40. data/lib/pgbus/deploy/runner.rb +259 -0
  41. data/lib/pgbus/deploy/task.rb +105 -0
  42. data/lib/pgbus/engine.rb +17 -0
  43. data/lib/pgbus/generators/migration_detector.rb +12 -1
  44. data/lib/pgbus/instrumentation.rb +14 -0
  45. data/lib/pgbus/integrations/appsignal/subscriber.rb +15 -0
  46. data/lib/pgbus/mcp/server.rb +2 -1
  47. data/lib/pgbus/mcp/tools/deploy_tasks_tool.rb +86 -0
  48. data/lib/pgbus/mcp.rb +1 -0
  49. data/lib/pgbus/metrics/subscriber.rb +12 -0
  50. data/lib/pgbus/outbox/poller.rb +1 -0
  51. data/lib/pgbus/process/consumer.rb +57 -3
  52. data/lib/pgbus/process/dispatcher.rb +18 -1
  53. data/lib/pgbus/process/heartbeat.rb +7 -2
  54. data/lib/pgbus/process/supervisor.rb +35 -3
  55. data/lib/pgbus/process/worker.rb +1 -0
  56. data/lib/pgbus/recurring/scheduler.rb +54 -20
  57. data/lib/pgbus/version.rb +1 -1
  58. data/lib/pgbus/web/data_source/deploy_task_list.rb +114 -0
  59. data/lib/pgbus/web/data_source/list_counts.rb +2 -1
  60. data/lib/pgbus/web/data_source.rb +1 -0
  61. data/lib/pgbus/web/deploy_task_state.rb +51 -0
  62. data/lib/pgbus.rb +11 -0
  63. data/sig/pgbus/configuration.rbs +10 -0
  64. data/sig/pgbus.rbs +9 -0
  65. metadata +21 -1
@@ -7,24 +7,33 @@ module Pgbus
7
7
  class Scheduler
8
8
  include Process::SignalHandler
9
9
 
10
- attr_reader :schedule, :config
10
+ attr_reader :schedule, :config, :deploy_runner
11
11
 
12
12
  def initialize(config: Pgbus.configuration)
13
13
  @config = config
14
14
  @schedule = Schedule.new(config: config)
15
+ # Deploy tasks (issue #536) ride this loop: the scheduler is already
16
+ # the one-per-deployment process, dedup'd across the overlap window.
17
+ @deploy_runner = Deploy::Runner.new(config: config) if config.deploy_tasks_enabled && config.deploy_tasks&.any?
15
18
  @shutting_down = false
16
19
  @last_runs = {}
17
20
  @loop_tick_at = Concurrent::AtomicReference.new(nil)
21
+ @deploy_error = nil
18
22
  end
19
23
 
24
+ # The order is load-bearing: the heartbeat registers this scheduler's row
25
+ # before it arms deploy tasks, so an old-revision scheduler in the
26
+ # overlap window sees it as lagging. A failed registration is logged and
27
+ # sync still runs; the new release's workers keep the old gate closed.
20
28
  def run
21
29
  setup_signals
22
30
  start_heartbeat
23
31
  sync_recurring_tasks
32
+ deploy_runner&.sync!
24
33
 
25
34
  Pgbus.logger.info do
26
35
  "[Pgbus] Scheduler started: #{schedule.tasks.size} recurring tasks, " \
27
- "interval=#{config.recurring_schedule_interval}s"
36
+ "#{deploy_task_count} deploy tasks, interval=#{config.recurring_schedule_interval}s"
28
37
  end
29
38
 
30
39
  loop do
@@ -44,22 +53,12 @@ module Pgbus
44
53
  end
45
54
 
46
55
  def tick(now)
47
- schedule.due_tasks(now).each do |task, run_at|
48
- Pgbus::Instrumentation.instrument(
49
- "pgbus.recurring.enqueue",
50
- task: task.key,
51
- class_name: task.class_name,
52
- queue: task.queue_name,
53
- run_at: run_at
54
- ) do
55
- schedule.enqueue_task(task, run_at: run_at)
56
- @last_runs[task.key] = now
57
- end
58
- rescue StandardError => e
59
- Pgbus.logger.error do
60
- "[Pgbus] Error scheduling recurring task #{task.key}: #{e.class}: #{e.message}"
61
- end
62
- end
56
+ tick_recurring(now) if config.recurring_enabled
57
+ tick_deploy_tasks(now)
58
+ end
59
+
60
+ def deploy_task_count
61
+ deploy_runner&.task_count || 0
63
62
  end
64
63
 
65
64
  def last_run_at(key)
@@ -94,9 +93,43 @@ module Pgbus
94
93
 
95
94
  private
96
95
 
96
+ def tick_recurring(now)
97
+ schedule.due_tasks(now).each do |task, run_at|
98
+ Pgbus::Instrumentation.instrument(
99
+ "pgbus.recurring.enqueue",
100
+ task: task.key,
101
+ class_name: task.class_name,
102
+ queue: task.queue_name,
103
+ run_at: run_at
104
+ ) do
105
+ schedule.enqueue_task(task, run_at: run_at)
106
+ @last_runs[task.key] = now
107
+ end
108
+ rescue StandardError => e
109
+ Pgbus.logger.error do
110
+ "[Pgbus] Error scheduling recurring task #{task.key}: #{e.class}: #{e.message}"
111
+ end
112
+ end
113
+ end
114
+
115
+ # A persistent failure (the tables missing, the database down) is logged
116
+ # and reported once per distinct error, then at debug, not every tick.
117
+ def tick_deploy_tasks(now)
118
+ deploy_runner&.tick(now)
119
+ @deploy_error = nil
120
+ rescue StandardError => e
121
+ signature = "#{e.class}: #{e.message}"
122
+ if signature == @deploy_error
123
+ Pgbus.logger.debug { "[Pgbus] Error running deploy tasks (repeated): #{signature}" }
124
+ else
125
+ @deploy_error = signature
126
+ ErrorReporter.report(e, { action: "deploy_tasks_tick" }) # logs at error itself
127
+ end
128
+ end
129
+
97
130
  def sync_recurring_tasks
98
131
  tasks = config.recurring_tasks
99
- return if tasks.nil?
132
+ return if tasks.nil? || !config.recurring_enabled
100
133
 
101
134
  RecurringTask.sync_from_config!(tasks)
102
135
  Pgbus.logger.info { "[Pgbus] Synced #{tasks.size} recurring task(s) from configuration" }
@@ -116,8 +149,9 @@ module Pgbus
116
149
  def start_heartbeat
117
150
  @heartbeat = Process::Heartbeat.new(
118
151
  kind: "scheduler",
152
+ config: @config,
119
153
  metadata: { pid: ::Process.pid, tasks: schedule.tasks.size, started_at: Time.now.to_f,
120
- supervisor_pid: ::Process.ppid },
154
+ supervisor_pid: ::Process.ppid }.merge(deploy_runner ? { deploy_tasks: deploy_task_count } : {}),
121
155
  loop_tick_supplier: -> { @loop_tick_at.get }
122
156
  )
123
157
  @heartbeat.start
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.19.0"
4
+ VERSION = "0.19.2"
5
5
  end
@@ -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
data/lib/pgbus.rb CHANGED
@@ -298,6 +298,17 @@ module Pgbus
298
298
  Streams::Key.queue_name_budget
299
299
  end
300
300
 
301
+ # Open a batch, yield a Pgbus::Batch::Builder, and start the batch when
302
+ # the block returns — the top-level shortcut for Pgbus::Batch.start.
303
+ #
304
+ # Pgbus.batch(description: "Import") do |b|
305
+ # b.on_complete ImportDoneJob
306
+ # b.add(*rows.map { |row| ImportRowJob.new(row.id) })
307
+ # end
308
+ def batch(**, &)
309
+ Batch.start(**, &)
310
+ end
311
+
301
312
  # Publish an event to the bus — the top-level shortcut for
302
313
  # `Pgbus::EventBus::Publisher.publish`, symmetric with `Pgbus.stream`.
303
314
  #
@@ -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
data/sig/pgbus.rbs CHANGED
@@ -49,6 +49,13 @@ module Pgbus
49
49
  end
50
50
  end
51
51
 
52
+ # Partial: only the types Pgbus.batch names. Batch's own methods are not
53
+ # covered by spec/pgbus/rbs_spec.rb yet.
54
+ class Batch
55
+ class Builder
56
+ end
57
+ end
58
+
52
59
  def self.configuration: () -> Configuration
53
60
  def self.configure: () { (Configuration) -> void } -> Configuration
54
61
  def self.finalize!: () -> Configuration
@@ -57,6 +64,8 @@ module Pgbus
57
64
  def self.stream_key: (*untyped parts, ?digest_bits: Integer) -> String
58
65
  def self.stream_key!: (String key) -> String
59
66
  def self.stream_name_budget: () -> Integer
67
+ # Keywords go to Pgbus::Batch.new (description:, properties:, on_complete:, uniqueness_key:, …).
68
+ def self.batch: (**untyped options) { (Batch::Builder) -> void } -> Batch
60
69
  # delay: seconds, as a Numeric or an ActiveSupport::Duration (1.hour).
61
70
  def self.publish: (String routing_key, untyped payload, ?headers: Hash[String, untyped]?, ?delay: Numeric | _ToI) -> untyped
62
71
  def self.publish_later: (String routing_key, untyped payload, delay: Numeric | _ToI, ?headers: Hash[String, untyped]?) -> 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.2
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
@@ -296,6 +307,7 @@ files:
296
307
  - lib/pgbus/active_job/executor.rb
297
308
  - lib/pgbus/autovacuum_tuning.rb
298
309
  - lib/pgbus/batch.rb
310
+ - lib/pgbus/batch/builder.rb
299
311
  - lib/pgbus/batch/sweep.rb
300
312
  - lib/pgbus/bus_record.rb
301
313
  - lib/pgbus/circuit_breaker.rb
@@ -315,6 +327,7 @@ files:
315
327
  - lib/pgbus/concurrency/semaphore.rb
316
328
  - lib/pgbus/configuration.rb
317
329
  - lib/pgbus/configuration/capsule_dsl.rb
330
+ - lib/pgbus/configuration/deploy_settings.rb
318
331
  - lib/pgbus/configuration/presence_settings.rb
319
332
  - lib/pgbus/configuration/stream_authorization.rb
320
333
  - lib/pgbus/current_attributes.rb
@@ -322,6 +335,10 @@ files:
322
335
  - lib/pgbus/dead_letter_header.rb
323
336
  - lib/pgbus/dedicated_connection.rb
324
337
  - lib/pgbus/dedup_cache.rb
338
+ - lib/pgbus/deploy/revision.rb
339
+ - lib/pgbus/deploy/rollover.rb
340
+ - lib/pgbus/deploy/runner.rb
341
+ - lib/pgbus/deploy/task.rb
325
342
  - lib/pgbus/doctor.rb
326
343
  - lib/pgbus/engine.rb
327
344
  - lib/pgbus/error_reporter.rb
@@ -357,6 +374,7 @@ files:
357
374
  - lib/pgbus/mcp/runner.rb
358
375
  - lib/pgbus/mcp/server.rb
359
376
  - lib/pgbus/mcp/tools/concurrency_tool.rb
377
+ - lib/pgbus/mcp/tools/deploy_tasks_tool.rb
360
378
  - lib/pgbus/mcp/tools/dlq_detail_tool.rb
361
379
  - lib/pgbus/mcp/tools/dlq_tool.rb
362
380
  - lib/pgbus/mcp/tools/health_tool.rb
@@ -447,12 +465,14 @@ files:
447
465
  - lib/pgbus/web/data_source.rb
448
466
  - lib/pgbus/web/data_source/dashboard_signals.rb
449
467
  - lib/pgbus/web/data_source/dead_letter.rb
468
+ - lib/pgbus/web/data_source/deploy_task_list.rb
450
469
  - lib/pgbus/web/data_source/event_list.rb
451
470
  - lib/pgbus/web/data_source/job_list.rb
452
471
  - lib/pgbus/web/data_source/list_counts.rb
453
472
  - lib/pgbus/web/data_source/process_list.rb
454
473
  - lib/pgbus/web/data_source/queue_summary.rb
455
474
  - lib/pgbus/web/dead_letter_reason.rb
475
+ - lib/pgbus/web/deploy_task_state.rb
456
476
  - lib/pgbus/web/event_state.rb
457
477
  - lib/pgbus/web/health_app.rb
458
478
  - lib/pgbus/web/health_server.rb