maintenance_tasks 2.10.1 → 2.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b660fdfcef27420bf9b08ddc50c9cfac5ff0977c4435b54ecd2b468a3e4fde8
4
- data.tar.gz: 30bac0da63839406dc623d07c85904b7a727d3af5c177736cbd72ea828ca41c9
3
+ metadata.gz: 4ac2fd148131a277d37ddc609173b8549d01d3e6a9f78786cf729bd5da4f65b0
4
+ data.tar.gz: a2db7dfe8a9b0a958d943f88305fbe6d8988fd171901c49cd688070d8125124e
5
5
  SHA512:
6
- metadata.gz: 071dafa23a89bd949e40f10ef199ae892d5ae81ca2ad1e15e6c7de0260dafbd55f735d67c310f5de7ef499991982eb3bad69fce4024f8f8761e4357ac3fdc42b
7
- data.tar.gz: 77265626bdeafaa6486a3843846d519168be0712399ebe3cd7a4ecbb42f5888f208f566346d6d628643ee0dcf06ff1c4b914f2ab3ef9ec73f60b9444ef3f05c0
6
+ metadata.gz: c8e4c707d55ca479cca9f1592cc65359cc460d70cfd1e4ea4b2caf7c0a92617b4f243feb83c9f3657cb81a76691ed660c91bdafba73634b819a0fa44a719a2c8
7
+ data.tar.gz: 0efab90ffbdea1131f72de57443c8ed01fc30550d88c3ebbfe77b8e48a37c27dec8ec2ed7591ebdf9ea463e82ff4df7fcb649b111076a7302e10fce5720eb437
data/README.md CHANGED
@@ -74,9 +74,9 @@ 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
- In case you use an exception reporting service (e.g. Bugsnag) you might want to
78
- define an error handler. See [Customizing the error
79
- handler](#customizing-the-error-handler) for more information.
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.
80
80
 
81
81
  ### Active Job Dependency
82
82
 
@@ -988,44 +988,58 @@ If you are stuck in `pausing` and wish to preserve your tasks's position
988
988
  There are a few configurable options for the gem. Custom configurations should
989
989
  be placed in a `maintenance_tasks.rb` initializer.
990
990
 
991
- #### Customizing the error handler
991
+ #### Reporting errors
992
992
 
993
993
  Exceptions raised while a Task is performing are rescued and information about
994
994
  the error is persisted and visible in the UI.
995
995
 
996
- If you want to integrate with an exception monitoring service (e.g. Bugsnag),
997
- you can define an error handler:
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.
998
998
 
999
- ```ruby
1000
- # config/initializers/maintenance_tasks.rb
1001
-
1002
- MaintenanceTasks.error_handler = ->(error, task_context, _errored_element) do
1003
- Bugsnag.notify(error) do |notification|
1004
- notification.add_metadata(:task, task_context)
1005
- end
1006
- end
1007
- ```
1008
-
1009
- The error handler should be a lambda that accepts three arguments:
999
+ Reports to the error reporter will contain the following data:
1010
1000
 
1011
1001
  * `error`: The exception that was raised.
1012
- * `task_context`: A hash with additional information about the Task and the
1002
+ * `context`: A hash with additional information about the Task and the
1013
1003
  error:
1014
1004
  * `task_name`: The name of the Task that errored
1015
1005
  * `started_at`: The time the Task started
1016
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`
1015
+
1016
+ 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).
1019
+
1020
+ #### Reporting errors during iteration
1021
+
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.
1026
+
1027
+ ```ruby
1028
+ class MyTask < MaintenanceTasks::Task
1029
+ report_on(MyException)
1030
+ end
1031
+ ```
1032
+
1033
+ `MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can use
1034
+ to implement custom error handling.
1017
1035
 
1018
- Note that `task_context` may be empty if the Task produced an error before any
1019
- context could be gathered (for example, if deserializing the job to process
1020
- your Task failed).
1021
- * `errored_element`: The element, if any, that was being processed when the Task
1022
- raised an exception. If you would like to pass this object to your exception
1023
- monitoring service, make sure you **sanitize the object** to avoid leaking
1024
- sensitive data and **convert it to a format** that is compatible with your bug
1025
- tracker. For example, Bugsnag only sends the id and class name of Active
1026
- Record objects in order to protect sensitive data. CSV rows, on the other
1027
- hand, are converted to strings and passed raw to Bugsnag, so make sure to
1028
- filter any personal data from these objects before adding them to a report.
1036
+ ```ruby
1037
+ class MyTask < MaintenanceTasks::Task
1038
+ rescue_from(MyException) do |exception|
1039
+ handle(exception)
1040
+ end
1041
+ end
1042
+ ```
1029
1043
 
1030
1044
  #### Customizing the maintenance tasks module
1031
1045
 
@@ -14,7 +14,7 @@ module MaintenanceTasks
14
14
  # @param datetime [ActiveSupport::TimeWithZone] the time to be presented.
15
15
  # @return [String] the HTML to render with the relative datetime in words.
16
16
  def time_ago(datetime)
17
- time_tag(datetime, title: datetime.utc.iso8601, class: "is-clickable") do
17
+ time_tag(datetime, title: datetime.utc, class: "is-clickable") do
18
18
  time_ago_in_words(datetime) + " ago"
19
19
  end
20
20
  end
@@ -112,7 +112,7 @@ module MaintenanceTasks
112
112
  end
113
113
  rescue => error
114
114
  @errored_element = input
115
- raise error
115
+ raise error unless @task.rescue_with_handler(error)
116
116
  end
117
117
 
118
118
  def before_perform
@@ -181,11 +181,20 @@ module MaintenanceTasks
181
181
  task_name: @run.task_name,
182
182
  started_at: @run.started_at,
183
183
  ended_at: @run.ended_at,
184
+ run_id: @run.id,
185
+ tick_count: @run.tick_count,
184
186
  }
185
187
  end
186
- errored_element = @errored_element if defined?(@errored_element)
188
+ task_context[:errored_element] = @errored_element if defined?(@errored_element)
187
189
  ensure
188
- MaintenanceTasks.error_handler.call(error, task_context, errored_element)
190
+ if MaintenanceTasks.instance_variable_get(:@error_handler)
191
+ errored_element = task_context.delete(:errored_element)
192
+ 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
+ else
196
+ Rails.error.report(error, handled: true, context: task_context)
197
+ end
189
198
  end
190
199
  end
191
200
  end
@@ -33,11 +33,7 @@ module MaintenanceTasks
33
33
  ]
34
34
  COMPLETED_STATUSES = [:succeeded, :errored, :cancelled]
35
35
 
36
- if Rails.gem_version >= Gem::Version.new("7.0.alpha")
37
- enum :status, STATUSES.to_h { |status| [status, status.to_s] }
38
- else
39
- enum status: STATUSES.to_h { |status| [status, status.to_s] }
40
- end
36
+ enum :status, STATUSES.to_h { |status| [status, status.to_s] }
41
37
 
42
38
  after_save :instrument_status_change
43
39
 
@@ -8,6 +8,7 @@ module MaintenanceTasks
8
8
  include ActiveModel::Attributes
9
9
  include ActiveModel::AttributeAssignment
10
10
  include ActiveModel::Validations
11
+ include ActiveSupport::Rescuable
11
12
 
12
13
  class NotFoundError < NameError; end
13
14
 
@@ -200,6 +201,20 @@ module MaintenanceTasks
200
201
  set_callback(:error, :after, *filter_list, &block)
201
202
  end
202
203
 
204
+ # Rescue listed exceptions during an iteration and report them to the error reporter, then
205
+ # continue iteration.
206
+ #
207
+ # @param exceptions list of exceptions to rescue and report
208
+ def report_on(*exceptions)
209
+ rescue_from(*exceptions) do |exception|
210
+ if Rails.gem_version >= Gem::Version.new("7.1")
211
+ Rails.error.report(exception, source: "maintenance_tasks")
212
+ else
213
+ Rails.error.report(exception, handled: true)
214
+ end
215
+ end
216
+ end
217
+
203
218
  private
204
219
 
205
220
  def load_constants
@@ -1,6 +1,6 @@
1
1
  <div class="box">
2
2
  <h5 class="title is-5">
3
- <%= time_tag run.created_at, title: run.created_at.utc.iso8601 %>
3
+ <%= time_tag run.created_at, title: run.created_at.utc %>
4
4
  <%= status_tag run.status %>
5
5
  <span class="is-pulled-right" title="Run ID">#<%= run.id %></span>
6
6
  </h5>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <% if (run = task.related_run) %>
8
8
  <h5 class="title is-5">
9
- <%= time_tag run.created_at, title: run.created_at %>
9
+ <%= time_tag run.created_at, title: run.created_at.utc %>
10
10
  </h5>
11
11
 
12
12
  <%= progress run %>
@@ -21,6 +21,12 @@ module MaintenanceTasks
21
21
  MaintenanceTasks.backtrace_cleaner = Rails.backtrace_cleaner
22
22
  end
23
23
 
24
+ if Rails.gem_version >= Gem::Version.new("7.1")
25
+ initializer "maintenance_tasks.deprecator" do
26
+ Rails.application.deprecators[:maintenance_tasks] = MaintenanceTasks.deprecator
27
+ end
28
+ end
29
+
24
30
  config.to_prepare do
25
31
  _ = TaskJobConcern # load this for JobIteration compatibility check
26
32
  end
@@ -8,8 +8,6 @@ require "active_record"
8
8
  require "job-iteration"
9
9
  require "maintenance_tasks/engine"
10
10
 
11
- require "patches/active_record_batch_enumerator"
12
-
13
11
  # The engine's namespace module. It provides isolation between the host
14
12
  # application's code and the engine-specific code. Top-level engine constants
15
13
  # and variables are defined under this module.
@@ -63,16 +61,6 @@ module MaintenanceTasks
63
61
  # use when cleaning a Run's backtrace.
64
62
  mattr_accessor :backtrace_cleaner
65
63
 
66
- # @!attribute error_handler
67
- # @scope class
68
- #
69
- # The callback to perform when an error occurs in the Task. See the
70
- # {file:README#label-Customizing+the+error+handler} for details.
71
- #
72
- # @return [Proc] the callback to perform when an error occurs in the Task.
73
- mattr_accessor :error_handler, default:
74
- ->(_error, _task_context, _errored_element) {}
75
-
76
64
  # @!attribute parent_controller
77
65
  # @scope class
78
66
  #
@@ -96,4 +84,30 @@ module MaintenanceTasks
96
84
  #
97
85
  # @return [ActiveSupport::Duration] the threshold in seconds after which a task is considered stuck.
98
86
  mattr_accessor :stuck_task_duration, default: 5.minutes
87
+
88
+ class << self
89
+ DEPRECATION_MESSAGE = "MaintenanceTasks.error_handler is deprecated and will be removed in the 3.0 release. " \
90
+ "Instead, reports will be sent to the Rails error reporter. Do not set a handler and subscribe" \
91
+ "to the error reporter instead."
92
+ private_constant :DEPRECATION_MESSAGE
93
+
94
+ # @deprecated
95
+ def error_handler
96
+ deprecator.warn(DEPRECATION_MESSAGE)
97
+
98
+ @error_handler
99
+ end
100
+
101
+ # @deprecated
102
+ def error_handler=(proc)
103
+ deprecator.warn(DEPRECATION_MESSAGE)
104
+
105
+ @error_handler = proc
106
+ end
107
+
108
+ # @api-private
109
+ def deprecator
110
+ @deprecator ||= ActiveSupport::Deprecation.new("3.0", "MaintenanceTasks")
111
+ end
112
+ end
99
113
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintenance_tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.1
4
+ version: 2.11.0
5
5
  platform: ruby
6
- original_platform: ''
7
6
  authors:
8
7
  - Shopify Engineering
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-12-18 00:00:00.000000000 Z
10
+ date: 2025-01-28 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: actionpack
@@ -16,42 +15,42 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '6.1'
18
+ version: '7.0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '6.1'
25
+ version: '7.0'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: activejob
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '6.1'
32
+ version: '7.0'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '6.1'
39
+ version: '7.0'
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: activerecord
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '6.1'
46
+ version: '7.0'
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: '6.1'
53
+ version: '7.0'
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: csv
57
56
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +85,14 @@ dependencies:
86
85
  requirements:
87
86
  - - ">="
88
87
  - !ruby/object:Gem::Version
89
- version: '6.1'
88
+ version: '7.0'
90
89
  type: :runtime
91
90
  prerelease: false
92
91
  version_requirements: !ruby/object:Gem::Requirement
93
92
  requirements:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
- version: '6.1'
95
+ version: '7.0'
97
96
  - !ruby/object:Gem::Dependency
98
97
  name: zeitwerk
99
98
  requirement: !ruby/object:Gem::Requirement
@@ -178,13 +177,12 @@ files:
178
177
  - lib/maintenance_tasks.rb
179
178
  - lib/maintenance_tasks/cli.rb
180
179
  - lib/maintenance_tasks/engine.rb
181
- - lib/patches/active_record_batch_enumerator.rb
182
180
  - lib/tasks/maintenance_tasks_tasks.rake
183
181
  homepage: https://github.com/Shopify/maintenance_tasks
184
182
  licenses:
185
183
  - MIT
186
184
  metadata:
187
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.10.1
185
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.11.0
188
186
  allowed_push_host: https://rubygems.org
189
187
  rdoc_options: []
190
188
  require_paths:
@@ -200,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
198
  - !ruby/object:Gem::Version
201
199
  version: '0'
202
200
  requirements: []
203
- rubygems_version: 3.6.1
201
+ rubygems_version: 3.6.3
204
202
  specification_version: 4
205
203
  summary: A Rails engine for queuing and managing maintenance tasks
206
204
  test_files: []
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Rails.gem_version < Gem::Version.new("7.0")
4
- # Add attribute readers.
5
- module ActiveRecordBatchEnumerator
6
- # The primary key value from which the BatchEnumerator starts,
7
- # inclusive of the value.
8
- attr_reader :start
9
-
10
- # The primary key value at which the BatchEnumerator ends,
11
- # inclusive of the value.
12
- attr_reader :finish
13
-
14
- # The relation from which the BatchEnumerator yields batches.
15
- attr_reader :relation
16
-
17
- # The size of the batches yielded by the BatchEnumerator.
18
- def batch_size
19
- @of
20
- end
21
- end
22
-
23
- ActiveRecord::Batches::BatchEnumerator.include(ActiveRecordBatchEnumerator)
24
- end