parallel_minion 1.2.1 → 1.4.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 +5 -5
- data/README.md +12 -4
- data/Rakefile +10 -10
- data/lib/parallel_minion/minion.rb +105 -49
- data/lib/parallel_minion/railtie.rb +2 -3
- data/lib/parallel_minion/version.rb +2 -2
- data/lib/parallel_minion.rb +3 -4
- metadata +8 -25
- data/test/config/database.yml +0 -5
- data/test/minion_scope_test.rb +0 -59
- data/test/minion_test.rb +0 -207
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8ee831ee7f2939c246ff997e0abbdeca9f2779a4faa21d68d1cd6d4e27c09b52
|
|
4
|
+
data.tar.gz: e681733d3cbd2e9da49a0552e070a9fa52be776208d2bbfa02943752fd3b6a53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa0986ec9d4c9c8713f9a19a37ba32739af5c184fd558e1ce9860a2b38de5caf6ccb8aa1a933fb4969f9ca94224a21386fd608551b6d582d0e2418976b9c26c6
|
|
7
|
+
data.tar.gz: 8d7b3ca45414d9154311fed2fa3d7ab487f201224532e07304c1f8965265d0d8bb96a44da10aa4b82b940869df0c60c3e91e1fbd43390f0595cc91a6aef716e8
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# Parallel Minion
|
|
2
|
+
[](https://rubygems.org/gems/parallel_minion) [](https://github.com/reidmorrison/parallel_minion/actions?query=workflow%3Abuild) [](https://rubygems.org/gems/parallel_minion) [](http://opensource.org/licenses/Apache-2.0) 
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Wrap Ruby code with a minion so that it is run on a parallel thread.
|
|
5
5
|
|
|
6
6
|
## Description
|
|
7
7
|
|
|
@@ -46,12 +46,20 @@ has been achieved by moving existing blocks of code into Minions.
|
|
|
46
46
|
|
|
47
47
|
gem install parallel_minion
|
|
48
48
|
|
|
49
|
+
## Rails 7.2 compatibility
|
|
50
|
+
|
|
51
|
+
- **Apps** need **Ruby ≥ 3.1** (Rails 7.2 requirement).
|
|
52
|
+
- **Code:** Thread cleanup uses `ActiveRecord::Base.connection_handler.clear_active_connections!` instead of `ActiveRecord::Base.clear_active_connections!` because Rails 7 deprecates the latter.
|
|
53
|
+
- **Railtie:** Unchanged (`config.parallel_minion` as before).
|
|
54
|
+
- **CI:** `rails_7.2` Appraisal `gemfiles/rails_7.2.gemfile`, Ruby 3.2. The 7.2 appraisal pins **Minitest ~> 5.0** (tests use `stub`, removed in Minitest 6).
|
|
55
|
+
- **sqlite3 (dev):** The 7.2 appraisal allows `sqlite3 >= 1.4` (including 2.x). Other gemfiles use `sqlite3 >= 1.5, < 2` to skip **1.4.4**, which often fails to compile on modern toolchains (all OSes).
|
|
56
|
+
|
|
49
57
|
## Meta
|
|
50
58
|
|
|
51
59
|
* Code: `git clone git://github.com/reidmorrison/parallel_minion.git`
|
|
52
60
|
* Home: <https://github.com/reidmorrison/parallel_minion>
|
|
53
61
|
* Bugs: <http://github.com/reidmorrison/parallel_minion/issues>
|
|
54
|
-
* Gems: <
|
|
62
|
+
* Gems: <https://rubygems.org/gems/parallel_minion>
|
|
55
63
|
|
|
56
64
|
This project uses [Semantic Versioning](http://semver.org/).
|
|
57
65
|
|
data/Rakefile
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
# Setup bundler to avoid having to run bundle exec all the time.
|
|
2
|
-
require
|
|
3
|
-
require
|
|
2
|
+
require "rubygems"
|
|
3
|
+
require "bundler/setup"
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
require_relative
|
|
5
|
+
require "rake/testtask"
|
|
6
|
+
require_relative "lib/parallel_minion/version"
|
|
7
7
|
|
|
8
8
|
task :gem do
|
|
9
|
-
system
|
|
9
|
+
system "gem build parallel_minion.gemspec"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
task :
|
|
13
|
-
system "git tag -a v#{ParallelMinion::VERSION} -m 'Tagging #{ParallelMinion::VERSION}'"
|
|
14
|
-
system
|
|
12
|
+
task publish: :gem do
|
|
13
|
+
# system "git tag -a v#{ParallelMinion::VERSION} -m 'Tagging #{ParallelMinion::VERSION}'"
|
|
14
|
+
# system "git push --tags"
|
|
15
15
|
system "gem push parallel_minion-#{ParallelMinion::VERSION}.gem"
|
|
16
16
|
system "rm parallel_minion-#{ParallelMinion::VERSION}.gem"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
Rake::TestTask.new(:test) do |t|
|
|
20
|
-
t.pattern =
|
|
20
|
+
t.pattern = "test/**/*_test.rb"
|
|
21
21
|
t.verbose = true
|
|
22
22
|
t.warning = false
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# By default run tests against all appraisals
|
|
26
26
|
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
|
27
|
-
require
|
|
27
|
+
require "appraisal"
|
|
28
28
|
task default: :appraisal
|
|
29
29
|
else
|
|
30
30
|
task default: :test
|
|
@@ -22,7 +22,7 @@ module ParallelMinion
|
|
|
22
22
|
# Metrics [String]
|
|
23
23
|
attr_reader :metric, :wait_metric
|
|
24
24
|
|
|
25
|
-
attr_reader :on_timeout, :log_exception, :start_time
|
|
25
|
+
attr_reader :on_timeout, :log_exception, :start_time, :on_exception_level
|
|
26
26
|
|
|
27
27
|
# Give an infinite amount of time to wait for a Minion to complete a task
|
|
28
28
|
INFINITE = 0
|
|
@@ -40,8 +40,8 @@ module ParallelMinion
|
|
|
40
40
|
# - Production:
|
|
41
41
|
# Batch processing in Rocket Job where throughput is more important than latency.
|
|
42
42
|
# http://rocketjob.io
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
class << self
|
|
44
|
+
attr_writer :enabled
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# Returns whether minions are enabled to run in their own threads
|
|
@@ -54,8 +54,8 @@ module ParallelMinion
|
|
|
54
54
|
#
|
|
55
55
|
# Example:
|
|
56
56
|
# ...
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
class << self
|
|
58
|
+
attr_reader :scoped_classes
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def self.scoped_classes=(scoped_classes)
|
|
@@ -70,11 +70,12 @@ module ParallelMinion
|
|
|
70
70
|
# :trace, :debug, :info, :warn, :error, :fatal
|
|
71
71
|
def self.started_log_level=(level)
|
|
72
72
|
raise(ArgumentError, "Invalid log level: #{level}") unless SemanticLogger::LEVELS.include?(level)
|
|
73
|
+
|
|
73
74
|
@started_log_level = level
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
class << self
|
|
78
|
+
attr_reader :started_log_level
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
# Change the log level for the Completed log message.
|
|
@@ -85,18 +86,18 @@ module ParallelMinion
|
|
|
85
86
|
# :trace, :debug, :info, :warn, :error, :fatal
|
|
86
87
|
def self.completed_log_level=(level)
|
|
87
88
|
raise(ArgumentError, "Invalid log level: #{level}") unless SemanticLogger::LEVELS.include?(level)
|
|
89
|
+
|
|
88
90
|
@completed_log_level = level
|
|
89
91
|
end
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
class << self
|
|
94
|
+
attr_reader :completed_log_level
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
self.started_log_level = :info
|
|
96
98
|
self.completed_log_level = :info
|
|
97
99
|
self.enabled = true
|
|
98
100
|
self.scoped_classes = []
|
|
99
|
-
logger.name = 'Minion'
|
|
100
101
|
|
|
101
102
|
# Create a new Minion
|
|
102
103
|
#
|
|
@@ -177,6 +178,10 @@ module ParallelMinion
|
|
|
177
178
|
# Any unhandled exception raised in the block will not be logged
|
|
178
179
|
# Default: :partial
|
|
179
180
|
#
|
|
181
|
+
# :on_exception_level [:trace | :debug | :info | :warn | :error | :fatal]
|
|
182
|
+
# Override the log level only when an exception occurs.
|
|
183
|
+
# Default: ParallelMinion::Minion.completed_log_level
|
|
184
|
+
#
|
|
180
185
|
# :enabled [Boolean]
|
|
181
186
|
# Override the global setting: `ParallelMinion::Minion.enabled?` for this minion instance.
|
|
182
187
|
#
|
|
@@ -193,25 +198,54 @@ module ParallelMinion
|
|
|
193
198
|
# thread.pool.enabled=true
|
|
194
199
|
#
|
|
195
200
|
# Example:
|
|
196
|
-
# ParallelMinion::Minion.new(
|
|
201
|
+
# ParallelMinion::Minion.new(
|
|
202
|
+
# 10.days.ago,
|
|
203
|
+
# description: 'Doing something else in parallel',
|
|
204
|
+
# timeout: 1000
|
|
205
|
+
# ) do |date|
|
|
197
206
|
# MyTable.where('created_at <= ?', date).count
|
|
198
207
|
# end
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
#
|
|
209
|
+
# Example, when the result is being ignored, log full exception as an error:
|
|
210
|
+
# ParallelMinion::Minion.new(
|
|
211
|
+
# customer,
|
|
212
|
+
# description: "We don't care about the result",
|
|
213
|
+
# log_exception: :full,
|
|
214
|
+
# on_exception_level: :error
|
|
215
|
+
# ) do |customer|
|
|
216
|
+
# customer.save!
|
|
217
|
+
# end
|
|
218
|
+
def initialize(*arguments,
|
|
219
|
+
description: "Minion",
|
|
220
|
+
metric: nil,
|
|
221
|
+
log_exception: nil,
|
|
222
|
+
on_exception_level: self.class.completed_log_level,
|
|
223
|
+
enabled: self.class.enabled?,
|
|
224
|
+
timeout: INFINITE,
|
|
225
|
+
on_timeout: nil,
|
|
226
|
+
wait_metric: nil,
|
|
227
|
+
&block)
|
|
228
|
+
raise "Missing mandatory block that Minion must perform" unless block
|
|
229
|
+
|
|
230
|
+
@start_time = Time.now
|
|
231
|
+
@exception = nil
|
|
232
|
+
@arguments = arguments
|
|
233
|
+
@timeout = timeout.to_f
|
|
234
|
+
@description = description.to_s
|
|
235
|
+
@metric = metric
|
|
236
|
+
@log_exception = log_exception
|
|
237
|
+
@on_exception_level = on_exception_level
|
|
238
|
+
@enabled = enabled
|
|
239
|
+
@on_timeout = on_timeout
|
|
240
|
+
|
|
241
|
+
@wait_metric = (wait_metric || "#{metric}/wait") if @metric
|
|
242
|
+
|
|
243
|
+
# When minion is disabled make it obvious in the logs by setting the name to 'Inline' instead of 'Minion'.
|
|
244
|
+
unless @enabled
|
|
245
|
+
l = self.class.logger.dup
|
|
246
|
+
l.name = "Inline"
|
|
247
|
+
self.logger = l
|
|
248
|
+
end
|
|
215
249
|
|
|
216
250
|
@enabled ? run(&block) : run_inline(&block)
|
|
217
251
|
end
|
|
@@ -225,7 +259,12 @@ module ParallelMinion
|
|
|
225
259
|
# Return nil if Minion is still working and has time left to finish
|
|
226
260
|
if working?
|
|
227
261
|
ms = time_left
|
|
228
|
-
logger.measure(
|
|
262
|
+
logger.measure(
|
|
263
|
+
self.class.completed_log_level,
|
|
264
|
+
"Waited for Minion to complete: #{description}",
|
|
265
|
+
min_duration: 0.01,
|
|
266
|
+
metric: wait_metric
|
|
267
|
+
) do
|
|
229
268
|
if @thread.join(ms.nil? ? nil : ms / 1000).nil?
|
|
230
269
|
@thread.raise(@on_timeout.new("Minion: #{description} timed out")) if @on_timeout
|
|
231
270
|
logger.warn("Timed out waiting for: #{description}")
|
|
@@ -257,8 +296,9 @@ module ParallelMinion
|
|
|
257
296
|
# Returns 0 if no time is left
|
|
258
297
|
# Returns nil if their is no time limit. I.e. :timeout was set to Minion::INFINITE (infinite time left)
|
|
259
298
|
def time_left
|
|
260
|
-
return nil if
|
|
261
|
-
|
|
299
|
+
return nil if timeout.zero? || (timeout == -1)
|
|
300
|
+
|
|
301
|
+
duration = timeout - ((Time.now - start_time) * 1000)
|
|
262
302
|
duration <= 0 ? 0 : duration
|
|
263
303
|
end
|
|
264
304
|
|
|
@@ -272,32 +312,40 @@ module ParallelMinion
|
|
|
272
312
|
if defined?(ActiveRecord)
|
|
273
313
|
if ActiveRecord::VERSION::MAJOR >= 4
|
|
274
314
|
def self.current_scopes
|
|
275
|
-
scoped_classes.collect
|
|
315
|
+
scoped_classes.collect(&:all)
|
|
276
316
|
end
|
|
277
317
|
else
|
|
278
318
|
def self.current_scopes
|
|
279
|
-
scoped_classes.collect
|
|
319
|
+
scoped_classes.collect(&:scoped)
|
|
280
320
|
end
|
|
281
321
|
end
|
|
282
322
|
end
|
|
283
323
|
|
|
284
324
|
private
|
|
285
325
|
|
|
326
|
+
# rubocop:disable Lint/RescueException
|
|
327
|
+
|
|
286
328
|
# Run the supplied block of code in the current thread.
|
|
287
329
|
# Useful for debugging, testing, and when running in batch environments.
|
|
288
330
|
def run_inline(&block)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@
|
|
331
|
+
logger.public_send(self.class.started_log_level, "Started #{description}")
|
|
332
|
+
logger.measure(
|
|
333
|
+
self.class.completed_log_level,
|
|
334
|
+
"Completed #{description}",
|
|
335
|
+
log_exception: log_exception,
|
|
336
|
+
on_exception_level: on_exception_level,
|
|
337
|
+
metric: metric
|
|
338
|
+
) do
|
|
339
|
+
@result = instance_exec(*arguments, &block)
|
|
298
340
|
end
|
|
341
|
+
rescue Exception => e
|
|
342
|
+
@exception = e
|
|
343
|
+
ensure
|
|
344
|
+
@duration = Time.now - start_time
|
|
299
345
|
end
|
|
300
346
|
|
|
347
|
+
# rubocop:enable Lint/RescueException
|
|
348
|
+
|
|
301
349
|
def run(&block)
|
|
302
350
|
# Capture tags from current thread
|
|
303
351
|
tags = SemanticLogger.tags
|
|
@@ -316,17 +364,26 @@ module ParallelMinion
|
|
|
316
364
|
SemanticLogger.tagged(*tags) do
|
|
317
365
|
SemanticLogger.named_tagged(named_tags) do
|
|
318
366
|
logger.public_send(self.class.started_log_level, "Started #{description}")
|
|
367
|
+
# rubocop:disable Lint/RescueException
|
|
319
368
|
begin
|
|
320
|
-
proc =
|
|
321
|
-
logger.measure(
|
|
322
|
-
|
|
323
|
-
|
|
369
|
+
proc = proc { run_in_scope(scopes, &block) }
|
|
370
|
+
logger.measure(
|
|
371
|
+
self.class.completed_log_level,
|
|
372
|
+
"Completed #{description}",
|
|
373
|
+
log_exception: log_exception,
|
|
374
|
+
on_exception_level: on_exception_level,
|
|
375
|
+
metric: metric,
|
|
376
|
+
&proc
|
|
377
|
+
)
|
|
378
|
+
rescue Exception => e
|
|
379
|
+
@exception = e
|
|
324
380
|
nil
|
|
325
381
|
ensure
|
|
326
382
|
@duration = Time.now - start_time
|
|
327
383
|
# Return any database connections used by this thread back to the pool
|
|
328
|
-
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord::Base)
|
|
384
|
+
ActiveRecord::Base.connection_handler.clear_active_connections! if defined?(ActiveRecord::Base)
|
|
329
385
|
end
|
|
386
|
+
# rubocop:enable Lint/RescueException
|
|
330
387
|
end
|
|
331
388
|
end
|
|
332
389
|
end
|
|
@@ -338,12 +395,11 @@ module ParallelMinion
|
|
|
338
395
|
else
|
|
339
396
|
# Use the captured scope when running the block.
|
|
340
397
|
# Each Class to scope requires passing a block to .scoping.
|
|
341
|
-
proc =
|
|
398
|
+
proc = proc { instance_exec(*@arguments, &block) }
|
|
342
399
|
first = scopes.shift
|
|
343
|
-
scopes.each { |scope| proc =
|
|
400
|
+
scopes.each { |scope| proc = proc { scope.scoping(&proc) } }
|
|
344
401
|
@result = first.scoping(&proc)
|
|
345
402
|
end
|
|
346
403
|
end
|
|
347
|
-
|
|
348
404
|
end
|
|
349
405
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
module ParallelMinion
|
|
2
|
-
class Railtie < Rails::Railtie
|
|
1
|
+
module ParallelMinion # :nodoc:
|
|
2
|
+
class Railtie < Rails::Railtie # :nodoc:
|
|
3
3
|
#
|
|
4
4
|
# Make the ParallelMinion config available in the Rails application config
|
|
5
5
|
#
|
|
@@ -26,6 +26,5 @@ module ParallelMinion #:nodoc:
|
|
|
26
26
|
# end
|
|
27
27
|
# end
|
|
28
28
|
config.parallel_minion = ::ParallelMinion::Minion
|
|
29
|
-
|
|
30
29
|
end
|
|
31
30
|
end
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module ParallelMinion
|
|
2
|
-
VERSION =
|
|
1
|
+
module ParallelMinion
|
|
2
|
+
VERSION = "1.4.0".freeze
|
|
3
3
|
end
|
data/lib/parallel_minion.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
require
|
|
2
|
-
require 'semantic_logger'
|
|
1
|
+
require "semantic_logger"
|
|
3
2
|
|
|
4
3
|
module ParallelMinion
|
|
5
|
-
autoload :Minion,
|
|
4
|
+
autoload :Minion, "parallel_minion/minion"
|
|
6
5
|
end
|
|
7
6
|
|
|
8
|
-
require
|
|
7
|
+
require "parallel_minion/railtie" if defined?(Rails)
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parallel_minion
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Reid Morrison
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: semantic_logger
|
|
@@ -24,10 +23,6 @@ dependencies:
|
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: '4.0'
|
|
27
|
-
description: Parallel Minion allows you to take existing blocks of code and wrap them
|
|
28
|
-
in a minion so that they can run asynchronously in a separate thread
|
|
29
|
-
email:
|
|
30
|
-
- reidmo@gmail.com
|
|
31
26
|
executables: []
|
|
32
27
|
extensions: []
|
|
33
28
|
extra_rdoc_files: []
|
|
@@ -39,16 +34,11 @@ files:
|
|
|
39
34
|
- lib/parallel_minion/minion.rb
|
|
40
35
|
- lib/parallel_minion/railtie.rb
|
|
41
36
|
- lib/parallel_minion/version.rb
|
|
42
|
-
- test/config/database.yml
|
|
43
|
-
- test/minion_scope_test.rb
|
|
44
|
-
- test/minion_test.rb
|
|
45
|
-
- test/test_db.sqlite3
|
|
46
|
-
- test/test_helper.rb
|
|
47
37
|
homepage: https://github.com/reidmorrison/parallel_minion
|
|
48
38
|
licenses:
|
|
49
39
|
- Apache License V2.0
|
|
50
|
-
metadata:
|
|
51
|
-
|
|
40
|
+
metadata:
|
|
41
|
+
rubygems_mfa_required: 'true'
|
|
52
42
|
rdoc_options: []
|
|
53
43
|
require_paths:
|
|
54
44
|
- lib
|
|
@@ -56,21 +46,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
56
46
|
requirements:
|
|
57
47
|
- - ">="
|
|
58
48
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '2.
|
|
49
|
+
version: '2.5'
|
|
60
50
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
51
|
requirements:
|
|
62
52
|
- - ">="
|
|
63
53
|
- !ruby/object:Gem::Version
|
|
64
54
|
version: '0'
|
|
65
55
|
requirements: []
|
|
66
|
-
|
|
67
|
-
rubygems_version: 2.6.11
|
|
68
|
-
signing_key:
|
|
56
|
+
rubygems_version: 3.6.9
|
|
69
57
|
specification_version: 4
|
|
70
|
-
summary:
|
|
71
|
-
test_files:
|
|
72
|
-
- test/config/database.yml
|
|
73
|
-
- test/minion_scope_test.rb
|
|
74
|
-
- test/minion_test.rb
|
|
75
|
-
- test/test_db.sqlite3
|
|
76
|
-
- test/test_helper.rb
|
|
58
|
+
summary: Wrap Ruby code with a minion so that it is run on a parallel thread
|
|
59
|
+
test_files: []
|
data/test/config/database.yml
DELETED
data/test/minion_scope_test.rb
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
require_relative 'test_helper'
|
|
2
|
-
require 'erb'
|
|
3
|
-
require 'active_record'
|
|
4
|
-
|
|
5
|
-
ActiveRecord::Base.logger = SemanticLogger[ActiveRecord]
|
|
6
|
-
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
|
7
|
-
ActiveRecord::Base.establish_connection(:test)
|
|
8
|
-
|
|
9
|
-
ActiveRecord::Schema.define :version => 0 do
|
|
10
|
-
create_table :people, :force => true do |t|
|
|
11
|
-
t.string :name
|
|
12
|
-
t.string :state
|
|
13
|
-
t.string :zip_code
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class Person < ActiveRecord::Base
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
class MinionScopeTest < Minitest::Test
|
|
21
|
-
describe ParallelMinion::Minion do
|
|
22
|
-
[false, true].each do |enabled|
|
|
23
|
-
describe ".new with enabled: #{enabled.inspect}" do
|
|
24
|
-
before do
|
|
25
|
-
Person.create(name: 'Jack', state: 'FL', zip_code: 38729)
|
|
26
|
-
Person.create(name: 'John', state: 'FL', zip_code: 35363)
|
|
27
|
-
Person.create(name: 'Jill', state: 'FL', zip_code: 73534)
|
|
28
|
-
Person.create(name: 'Joe', state: 'NY', zip_code: 45325)
|
|
29
|
-
Person.create(name: 'Jane', state: 'NY', zip_code: 45325)
|
|
30
|
-
Person.create(name: 'James', state: 'CA', zip_code: 123123)
|
|
31
|
-
# Instruct Minions to adhere to any dynamic scopes for Person model
|
|
32
|
-
ParallelMinion::Minion.scoped_classes << Person
|
|
33
|
-
ParallelMinion::Minion.enabled = enabled
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
after do
|
|
37
|
-
Person.destroy_all
|
|
38
|
-
ParallelMinion::Minion.scoped_classes.clear
|
|
39
|
-
SemanticLogger.flush
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'copy across model scope' do
|
|
43
|
-
assert_equal 6, Person.count
|
|
44
|
-
|
|
45
|
-
Person.unscoped.where(state: 'FL').scoping { Person.count }
|
|
46
|
-
|
|
47
|
-
Person.unscoped.where(state: 'FL').scoping do
|
|
48
|
-
assert_equal 3, Person.count
|
|
49
|
-
minion = ParallelMinion::Minion.new(description: 'Scope Test', log_exception: :full) do
|
|
50
|
-
Person.count
|
|
51
|
-
end
|
|
52
|
-
assert_equal 3, minion.result
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
data/test/minion_test.rb
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
require_relative './test_helper'
|
|
2
|
-
|
|
3
|
-
# Test ParallelMinion standalone without Rails
|
|
4
|
-
# Run this test standalone to verify it has no Rails dependencies
|
|
5
|
-
class MinionTest < Minitest::Test
|
|
6
|
-
include SemanticLogger::Loggable
|
|
7
|
-
|
|
8
|
-
describe ParallelMinion::Minion do
|
|
9
|
-
|
|
10
|
-
[false, true].each do |enabled|
|
|
11
|
-
describe enabled ? 'enabled' : 'disabled' do
|
|
12
|
-
before do
|
|
13
|
-
ParallelMinion::Minion.enabled = enabled
|
|
14
|
-
$log_structs.clear
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it 'without parameters' do
|
|
18
|
-
minion = ParallelMinion::Minion.new { 196 }
|
|
19
|
-
assert_equal 196, minion.result
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'with a description' do
|
|
23
|
-
minion = ParallelMinion::Minion.new(description: 'Test') { 197 }
|
|
24
|
-
assert_equal 197, minion.result
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'with an argument' do
|
|
28
|
-
p1 = {name: 198}
|
|
29
|
-
minion = ParallelMinion::Minion.new(p1, description: 'Test') do |v|
|
|
30
|
-
v[:name]
|
|
31
|
-
end
|
|
32
|
-
assert_equal 198, minion.result
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'raise exception' do
|
|
36
|
-
minion = ParallelMinion::Minion.new(description: 'Test') { raise "An exception" }
|
|
37
|
-
assert_raises RuntimeError do
|
|
38
|
-
minion.result
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'has correct logger name' do
|
|
43
|
-
minion = ParallelMinion::Minion.new { 196 }
|
|
44
|
-
name = enabled ? 'Minion' : 'Inline'
|
|
45
|
-
assert_equal name, minion.logger.name
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# TODO Blocks still have access to their original scope if variables cannot be
|
|
49
|
-
# resolved first by the parameters, then by the values in Minion itself
|
|
50
|
-
# it 'not have access to local variables' do
|
|
51
|
-
# name = 'Jack'
|
|
52
|
-
# minion = ParallelMinion::Minion.new(description: 'Test') { puts name }
|
|
53
|
-
# assert_raises NameError do
|
|
54
|
-
# minion.result
|
|
55
|
-
# end
|
|
56
|
-
# end
|
|
57
|
-
|
|
58
|
-
it 'run minion' do
|
|
59
|
-
hash = {value: 23}
|
|
60
|
-
value = 47
|
|
61
|
-
minion = ParallelMinion::Minion.new(hash, description: 'Test') do |h|
|
|
62
|
-
value = 321
|
|
63
|
-
h[:value] = 123
|
|
64
|
-
456
|
|
65
|
-
end
|
|
66
|
-
assert_equal 456, minion.result
|
|
67
|
-
assert_equal 123, hash[:value]
|
|
68
|
-
assert_equal 321, value
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it 'copy across logging tags' do
|
|
72
|
-
minion = nil
|
|
73
|
-
SemanticLogger.tagged('TAG') do
|
|
74
|
-
assert_equal 'TAG', SemanticLogger.tags.last
|
|
75
|
-
minion = ParallelMinion::Minion.new(description: 'Tag Test') do
|
|
76
|
-
logger.info "Tag Test"
|
|
77
|
-
logger.tags.last
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
assert_equal 'TAG', minion.result
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it 'copy across named tags' do
|
|
84
|
-
minion = nil
|
|
85
|
-
SemanticLogger.named_tagged(tag: 'TAG') do
|
|
86
|
-
assert_equal({tag: 'TAG'}, SemanticLogger.named_tags)
|
|
87
|
-
minion = ParallelMinion::Minion.new(description: 'Named Tags Test') do
|
|
88
|
-
logger.info "Named Tags Test"
|
|
89
|
-
SemanticLogger.named_tags
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
assert_equal({tag: 'TAG'}, minion.result)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it 'copy across tags and named tags' do
|
|
96
|
-
minion = nil
|
|
97
|
-
SemanticLogger.tagged('TAG') do
|
|
98
|
-
SemanticLogger.named_tagged(tag: 'TAG') do
|
|
99
|
-
assert_equal({tag: 'TAG'}, SemanticLogger.named_tags)
|
|
100
|
-
assert_equal 'TAG', SemanticLogger.tags.last
|
|
101
|
-
minion = ParallelMinion::Minion.new(description: 'Tags Test') do
|
|
102
|
-
logger.info "Tags Test"
|
|
103
|
-
[SemanticLogger.named_tags, SemanticLogger.tags.last]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
assert_equal({tag: 'TAG'}, minion.result.first)
|
|
107
|
-
assert_equal 'TAG', minion.result.last
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it 'include metric' do
|
|
114
|
-
metric_name = 'model/method'
|
|
115
|
-
hash = {value: 23}
|
|
116
|
-
value = 47
|
|
117
|
-
minion = ParallelMinion::Minion.new(hash, description: 'Test', metric: metric_name) do |h|
|
|
118
|
-
value = 321
|
|
119
|
-
h[:value] = 123
|
|
120
|
-
sleep 1
|
|
121
|
-
456
|
|
122
|
-
end
|
|
123
|
-
assert_equal 456, minion.result
|
|
124
|
-
assert_equal 123, hash[:value]
|
|
125
|
-
assert_equal 321, value
|
|
126
|
-
SemanticLogger.flush
|
|
127
|
-
assert log = $log_structs.first, -> { $log_structs.ai }
|
|
128
|
-
if enabled
|
|
129
|
-
# Completed log message
|
|
130
|
-
assert_equal metric_name, log.metric, -> { $log_structs.ai }
|
|
131
|
-
# Wait log message
|
|
132
|
-
assert log = $log_structs.last, -> { $log_structs.ai }
|
|
133
|
-
assert_equal "#{metric_name}/wait", log.metric, -> { $log_structs.ai }
|
|
134
|
-
else
|
|
135
|
-
# Timeout and wait has no effect when run inline
|
|
136
|
-
assert_equal metric_name, log.metric, -> { $log_structs.ai }
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
it 'handle multiple minions concurrently' do
|
|
141
|
-
# Start 10 minions
|
|
142
|
-
minions = 10.times.collect do |i|
|
|
143
|
-
# Each Minion returns its index in the collection
|
|
144
|
-
ParallelMinion::Minion.new(i, description: "Minion:#{i}") { |counter| counter }
|
|
145
|
-
end
|
|
146
|
-
assert_equal 10, minions.count
|
|
147
|
-
# Fetch the result from each Minion
|
|
148
|
-
minions.each_with_index do |minion, index|
|
|
149
|
-
assert_equal index, minion.result
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
it 'timeout' do
|
|
154
|
-
if enabled
|
|
155
|
-
minion = ParallelMinion::Minion.new(description: 'Test', timeout: 100) { sleep 1 }
|
|
156
|
-
assert_nil minion.result
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it 'timeout and terminate thread with Exception' do
|
|
161
|
-
if enabled
|
|
162
|
-
minion = ParallelMinion::Minion.new(description: 'Test', timeout: 100, on_timeout: Timeout::Error) { sleep 1 }
|
|
163
|
-
assert_nil minion.result
|
|
164
|
-
# Give time for thread to terminate
|
|
165
|
-
sleep 0.1
|
|
166
|
-
assert_equal Timeout::Error, minion.exception.class
|
|
167
|
-
assert_equal false, minion.working?
|
|
168
|
-
assert_equal true, minion.completed?
|
|
169
|
-
assert_equal true, minion.failed?
|
|
170
|
-
assert_equal 0, minion.time_left
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it 'make description instance variable available' do
|
|
175
|
-
minion = ParallelMinion::Minion.new(description: 'Test') do
|
|
176
|
-
description
|
|
177
|
-
end
|
|
178
|
-
assert_equal 'Test', minion.result
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
it 'make timeout instance variable available' do
|
|
182
|
-
minion = ParallelMinion::Minion.new(description: 'Test', timeout: 1000) do
|
|
183
|
-
timeout
|
|
184
|
-
end
|
|
185
|
-
assert_equal 1000, minion.result
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
it 'make enabled? method available' do
|
|
189
|
-
minion = ParallelMinion::Minion.new(description: 'Test') do
|
|
190
|
-
enabled?
|
|
191
|
-
end
|
|
192
|
-
assert_equal enabled, minion.result
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
it 'keep the original arguments' do
|
|
196
|
-
minion = ParallelMinion::Minion.new(1, 'data', 14.1, description: 'Test') do |num, str, float|
|
|
197
|
-
num + float
|
|
198
|
-
end
|
|
199
|
-
assert_equal 15.1, minion.result
|
|
200
|
-
assert_equal [1, 'data', 14.1], minion.arguments
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
end
|
|
207
|
-
end
|
data/test/test_db.sqlite3
DELETED
|
Binary file
|
data/test/test_helper.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
|
-
|
|
3
|
-
require 'minitest/autorun'
|
|
4
|
-
require 'parallel_minion'
|
|
5
|
-
require 'semantic_logger'
|
|
6
|
-
|
|
7
|
-
SemanticLogger.default_level = :trace
|
|
8
|
-
SemanticLogger.add_appender(file_name: 'test.log', formatter: :color)
|
|
9
|
-
|
|
10
|
-
# Setup global callback for metric so that it can be tested below
|
|
11
|
-
$log_structs = []
|
|
12
|
-
SemanticLogger.on_metric do |log_struct|
|
|
13
|
-
$log_structs << log_struct.dup
|
|
14
|
-
end
|