maintenance_tasks 1.1.1 → 1.3.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 +4 -4
- data/LICENSE.md +18 -0
- data/README.md +90 -32
- data/app/controllers/maintenance_tasks/application_controller.rb +2 -4
- data/app/controllers/maintenance_tasks/tasks_controller.rb +1 -1
- data/app/helpers/maintenance_tasks/application_helper.rb +2 -13
- data/app/helpers/maintenance_tasks/tasks_helper.rb +17 -16
- data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +150 -0
- data/app/jobs/maintenance_tasks/task_job.rb +1 -129
- data/app/models/maintenance_tasks/csv_collection.rb +1 -1
- data/app/models/maintenance_tasks/progress.rb +13 -11
- data/app/models/maintenance_tasks/run.rb +26 -4
- data/app/models/maintenance_tasks/runner.rb +2 -2
- data/app/models/maintenance_tasks/runs_page.rb +55 -0
- data/app/models/maintenance_tasks/task_data.rb +3 -3
- data/app/tasks/maintenance_tasks/task.rb +24 -1
- data/app/validators/maintenance_tasks/run_status_validator.rb +11 -11
- data/app/views/maintenance_tasks/runs/_info.html.erb +1 -1
- data/app/views/maintenance_tasks/runs/info/_running.html.erb +0 -2
- data/app/views/maintenance_tasks/tasks/show.html.erb +3 -3
- data/config/routes.rb +4 -4
- data/db/migrate/20210225152418_remove_index_on_task_name.rb +1 -1
- data/exe/maintenance_tasks +2 -2
- data/lib/generators/maintenance_tasks/install_generator.rb +4 -4
- data/lib/generators/maintenance_tasks/task_generator.rb +10 -10
- data/lib/generators/maintenance_tasks/templates/task_spec.rb.tt +7 -5
- data/lib/maintenance_tasks.rb +51 -29
- data/lib/maintenance_tasks/cli.rb +3 -3
- data/lib/maintenance_tasks/engine.rb +4 -3
- metadata +10 -21
- data/Rakefile +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 649f66cc11a303666134c75aad8575b81eb376e358f69312180e21a14528ce50
|
4
|
+
data.tar.gz: b564b46e647c467c5d93a6198ab112729623059cfe194d1115d095c8ab8d9953
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f313350c5a80fa8840b23976eb7cde8776f825bc25a9b6a8fb7ce5d6f1abe8148d547ae7b2c1368a72caf980a2a12377e33b7c325e1447de3c0a0b0686d87a8d
|
7
|
+
data.tar.gz: '084950e7be06503e12d437294c784d02e2f0449c76db6297c5f95fcfd40845e4ce7e3354e16fbe12dd99b82554946f16fbc3415c047c17109aab1303c6b81b9b'
|
data/LICENSE.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright 2020-present, Shopify Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -2,34 +2,6 @@
|
|
2
2
|
|
3
3
|
A Rails engine for queuing and managing maintenance tasks.
|
4
4
|
|
5
|
-
## Table of Contents
|
6
|
-
|
7
|
-
* [Demo](#demo)
|
8
|
-
* [Installation](#installation)
|
9
|
-
* [Active Job Dependency](#active-job-dependency)
|
10
|
-
* [Usage](#usage)
|
11
|
-
* [Creating a Task](#creating-a-task)
|
12
|
-
* [Creating a CSV Task](#creating-a-csv-task)
|
13
|
-
* [Considerations when writing Tasks](#considerations-when-writing-tasks)
|
14
|
-
* [Writing tests for a Task](#writing-tests-for-a-task)
|
15
|
-
* [Writing tests for a CSV Task](#writing-tests-for-a-csv-task)
|
16
|
-
* [Running a Task](#running-a-task)
|
17
|
-
* [Monitoring your Task's status](#monitoring-your-tasks-status)
|
18
|
-
* [How Maintenance Tasks runs a Task](#how-maintenance-tasks-runs-a-task)
|
19
|
-
* [Help! My Task is stuck](#help-my-task-is-stuck)
|
20
|
-
* [Configuring the gem](#configuring-the-gem)
|
21
|
-
* [Customizing the error handler](#customizing-the-error-handler)
|
22
|
-
* [Customizing the maintenance tasks module](#customizing-the-maintenance-tasks-module)
|
23
|
-
* [Customizing the underlying job class](#customizing-the-underlying-job-class)
|
24
|
-
* [Customizing the rate at which task progress gets updated](#customizing-the-rate-at-which-task-progress-gets-updated)
|
25
|
-
* [Upgrading](#upgrading)
|
26
|
-
* [Contributing](#contributing)
|
27
|
-
* [Releasing new versions](#releasing-new-versions)
|
28
|
-
|
29
|
-
## Demo
|
30
|
-
|
31
|
-
Watch this demo video to see the gem in action:
|
32
|
-
|
33
5
|
[](https://www.youtube.com/watch?v=BTuvTQxlFzs)
|
34
6
|
|
35
7
|
## Installation
|
@@ -140,6 +112,46 @@ title,content
|
|
140
112
|
My Title,Hello World!
|
141
113
|
```
|
142
114
|
|
115
|
+
### Throttling
|
116
|
+
|
117
|
+
Maintenance Tasks often modify a lot of data and can be taxing on your database.
|
118
|
+
The gem provides a throttling mechanism that can be used to throttle a Task when
|
119
|
+
a given condition is met. If a Task is throttled, it will be interrupted and
|
120
|
+
retried after a backoff period has passed. The default backoff is 30 seconds.
|
121
|
+
Specify the throttle condition as a block:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# app/tasks/maintenance/update_posts_throttled_task.rb
|
125
|
+
module Maintenance
|
126
|
+
class UpdatePostsThrottledTask < MaintenanceTasks::Task
|
127
|
+
throttle_on(backoff: 1.minute) do
|
128
|
+
DatabaseStatus.unhealthy?
|
129
|
+
end
|
130
|
+
|
131
|
+
def collection
|
132
|
+
Post.all
|
133
|
+
end
|
134
|
+
|
135
|
+
def count
|
136
|
+
collection.count
|
137
|
+
end
|
138
|
+
|
139
|
+
def process(post)
|
140
|
+
post.update!(content: "New content added on #{Time.now.utc}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Note that it's up to you to define a throttling condition that makes sense for
|
147
|
+
your app. Shopify implements `DatabaseStatus.healthy?` to check various MySQL
|
148
|
+
metrics such as replication lag, DB threads, whether DB writes are available,
|
149
|
+
etc.
|
150
|
+
|
151
|
+
Tasks can define multiple throttle conditions. Throttle conditions are inherited
|
152
|
+
by descendants, and new conditions will be appended without impacting existing
|
153
|
+
conditions.
|
154
|
+
|
143
155
|
### Considerations when writing Tasks
|
144
156
|
|
145
157
|
MaintenanceTasks relies on the queue adapter configured for your application to
|
@@ -338,12 +350,15 @@ end
|
|
338
350
|
|
339
351
|
The error handler should be a lambda that accepts three arguments:
|
340
352
|
|
341
|
-
* `error`: The
|
353
|
+
* `error`: The exception that was raised.
|
342
354
|
* `task_context`: A hash with additional information about the Task and the
|
343
355
|
error:
|
344
356
|
* `task_name`: The name of the Task that errored
|
345
357
|
* `started_at`: The time the Task started
|
346
358
|
* `ended_at`: The time the Task errored
|
359
|
+
Note that `task_context` may be empty if the Task produced an error before any
|
360
|
+
context could be gathered (for example, if deserializing the job to process
|
361
|
+
your Task failed).
|
347
362
|
* `errored_element`: The element, if any, that was being processed when the
|
348
363
|
Task raised an exception. If you would like to pass this object to your
|
349
364
|
exception monitoring service, make sure you **sanitize the object** to avoid
|
@@ -400,6 +415,36 @@ MaintenanceTasks.ticker_delay = 2.seconds
|
|
400
415
|
|
401
416
|
If no value is specified, it will default to 1 second.
|
402
417
|
|
418
|
+
#### Customizing which Active Storage service to use
|
419
|
+
|
420
|
+
The Active Storage framework in Rails 6.1 and up supports multiple storage
|
421
|
+
services per environment. To specify which service to use,
|
422
|
+
`MaintenanceTasks.active_storage_service` can be configured with the service's
|
423
|
+
key, as specified in your application's `config/storage.yml`:
|
424
|
+
|
425
|
+
```yaml
|
426
|
+
# config/storage.yml
|
427
|
+
user_data:
|
428
|
+
service: GCS
|
429
|
+
credentials: <%= Rails.root.join("path/to/user/data/keyfile.json") %>
|
430
|
+
project: "my-project"
|
431
|
+
bucket: "user-data-bucket"
|
432
|
+
|
433
|
+
internal:
|
434
|
+
service: GCS
|
435
|
+
credentials: <%= Rails.root.join("path/to/internal/keyfile.json") %>
|
436
|
+
project: "my-project"
|
437
|
+
bucket: "internal-bucket"
|
438
|
+
```
|
439
|
+
|
440
|
+
```ruby
|
441
|
+
# config/initializers/maintenance_tasks.rb
|
442
|
+
MaintenanceTasks.active_storage_service = :internal
|
443
|
+
```
|
444
|
+
|
445
|
+
There is no need to configure this option if your application uses only one
|
446
|
+
storage service per environment.
|
447
|
+
|
403
448
|
## Upgrading
|
404
449
|
|
405
450
|
Use bundler to check for and upgrade to newer versions. After installing a new
|
@@ -419,15 +464,28 @@ pull requests. You can find the contribution guidelines on
|
|
419
464
|
|
420
465
|
[contributing]: https://github.com/Shopify/maintenance_tasks/blob/main/.github/CONTRIBUTING.md
|
421
466
|
|
467
|
+
### Dependabot updates
|
468
|
+
|
469
|
+
Whenever Dependabot creates a PR for a gem bump, check out the branch locally
|
470
|
+
and run `bin/update-gemfile <gem>` to ensure all the gemfiles have the gem
|
471
|
+
updated consistently.
|
472
|
+
|
422
473
|
## Releasing new versions
|
423
474
|
|
475
|
+
Updates should be added to the latest draft release on GitHub as Pull Requests
|
476
|
+
are merged.
|
477
|
+
|
478
|
+
Once a release is ready, follow these steps:
|
479
|
+
|
424
480
|
* Update `spec.version` in `maintenance_tasks.gemspec`.
|
425
|
-
* Run `
|
481
|
+
* Run `bin/gemfile-update install` to bump the version in all the lockfiles.
|
426
482
|
* Open a PR and merge on approval.
|
427
|
-
* Create a [release on GitHub][release] with a version number that matches the
|
428
|
-
version defined in the gemspec.
|
429
483
|
* Deploy via [Shipit][shipit] and see the new version on
|
430
484
|
<https://rubygems.org/gems/maintenance_tasks>.
|
485
|
+
* Ensure the release has documented all changes and publish it.
|
486
|
+
* Create a new [draft release on GitHub][release] with the title 'Upcoming
|
487
|
+
Release'. The tag version can be left blank. This will be the starting point
|
488
|
+
for documenting changes related to the next release.
|
431
489
|
|
432
490
|
[release]: https://help.github.com/articles/creating-releases/
|
433
491
|
[shipit]: https://shipit.shopify.io/shopify/maintenance_tasks/rubygems
|
@@ -5,9 +5,7 @@ module MaintenanceTasks
|
|
5
5
|
#
|
6
6
|
# Can be extended to add different authentication and authorization code.
|
7
7
|
class ApplicationController < ActionController::Base
|
8
|
-
|
9
|
-
|
10
|
-
BULMA_CDN = 'https://cdn.jsdelivr.net'
|
8
|
+
BULMA_CDN = "https://cdn.jsdelivr.net"
|
11
9
|
|
12
10
|
content_security_policy do |policy|
|
13
11
|
policy.style_src(BULMA_CDN)
|
@@ -17,7 +15,7 @@ module MaintenanceTasks
|
|
17
15
|
before_action do
|
18
16
|
request.content_security_policy_nonce_generator ||=
|
19
17
|
->(_request) { SecureRandom.base64(16) }
|
20
|
-
request.content_security_policy_nonce_directives = [
|
18
|
+
request.content_security_policy_nonce_directives = ["style-src"]
|
21
19
|
end
|
22
20
|
|
23
21
|
protect_from_forgery with: :exception
|
@@ -20,7 +20,7 @@ module MaintenanceTasks
|
|
20
20
|
def show
|
21
21
|
@task = TaskData.find(params.fetch(:id))
|
22
22
|
set_refresh if @task.last_run&.active?
|
23
|
-
@
|
23
|
+
@runs_page = RunsPage.new(@task.previous_runs, params[:cursor])
|
24
24
|
end
|
25
25
|
|
26
26
|
# Runs a given Task and redirects to the Task page.
|
@@ -4,17 +4,6 @@ module MaintenanceTasks
|
|
4
4
|
#
|
5
5
|
# @api private
|
6
6
|
module ApplicationHelper
|
7
|
-
include Pagy::Frontend
|
8
|
-
|
9
|
-
# Renders pagination for the page, if there is more than one page present.
|
10
|
-
#
|
11
|
-
# @param pagy [Pagy] the pagy instance containing pagination details,
|
12
|
-
# including the number of pages the results are spread across.
|
13
|
-
# @return [String] the HTML to render for pagination.
|
14
|
-
def pagination(pagy)
|
15
|
-
raw(pagy_bulma_nav(pagy)) if pagy.pages > 1
|
16
|
-
end
|
17
|
-
|
18
7
|
# Renders a time element with the given datetime, worded as relative to the
|
19
8
|
# current time.
|
20
9
|
#
|
@@ -24,8 +13,8 @@ module MaintenanceTasks
|
|
24
13
|
# @param datetime [ActiveSupport::TimeWithZone] the time to be presented.
|
25
14
|
# @return [String] the HTML to render with the relative datetime in words.
|
26
15
|
def time_ago(datetime)
|
27
|
-
time_tag(datetime, title: datetime.utc.iso8601, class:
|
28
|
-
time_ago_in_words(datetime) +
|
16
|
+
time_tag(datetime, title: datetime.utc.iso8601, class: "is-clickable") do
|
17
|
+
time_ago_in_words(datetime) + " ago"
|
29
18
|
end
|
30
19
|
end
|
31
20
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "ripper"
|
4
4
|
|
5
5
|
module MaintenanceTasks
|
6
6
|
# Helpers for formatting data in the maintenance_tasks views.
|
@@ -8,16 +8,16 @@ module MaintenanceTasks
|
|
8
8
|
# @api private
|
9
9
|
module TasksHelper
|
10
10
|
STATUS_COLOURS = {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
"new" => ["is-primary"],
|
12
|
+
"enqueued" => ["is-primary is-light"],
|
13
|
+
"running" => ["is-info"],
|
14
|
+
"interrupted" => ["is-info", "is-light"],
|
15
|
+
"pausing" => ["is-warning", "is-light"],
|
16
|
+
"paused" => ["is-warning"],
|
17
|
+
"succeeded" => ["is-success"],
|
18
|
+
"cancelling" => ["is-light"],
|
19
|
+
"cancelled" => ["is-dark"],
|
20
|
+
"errored" => ["is-danger"],
|
21
21
|
}
|
22
22
|
|
23
23
|
# Formats a run backtrace.
|
@@ -44,12 +44,13 @@ module MaintenanceTasks
|
|
44
44
|
|
45
45
|
progress = Progress.new(run)
|
46
46
|
|
47
|
-
tag.progress(
|
47
|
+
progress_bar = tag.progress(
|
48
48
|
value: progress.value,
|
49
49
|
max: progress.max,
|
50
|
-
|
51
|
-
class: ['progress'] + STATUS_COLOURS.fetch(run.status)
|
50
|
+
class: ["progress"] + STATUS_COLOURS.fetch(run.status)
|
52
51
|
)
|
52
|
+
progress_text = tag.p(tag.i(progress.text))
|
53
|
+
tag.div(progress_bar + progress_text, class: "block")
|
53
54
|
end
|
54
55
|
|
55
56
|
# Renders a span with a Run's status, with the corresponding tag class
|
@@ -59,7 +60,7 @@ module MaintenanceTasks
|
|
59
60
|
# @return [String] the span element containing the status, with the
|
60
61
|
# appropriate tag class attached.
|
61
62
|
def status_tag(status)
|
62
|
-
tag.span(status.capitalize, class: [
|
63
|
+
tag.span(status.capitalize, class: ["tag"] + STATUS_COLOURS.fetch(status))
|
63
64
|
end
|
64
65
|
|
65
66
|
# Returns the distance between now and the Run's expected completion time,
|
@@ -100,7 +101,7 @@ module MaintenanceTasks
|
|
100
101
|
when :on_nl, :on_sp, :on_ignored_nl
|
101
102
|
content
|
102
103
|
else
|
103
|
-
tag.span(content, class: type.to_s.sub(
|
104
|
+
tag.span(content, class: type.to_s.sub("on_", "ruby-").dasherize)
|
104
105
|
end
|
105
106
|
end
|
106
107
|
safe_join(tokens)
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module MaintenanceTasks
|
3
|
+
# Concern that holds the behaviour of the job that runs the tasks. It is
|
4
|
+
# included in {TaskJob} and if MaintenanceTasks.job is overridden, it must be
|
5
|
+
# included in the job.
|
6
|
+
module TaskJobConcern
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include JobIteration::Iteration
|
9
|
+
|
10
|
+
included do
|
11
|
+
before_perform(:before_perform)
|
12
|
+
|
13
|
+
on_start(:on_start)
|
14
|
+
on_complete(:on_complete)
|
15
|
+
on_shutdown(:on_shutdown)
|
16
|
+
|
17
|
+
after_perform(:after_perform)
|
18
|
+
|
19
|
+
rescue_from StandardError, with: :on_error
|
20
|
+
end
|
21
|
+
|
22
|
+
class_methods do
|
23
|
+
# Overrides ActiveJob::Exceptions.retry_on to declare it unsupported.
|
24
|
+
# The use of rescue_from prevents retry_on from being usable.
|
25
|
+
def retry_on(*, **)
|
26
|
+
raise NotImplementedError, "retry_on is not supported"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_enumerator(_run, cursor:)
|
33
|
+
cursor ||= @run.cursor
|
34
|
+
collection = @task.collection
|
35
|
+
|
36
|
+
collection_enum = case collection
|
37
|
+
when ActiveRecord::Relation
|
38
|
+
enumerator_builder.active_record_on_records(collection, cursor: cursor)
|
39
|
+
when Array
|
40
|
+
enumerator_builder.build_array_enumerator(collection, cursor: cursor)
|
41
|
+
when CSV
|
42
|
+
JobIteration::CsvEnumerator.new(collection).rows(cursor: cursor)
|
43
|
+
else
|
44
|
+
raise ArgumentError, "#{@task.class.name}#collection must be either "\
|
45
|
+
"an Active Record Relation, Array, or CSV."
|
46
|
+
end
|
47
|
+
|
48
|
+
@task.throttle_conditions.reduce(collection_enum) do |enum, condition|
|
49
|
+
enumerator_builder.build_throttle_enumerator(enum, **condition)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Performs task iteration logic for the current input returned by the
|
54
|
+
# enumerator.
|
55
|
+
#
|
56
|
+
# @param input [Object] the current element from the enumerator.
|
57
|
+
# @param _run [Run] the current Run, passed as an argument by Job Iteration.
|
58
|
+
def each_iteration(input, _run)
|
59
|
+
throw(:abort, :skip_complete_callbacks) if @run.stopping?
|
60
|
+
task_iteration(input)
|
61
|
+
@ticker.tick
|
62
|
+
@run.reload_status
|
63
|
+
end
|
64
|
+
|
65
|
+
def task_iteration(input)
|
66
|
+
@task.process(input)
|
67
|
+
rescue => error
|
68
|
+
@errored_element = input
|
69
|
+
raise error
|
70
|
+
end
|
71
|
+
|
72
|
+
def before_perform
|
73
|
+
@run = arguments.first
|
74
|
+
@task = Task.named(@run.task_name).new
|
75
|
+
if @task.respond_to?(:csv_content=)
|
76
|
+
@task.csv_content = @run.csv_file.download
|
77
|
+
end
|
78
|
+
@run.job_id = job_id
|
79
|
+
|
80
|
+
@run.running! unless @run.stopping?
|
81
|
+
|
82
|
+
@ticker = Ticker.new(MaintenanceTasks.ticker_delay) do |ticks, duration|
|
83
|
+
@run.persist_progress(ticks, duration)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def on_start
|
88
|
+
@run.update!(started_at: Time.now, tick_total: @task.count)
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_complete
|
92
|
+
@run.status = :succeeded
|
93
|
+
@run.ended_at = Time.now
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_shutdown
|
97
|
+
if @run.cancelling?
|
98
|
+
@run.status = :cancelled
|
99
|
+
@run.ended_at = Time.now
|
100
|
+
else
|
101
|
+
@run.status = @run.pausing? ? :paused : :interrupted
|
102
|
+
@run.cursor = cursor_position
|
103
|
+
end
|
104
|
+
|
105
|
+
@ticker.persist
|
106
|
+
end
|
107
|
+
|
108
|
+
# We are reopening a private part of Job Iteration's API here, so we should
|
109
|
+
# ensure the method is still defined upstream. This way, in the case where
|
110
|
+
# the method changes upstream, we catch it at load time instead of at
|
111
|
+
# runtime while calling `super`.
|
112
|
+
unless JobIteration::Iteration
|
113
|
+
.private_method_defined?(:reenqueue_iteration_job)
|
114
|
+
error_message = <<~HEREDOC
|
115
|
+
JobIteration::Iteration#reenqueue_iteration_job is expected to be
|
116
|
+
defined. Upgrading the maintenance_tasks gem should solve this problem.
|
117
|
+
HEREDOC
|
118
|
+
raise error_message
|
119
|
+
end
|
120
|
+
def reenqueue_iteration_job(should_ignore: true)
|
121
|
+
super() unless should_ignore
|
122
|
+
@reenqueue_iteration_job = true
|
123
|
+
end
|
124
|
+
|
125
|
+
def after_perform
|
126
|
+
@run.save!
|
127
|
+
if defined?(@reenqueue_iteration_job) && @reenqueue_iteration_job
|
128
|
+
reenqueue_iteration_job(should_ignore: false)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def on_error(error)
|
133
|
+
@ticker.persist if defined?(@ticker)
|
134
|
+
|
135
|
+
if defined?(@run)
|
136
|
+
@run.persist_error(error)
|
137
|
+
|
138
|
+
task_context = {
|
139
|
+
task_name: @run.task_name,
|
140
|
+
started_at: @run.started_at,
|
141
|
+
ended_at: @run.ended_at,
|
142
|
+
}
|
143
|
+
else
|
144
|
+
task_context = {}
|
145
|
+
end
|
146
|
+
errored_element = @errored_element if defined?(@errored_element)
|
147
|
+
MaintenanceTasks.error_handler.call(error, task_context, errored_element)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|