parallel_minion 0.0.1 → 0.1.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
  SHA1:
3
- metadata.gz: a490fae075880a809d3d3542381454e2737ee560
4
- data.tar.gz: bec1bb591e8d19b490821f5153cb195083e9e58a
3
+ metadata.gz: ba4bc73da8594cb544d75365ae616634584cb197
4
+ data.tar.gz: 14a7ccfde7406ba6f41b0c28b133fb989160c67f
5
5
  SHA512:
6
- metadata.gz: 49c1e58bcb4011633cd0f80d666a6872f8fea1719cb5e2085dc463e6afd0ed71672ee1e49c5846fca668a0704beebdf22f0b816d804a7590f88e02571bf7ecd6
7
- data.tar.gz: 383505b36f1990379463117143231f2c069dc3451658a05fcc8d5c303a1394cdf76a232a4439f0d90660e8af3305ddc6445f5c74171f75f70940df7f0f43f534
6
+ metadata.gz: d9dca76b499fa4484575825bfe0e7a86feb49e3a46f921e1a34211c5db4bca6506ad3456a947b1423a8914fb0f371f29443bbd465d7db5525bae1c4b40f97ca6
7
+ data.tar.gz: 571076e7b09162becba4c8d1c8a79f3f568722b6f276798cbaf5f97cae8abff608c778ec6d0289e9ecc1c931a17bb8dc788f1170a7191e31377b2d989eb66b9e
data/README.md CHANGED
@@ -1,49 +1,67 @@
1
1
  parallel_minion
2
2
  ===============
3
3
 
4
- Parallel Minion supports easily handing work off to Minions (Threads) so that tasks
4
+ Parallel Minion supports easily handing work off to minions (threads) so that tasks
5
5
  that would normally be performed sequentially can easily be executed in parallel.
6
6
  This allows Ruby and Rails applications to very easily do many tasks at the same
7
7
  time so that results are returned more quickly.
8
8
 
9
- Our use-case for Minions is where an application grew to a point where it would
9
+ Our use-case for minions is where an application grew to a point where it would
10
10
  be useful to run some of the steps in fulfilling a single request in parallel.
11
11
 
12
12
  ## Features:
13
13
 
14
14
  Exceptions
15
15
 
16
- - Any exceptions raised in Minions are captured and propagated back to the
16
+ - Any exceptions raised in minions are captured and propagated back to the
17
17
  calling thread when #result is called
18
- - Makes exception handling simple with a drop-in replacement in existing code
18
+ - Makes exception handling simple with a drop-in replacement for existing code
19
+ - Avoids having to implement more complex actors and supervisors required
20
+ by some concurrency frameworks
19
21
 
20
22
  Timeouts
21
23
 
22
- - Timeout when a Minion does not return within a specified time
23
- - Timeouts are a useful feature when one of the Minions fails to respond in a
24
+ - Timeout when a minion does not return within a specified time
25
+ - Timeouts are a useful feature when one of the minions fails to respond in a
24
26
  reasonable amount of time. For example when a call to a remote service hangs
25
27
  we can send back a partial response of other work that was completed rather
26
28
  than just "hanging" or failing completely.
27
29
 
28
30
  Logging
29
31
 
30
- - Built-in support to log the duration of all Minion tasks to make future analysis
32
+ - Built-in support to log the duration of all minion tasks to make future analysis
31
33
  of performance issues much easier
32
- - Logs any exceptions thrown to assist fwith problem diagnosis
34
+ - Logs any exceptions thrown to assist with problem diagnosis
35
+ - Logging tags from the current thread are propagated to the minions thread
36
+ - The name of the thread in log entries is set to the description supplied for
37
+ the minion to make it easy to distinguish log entries by minion / thread
38
+
39
+ Rails Support
40
+
41
+ - When used in a Rails environment the current scope of specified models can be
42
+ propagated to the minions thread
33
43
 
34
44
  ## Example
35
45
 
36
46
  Simple example
37
47
 
38
48
  ```ruby
39
- ParallelMinion::Minion.new(10.days.ago, description: 'Doing something else in parallel', timeout: 1000) do |date|
49
+ minion = ParallelMinion::Minion.new(10.days.ago, description: 'Doing something else in parallel', timeout: 1000) do |date|
40
50
  MyTable.where('created_at <= ?', date).count
41
51
  end
52
+
53
+ # Do other work here...
54
+
55
+ # Retrieve the result of the minion
56
+ count = minion.result
57
+
58
+ puts "Found #{count} records"
42
59
  ```
43
60
 
44
61
  ## Example
45
62
 
46
- For example, in the code below there are several steps that are performed sequentially:
63
+ For example, in the code below there are several steps that are performed
64
+ sequentially and does not yet use minions:
47
65
 
48
66
  ```ruby
49
67
  # Contrived example to show how to do parallel code execution
@@ -190,13 +208,39 @@ end
190
208
 
191
209
  The above #process_request method should now take on average 1,810 milli-seconds
192
210
  which is significantly faster than the 3,780 milli-seconds it took to perform
193
- the exact same request, but using only a single thread
211
+ the exact same request prior to using minions.
194
212
 
195
- The exact breakdown of which calls to do in the main thread versus a Minion is determined
213
+ The exact breakdown of which calls to do in the main thread versus a minion is determined
196
214
  through experience and trial and error over time. The key is logging the duration
197
- of each call which Minion does by default so that the exact processing breakdown
215
+ of each call which minion does by default so that the exact processing breakdown
198
216
  can be fine-tuned over time.
199
217
 
218
+ ## Disabling Minions
219
+
220
+ In the event that strange problems are occurring in production and no one is
221
+ sure if it is due to running the minion tasks in parallel, it is simple to make
222
+ all minion tasks run in the calling thread.
223
+
224
+ It may also be useful to disable minions on a single production server to compare
225
+ its performance to that of the servers running with minions active.
226
+
227
+ To disable minions / make them run in the calling thread, add the following
228
+ lines to config/environments/production.rb:
229
+
230
+ ```ruby
231
+ # Make minions run immediately in the current thread
232
+ config.parallel_minion.enabled = false
233
+ ```
234
+
235
+ ## Notes:
236
+
237
+ - When using JRuby it is important to enable it's built-in thread-pooling by
238
+ adding the following line to .jrubyrc, or setting the appropriate command line option:
239
+
240
+ ```ruby
241
+ thread.pool.enabled=true
242
+ ```
243
+
200
244
  Meta
201
245
  ----
202
246
 
@@ -15,24 +15,23 @@ module ParallelMinion
15
15
  # Give an infinite amount of time to wait for a Minion to complete a task
16
16
  INFINITE = -1
17
17
 
18
- # Sets whether to run in Synchronous mode
18
+ # Sets whether minions are enabled to run in their own threads
19
19
  #
20
- # By Setting synchronous to true all Minions that have not yet been started
21
- # will run in the thread from which they are started and not in their own
22
- # threads
20
+ # By Setting _enabled_ to false all Minions that have not yet been started
21
+ # will run in the thread from which it is created and not on its own thread
23
22
  #
24
23
  # This is useful:
25
24
  # - to run tests under the Capybara gem
26
25
  # - when debugging code so that all code is run sequentially in the current thread
27
26
  #
28
- # Note: Do not set this setting to true in Production
29
- def self.synchronous=(synchronous)
30
- @@synchronous = synchronous
27
+ # Note: Not recommended to set this setting to false in Production
28
+ def self.enabled=(enabled)
29
+ @@enabled = enabled
31
30
  end
32
31
 
33
- # Returns whether running in Synchronous mode
34
- def self.synchronous?
35
- @@synchronous
32
+ # Returns whether minions are enabled to run in their own threads
33
+ def self.enabled?
34
+ @@enabled
36
35
  end
37
36
 
38
37
  # The list of classes for which the current scope must be copied into the
@@ -66,14 +65,14 @@ module ParallelMinion
66
65
  # - :timeout does not affect what happens to the Minion running the
67
66
  # the task, it only affects how long #result will take to return.
68
67
  # - The Minion will continue to run even after the timeout has been exceeded
69
- # - If :synchronous is true, or ParallelMinion::Minion.synchronous is
70
- # set to true, then :timeout is ignored and assumed to be Minion::INFINITE
68
+ # - If :enabled is false, or ParallelMinion::Minion.enabled is false,
69
+ # then :timeout is ignored and assumed to be Minion::INFINITE
71
70
  # since the code is run in the calling thread when the Minion is created
72
71
  #
73
- # :synchronous [Boolean]
74
- # Whether the Minion should run in the current thread
72
+ # :enabled [Boolean]
73
+ # Whether the minion should run in a separate thread
75
74
  # Not recommended in Production, but is useful for debugging purposes
76
- # Default: false
75
+ # Default: ParallelMinion::Minion.enabled?
77
76
  #
78
77
  # *args
79
78
  # Any number of arguments can be supplied that are passed into the block
@@ -112,20 +111,24 @@ module ParallelMinion
112
111
 
113
112
  options = self.class.extract_options!(args).dup
114
113
 
115
- @timeout = (options.delete(:timeout) || Minion::INFINITE).to_f
114
+ @timeout = (options.delete(:timeout) || Minion::INFINITE).to_f
116
115
  @description = (options.delete(:description) || 'Minion').to_s
117
116
  @log_exception = options.delete(:log_exception)
118
- @synchronous = options.delete(:synchronous) || self.class.synchronous?
117
+ @enabled = options.delete(:enabled)
118
+ @enabled = self.class.enabled? if @enabled.nil?
119
119
 
120
120
  # Warn about any unknown options.
121
- options.each_pair { |key,val| logger.warn "Ignoring unknown option: #{key.inspect} => #{val.inspect}" }
121
+ options.each_pair do |key,val|
122
+ logger.warn "Ignoring unknown option: #{key.inspect} => #{val.inspect}"
123
+ warn "ParallelMinion::Minion Ignoring unknown option: #{key.inspect} => #{val.inspect}"
124
+ end
122
125
 
123
126
  # Run the supplied block of code in the current thread for testing or
124
127
  # debugging purposes
125
- if @synchronous == true
128
+ if @enabled == false
126
129
  begin
127
- logger.info("Started synchronously #{@description}")
128
- logger.benchmark_info("Completed synchronously #{@description}", log_exception: @log_exception) do
130
+ logger.info("Started in the current thread: #{@description}")
131
+ logger.benchmark_info("Completed in the current thread: #{@description}", log_exception: @log_exception) do
129
132
  @result = instance_exec(*args, &block)
130
133
  end
131
134
  rescue Exception => exc
@@ -189,12 +192,12 @@ module ParallelMinion
189
192
 
190
193
  # Returns [Boolean] whether the minion is still working on the assigned task
191
194
  def working?
192
- synchronous? ? false : @thread.alive?
195
+ enabled? ? @thread.alive? : false
193
196
  end
194
197
 
195
198
  # Returns [Boolean] whether the minion has completed working on the task
196
199
  def completed?
197
- synchronous? ? true : @thread.stop?
200
+ enabled? ? @thread.stop? : true
198
201
  end
199
202
 
200
203
  # Returns [Boolean] whether the minion failed while performing the assigned task
@@ -211,9 +214,9 @@ module ParallelMinion
211
214
  duration <= 0 ? 0 : duration
212
215
  end
213
216
 
214
- # Returns [Boolean] whether synchronous mode has been enabled for this minion instance
215
- def synchronous?
216
- @synchronous
217
+ # Returns [Boolean] whether this minion is enabled to run in a separate thread
218
+ def enabled?
219
+ @enabled
217
220
  end
218
221
 
219
222
  # Returns the current scopes for each of the models for which scopes will be
@@ -225,7 +228,7 @@ module ParallelMinion
225
228
 
226
229
  protected
227
230
 
228
- @@synchronous = false
231
+ @@enabled = true
229
232
  @@scoped_classes = []
230
233
 
231
234
  # Extract options from a hash.
@@ -9,7 +9,7 @@ module ParallelMinion #:nodoc:
9
9
  # Rails::Application.configure do
10
10
  #
11
11
  # # Run Minions in the current thread to make debugging easier
12
- # config.parallel_minion.synchronous = true
12
+ # config.parallel_minion.enabled = false
13
13
  #
14
14
  # # Add a model so that its current scope is copied to the Minion
15
15
  # config.parallel_minion.scoped_classes << MyScopedModel
@@ -1,3 +1,3 @@
1
1
  module ParallelMinion #:nodoc
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -34,8 +34,8 @@ end
34
34
  class MinionScopeTest < Test::Unit::TestCase
35
35
 
36
36
  context ParallelMinion::Minion do
37
- [false, true].each do |synchronous|
38
- context ".new with synchronous: #{synchronous.inspect}" do
37
+ [false, true].each do |enabled|
38
+ context ".new with enabled: #{enabled.inspect}" do
39
39
  setup do
40
40
  Person.create(name: 'Jack', state: 'FL', zip_code: 38729)
41
41
  Person.create(name: 'John', state: 'FL', zip_code: 35363)
@@ -45,7 +45,7 @@ class MinionScopeTest < Test::Unit::TestCase
45
45
  Person.create(name: 'James', state: 'CA', zip_code: 123123)
46
46
  # Instruct Minions to adhere to any dynamic scopes for Person model
47
47
  ParallelMinion::Minion.scoped_classes << Person
48
- ParallelMinion::Minion.synchronous = synchronous
48
+ ParallelMinion::Minion.enabled = enabled
49
49
  end
50
50
 
51
51
  teardown do
data/test/minion_test.rb CHANGED
@@ -16,10 +16,10 @@ class MinionTest < Test::Unit::TestCase
16
16
 
17
17
  context ParallelMinion::Minion do
18
18
 
19
- [false, true].each do |synchronous|
20
- context ".new with synchronous: #{synchronous.inspect}" do
19
+ [false, true].each do |enabled|
20
+ context ".new with enabled: #{enabled.inspect}" do
21
21
  setup do
22
- ParallelMinion::Minion.synchronous = synchronous
22
+ ParallelMinion::Minion.enabled = enabled
23
23
  end
24
24
 
25
25
  should 'without parameters' do
@@ -96,8 +96,8 @@ class MinionTest < Test::Unit::TestCase
96
96
 
97
97
  should 'timeout' do
98
98
  minion = ParallelMinion::Minion.new(description: 'Test', timeout: 100) { sleep 1 }
99
- # Only Parallel Minions time-out when they exceed timeout
100
- unless synchronous
99
+ # Only Parallel Minions time-out when they exceed the timeout
100
+ if enabled
101
101
  assert_equal nil, minion.result
102
102
  end
103
103
  end
data/test/test_db.sqlite3 CHANGED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_minion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-03 00:00:00.000000000 Z
11
+ date: 2013-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semantic_logger