maintenance_tasks 2.11.0 → 2.13.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 (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +145 -44
  3. data/app/controllers/maintenance_tasks/application_controller.rb +15 -5
  4. data/app/helpers/maintenance_tasks/tasks_helper.rb +28 -11
  5. data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +24 -4
  6. data/app/models/maintenance_tasks/run.rb +102 -66
  7. data/app/models/maintenance_tasks/task.rb +33 -6
  8. data/app/models/maintenance_tasks/task_data_index.rb +2 -1
  9. data/app/views/layouts/maintenance_tasks/_navbar.html.erb +1 -1
  10. data/app/views/layouts/maintenance_tasks/application.html.erb +35 -4
  11. data/app/views/maintenance_tasks/runs/_arguments.html.erb +1 -1
  12. data/app/views/maintenance_tasks/runs/_run.html.erb +14 -8
  13. data/app/views/maintenance_tasks/runs/_serializable.html.erb +15 -18
  14. data/app/views/maintenance_tasks/tasks/_task.html.erb +4 -4
  15. data/app/views/maintenance_tasks/tasks/index.html.erb +6 -4
  16. data/app/views/maintenance_tasks/tasks/show.html.erb +16 -12
  17. data/db/migrate/20201211151756_create_maintenance_tasks_runs.rb +10 -2
  18. data/db/migrate/20210219212931_change_cursor_to_string.rb +1 -1
  19. data/db/migrate/20210225152418_remove_index_on_task_name.rb +1 -1
  20. data/db/migrate/20210517131953_add_arguments_to_maintenance_tasks_runs.rb +1 -1
  21. data/db/migrate/20211210152329_add_lock_version_to_maintenance_tasks_runs.rb +1 -1
  22. data/db/migrate/20220706101937_change_runs_tick_columns_to_bigints.rb +1 -1
  23. data/db/migrate/20220713131925_add_index_on_task_name_and_status_to_runs.rb +1 -1
  24. data/db/migrate/20230622035229_add_metadata_to_runs.rb +1 -1
  25. data/lib/maintenance_tasks/engine.rb +2 -4
  26. data/lib/maintenance_tasks.rb +25 -1
  27. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ac2fd148131a277d37ddc609173b8549d01d3e6a9f78786cf729bd5da4f65b0
4
- data.tar.gz: a2db7dfe8a9b0a958d943f88305fbe6d8988fd171901c49cd688070d8125124e
3
+ metadata.gz: 8402f1f4b8919e892a6334373ad397d49b5c6c78893f2f107b990b90653435d1
4
+ data.tar.gz: 1459d5b92233a1196aeb83c80bec9da525f04e975a50f6a415cdcd8dc647bd59
5
5
  SHA512:
6
- metadata.gz: c8e4c707d55ca479cca9f1592cc65359cc460d70cfd1e4ea4b2caf7c0a92617b4f243feb83c9f3657cb81a76691ed660c91bdafba73634b819a0fa44a719a2c8
7
- data.tar.gz: 0efab90ffbdea1131f72de57443c8ed01fc30550d88c3ebbfe77b8e48a37c27dec8ec2ed7591ebdf9ea463e82ff4df7fcb649b111076a7302e10fce5720eb437
6
+ metadata.gz: 3dff16b85a650f4d73cf79df20267d1278b1181f2ba6717742581909ebfd39145dfdd9b9cf1453edaa785474fb6f7dda87af2bad534e6398bf010945b02f2da3
7
+ data.tar.gz: c7b75c1edc684fad66c5080ee3642fe469038e1f4ab2329ead0fd3c65e5f0ca2a940250dc988658dce672e2de0d056741b9a317310afa23165ba27794722ce1d
data/README.md CHANGED
@@ -74,9 +74,11 @@ The generator creates and runs a migration to add the necessary table to your
74
74
  database. It also mounts Maintenance Tasks in your `config/routes.rb`. By
75
75
  default the web UI can be accessed in the new `/maintenance_tasks` path.
76
76
 
77
- This gem uses the [Rails Error Reporter](https://guides.rubyonrails.org/error_reporting.html) to report errors. If you are using a bug
78
- tracking service you may want to subscribe to the reporter. See [Reporting Errors](#reporting-errors)
79
- for more information.
77
+ This gem uses the [Rails Error Reporter][rails-error-reporting] to report errors.
78
+ If you are using a bug tracking service you may want to subscribe to the
79
+ reporter. See [Reporting Errors](#reporting-errors) for more information.
80
+
81
+ [rails-error-reporting]: https://guides.rubyonrails.org/error_reporting.html
80
82
 
81
83
  ### Active Job Dependency
82
84
 
@@ -114,12 +116,12 @@ The typical Maintenance Tasks workflow is as follows:
114
116
  1. [Generate a class describing the Task](#creating-a-task) and the work to be
115
117
  done.
116
118
  2. Run the Task
117
- - either by [using the included web UI](#running-a-task-from-the-web-ui),
118
- - or by [using the command line](#running-a-task-from-the-command-line),
119
- - or by [using Ruby](#running-a-task-from-ruby).
119
+ - either by [using the included web UI](#running-a-task-from-the-web-ui),
120
+ - or by [using the command line](#running-a-task-from-the-command-line),
121
+ - or by [using Ruby](#running-a-task-from-ruby).
120
122
  3. [Monitor the Task](#monitoring-your-tasks-status)
121
- - either by using the included web UI,
122
- - or by manually checking your task’s run’s status in your database.
123
+ - either by using the included web UI,
124
+ - or by manually checking your task’s run’s status in your database.
123
125
  4. Optionally, delete the Task code if you no longer need it.
124
126
 
125
127
  ### Creating a Task
@@ -168,7 +170,8 @@ end
168
170
  When processing records from an Active Record Relation, records are fetched in
169
171
  batches internally, and then each record is passed to the `#process` method.
170
172
  Maintenance Tasks will query the database to fetch records in batches of 100 by
171
- default, but the batch size can be modified using the `collection_batch_size` macro:
173
+ default, but the batch size can be modified using the `collection_batch_size`
174
+ macro:
172
175
 
173
176
  ```ruby
174
177
  # app/tasks/maintenance/update_posts_task.rb
@@ -502,13 +505,44 @@ set of values will be used to populate a dropdown in the user interface. The
502
505
  following types are supported:
503
506
 
504
507
  * Arrays
505
- * Procs and lambdas that optionally accept the Task instance, and return an Array.
506
- * Callable objects that receive one argument, the Task instance, and return an Array.
508
+ * Procs and lambdas that optionally accept the Task instance, and return an
509
+ Array.
510
+ * Callable objects that receive one argument, the Task instance, and return an
511
+ Array.
507
512
  * Methods that return an Array, called on the Task instance.
508
513
 
509
514
  For enumerables that don't match the supported types, a text field will be
510
515
  rendered instead.
511
516
 
517
+ ### Masking Task Parameters
518
+
519
+ Task attributes can be masked in the UI by adding `mask_attribute` class method
520
+ in the task class. This will replace the value in the arguments list with
521
+ `[FILTERED]` in the UI.
522
+
523
+ ```ruby
524
+ # app/tasks/maintenance/sensitive_params_task.rb
525
+
526
+ module Maintenance
527
+ class SensitiveParamsTask < MaintenanceTasks::Task
528
+ attribute :sensitive_content, :string
529
+
530
+ mask_attribute :sensitive_content
531
+ end
532
+ end
533
+ ```
534
+
535
+ If you have any filtered parameters in the global [Rails parameter
536
+ filter][rails-parameter-filter], they will be automatically taken into account
537
+ when masking the parameters, which means that you can mask parameters across all
538
+ tasks by adding them to the global rails parameters filter.
539
+
540
+ [rails-parameter-filter]:https://guides.rubyonrails.org/configuring.html#config-filter-parameters
541
+
542
+ ```ruby
543
+ Rails.application.config.filter_parameters += %i[token]
544
+ ```
545
+
512
546
  ### Custom cursor columns to improve performance
513
547
 
514
548
  The [job-iteration gem][job-iteration], on which this gem depends, adds an
@@ -899,10 +933,10 @@ a Task can be in:
899
933
 
900
934
  The Maintenance Tasks engine uses Rails sessions for flash messages and storing
901
935
  the CSRF token. For the engine to work in an API-only Rails application, you
902
- need to add a [session middleware][] and the `ActionDispatch::Flash`
903
- middleware. The engine also defines a strict [Content Security Policy][], make
904
- sure to include `ActionDispatch::ContentSecurityPolicy::Middleware` in your
905
- app's middleware stack to ensure the CSP is delivered to the user's browser.
936
+ need to add a [session middleware][] and the `ActionDispatch::Flash` middleware.
937
+ The engine also defines a strict [Content Security Policy][], make sure to
938
+ include `ActionDispatch::ContentSecurityPolicy::Middleware` in your app's
939
+ middleware stack to ensure the CSP is delivered to the user's browser.
906
940
 
907
941
  [session middleware]: https://guides.rubyonrails.org/api_app.html#using-session-middlewares
908
942
  [Content Security Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
@@ -926,8 +960,8 @@ module YourApplication
926
960
  end
927
961
  ```
928
962
 
929
- You can read more in the [Using Rails for API-only Applications][rails api] Rails
930
- guide.
963
+ You can read more in the [Using Rails for API-only Applications][rails api]
964
+ Rails guide.
931
965
 
932
966
  [rails api]: https://guides.rubyonrails.org/api_app.html
933
967
 
@@ -944,9 +978,9 @@ infrastructure or code changes.
944
978
  This means a Task can safely be interrupted, re-enqueued and resumed without any
945
979
  intervention at the end of an iteration, after the `process` method returns.
946
980
 
947
- By default, a running Task will be interrupted after running for more 5 minutes.
948
- This is [configured in the `job-iteration` gem][max-job-runtime] and can be
949
- tweaked in an initializer if necessary.
981
+ By default, a running Task will be interrupted after running for more than 5
982
+ minutes. This is [configured in the `job-iteration` gem][max-job-runtime] and
983
+ can be tweaked in an initializer if necessary.
950
984
 
951
985
  [max-job-runtime]: https://github.com/Shopify/job-iteration/blob/-/guides/best-practices.md#max-job-runtime
952
986
 
@@ -993,45 +1027,76 @@ be placed in a `maintenance_tasks.rb` initializer.
993
1027
  Exceptions raised while a Task is performing are rescued and information about
994
1028
  the error is persisted and visible in the UI.
995
1029
 
996
- Errors are also sent to the `Rails.error.reporter`, which can be configured by your
997
- application. See the [Error Reporting in Rails Applications](https://guides.rubyonrails.org/error_reporting.html) guide for more details.
1030
+ Errors are also sent to `Rails.error.report`, which can be configured by
1031
+ your application. See the [Error Reporting in Rails
1032
+ Applications][rails-error-reporting] guide for more details.
998
1033
 
999
1034
  Reports to the error reporter will contain the following data:
1000
1035
 
1001
1036
  * `error`: The exception that was raised.
1002
- * `context`: A hash with additional information about the Task and the
1003
- error:
1004
- * `task_name`: The name of the Task that errored
1005
- * `started_at`: The time the Task started
1006
- * `ended_at`: The time the Task errored
1007
- * `run_id`: The id of the errored Task run
1008
- * `tick_count`: The tick count at the time of the error
1009
- * `errored_element`: The element, if any, that was being processed when the Task
1010
- raised an exception. If you would like to pass this object to your exception
1011
- monitoring service, make sure you **sanitize the object** to avoid leaking
1012
- sensitive data and **convert it to a format** that is compatible with your bug
1013
- tracker.
1014
- * `source`: This will be `maintenance_tasks`
1037
+ * `context`: A hash with additional information about the Task and the error:
1038
+ * `task_name`: The name of the Task that errored
1039
+ * `started_at`: The time the Task started
1040
+ * `ended_at`: The time the Task errored
1041
+ * `run_id`: The id of the errored Task run
1042
+ * `tick_count`: The tick count at the time of the error
1043
+ * `errored_element`: The element, if any, that was being processed when the
1044
+ * `source`: This will be `maintenance-tasks`
1045
+ * `handled`: the value of `MaintenanceTasks.report_errors_as_handled` (default `true`, see below)
1015
1046
 
1016
1047
  Note that `context` may be empty if the Task produced an error before any
1017
- context could be gathered (for example, if deserializing the job to process
1018
- your Task failed).
1048
+ context could be gathered (for example, if deserializing the job to process your
1049
+ Task failed).
1050
+
1051
+ Here's an example custom subscriber to the Rails error reporter for integrating
1052
+ with an exception monitoring service (Bugsnag):
1053
+
1054
+ ```ruby
1055
+ # config/initializers/maintenance_tasks.rb
1056
+ MaintenanceTasks.report_errors_as_handled = false
1057
+
1058
+ class MaintenanceTasksErrorSubscriber
1059
+ def report(error, handled:, severity:, context:, source: nil)
1060
+ return unless source == "maintenance-tasks"
1061
+
1062
+ unless handled
1063
+ Bugsnag.notify(error) do |notification|
1064
+ notification.add_metadata(:task, context)
1065
+ end
1066
+ else
1067
+ Rails.logger.info(error)
1068
+ end
1069
+ end
1070
+ end
1071
+
1072
+ Rails.error.subscribe(MaintenanceTasksErrorSubscriber.new)
1073
+ ```
1074
+
1075
+ `MaintenanceTasks.report_errors_as_handled` determines the value for `handled` in this example.
1076
+ By default (for backwards compatibility) this is `true`.
1077
+ Setting this to `false` provides more accurate error reporting as it allows to distinguish between
1078
+ expected (e.g., via `report_on`) and unexpected errors in error subscribers.
1079
+ `false` will be the default in v3.0.
1019
1080
 
1020
1081
  #### Reporting errors during iteration
1021
1082
 
1022
- By default, errors raised during task iteration will be raised to the application
1023
- and iteration will stop. However, you may want to handle some errors and continue
1024
- iteration. `MaintenanceTasks::Task.report_on` can be used to rescue certain
1025
- exceptions and report them to the Rails error reporter.
1083
+ By default, errors raised during task iteration will be raised to the
1084
+ application and iteration will stop. However, you may want to handle some errors
1085
+ and continue iteration. `MaintenanceTasks::Task.report_on` can be used to rescue
1086
+ certain exceptions and report them to the Rails error reporter. Any keyword
1087
+ arguments are passed to
1088
+ [ActiveSupport::ErrorReporter#report][as-error-reporter-report]:
1089
+
1090
+ [as-error-reporter-report]: https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-report
1026
1091
 
1027
1092
  ```ruby
1028
1093
  class MyTask < MaintenanceTasks::Task
1029
- report_on(MyException)
1094
+ report_on(MyException, OtherException, severity: :info, context: {task_name: "my_task"})
1030
1095
  end
1031
1096
  ```
1032
1097
 
1033
- `MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can use
1034
- to implement custom error handling.
1098
+ `MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can
1099
+ use to implement custom error handling.
1035
1100
 
1036
1101
  ```ruby
1037
1102
  class MyTask < MaintenanceTasks::Task
@@ -1202,6 +1267,42 @@ The value for `MaintenanceTasks.stuck_task_duration` must be an
1202
1267
  `ActiveSupport::Duration`. If no value is specified, it will default to 5
1203
1268
  minutes.
1204
1269
 
1270
+ #### Configure status reload frequency
1271
+
1272
+ `MaintenanceTasks.status_reload_frequency` can be configured to specify how often
1273
+ the run status should be reloaded during iteration. By default, the status is
1274
+ reloaded every second, but this can be increased to improve performance. Note that increasing the reload interval impacts how quickly
1275
+ your task will stop if it is paused or interrupted.
1276
+
1277
+ ```ruby
1278
+ # config/initializers/maintenance_tasks.rb
1279
+ MaintenanceTasks.status_reload_frequency = 10.seconds # Reload status every 10 seconds
1280
+ ```
1281
+
1282
+ Individual tasks can also override this setting using the `reload_status_every` method:
1283
+
1284
+ ```ruby
1285
+ # app/tasks/maintenance/update_posts_task.rb
1286
+
1287
+ module Maintenance
1288
+ class UpdatePostsTask < MaintenanceTasks::Task
1289
+ # Reload status every 5 seconds instead of the global default
1290
+ reload_status_every(5.seconds)
1291
+
1292
+ def collection
1293
+ Post.all
1294
+ end
1295
+
1296
+ def process(post)
1297
+ post.update!(content: "New content!")
1298
+ end
1299
+ end
1300
+ end
1301
+ ```
1302
+
1303
+ This optimization can significantly reduce database queries, especially for short iterations.
1304
+ This is especially useful if the task doesn't need to check for cancellation/pausing very often.
1305
+
1205
1306
  #### Metadata
1206
1307
 
1207
1308
  `MaintenanceTasks.metadata` can be configured to specify a proc from which to
@@ -8,16 +8,26 @@ module MaintenanceTasks
8
8
  BULMA_CDN = "https://cdn.jsdelivr.net"
9
9
 
10
10
  content_security_policy do |policy|
11
- policy.style_src(
11
+ policy.style_src_elem(
12
12
  BULMA_CDN,
13
- # ruby syntax highlighting
14
- "'sha256-y9V0na/WU44EUNI/HDP7kZ7mfEci4PAOIjYOOan6JMA='",
13
+ # <style> tag in app/views/layouts/maintenance_tasks/application.html.erb
14
+ "'sha256-WHHDQLdkleXnAN5zs0GDXC5ls41CHUaVsJtVpaNx+EM='",
15
15
  )
16
- policy.script_src(
17
- # page refresh script
16
+ capybara_lockstep_scripts = [
17
+ "'sha256-1AoN3ZtJC5OvqkMgrYvhZjp4kI8QjJjO7TAyKYiDw+U='",
18
+ "'sha256-QVSzZi6ZsX/cu4h+hIs1iVivG1BxUmJggiEsGDIXBG0='", # with debug on
19
+ ] if defined?(Capybara::Lockstep)
20
+ policy.script_src_elem(
21
+ # <script> tag in app/views/layouts/maintenance_tasks/application.html.erb
18
22
  "'sha256-NiHKryHWudRC2IteTqmY9v1VkaDUA/5jhgXkMTkgo2w='",
23
+ # <script> tag for capybara-lockstep
24
+ *capybara_lockstep_scripts,
19
25
  )
26
+
27
+ policy.require_trusted_types_for # disable because we use new DOMParser().parseFromString
20
28
  policy.frame_ancestors(:self)
29
+ policy.connect_src(:self)
30
+ policy.form_action(:self)
21
31
  end
22
32
 
23
33
  protect_from_forgery with: :exception
@@ -47,7 +47,7 @@ module MaintenanceTasks
47
47
  progress_bar = tag.progress(
48
48
  value: progress.value,
49
49
  max: progress.max,
50
- class: ["progress"] + STATUS_COLOURS.fetch(run.status),
50
+ class: ["progress", "mt-4"] + STATUS_COLOURS.fetch(run.status),
51
51
  )
52
52
  progress_text = tag.p(tag.i(progress.text))
53
53
  tag.div(progress_bar + progress_text, class: "block")
@@ -60,7 +60,10 @@ module MaintenanceTasks
60
60
  # @return [String] the span element containing the status, with the
61
61
  # appropriate tag class attached.
62
62
  def status_tag(status)
63
- tag.span(status.capitalize, class: ["tag"] + STATUS_COLOURS.fetch(status))
63
+ tag.span(
64
+ status.capitalize,
65
+ class: ["tag", "has-text-weight-medium", "pr-2", "mr-4"] + STATUS_COLOURS.fetch(status),
66
+ )
64
67
  end
65
68
 
66
69
  # Reports the approximate elapsed time a Run has been processed so far based
@@ -101,7 +104,7 @@ module MaintenanceTasks
101
104
  )
102
105
  end
103
106
 
104
- # Resolves values covered by the inclusion validator for a task attribute.
107
+ # Resolves values covered by the inclusion validator for a Task attribute.
105
108
  # Supported option types:
106
109
  # - Arrays
107
110
  # - Procs and lambdas that optionally accept the Task instance, and return an Array.
@@ -112,7 +115,7 @@ module MaintenanceTasks
112
115
  #
113
116
  # Returned values are used to populate a dropdown list of options.
114
117
  #
115
- # @param task_class [Class<Task>] The task class for which the value needs to be resolved.
118
+ # @param task [Task] The Task for which the value needs to be resolved.
116
119
  # @param parameter_name [String] The parameter name.
117
120
  #
118
121
  # @return [Array] value of the resolved inclusion option.
@@ -149,24 +152,27 @@ module MaintenanceTasks
149
152
  # If the parameter has a `validates_inclusion_of` validator, return a dropdown list of options instead.
150
153
  def parameter_field(form_builder, parameter_name)
151
154
  inclusion_values = resolve_inclusion_value(form_builder.object, parameter_name)
152
- return form_builder.select(parameter_name, inclusion_values, prompt: "Select a value") if inclusion_values
155
+ if inclusion_values
156
+ return tag.div(form_builder.select(parameter_name, inclusion_values, prompt: "Select a value"), class: "select")
157
+ end
153
158
 
154
159
  case form_builder.object.class.attribute_types[parameter_name]
155
160
  when ActiveModel::Type::Integer
156
- form_builder.number_field(parameter_name)
161
+ form_builder.number_field(parameter_name, class: "input")
157
162
  when ActiveModel::Type::Decimal, ActiveModel::Type::Float
158
- form_builder.number_field(parameter_name, { step: "any" })
163
+ form_builder.number_field(parameter_name, { step: "any", class: "input" })
159
164
  when ActiveModel::Type::DateTime
160
- form_builder.datetime_field(parameter_name) + datetime_field_help_text
165
+ form_builder.datetime_field(parameter_name, class: "input") + datetime_field_help_text
161
166
  when ActiveModel::Type::Date
162
- form_builder.date_field(parameter_name)
167
+ form_builder.date_field(parameter_name, class: "input")
163
168
  when ActiveModel::Type::Time
164
- form_builder.time_field(parameter_name)
169
+ form_builder.time_field(parameter_name, class: "input")
165
170
  when ActiveModel::Type::Boolean
166
- form_builder.check_box(parameter_name)
171
+ form_builder.check_box(parameter_name, class: "checkbox")
167
172
  else
168
173
  form_builder.text_area(parameter_name, class: "textarea")
169
174
  end
175
+ .then { |input| tag.div(input, class: "control") }
170
176
  end
171
177
 
172
178
  # Return helper text for the datetime-local form field.
@@ -182,5 +188,16 @@ module MaintenanceTasks
182
188
  class: "content is-small",
183
189
  )
184
190
  end
191
+
192
+ # Checks if an attribute is required for a given Task.
193
+ #
194
+ # @param task [MaintenanceTasks::TaskDataShow] The TaskDataShow instance.
195
+ # @param parameter_name [Symbol] The name of the attribute to check.
196
+ # @return [Boolean] Whether the attribute is required.
197
+ def attribute_required?(task, parameter_name)
198
+ task.class.validators_on(parameter_name).any? do |validator|
199
+ validator.kind == :presence
200
+ end
201
+ end
185
202
  end
186
203
  end
@@ -101,7 +101,8 @@ module MaintenanceTasks
101
101
  throw(:abort, :skip_complete_callbacks) if @run.stopping?
102
102
  task_iteration(input)
103
103
  @ticker.tick
104
- @run.reload_status
104
+
105
+ reload_run_status
105
106
  end
106
107
 
107
108
  def task_iteration(input)
@@ -127,6 +128,8 @@ module MaintenanceTasks
127
128
  @ticker = Ticker.new(MaintenanceTasks.ticker_delay) do |ticks, duration|
128
129
  @run.persist_progress(ticks, duration)
129
130
  end
131
+
132
+ @last_status_reload = nil
130
133
  end
131
134
 
132
135
  def on_start
@@ -190,11 +193,28 @@ module MaintenanceTasks
190
193
  if MaintenanceTasks.instance_variable_get(:@error_handler)
191
194
  errored_element = task_context.delete(:errored_element)
192
195
  MaintenanceTasks.error_handler.call(error, task_context.except(:run_id, :tick_count), errored_element)
193
- elsif Rails.gem_version >= Gem::Version.new("7.1")
194
- Rails.error.report(error, context: task_context, source: "maintenance-tasks")
195
196
  else
196
- Rails.error.report(error, handled: true, context: task_context)
197
+ Rails.error.report(
198
+ error,
199
+ handled: MaintenanceTasks.report_errors_as_handled,
200
+ context: task_context,
201
+ source: "maintenance-tasks",
202
+ )
197
203
  end
198
204
  end
205
+
206
+ def reload_run_status
207
+ return unless should_reload_status?
208
+
209
+ @run.reload_status
210
+ @last_status_reload = Time.now
211
+ end
212
+
213
+ def should_reload_status?
214
+ return true if @last_status_reload.nil?
215
+
216
+ time_since_last_reload = Time.now - @last_status_reload
217
+ time_since_last_reload >= @task.status_reload_frequency
218
+ end
199
219
  end
200
220
  end