rocketjob 6.3.2 → 7.0.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -316
  3. data/bin/rocketjob_batch_perf +1 -1
  4. data/lib/rocket_job/batch/categories.rb +25 -25
  5. data/lib/rocket_job/batch/io.rb +21 -15
  6. data/lib/rocket_job/batch/logger.rb +2 -2
  7. data/lib/rocket_job/batch/model.rb +2 -2
  8. data/lib/rocket_job/batch/performance.rb +1 -1
  9. data/lib/rocket_job/batch/statistics.rb +9 -2
  10. data/lib/rocket_job/batch/throttle.rb +7 -2
  11. data/lib/rocket_job/batch/throttle_running_workers.rb +5 -1
  12. data/lib/rocket_job/batch/throttle_windows.rb +4 -2
  13. data/lib/rocket_job/batch/worker.rb +51 -11
  14. data/lib/rocket_job/config.rb +6 -1
  15. data/lib/rocket_job/dirmon_entry.rb +6 -4
  16. data/lib/rocket_job/event.rb +84 -4
  17. data/lib/rocket_job/extensions/mongo/logging.rb +1 -0
  18. data/lib/rocket_job/extensions/mongoid/clients/options.rb +3 -3
  19. data/lib/rocket_job/extensions/mongoid/contextual/mongo.rb +3 -4
  20. data/lib/rocket_job/extensions/mongoid/factory.rb +2 -2
  21. data/lib/rocket_job/extensions/psych/yaml_tree.rb +8 -4
  22. data/lib/rocket_job/extensions/rocket_job_adapter.rb +1 -1
  23. data/lib/rocket_job/jobs/active_job.rb +1 -1
  24. data/lib/rocket_job/jobs/dirmon_job.rb +3 -1
  25. data/lib/rocket_job/jobs/on_demand_batch_job.rb +1 -1
  26. data/lib/rocket_job/jobs/upload_file_job.rb +8 -5
  27. data/lib/rocket_job/lookup_collection.rb +2 -2
  28. data/lib/rocket_job/performance.rb +1 -1
  29. data/lib/rocket_job/plugins/cron.rb +6 -3
  30. data/lib/rocket_job/plugins/document.rb +0 -2
  31. data/lib/rocket_job/plugins/job/logger.rb +2 -2
  32. data/lib/rocket_job/plugins/job/model.rb +1 -1
  33. data/lib/rocket_job/plugins/job/persistence.rb +2 -2
  34. data/lib/rocket_job/plugins/job/throttle.rb +14 -2
  35. data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +4 -1
  36. data/lib/rocket_job/plugins/job/worker.rb +5 -2
  37. data/lib/rocket_job/plugins/retry.rb +1 -1
  38. data/lib/rocket_job/plugins/throttle_dependent_jobs.rb +7 -2
  39. data/lib/rocket_job/plugins/transaction.rb +2 -2
  40. data/lib/rocket_job/sliced/input.rb +17 -12
  41. data/lib/rocket_job/sliced/slice.rb +3 -3
  42. data/lib/rocket_job/sliced/slices.rb +3 -6
  43. data/lib/rocket_job/supervisor.rb +2 -2
  44. data/lib/rocket_job/thread_worker.rb +3 -3
  45. data/lib/rocket_job/throttle_definition.rb +19 -2
  46. data/lib/rocket_job/throttle_definitions.rb +14 -9
  47. data/lib/rocket_job/version.rb +1 -1
  48. data/lib/rocket_job/worker.rb +77 -28
  49. data/lib/rocketjob.rb +1 -2
  50. metadata +33 -27
  51. data/lib/rocket_job/ractor_worker.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d48a3c90185bfb29f8d18060f54cd0f707eec8679d3ece753600034d93d7e16a
4
- data.tar.gz: 1fb550f51f4a1c2790bd0728bf06baea2b6750f42e118b9fe498255341d9baaa
3
+ metadata.gz: 7cf0e18a83d97aadac0d2741a399bcb55fa33884ccf2660836a5a3cfd946f772
4
+ data.tar.gz: d95563d0c77495dcdb3498dccf7cfc22fefee78bd22df244b823fc3939ef5373
5
5
  SHA512:
6
- metadata.gz: 7f89a635e43ee05ae8d980b8db70052c7783477e6451cc37d71be4952ef00711dbc15a550e75947d8197b12108ea8122a216bff0ca7bb3e141f4b9a1ee0a2598
7
- data.tar.gz: b51fa5e89696defe6326c85c32e00661b68b6146ed02f2222656fddfef2317468d303f36914649389fff477597bfda8747f09d8a22881d870603c9acef8e3d90
6
+ metadata.gz: 8f379b7890ddce715719616c311b501a916bc081bfd8eaba5055fa124badd58084985d9f38da0c081a8b5bcff3c67108ba3ea5ca52d8f2a8d7a92aa7297c94e8
7
+ data.tar.gz: db4e047d9c07faed020db569f7268fc0dea981ede5792ec40b7636718b118514fc6edc8d08b961d3dfe1d4ecadada40f548dde490ed4ede5326bbcb12ef5bedd
data/README.md CHANGED
@@ -1,344 +1,66 @@
1
1
  # Rocket Job
2
- [![Gem Version](https://img.shields.io/gem/v/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![Downloads](https://img.shields.io/gem/dt/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg)
2
+ [![Gem Version](https://img.shields.io/gem/v/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![Downloads](https://img.shields.io/gem/dt/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg)
3
3
 
4
- Ruby's missing batch system
4
+ **Process millions of records across thousands of workers.**
5
5
 
6
- Checkout https://rocketjob.io/
6
+ Rocket Job is a distributed, priority-based, MongoDB-backed batch processing system for Ruby. Run
7
+ conventional background jobs, or split a single job's input into slices and process it concurrently
8
+ across thousands of workers, spilling from memory to disk so very large files never fall over.
7
9
 
8
- ![Rocket Job](https://rocketjob.io/images/rocket/rocket-icon-512x512.png)
10
+ Full documentation is at **[rocketjob.reidmorrison.com](https://rocketjob.reidmorrison.com/)**.
11
+
12
+ ![Rocket Job](https://rocketjob.reidmorrison.com/images/rocket/rocket-icon-512x512.png)
9
13
 
10
14
  ## Documentation
11
15
 
12
- * [Guide](http://rocketjob.io/)
13
- * [API Reference](http://www.rubydoc.info/gems/rocketjob/)
16
+ * [Introduction](https://rocketjob.reidmorrison.com/) — what Rocket Job is and why it exists
17
+ * [Installation](https://rocketjob.reidmorrison.com/installation.html)
18
+ * [Programmer's Guide](https://rocketjob.reidmorrison.com/guide.html) — simple jobs
19
+ * [Batch Guide](https://rocketjob.reidmorrison.com/batch.html) — parallel batch jobs
20
+ * [Included Jobs](https://rocketjob.reidmorrison.com/jobs.html) and [Directory Monitor](https://rocketjob.reidmorrison.com/dirmon.html)
21
+ * [Events](https://rocketjob.reidmorrison.com/events.html)
22
+ * [Web UI (Mission Control)](https://rocketjob.reidmorrison.com/mission_control.html)
23
+ * [Deployment](https://rocketjob.reidmorrison.com/deployment.html)
24
+ * [Architecture and Internals](https://rocketjob.reidmorrison.com/architecture.html)
25
+ * [API Reference](https://www.rubydoc.info/gems/rocketjob/)
14
26
 
15
27
  ## Support
16
28
 
17
29
  * Ask questions in [Rocket Job Discussions](https://github.com/reidmorrison/rocketjob/discussions)
18
- * [Report bugs](https://github.com/rocketjob/rocketjob/issues)
19
-
20
- ## Rocket Job v6
21
-
22
- - Support for Ruby v3 and Rails 6.
23
- - Major enhancements in Batch job support:
24
- - Direct built-in Tabular support for all input and output categories.
25
- - Multiple output file support, each with its own settings for:
26
- - Compression
27
- - GZip, Zip, BZip2 (Chunked for much faster loading into Apache Spark).
28
- - Encryption
29
- - PGP, Symmetric Encryption.
30
- - File format
31
- - CSV, PSV, JSON, Fixed Format, xlsx.
32
- - Significant error handling improvements, especially around throttle failures
33
- that used to result in "hanging" jobs.
34
- - Support AWS DocumentDB in addition to MongoDB as the data store.
35
- - Removed use of Symbols to meet Symbol deprecation in MongoDB and Mongoid.
36
-
37
- ### Upgrading to Rocket Job v6
38
-
39
- The following plugins have been deprecated and are no longer loaded by default.
40
- - `RocketJob::Batch::Tabular::Input`
41
- - `RocketJob::Batch::Tabular::Output`
42
-
43
- If your code relies on these plugins and you still want to upgrade to Rocket Job v6,
44
- add the following require statement to any jobs that still use them:
45
-
46
- ~~~ruby
47
- require "rocket_job/batch/tabular"
48
- ~~~
49
-
50
- It is important to migrate away from these plugins, since they will be removed in a future release.
51
-
52
- #### Scheduled Jobs
53
-
54
- For any scheduled jobs that include the `RocketJob::Plugins::Cron` plugin, the default behavior has changed
55
- so that the scheduled job instance is created immediately after the currently scheduled instance starts.
56
-
57
- To maintain the old behavior of creating the job when it fails, aborts, or completes, add the following line
58
- to each of the applicable jobs:
59
-
60
- ~~~ruby
61
- self.cron_after_start = false
62
- ~~~
63
-
64
- Additionally, scheduled jobs will now prevent a new one from being created when another scheduled instance
65
- of the same job is already queued, or running with the _same_ `cron_schedule`.
66
-
67
- To maintain the old behavior of allowing multiple instances with the same cron schedule, add the following
68
- line to each of the applicable jobs:
69
-
70
- ~~~ruby
71
- self.cron_singleton = false
72
- ~~~
73
-
74
- ##### Singleton
75
-
76
- Since Scheduled jobs now implement their own singleton logic, remove the singleton plugin from any scheduled jobs.
77
-
78
- #### Upgrading Batch Jobs to Rocket Job v6
79
-
80
- Rocket Job v6 replaces the array of symbol type for `input_categories` and `output_categories`
81
- with an array of `RocketJob::Category::Input` and `RocketJob::Category::Output`.
82
-
83
- Jobs that added or modified the input or output categories need to be upgraded. For example:
84
- ~~~ruby
85
- class MyJob < RocketJob::Job
86
- include RocketJob::Batch
87
-
88
- self.output_categories = [:main, :errors, :ignored]
89
- end
90
- ~~~
30
+ * [Report bugs](https://github.com/reidmorrison/rocketjob/issues)
91
31
 
92
- Needs to be changed to:
93
- ~~~ruby
94
- class MyJob < RocketJob::Job
95
- include RocketJob::Batch
96
-
97
- output_category name: :main
98
- output_category name: :errors
99
- output_category name: :ignored
100
- end
101
- ~~~
102
-
103
- ##### slice_size, encrypt, compress
104
-
105
- These fields have been removed from the job itself:
106
- ~~~ruby
107
- class MyJob < RocketJob::Job
108
- include RocketJob::Batch
109
-
110
- self.slice_size = 1_000
111
- self.encrypt = true
112
- self.compress = true
113
- end
114
- ~~~
115
-
116
- They are now specified on the `input_category` as follows:
117
- - `slice_size` just moves under `input_category`.
118
- - `encrypt` becomes an option to `serializer`.
119
- - `compress` is now the default for all batch jobs so is not needed.
120
-
121
- If the serializer is set to `encrypt` then it is automatically compressed.
122
-
123
- ~~~ruby
124
- class MyJob < RocketJob::Job
125
- include RocketJob::Batch
126
-
127
- input_category slice_size: 1_000, serializer: :encrypt
128
- end
129
- ~~~
130
-
131
- ##### collect_output, collect_nil_output
132
-
133
- The following fields have been moved from the job itself:
134
- ~~~ruby
135
- class MyJob < RocketJob::Job
136
- include RocketJob::Batch
137
-
138
- self.collect_output = true
139
- self.collect_nil_output = true
140
- end
141
- ~~~
142
-
143
- Into the corresponding `output_category`:
144
- - `collect_output` no longer has any meaning. Output is collected anytime an `output_category` is defined.
145
- - `collect_nil_output` is now the option `nils` on the `output_category.
146
- It defaults to `false` so that by default any `nil` output from the `perform` method is not collected.
147
- ~~~ruby
148
- class MyJob < RocketJob::Job
149
- include RocketJob::Batch
150
-
151
- output_category nils: true
152
- end
153
- ~~~
154
-
155
- ##### name
32
+ ## Upgrading
156
33
 
157
- For both `input_category` and `output_category`, when the `name` argument is not supplied
158
- it defaults to `:main`.
34
+ See the [Upgrading guide](https://rocketjob.reidmorrison.com/upgrading.html) for the code and data changes needed
35
+ between major versions. Per-release notes are in the
36
+ [GitHub Releases](https://github.com/reidmorrison/rocketjob/releases).
159
37
 
160
- For Example:
161
- ~~~ruby
162
- class MyJob < RocketJob::Job
163
- include RocketJob::Batch
38
+ ## Contributing
164
39
 
165
- input_category name: :main, serializer: :encrypt
166
- output_category name: :main
167
- end
168
- ~~~
40
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup and how
41
+ to run the test suite, and the [Architecture and Internals](https://rocketjob.reidmorrison.com/architecture.html)
42
+ page for how Rocket Job is put together.
169
43
 
170
- Is the same as:
171
- ~~~ruby
172
- class MyJob < RocketJob::Job
173
- include RocketJob::Batch
44
+ The documentation site lives in [`docs/`](docs/) as Jekyll markdown. To preview changes locally:
174
45
 
175
- input_category serializer: :encrypt
176
- output_category
177
- end
46
+ ~~~bash
47
+ cd docs
48
+ bundle update
49
+ jekyll serve
178
50
  ~~~
179
51
 
180
- ##### Existing and inflight jobs
181
-
182
- When migrating to Rocket Job 6, it is recommended to load every job and then save it back again as part of the
183
- deployment. When the job loads it will automatically convert itself from the old schema to the new v6 schema.
184
-
185
- In flight jobs should not be affected, other than it is important to shutdown all running batch
186
- servers _before_ running any new instances.
187
-
188
- ## Rocket Job v4
189
-
190
- Rocket Job Pro is now fully open source and included in Rocket Job under the Apache License.
191
-
192
- The `RocketJob::Batch` plugin now adds batch processing capabilities to break up a single task into many
193
- concurrent workers processing slices of the entire job at the same time.
194
-
52
+ Then open [http://127.0.0.1:4000](http://127.0.0.1:4000) and edit the files under `docs/`.
195
53
 
196
- Example:
197
-
198
- ```ruby
199
- class MyJob < RocketJob::Job
200
- include RocketJob::Batch
201
-
202
- self.description = "Reverse names"
203
- self.destroy_on_complete = false
204
-
205
- # Collect the output for this job in the default output category: `:main`
206
- output_category
207
-
208
- # Method to call by all available workers at the same time.
209
- # Reverse the characters for each line:
210
- def perform(line)
211
- line.reverse
212
- end
213
- end
214
- ```
215
-
216
- Upload a file for processing, for example `names.csv` which could contain:
217
-
218
- ```
219
- jack
220
- jane
221
- bill
222
- john
223
- blake
224
- chris
225
- dave
226
- marc
227
- ```
228
-
229
- To queue the above job for processing:
230
-
231
- ```ruby
232
- job = MyJob.new
233
- job.upload('names.csv')
234
- job.save!
235
- ```
236
-
237
- Once the job has completed, download the results into a file:
238
-
239
- ```ruby
240
- job.download('names_reversed.csv')
241
- ```
242
-
243
- ## Contributing to the documentation
244
-
245
- To contribute to the documentation it is as easy as forking the repository
246
- and then editing the markdown pages directly via the github web interface.
247
-
248
- For more complex documentation changes checkout the source code locally.
249
-
250
- #### Local checkout
251
-
252
- * Fork the repository in github.
253
- * Checkout your fork of the source code locally.
254
- * Install Jekyll
255
- ~~~
256
- cd docs
257
- bundle update
258
- ~~~
259
- * Run Jekyll web server:
260
- ~~~
261
- jekyll serve
262
- ~~~
263
- * Open a web browser to view the local documentation:
264
- [http://127.0.0.1:4000](http://127.0.0.1:4000)
265
- * Edit the files in the `/docs` folder.
266
- * Refresh the page to see the changes.
267
-
268
- Once the changes are complete, submit a github pull request.
269
-
270
- ## Upgrading to V3
271
-
272
- V3 replaces MongoMapper with Mongoid which supports the latest MongoDB Ruby client driver.
273
-
274
- ### Upgrading Mongo Config file
275
- Replace `mongo.yml` with `mongoid.yml`.
276
-
277
- Start with the sample [mongoid.yml](https://github.com/rocketjob/rocketjob/blob/feature/mongoid/test/config/mongoid.yml).
278
-
279
- For more information on the new [Mongoid config file](https://docs.mongodb.com/ruby-driver/master/tutorials/5.1.0/mongoid-installation/).
280
-
281
- Note: The `rocketjob` and `rocketjob_slices` clients in the above `mongoid.yml` file are required.
282
-
283
- ### Other changes
284
-
285
- * Arguments are no longer supported, use fields for defining all named arguments for a job.
286
-
287
- * Replace usages of `rocket_job do` to set default values:
288
-
289
- ~~~ruby
290
- rocket_job do |job|
291
- job.priority = 25
292
- end
293
- ~~~
294
-
295
- With:
296
-
297
- ~~~ruby
298
- self.priority = 25
299
- ~~~
300
-
301
- * Replace `key` with `field` when adding attributes to a job:
302
-
303
- ~~~ruby
304
- key :inquiry_defaults, Hash
305
- ~~~
306
-
307
- With:
308
-
309
- ~~~ruby
310
- field :inquiry_defaults, type: Hash, default: {}
311
- ~~~
312
-
313
- * Replace usage of `public_rocket_job_properties` with the `user_editable` option:
314
-
315
- ~~~ruby
316
- field :priority, type: Integer, default: 50, user_editable: true
317
- ~~~
318
-
319
- ## Ruby Support
320
-
321
- Rocket Job is tested and supported on the following Ruby platforms:
322
- - Ruby 2.1, 2.2, 2.3, 2.4, and above
323
- - JRuby 9.0.5 and above
324
-
325
- ## Dependencies
54
+ ## Versioning
326
55
 
327
- * [MongoDB](https://www.mongodb.org)
328
- * Persists job information.
329
- * Version 2.7 or greater.
330
- * [Semantic Logger](https://rocketjob.github.io/semantic_logger)
331
- * Highly concurrent scalable logging.
56
+ This project uses [Semantic Versioning](https://semver.org/).
332
57
 
333
- ## Versioning
58
+ ## License
334
59
 
335
- This project uses [Semantic Versioning](http://semver.org/).
60
+ Apache License v2.0. See [LICENSE.txt](LICENSE.txt).
336
61
 
337
62
  ## Author
338
63
 
339
64
  [Reid Morrison](https://github.com/reidmorrison)
340
65
 
341
- ## Contributors
342
-
343
- [Contributors](https://github.com/rocketjob/rocketjob/graphs/contributors)
344
-
66
+ [Contributors](https://github.com/reidmorrison/rocketjob/graphs/contributors)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require "rocketjob_batch"
2
+ require "rocketjob"
3
3
 
4
4
  # Log to console
5
5
  SemanticLogger.add_appender(io: $stdout, formatter: :color)
@@ -51,12 +51,12 @@ module RocketJob
51
51
  # Builds this job instance from the supplied properties hash that may contain input and output categories.
52
52
  # Keeps the defaults and merges in settings without replacing existing categories.
53
53
  def from_properties(properties)
54
- return super(properties) unless properties.key?("input_categories") || properties.key?("output_categories")
54
+ return super unless properties.key?("input_categories") || properties.key?("output_categories")
55
55
 
56
56
  properties = properties.dup
57
57
  input_categories = properties.delete("input_categories")
58
58
  output_categories = properties.delete("output_categories")
59
- job = super(properties)
59
+ job = super
60
60
  job.merge_input_categories(input_categories)
61
61
  job.merge_output_categories(output_categories)
62
62
  job
@@ -126,7 +126,7 @@ module RocketJob
126
126
  categories.each do |properties|
127
127
  category_name = (properties["name"] || properties[:name] || :main).to_sym
128
128
  category = input_category(category_name)
129
- properties.each { |key, value| category.public_send("#{key}=".to_sym, value) }
129
+ properties.each { |key, value| category.public_send(:"#{key}=", value) }
130
130
  end
131
131
  end
132
132
 
@@ -136,7 +136,7 @@ module RocketJob
136
136
  categories.each do |properties|
137
137
  category_name = (properties["name"] || properties[:name] || :main).to_sym
138
138
  category = output_category(category_name)
139
- properties.each { |key, value| category.public_send("#{key}=".to_sym, value) }
139
+ properties.each { |key, value| category.public_send(:"#{key}=", value) }
140
140
  end
141
141
  end
142
142
 
@@ -314,27 +314,27 @@ module RocketJob
314
314
 
315
315
  existing = self[:output_categories]
316
316
  self[:output_categories] = []
317
- if collect_output
318
- if existing.blank?
319
- self[:output_categories] = [
320
- RocketJob::Category::Output.new(
321
- nils: collect_nil_output,
322
- format: main_output_format,
323
- columns: main_output_columns,
324
- format_options: main_output_options
325
- ).as_document
326
- ]
327
- elsif existing.first.is_a?(Symbol)
328
- self[:output_categories] = existing.collect do |category_name|
329
- RocketJob::Category::Output.new(
330
- name: category_name,
331
- serializer: serializer,
332
- nils: collect_nil_output,
333
- format: [:main, "main"].include?(category_name) ? main_output_format : nil,
334
- columns: [:main, "main"].include?(category_name) ? main_output_columns : nil,
335
- format_options: [:main, "main"].include?(category_name) ? main_output_options : nil
336
- ).as_document
337
- end
317
+ return unless collect_output
318
+
319
+ if existing.blank?
320
+ self[:output_categories] = [
321
+ RocketJob::Category::Output.new(
322
+ nils: collect_nil_output,
323
+ format: main_output_format,
324
+ columns: main_output_columns,
325
+ format_options: main_output_options
326
+ ).as_document
327
+ ]
328
+ elsif existing.first.is_a?(Symbol)
329
+ self[:output_categories] = existing.collect do |category_name|
330
+ RocketJob::Category::Output.new(
331
+ name: category_name,
332
+ serializer: serializer,
333
+ nils: collect_nil_output,
334
+ format: [:main, "main"].include?(category_name) ? main_output_format : nil,
335
+ columns: [:main, "main"].include?(category_name) ? main_output_columns : nil,
336
+ format_options: [:main, "main"].include?(category_name) ? main_output_options : nil
337
+ ).as_document
338
338
  end
339
339
  end
340
340
  end
@@ -269,13 +269,15 @@ module RocketJob
269
269
  # * If an exception is raised while uploading data, the input collection is cleared out
270
270
  # so that if a job is retried during an upload failure, data is not duplicated.
271
271
 
272
- def upload(object = nil, category: :main, file_name: nil, stream_mode: nil, on_first: nil, columns: nil, slice_batch_size: nil, **args, &block)
272
+ def upload(object = nil, category: :main, file_name: nil, stream_mode: nil, on_first: nil, columns: nil,
273
+ slice_batch_size: nil, **args, &block)
273
274
  input_collection = input(category)
274
275
 
275
276
  if block
276
277
  raise(ArgumentError, "Cannot supply both an object to upload, and a block.") if object
277
- if stream_mode || columns || slice_batch_size || args.size > 0
278
- raise(ArgumentError, "Unknown keyword arguments when uploading a block. Only accepts :category, :file_name, or :on_first")
278
+ if stream_mode || columns || slice_batch_size || args.size.positive?
279
+ raise(ArgumentError,
280
+ "Unknown keyword arguments when uploading a block. Only accepts :category, :file_name, or :on_first")
279
281
  end
280
282
 
281
283
  category = input_category(category)
@@ -292,8 +294,9 @@ module RocketJob
292
294
  count =
293
295
  case object
294
296
  when Range
295
- if file_name || stream_mode || on_first || args.size > 0
296
- raise(ArgumentError, "Unknown keyword arguments when uploading a Range. Only accepts :category, :columns, or :slice_batch_size")
297
+ if file_name || stream_mode || on_first || args.size.positive?
298
+ raise(ArgumentError,
299
+ "Unknown keyword arguments when uploading a Range. Only accepts :category, :columns, or :slice_batch_size")
297
300
  end
298
301
 
299
302
  first = object.first
@@ -304,14 +307,16 @@ module RocketJob
304
307
  input_collection.upload_integer_range_in_reverse_order(last, first, slice_batch_size: slice_batch_size || 1_000)
305
308
  end
306
309
  when Mongoid::Criteria
307
- if file_name || stream_mode || on_first || args.size > 0
308
- raise(ArgumentError, "Unknown keyword arguments when uploading a Mongoid::Criteria. Only accepts :category, :columns, or :slice_batch_size")
310
+ if file_name || stream_mode || on_first || args.size.positive?
311
+ raise(ArgumentError,
312
+ "Unknown keyword arguments when uploading a Mongoid::Criteria. Only accepts :category, :columns, or :slice_batch_size")
309
313
  end
310
314
 
311
315
  input_collection.upload_mongo_query(object, columns: columns, slice_batch_size: slice_batch_size, &block)
312
316
  when defined?(ActiveRecord::Relation) ? ActiveRecord::Relation : false
313
- if file_name || stream_mode || on_first || args.size > 0
314
- raise(ArgumentError, "Unknown keyword arguments when uploading an ActiveRecord::Relation. Only accepts :category, :columns, or :slice_batch_size")
317
+ if file_name || stream_mode || on_first || args.size.positive?
318
+ raise(ArgumentError,
319
+ "Unknown keyword arguments when uploading an ActiveRecord::Relation. Only accepts :category, :columns, or :slice_batch_size")
315
320
  end
316
321
 
317
322
  input_collection.upload_arel(object, columns: columns, slice_batch_size: slice_batch_size, &block)
@@ -336,15 +341,15 @@ module RocketJob
336
341
  end
337
342
 
338
343
  # @deprecated
339
- def upload_arel(arel, *column_names, category: :main, &block)
340
- count = input(category).upload_arel(arel, columns: column_names, &block)
344
+ def upload_arel(arel, *column_names, category: :main, &)
345
+ count = input(category).upload_arel(arel, columns: column_names, &)
341
346
  self.record_count = (record_count || 0) + count
342
347
  count
343
348
  end
344
349
 
345
350
  # @deprecated
346
- def upload_mongo_query(criteria, *column_names, category: :main, &block)
347
- count = input(category).upload_mongo_query(criteria, columns: column_names, &block)
351
+ def upload_mongo_query(criteria, *column_names, category: :main, &)
352
+ count = input(category).upload_mongo_query(criteria, columns: column_names, &)
348
353
  self.record_count = (record_count || 0) + count
349
354
  count
350
355
  end
@@ -358,7 +363,8 @@ module RocketJob
358
363
 
359
364
  # @deprecated
360
365
  def upload_integer_range_in_reverse_order(start_id, last_id, category: :main, slice_batch_size: 1_000)
361
- count = input(category).upload_integer_range_in_reverse_order(start_id, last_id, slice_batch_size: slice_batch_size)
366
+ count = input(category).upload_integer_range_in_reverse_order(start_id, last_id,
367
+ slice_batch_size: slice_batch_size)
362
368
  self.record_count = (record_count || 0) + count
363
369
  count
364
370
  end
@@ -459,7 +465,7 @@ module RocketJob
459
465
  binary_header_line = output_collection.slice_class.to_binary(header_line) if header_line
460
466
 
461
467
  # Don't overwrite supplied stream options if any
462
- stream = stream&.is_a?(IOStreams::Stream) ? stream.dup : IOStreams.new(category.file_name)
468
+ stream = stream.is_a?(IOStreams::Stream) ? stream.dup : IOStreams.new(category.file_name)
463
469
  stream.remove_from_pipeline(output_collection.slice_class.binary_format)
464
470
  stream.writer(**args) do |io|
465
471
  # TODO: Binary formats should return the record count, instead of the slice count.
@@ -29,7 +29,7 @@ module RocketJob
29
29
  # - log_exception logs entire exception if raised
30
30
  # - on_exception_level changes log level from info to error on exception
31
31
  # - silence noisy jobs by raising log level
32
- def rocket_job_batch_slice_logger(&block)
32
+ def rocket_job_batch_slice_logger(&)
33
33
  logger.measure_info(
34
34
  "Completed slice",
35
35
  metric: "#{self.class.name}/slice",
@@ -37,7 +37,7 @@ module RocketJob
37
37
  on_exception_level: :error,
38
38
  silence: log_level,
39
39
  payload: {records: rocket_job_slice&.size},
40
- &block
40
+ &
41
41
  )
42
42
  end
43
43
 
@@ -66,14 +66,14 @@ module RocketJob
66
66
  percent = percent_complete
67
67
  if percent >= 5
68
68
  secs = seconds.to_f
69
- h["est_remaining_duration"] = RocketJob.seconds_as_duration((((secs / percent) * 100) - secs))
69
+ h["est_remaining_duration"] = RocketJob.seconds_as_duration(((secs / percent) * 100) - secs)
70
70
  end
71
71
  end
72
72
  elsif completed?
73
73
  secs = seconds.to_f
74
74
  h["records_per_hour"] = ((record_count.to_f / secs) * 60 * 60).round if record_count&.positive? && (secs > 0.0)
75
75
  end
76
- h.merge!(super(time_zone))
76
+ h.merge!(super)
77
77
  h.delete("result")
78
78
  # Worker name should be retrieved from the slices when processing
79
79
  h.delete("worker_name") if sub_state == :processing
@@ -81,7 +81,7 @@ module RocketJob
81
81
  o.on("-E", "--encrypt", "Turn on encryption") do
82
82
  self.encrypt = true
83
83
  end
84
- o.on("-s", "--slice_size COUNT", "Slice size") do
84
+ o.on("-s", "--slice_size COUNT", "Slice size") do |arg|
85
85
  self.slice_size = arg.to_i
86
86
  end
87
87
  end
@@ -50,9 +50,16 @@ module RocketJob
50
50
  return unless last
51
51
 
52
52
  last_target = paths.inject(in_memory) do |target, sub_key|
53
- target.key?(sub_key) ? target[sub_key] : target[sub_key] = Hash.new(0)
53
+ # Assigning a Hash into a BSON::Document (Mongoid 9) stores a
54
+ # converted copy, so read the value back rather than reusing the
55
+ # assigned object, otherwise nested increments are lost.
56
+ target[sub_key] = Hash.new(0) unless target.key?(sub_key)
57
+ target[sub_key]
54
58
  end
55
- last_target[last] += increment
59
+ # Mongoid 9 demongoizes Hash fields to a BSON::Document without a
60
+ # default, so the leaf value may be nil even when the field default
61
+ # is `Hash.new(0)`. Default missing leaves to zero before adding.
62
+ last_target[last] = (last_target[last] || 0) + increment
56
63
  end
57
64
  end
58
65
 
@@ -45,11 +45,16 @@ module RocketJob
45
45
  # Or, a block that will return the filter.
46
46
  # Default: :throttle_filter_class (Throttle all jobs of this class)
47
47
  #
48
+ # description: [String|Proc]
49
+ # Human readable reason why the job is throttled, persisted to `throttled_by`
50
+ # and shown in Mission Control. A Proc is called with the job and slice and
51
+ # must return a String. Default: a humanized version of the method name.
52
+ #
48
53
  # Note: Throttles are executed in the order they are defined.
49
- def define_batch_throttle(method_name, filter: :throttle_filter_class)
54
+ def define_batch_throttle(method_name, filter: :throttle_filter_class, description: nil)
50
55
  # Duplicate to prevent modifying parent class throttles
51
56
  definitions = rocket_job_batch_throttles ? rocket_job_batch_throttles.dup : ThrottleDefinitions.new
52
- definitions.add(method_name, filter)
57
+ definitions.add(method_name, filter, description)
53
58
  self.rocket_job_batch_throttles = definitions
54
59
  end
55
60