maintenance_tasks 2.10.1 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
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