maintenance_tasks 1.7.0 → 1.8.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/README.md +30 -0
- data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +7 -1
- data/app/models/maintenance_tasks/csv_collection_builder.rb +5 -0
- data/app/models/maintenance_tasks/no_collection_builder.rb +29 -0
- data/app/models/maintenance_tasks/null_collection_builder.rb +7 -0
- data/app/models/maintenance_tasks/run.rb +8 -2
- data/app/models/maintenance_tasks/task.rb +19 -8
- data/lib/generators/maintenance_tasks/task_generator.rb +13 -0
- data/lib/generators/maintenance_tasks/templates/no_collection_task.rb.tt +13 -0
- data/lib/generators/maintenance_tasks/templates/no_collection_task_test.rb.tt +12 -0
- data/lib/generators/maintenance_tasks/templates/task_test.rb.tt +4 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e66a4f3f2755a31f9dac0125d41d63d5b1b8ca28a4f11642c8648d94dbc2a044
|
4
|
+
data.tar.gz: 1cf5d80050866d6e78d4cf392c9ff888953a3d15da8c19f83f511aaaf94640d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffc019d73a707936a483349862481e5a227e270be3578f859b7d385fa7d997d327082a6f090881dcb83306feaf496d36f03d75421533ac507d7472adc5cad991
|
7
|
+
data.tar.gz: ae2ca92ea5b989e11d6ffaa50bd6ce1e5fcd41f735f81e6261ae496b81de2b90e7da2f58fdc04803dc77d44f6c0a8814a86794413f2e7ce374aa557c7e0073fb
|
data/README.md
CHANGED
@@ -160,6 +160,36 @@ primary keys of the records of the batch first, and then perform an additional
|
|
160
160
|
query to load the records when calling `each` (or any `Enumerable` method)
|
161
161
|
inside `#process`.
|
162
162
|
|
163
|
+
### Tasks that don't need a Collection
|
164
|
+
|
165
|
+
Sometimes, you might want to run a Task that performs a single operation, such
|
166
|
+
as enqueuing another background job or hitting an external API. The gem supports
|
167
|
+
collection-less tasks.
|
168
|
+
|
169
|
+
Generate a collection-less Task by running:
|
170
|
+
|
171
|
+
```bash
|
172
|
+
$ bin/rails generate maintenance_tasks:task no_collection_task --no-collection
|
173
|
+
```
|
174
|
+
|
175
|
+
The generated task is a subclass of `MaintenanceTasks::Task` that implements:
|
176
|
+
|
177
|
+
* `process`: do the work of your maintenance task
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
# app/tasks/maintenance/no_collection_task.rb
|
181
|
+
|
182
|
+
module Maintenance
|
183
|
+
class NoCollectionTask < MaintenanceTasks::Task
|
184
|
+
no_collection
|
185
|
+
|
186
|
+
def process
|
187
|
+
SomeAsyncJob.perform_later
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
163
193
|
### Throttling
|
164
194
|
|
165
195
|
Maintenance Tasks often modify a lot of data and can be taxing on your database.
|
@@ -36,6 +36,8 @@ module MaintenanceTasks
|
|
36
36
|
@enumerator = nil
|
37
37
|
|
38
38
|
collection_enum = case collection
|
39
|
+
when :no_collection
|
40
|
+
enumerator_builder.build_once_enumerator(cursor: nil)
|
39
41
|
when ActiveRecord::Relation
|
40
42
|
enumerator_builder.active_record_on_records(collection, cursor: cursor)
|
41
43
|
when ActiveRecord::Batches::BatchEnumerator
|
@@ -89,7 +91,11 @@ module MaintenanceTasks
|
|
89
91
|
end
|
90
92
|
|
91
93
|
def task_iteration(input)
|
92
|
-
@task.
|
94
|
+
if @task.no_collection?
|
95
|
+
@task.process
|
96
|
+
else
|
97
|
+
@task.process(input)
|
98
|
+
end
|
93
99
|
rescue => error
|
94
100
|
@errored_element = input
|
95
101
|
raise error
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaintenanceTasks
|
4
|
+
# Strategy for building a Task that has no collection. These Tasks
|
5
|
+
# consist of a single iteration.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class NoCollectionBuilder
|
9
|
+
# Specifies that this task does not process a collection.
|
10
|
+
def collection(_task)
|
11
|
+
:no_collection
|
12
|
+
end
|
13
|
+
|
14
|
+
# The number of rows to be processed. Always returns 1.
|
15
|
+
def count(_task)
|
16
|
+
1
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return that the Task does not process CSV content.
|
20
|
+
def has_csv_content?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns that the Task is collection-less.
|
25
|
+
def no_collection?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module MaintenanceTasks
|
4
4
|
# Base strategy for building a collection-based Task to be performed.
|
5
|
+
#
|
6
|
+
# @api private
|
5
7
|
class NullCollectionBuilder
|
6
8
|
# Placeholder method to raise in case a subclass fails to implement the
|
7
9
|
# expected instance method.
|
@@ -27,5 +29,10 @@ module MaintenanceTasks
|
|
27
29
|
def has_csv_content?
|
28
30
|
false
|
29
31
|
end
|
32
|
+
|
33
|
+
# Returns that the Task processes a collection.
|
34
|
+
def no_collection?
|
35
|
+
false
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
@@ -131,8 +131,8 @@ module MaintenanceTasks
|
|
131
131
|
self.started_at ||= Time.now
|
132
132
|
update!(
|
133
133
|
status: :errored,
|
134
|
-
error_class: error.class.
|
135
|
-
error_message: error.message,
|
134
|
+
error_class: truncate(:error_class, error.class.name),
|
135
|
+
error_message: truncate(:error_message, error.message),
|
136
136
|
backtrace: MaintenanceTasks.backtrace_cleaner.clean(error.backtrace),
|
137
137
|
ended_at: Time.now,
|
138
138
|
)
|
@@ -422,5 +422,11 @@ module MaintenanceTasks
|
|
422
422
|
errors.add(:base, error_message)
|
423
423
|
end
|
424
424
|
end
|
425
|
+
|
426
|
+
def truncate(attribute_name, value)
|
427
|
+
limit = MaintenanceTasks::Run.column_for_attribute(attribute_name).limit
|
428
|
+
return value unless limit
|
429
|
+
value&.first(limit)
|
430
|
+
end
|
425
431
|
end
|
426
432
|
end
|
@@ -18,6 +18,7 @@ module MaintenanceTasks
|
|
18
18
|
# @api private
|
19
19
|
class_attribute :throttle_conditions, default: []
|
20
20
|
|
21
|
+
# @api private
|
21
22
|
class_attribute :collection_builder_strategy,
|
22
23
|
default: NullCollectionBuilder.new
|
23
24
|
|
@@ -63,20 +64,23 @@ module MaintenanceTasks
|
|
63
64
|
MaintenanceTasks::CsvCollectionBuilder.new
|
64
65
|
end
|
65
66
|
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
# Make this a Task that calls #process once, instead of iterating over
|
68
|
+
# a collection.
|
69
|
+
def no_collection
|
70
|
+
self.collection_builder_strategy =
|
71
|
+
MaintenanceTasks::NoCollectionBuilder.new
|
71
72
|
end
|
72
73
|
|
74
|
+
delegate :has_csv_content?, :no_collection?,
|
75
|
+
to: :collection_builder_strategy
|
76
|
+
|
73
77
|
# Processes one item.
|
74
78
|
#
|
75
79
|
# Especially useful for tests.
|
76
80
|
#
|
77
|
-
# @param
|
78
|
-
def process(
|
79
|
-
new.process(
|
81
|
+
# @param args [Object, nil] the item to process
|
82
|
+
def process(*args)
|
83
|
+
new.process(*args)
|
80
84
|
end
|
81
85
|
|
82
86
|
# Returns the collection for this Task.
|
@@ -197,6 +201,13 @@ module MaintenanceTasks
|
|
197
201
|
self.class.has_csv_content?
|
198
202
|
end
|
199
203
|
|
204
|
+
# Returns whether the Task is collection-less.
|
205
|
+
#
|
206
|
+
# @return [Boolean] whether the Task is collection-less.
|
207
|
+
def no_collection?
|
208
|
+
self.class.no_collection?
|
209
|
+
end
|
210
|
+
|
200
211
|
# The collection to be processed, delegated to the strategy.
|
201
212
|
#
|
202
213
|
# @return the collection.
|
@@ -12,10 +12,17 @@ module MaintenanceTasks
|
|
12
12
|
class_option :csv, type: :boolean, default: false,
|
13
13
|
desc: "Generate a CSV Task."
|
14
14
|
|
15
|
+
class_option :no_collection, type: :boolean, default: false,
|
16
|
+
desc: "Generate a collection-less Task."
|
17
|
+
|
15
18
|
check_class_collision suffix: "Task"
|
16
19
|
|
17
20
|
# Creates the Task file.
|
18
21
|
def create_task_file
|
22
|
+
if options[:csv] && options[:no_collection]
|
23
|
+
raise "Multiple Task type options provided. Please use either "\
|
24
|
+
"--csv or --no-collection."
|
25
|
+
end
|
19
26
|
template_file = File.join(
|
20
27
|
"app/tasks/#{tasks_module_file_path}",
|
21
28
|
class_path,
|
@@ -23,6 +30,8 @@ module MaintenanceTasks
|
|
23
30
|
)
|
24
31
|
if options[:csv]
|
25
32
|
template("csv_task.rb", template_file)
|
33
|
+
elsif no_collection?
|
34
|
+
template("no_collection_task.rb", template_file)
|
26
35
|
else
|
27
36
|
template("task.rb", template_file)
|
28
37
|
end
|
@@ -76,5 +85,9 @@ module MaintenanceTasks
|
|
76
85
|
def test_framework
|
77
86
|
Rails.application.config.generators.options[:rails][:test_framework]
|
78
87
|
end
|
88
|
+
|
89
|
+
def no_collection?
|
90
|
+
options[:no_collection]
|
91
|
+
end
|
79
92
|
end
|
80
93
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module <%= tasks_module %>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= class_name %>TaskTest < ActiveSupport::TestCase
|
7
|
+
# test "#process performs a task iteration" do
|
8
|
+
# <%= tasks_module %>::<%= class_name %>Task.process
|
9
|
+
# end
|
10
|
+
end
|
11
|
+
<% end -%>
|
12
|
+
end
|
@@ -5,7 +5,11 @@ module <%= tasks_module %>
|
|
5
5
|
<% module_namespacing do -%>
|
6
6
|
class <%= class_name %>TaskTest < ActiveSupport::TestCase
|
7
7
|
# test "#process performs a task iteration" do
|
8
|
+
<%- if no_collection? -%>
|
9
|
+
# <%= tasks_module %>::<%= class_name %>Task.process
|
10
|
+
<%- else -%>
|
8
11
|
# <%= tasks_module %>::<%= class_name %>Task.process(element)
|
12
|
+
<%- end -%>
|
9
13
|
# end
|
10
14
|
end
|
11
15
|
<% end -%>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maintenance_tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- app/jobs/maintenance_tasks/task_job.rb
|
99
99
|
- app/models/maintenance_tasks/application_record.rb
|
100
100
|
- app/models/maintenance_tasks/csv_collection_builder.rb
|
101
|
+
- app/models/maintenance_tasks/no_collection_builder.rb
|
101
102
|
- app/models/maintenance_tasks/null_collection_builder.rb
|
102
103
|
- app/models/maintenance_tasks/progress.rb
|
103
104
|
- app/models/maintenance_tasks/run.rb
|
@@ -135,6 +136,8 @@ files:
|
|
135
136
|
- lib/generators/maintenance_tasks/install_generator.rb
|
136
137
|
- lib/generators/maintenance_tasks/task_generator.rb
|
137
138
|
- lib/generators/maintenance_tasks/templates/csv_task.rb.tt
|
139
|
+
- lib/generators/maintenance_tasks/templates/no_collection_task.rb.tt
|
140
|
+
- lib/generators/maintenance_tasks/templates/no_collection_task_test.rb.tt
|
138
141
|
- lib/generators/maintenance_tasks/templates/task.rb.tt
|
139
142
|
- lib/generators/maintenance_tasks/templates/task_spec.rb.tt
|
140
143
|
- lib/generators/maintenance_tasks/templates/task_test.rb.tt
|
@@ -147,7 +150,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
|
|
147
150
|
licenses:
|
148
151
|
- MIT
|
149
152
|
metadata:
|
150
|
-
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.
|
153
|
+
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.8.0
|
151
154
|
allowed_push_host: https://rubygems.org
|
152
155
|
post_install_message:
|
153
156
|
rdoc_options: []
|