maintenance_tasks 2.12.0 → 2.14.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.
@@ -5,499 +5,6 @@ module MaintenanceTasks
5
5
  #
6
6
  # @api private
7
7
  class Run < ApplicationRecord
8
- # Various statuses a run can be in.
9
- STATUSES = [
10
- :enqueued, # The task has been enqueued by the user.
11
- :running, # The task is being performed by a job worker.
12
- :succeeded, # The task finished without error.
13
- :cancelling, # The task has been told to cancel but is finishing work.
14
- :cancelled, # The user explicitly halted the task's execution.
15
- :interrupted, # The task was interrupted by the job infrastructure.
16
- :pausing, # The task has been told to pause but is finishing work.
17
- :paused, # The task was paused in the middle of the run by the user.
18
- :errored, # The task code produced an unhandled exception.
19
- ]
20
-
21
- ACTIVE_STATUSES = [
22
- :enqueued,
23
- :running,
24
- :paused,
25
- :pausing,
26
- :cancelling,
27
- :interrupted,
28
- ]
29
- STOPPING_STATUSES = [
30
- :pausing,
31
- :cancelling,
32
- :cancelled,
33
- ]
34
- COMPLETED_STATUSES = [:succeeded, :errored, :cancelled]
35
-
36
- enum :status, STATUSES.to_h { |status| [status, status.to_s] }
37
-
38
- after_save :instrument_status_change
39
-
40
- validate :task_name_belongs_to_a_valid_task, on: :create
41
- validate :csv_attachment_presence, on: :create
42
- validate :csv_content_type, on: :create
43
- validate :validate_task_arguments, on: :create
44
-
45
- attr_readonly :task_name
46
-
47
- if Rails.gem_version >= Gem::Version.new("7.1.alpha")
48
- serialize :backtrace, coder: YAML
49
- serialize :arguments, coder: JSON
50
- serialize :metadata, coder: JSON
51
- else
52
- serialize :backtrace
53
- serialize :arguments, JSON
54
- serialize :metadata, JSON
55
- end
56
-
57
- scope :active, -> { where(status: ACTIVE_STATUSES) }
58
- scope :completed, -> { where(status: COMPLETED_STATUSES) }
59
-
60
- # Ensure ActiveStorage is in use before preloading the attachments
61
- scope :with_attached_csv, -> do
62
- return unless defined?(ActiveStorage)
63
-
64
- with_attached_csv_file if ActiveStorage::Attachment.table_exists?
65
- end
66
-
67
- validates_with RunStatusValidator, on: :update
68
-
69
- if MaintenanceTasks.active_storage_service.present?
70
- has_one_attached :csv_file,
71
- service: MaintenanceTasks.active_storage_service
72
- elsif respond_to?(:has_one_attached)
73
- has_one_attached :csv_file
74
- end
75
-
76
- # Sets the run status to enqueued, making sure the transition is validated
77
- # in case it's already enqueued.
78
- #
79
- # Rescues and retries status transition if an ActiveRecord::StaleObjectError
80
- # is encountered.
81
- def enqueued!
82
- status_will_change!
83
- super
84
- rescue ActiveRecord::StaleObjectError
85
- reload_status
86
- retry
87
- end
88
-
89
- CALLBACKS_TRANSITION = {
90
- cancelled: :cancel,
91
- interrupted: :interrupt,
92
- paused: :pause,
93
- succeeded: :complete,
94
- }.transform_keys(&:to_s)
95
- private_constant :CALLBACKS_TRANSITION
96
-
97
- # Saves the run, persisting the transition of its status, and all other
98
- # changes to the object.
99
- def persist_transition
100
- save!
101
- callback = CALLBACKS_TRANSITION[status]
102
- run_task_callbacks(callback) if callback
103
- rescue ActiveRecord::StaleObjectError
104
- success = succeeded?
105
- reload_status
106
- if success
107
- self.status = :succeeded
108
- else
109
- job_shutdown
110
- end
111
- retry
112
- end
113
-
114
- # Increments +tick_count+ by +number_of_ticks+ and +time_running+ by
115
- # +duration+, both directly in the DB.
116
- # The attribute values are not set in the current instance, you need
117
- # to reload the record.
118
- #
119
- # @param number_of_ticks [Integer] number of ticks to add to tick_count.
120
- # @param duration [Float] the time in seconds that elapsed since the last
121
- # increment of ticks.
122
- def persist_progress(number_of_ticks, duration)
123
- self.class.update_counters(
124
- id,
125
- tick_count: number_of_ticks,
126
- time_running: duration,
127
- touch: true,
128
- )
129
- if locking_enabled?
130
- locking_column = self.class.locking_column
131
- self[locking_column] += 1
132
- clear_attribute_change(locking_column)
133
- end
134
- end
135
-
136
- # Marks the run as errored and persists the error data.
137
- #
138
- # @param error [StandardError] the Error being persisted.
139
- def persist_error(error)
140
- self.started_at ||= Time.now
141
- update!(
142
- status: :errored,
143
- error_class: truncate(:error_class, error.class.name),
144
- error_message: truncate(:error_message, error.message),
145
- backtrace: MaintenanceTasks.backtrace_cleaner.clean(error.backtrace),
146
- ended_at: Time.now,
147
- )
148
- run_error_callback
149
- rescue ActiveRecord::StaleObjectError
150
- reload_status
151
- retry
152
- end
153
-
154
- # Refreshes the status and lock version attributes on the Active Record
155
- # object, and ensures ActiveModel::Dirty doesn't mark the object as changed.
156
- #
157
- # This allows us to get the Run's most up-to-date status without needing
158
- # to reload the entire record.
159
- #
160
- # @return [MaintenanceTasks::Run] the Run record with its updated status.
161
- def reload_status
162
- columns_to_reload = if locking_enabled?
163
- [:status, self.class.locking_column]
164
- else
165
- [:status]
166
- end
167
- updated_status, updated_lock_version = self.class.uncached do
168
- self.class.where(id: id).pluck(*columns_to_reload).first
169
- end
170
-
171
- self.status = updated_status
172
- if updated_lock_version
173
- self[self.class.locking_column] = updated_lock_version
174
- end
175
- clear_attribute_changes(columns_to_reload)
176
- self
177
- end
178
-
179
- # Returns whether the Run is stopping, which is defined as having a status
180
- # of pausing or cancelling. The status of cancelled is also considered
181
- # stopping since a Run can be cancelled while its job still exists in the
182
- # queue, and we want to handle it the same way as a cancelling run.
183
- #
184
- # @return [Boolean] whether the Run is stopping.
185
- def stopping?
186
- STOPPING_STATUSES.include?(status.to_sym)
187
- end
188
-
189
- # Returns whether the Run is stopped, which is defined as having a status of
190
- # paused, succeeded, cancelled, or errored.
191
- #
192
- # @return [Boolean] whether the Run is stopped.
193
- def stopped?
194
- completed? || paused?
195
- end
196
-
197
- # Returns whether the Run has been started, which is indicated by the
198
- # started_at timestamp being present.
199
- #
200
- # @return [Boolean] whether the Run was started.
201
- def started?
202
- started_at.present?
203
- end
204
-
205
- # Returns whether the Run is completed, which is defined as
206
- # having a status of succeeded, cancelled, or errored.
207
- #
208
- # @return [Boolean] whether the Run is completed.
209
- def completed?
210
- COMPLETED_STATUSES.include?(status.to_sym)
211
- end
212
-
213
- # Returns whether the Run is active, which is defined as
214
- # having a status of enqueued, running, pausing, cancelling,
215
- # paused or interrupted.
216
- #
217
- # @return [Boolean] whether the Run is active.
218
- def active?
219
- ACTIVE_STATUSES.include?(status.to_sym)
220
- end
221
-
222
- # Returns the duration left for the Run to finish based on the number of
223
- # ticks left and the average time needed to process a tick. Returns nil if
224
- # the Run is completed, or if tick_count or tick_total is zero.
225
- #
226
- # @return [ActiveSupport::Duration] the estimated duration left for the Run
227
- # to finish.
228
- def time_to_completion
229
- return if completed? || tick_count == 0 || tick_total.to_i == 0
230
-
231
- processed_per_second = (tick_count.to_f / time_running)
232
- ticks_left = (tick_total - tick_count)
233
- seconds_to_finished = ticks_left / processed_per_second
234
- seconds_to_finished.seconds
235
- end
236
-
237
- # Marks a Run as running.
238
- #
239
- # If the run is stopping already, it will not transition to running.
240
- # Rescues and retries status transition if an ActiveRecord::StaleObjectError
241
- # is encountered.
242
- def running
243
- if locking_enabled?
244
- begin
245
- running! unless stopping?
246
- rescue ActiveRecord::StaleObjectError
247
- reload_status
248
- retry
249
- end
250
- else
251
- # Preserve swap-and-replace solution for data races until users
252
- # run migration to upgrade to optimistic locking solution
253
- return if stopping?
254
-
255
- updated = self.class.where(id: id).where.not(status: STOPPING_STATUSES)
256
- .update_all(status: :running, updated_at: Time.now) > 0
257
- if updated
258
- self.status = :running
259
- clear_attribute_changes([:status])
260
- else
261
- reload_status
262
- end
263
- end
264
- end
265
-
266
- # Starts a Run, setting its started_at timestamp and tick_total.
267
- #
268
- # @param count [Integer] the total iterations to be performed, as
269
- # specified by the Task.
270
- def start(count)
271
- update!(started_at: Time.now, tick_total: count)
272
- task.run_callbacks(:start)
273
- rescue ActiveRecord::StaleObjectError
274
- reload_status
275
- retry
276
- end
277
-
278
- # Handles transitioning the status on a Run when the job shuts down.
279
- def job_shutdown
280
- if cancelling?
281
- self.status = :cancelled
282
- self.ended_at = Time.now
283
- elsif pausing?
284
- self.status = :paused
285
- elsif cancelled?
286
- else
287
- self.status = :interrupted
288
- end
289
- end
290
-
291
- # Handles the completion of a Run, setting a status of succeeded and the
292
- # ended_at timestamp.
293
- def complete
294
- self.status = :succeeded
295
- self.ended_at = Time.now
296
- end
297
-
298
- # Cancels a Run.
299
- #
300
- # If the Run is paused, it will transition directly to cancelled, since the
301
- # Task is not being performed. In this case, the ended_at timestamp
302
- # will be updated.
303
- #
304
- # If the Run is not paused, the Run will transition to cancelling.
305
- #
306
- # If the Run is already cancelling, and has last been updated more than 5
307
- # minutes ago, it will transition to cancelled, and the ended_at timestamp
308
- # will be updated.
309
- def cancel
310
- if paused? || stuck?
311
- self.status = :cancelled
312
- self.ended_at = Time.now
313
- persist_transition
314
- else
315
- cancelling!
316
- end
317
- rescue ActiveRecord::StaleObjectError
318
- reload_status
319
- retry
320
- end
321
-
322
- # Marks a Run as pausing.
323
- #
324
- # If the Run has been stuck on pausing for more than 5 minutes, it forces
325
- # the transition to paused. The ended_at timestamp will be updated.
326
- #
327
- # Rescues and retries status transition if an ActiveRecord::StaleObjectError
328
- # is encountered.
329
- def pause
330
- if stuck?
331
- self.status = :paused
332
- persist_transition
333
- else
334
- pausing!
335
- end
336
- rescue ActiveRecord::StaleObjectError
337
- reload_status
338
- retry
339
- end
340
-
341
- # Returns whether a Run is stuck, which is defined as having a status of
342
- # cancelling or pausing, and not having been updated in the last 5 minutes.
343
- #
344
- # @return [Boolean] whether the Run is stuck.
345
- def stuck?
346
- (cancelling? || pausing?) && updated_at <= MaintenanceTasks.stuck_task_duration.ago
347
- end
348
-
349
- # Performs validation on the task_name attribute.
350
- # A Run must be associated with a valid Task to be valid.
351
- # In order to confirm that, the Task is looked up by name.
352
- def task_name_belongs_to_a_valid_task
353
- Task.named(task_name)
354
- rescue Task::NotFoundError
355
- errors.add(:task_name, "must be the name of an existing Task.")
356
- end
357
-
358
- # Performs validation on the presence of a :csv_file attachment.
359
- # A Run for a Task that uses CsvCollection must have an attached :csv_file
360
- # to be valid. Conversely, a Run for a Task that doesn't use CsvCollection
361
- # should not have an attachment to be valid. The appropriate error is added
362
- # if the Run does not meet the above criteria.
363
- def csv_attachment_presence
364
- if Task.named(task_name).has_csv_content? && !csv_file.attached?
365
- errors.add(:csv_file, "must be attached to CSV Task.")
366
- elsif !Task.named(task_name).has_csv_content? && csv_file.present?
367
- errors.add(:csv_file, "should not be attached to non-CSV Task.")
368
- end
369
- rescue Task::NotFoundError
370
- nil
371
- end
372
-
373
- # Performs validation on the content type of the :csv_file attachment.
374
- # A Run for a Task that uses CsvCollection must have a present :csv_file
375
- # and a content type of "text/csv" to be valid. The appropriate error is
376
- # added if the Run does not meet the above criteria.
377
- def csv_content_type
378
- if csv_file.present? && csv_file.content_type != "text/csv"
379
- errors.add(:csv_file, "must be a CSV")
380
- end
381
- rescue Task::NotFoundError
382
- nil
383
- end
384
-
385
- # Performs validation on the arguments to use for the Task. If the Task is
386
- # invalid, the errors are added to the Run.
387
- def validate_task_arguments
388
- arguments_match_task_attributes if arguments.present?
389
- if task.invalid?
390
- error_messages = task.errors
391
- .map { |error| "#{error.attribute.inspect} #{error.message}" }
392
- errors.add(
393
- :arguments,
394
- "are invalid: #{error_messages.join("; ")}",
395
- )
396
- end
397
- rescue Task::NotFoundError
398
- nil
399
- end
400
-
401
- # Fetches the attached ActiveStorage CSV file for the run. Checks first
402
- # whether the ActiveStorage::Attachment table exists so that we are
403
- # compatible with apps that are not using ActiveStorage.
404
- #
405
- # @return [ActiveStorage::Attached::One] the attached CSV file
406
- def csv_file
407
- return unless defined?(ActiveStorage)
408
- return unless ActiveStorage::Attachment.table_exists?
409
-
410
- super
411
- end
412
-
413
- # Returns a Task instance for this Run. Assigns any attributes to the Task
414
- # based on the Run's parameters. Note that the Task instance is not supplied
415
- # with :csv_content yet if it's a CSV Task. This is done in the job, since
416
- # downloading the CSV file can take some time.
417
- #
418
- # @return [Task] a Task instance.
419
- def task
420
- @task ||= begin
421
- task = Task.named(task_name).new
422
- if task.attribute_names.any? && arguments.present?
423
- task.assign_attributes(arguments)
424
- end
425
-
426
- task.metadata = metadata
427
- task
428
- rescue ActiveModel::UnknownAttributeError
429
- task
430
- end
431
- end
432
-
433
- # Returns all the run arguments with sensitive information masked.
434
- #
435
- # @return [Hash] The masked arguments.
436
- def masked_arguments
437
- return unless arguments.present?
438
-
439
- argument_filter.filter(arguments)
440
- end
441
-
442
- private
443
-
444
- def instrument_status_change
445
- return unless status_previously_changed? || id_previously_changed?
446
- return if running? || pausing? || cancelling? || interrupted?
447
-
448
- attr = {
449
- run_id: id,
450
- job_id: job_id,
451
- task_name: task_name,
452
- arguments: arguments,
453
- metadata: metadata,
454
- time_running: time_running,
455
- started_at: started_at,
456
- ended_at: ended_at,
457
- }
458
-
459
- attr[:error] = {
460
- message: error_message,
461
- class: error_class,
462
- backtrace: backtrace,
463
- } if errored?
464
-
465
- ActiveSupport::Notifications.instrument("#{status}.maintenance_tasks", attr)
466
- end
467
-
468
- def run_task_callbacks(callback)
469
- task.run_callbacks(callback)
470
- rescue Task::NotFoundError
471
- nil
472
- end
473
-
474
- def run_error_callback
475
- task.run_callbacks(:error)
476
- rescue
477
- nil
478
- end
479
-
480
- def arguments_match_task_attributes
481
- invalid_argument_keys = arguments.keys - task.attribute_names
482
- if invalid_argument_keys.any?
483
- error_message = <<~MSG.squish
484
- Unknown parameters: #{invalid_argument_keys.map(&:to_sym).join(", ")}
485
- MSG
486
- errors.add(:base, error_message)
487
- end
488
- end
489
-
490
- def truncate(attribute_name, value)
491
- limit = self.class.column_for_attribute(attribute_name).limit
492
- return value unless limit
493
-
494
- value&.first(limit)
495
- end
496
-
497
- def argument_filter
498
- @argument_filter ||= ActiveSupport::ParameterFilter.new(
499
- Rails.application.config.filter_parameters + task.masked_arguments,
500
- )
501
- end
8
+ include RunConcern
502
9
  end
503
10
  end
@@ -33,6 +33,12 @@ module MaintenanceTasks
33
33
  # @api private
34
34
  class_attribute :masked_arguments, default: []
35
35
 
36
+ # The frequency at which to reload the run status during iteration.
37
+ # Defaults to the global MaintenanceTasks.status_reload_frequency setting.
38
+ #
39
+ # @api private
40
+ class_attribute :status_reload_frequency, default: MaintenanceTasks.status_reload_frequency
41
+
36
42
  define_callbacks :start, :complete, :error, :cancel, :pause, :interrupt
37
43
 
38
44
  attr_accessor :metadata
@@ -167,6 +173,15 @@ module MaintenanceTasks
167
173
  self.masked_arguments += attributes
168
174
  end
169
175
 
176
+ # Configure how frequently the run status should be reloaded during iteration.
177
+ # Use this to reduce database queries when processing large collections.
178
+ #
179
+ # @param frequency [ActiveSupport::Duration, Numeric] reload status every N seconds (default: 1 second).
180
+ # Setting this to 10.seconds means status will be reloaded every 10 seconds.
181
+ def reload_status_every(frequency)
182
+ self.status_reload_frequency = frequency
183
+ end
184
+
170
185
  # Initialize a callback to run after the task starts.
171
186
  #
172
187
  # @param filter_list apply filters to the callback
@@ -220,14 +235,10 @@ module MaintenanceTasks
220
235
  #
221
236
  # @param exceptions list of exceptions to rescue and report
222
237
  # @param report_options [Hash] optionally, supply additional options for `Rails.error.report`.
223
- # By default: <code>{ source: "maintenance_tasks" }</code> or (Rails <v7.1) <code>{ handled: true }</code>.
238
+ # By default: <code>{ handled: true, source: "maintenance-tasks" }</code>.
224
239
  def report_on(*exceptions, **report_options)
225
240
  rescue_from(*exceptions) do |exception|
226
- if Rails.gem_version >= Gem::Version.new("7.1")
227
- Rails.error.report(exception, source: "maintenance_tasks", **report_options)
228
- else
229
- Rails.error.report(exception, handled: true, **report_options)
230
- end
241
+ Rails.error.report(exception, source: "maintenance-tasks", **report_options)
231
242
  end
232
243
  end
233
244
 
@@ -1,4 +1,4 @@
1
- <nav class="navbar is-light" role="navigation" aria-label="main navigation">
1
+ <nav class="navbar" role="navigation" aria-label="main navigation">
2
2
  <div class="navbar-brand">
3
3
  <%= link_to 'Maintenance Tasks', root_path, class: 'navbar-item is-size-4 has-text-weight-semibold has-text-danger' %>
4
4
  </div>
@@ -3,6 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <%= capybara_lockstep if defined?(Capybara::Lockstep) %>
6
7
 
7
8
  <title>
8
9
  <% if content_for?(:page_title) %>
@@ -15,20 +16,44 @@
15
16
  <%= csrf_meta_tags %>
16
17
 
17
18
  <%=
18
- stylesheet_link_tag(URI.join(controller.class::BULMA_CDN, "npm/bulma@1.0.3/css/versions/bulma-no-dark-mode.min.css"),
19
+ stylesheet_link_tag(URI.join(controller.class::BULMA_CDN, "npm/bulma@1.0.4/css/bulma.min.css"),
19
20
  media: :all,
20
- integrity: "sha256-HCNMQcqH/4MnGR0EYg2S3/BXYMM1z9lrFV10ANRd79o",
21
+ integrity: "sha256-Z/om3xyp6V2PKtx8BPobFfo9JCV0cOvBDMaLmquRS+4=",
21
22
  crossorigin: "anonymous") unless request.xhr?
22
23
  %>
23
24
 
24
25
  <style>
25
- .ruby-comment { color: #6a737d;}
26
- .ruby-const { color: #e36209; }
27
- .ruby-embexpr-beg, .ruby-embexpr-end, .ruby-period { color: #24292e; }
28
- .ruby-ident, .ruby-symbeg { color: #6f42c1; }
29
- .ruby-ivar, .ruby-cvar, .ruby-gvar, .ruby-int, .ruby-imaginary, .ruby-float, .ruby-rational { color: #005cc5; }
30
- .ruby-kw { color: #d73a49; }
31
- .ruby-label, .ruby-tstring-beg, .ruby-tstring-content, .ruby-tstring-end { color: #032f62; }
26
+ :root {
27
+ --ruby-comment: #6a737d;
28
+ --ruby-const: #e36209;
29
+ --ruby-embexpr: #24292e;
30
+ --ruby-ident: #6f42c1;
31
+ --ruby-number: #005cc5;
32
+ --ruby-keyword: #d73a49;
33
+ --ruby-string: #032f62;
34
+ --required-color: #ff6685;
35
+ }
36
+
37
+ @media (prefers-color-scheme: dark) {
38
+ :root {
39
+ --ruby-comment: #8b949e;
40
+ --ruby-const: #ffa657;
41
+ --ruby-embexpr: #c9d1d9;
42
+ --ruby-ident: #d2a8ff;
43
+ --ruby-number: #79c0ff;
44
+ --ruby-keyword: #ff7b72;
45
+ --ruby-string: #a5d6ff;
46
+ --required-color: #ff6685;
47
+ }
48
+ }
49
+
50
+ .ruby-comment { color: var(--ruby-comment); }
51
+ .ruby-const { color: var(--ruby-const); }
52
+ .ruby-embexpr-beg, .ruby-embexpr-end, .ruby-period { color: var(--ruby-embexpr); }
53
+ .ruby-ident, .ruby-symbeg { color: var(--ruby-ident); }
54
+ .ruby-ivar, .ruby-cvar, .ruby-gvar, .ruby-int, .ruby-imaginary, .ruby-float, .ruby-rational { color: var(--ruby-number); }
55
+ .ruby-kw { color: var(--ruby-keyword); }
56
+ .ruby-label, .ruby-tstring-beg, .ruby-tstring-content, .ruby-tstring-end { color: var(--ruby-string); }
32
57
 
33
58
  .select, select { width: 100%; }
34
59
  summary { cursor: pointer; }
@@ -51,12 +76,19 @@
51
76
  }
52
77
 
53
78
  .box {
54
- box-shadow: 0 4px 6px -1px #0000001a,
55
- 0 2px 4px -2px #0000001a;
79
+ box-shadow: 0 0 6px 0 #0000001a,
80
+ 0 2px 4px -1px #0000001a;
56
81
  }
82
+
83
+ @media (prefers-color-scheme: dark) {
84
+ .box {
85
+ background-color: var(--bulma-background);
86
+ }
87
+ }
88
+
57
89
  .label.is-required:after {
58
90
  content: " (required)";
59
- color: #ff6685;
91
+ color: var(--required-color);
60
92
  font-size: 12px;
61
93
  }
62
94
  </style>
@@ -99,6 +131,6 @@
99
131
 
100
132
  <%= yield %>
101
133
  </div>
102
- </div>
134
+ </section>
103
135
  </body>
104
136
  </html>
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreateMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
3
+ class CreateMaintenanceTasksRuns < ActiveRecord::Migration[7.0]
4
4
  def change
5
5
  create_table(:maintenance_tasks_runs, id: primary_key_type) do |t|
6
6
  t.string(:task_name, null: false)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class ChangeCursorToString < ActiveRecord::Migration[6.0]
3
+ class ChangeCursorToString < ActiveRecord::Migration[7.0]
4
4
  # This migration will clear all existing data in the cursor column with MySQL.
5
5
  # Ensure no Tasks are paused when this migration is deployed, or they will be resumed from the start.
6
6
  # Running tasks are able to gracefully handle this change, even if interrupted.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class RemoveIndexOnTaskName < ActiveRecord::Migration[6.0]
3
+ class RemoveIndexOnTaskName < ActiveRecord::Migration[7.0]
4
4
  def up
5
5
  change_table(:maintenance_tasks_runs) do |t|
6
6
  t.remove_index(:task_name)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AddArgumentsToMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
3
+ class AddArgumentsToMaintenanceTasksRuns < ActiveRecord::Migration[7.0]
4
4
  def change
5
5
  add_column(:maintenance_tasks_runs, :arguments, :text)
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AddLockVersionToMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
3
+ class AddLockVersionToMaintenanceTasksRuns < ActiveRecord::Migration[7.0]
4
4
  def change
5
5
  add_column(
6
6
  :maintenance_tasks_runs,
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class ChangeRunsTickColumnsToBigints < ActiveRecord::Migration[6.0]
3
+ class ChangeRunsTickColumnsToBigints < ActiveRecord::Migration[7.0]
4
4
  def up
5
5
  change_table(:maintenance_tasks_runs, bulk: true) do |t|
6
6
  t.change(:tick_count, :bigint)