in-parallel 0.1.12 → 0.1.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +55 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +68 -0
- data/HISTORY.md +23 -0
- data/MAINTAINERS.md +1 -0
- data/README.md +46 -39
- data/Rakefile +9 -1
- data/lib/in-parallel/version.rb +3 -0
- data/lib/in_parallel.rb +1 -1
- data/lib/parallel_enumerable.rb +1 -1
- data/spec/in-paralell_spec.rb +251 -0
- metadata +17 -47
- data/in_parallel.gemspec +0 -34
- data/in_parallel/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b5dc6a05d28155a49a6c5d07c3be8385583ef58
|
4
|
+
data.tar.gz: 6eb5ddb93219985c1513a3a5f1abe23391d5590b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f36a6e1b63a18514c9e17713d95d29725c5b6ddfb7cd91074a8e8b25c3bc8004dbcd272fad00c82ac4e155931673bc494d83497b8310e54dca074dabe384d39
|
7
|
+
data.tar.gz: 2a1d6707489ebe47beec5c6959394b66c57926843f377ff5cf24bad66124935afe35a1f171e84c2266e6ab2c229d2085168c8ceb6244c0846977fb534c11330a
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# How To Contribute To in-parallel
|
2
|
+
|
3
|
+
## Getting Started
|
4
|
+
|
5
|
+
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
6
|
+
* Fork the [in-parallel repository on GitHub](https://github.com/puppetlabs/in-parallel)
|
7
|
+
|
8
|
+
## Making Changes
|
9
|
+
|
10
|
+
* Create a topic branch from where you want to base your work.
|
11
|
+
* This is the `master` branch in the case of in-parallel
|
12
|
+
* To quickly create a topic branch based on master use `git checkout -b my_contribution master`. Do not work directly on the `master` branch.
|
13
|
+
* Make commits of logical _working_ and _functional_ units.
|
14
|
+
* Check for unnecessary whitespace with `git diff --check` before committing.
|
15
|
+
* Make sure your commit messages are in the proper format.
|
16
|
+
|
17
|
+
(BKR-1234) Make the example in CONTRIBUTING imperative and concrete
|
18
|
+
|
19
|
+
Without this patch applied the example commit message in the CONTRIBUTING
|
20
|
+
document is not a concrete example. This is a problem because the
|
21
|
+
contributor is left to imagine what the commit message should look like
|
22
|
+
based on a description rather than an example. This patch fixes the
|
23
|
+
problem by making the example concrete and imperative.
|
24
|
+
|
25
|
+
The first line is a real life imperative statement with a ticket number
|
26
|
+
from our issue tracker. The body describes the behavior without the patch,
|
27
|
+
why this is a problem, and how the patch fixes the problem when applied.
|
28
|
+
|
29
|
+
* Make sure you have added [RSpec](http://rspec.info/) tests that exercise your new code. These test should be located in the appropriate `in-parallel/spec/` subdirectory. The addition of new methods/classes or the addition of code paths to existing methods/classes requires additional RSpec coverage.
|
30
|
+
* One should **NOT USE** the deprecated `should`/`stub` methods - **USE** `expect`/`allow`. Use of deprecated RSpec methods will result in your patch being rejected. See a nice blog post from 2013 on [RSpec's new message expectation syntax](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/).
|
31
|
+
* Run the spec unit tests to assure nothing else was accidentally broken, using `rake test`
|
32
|
+
* **Bonus**: if possible ensure that `rake test` runs without failures for additional Ruby versions (1.9, 2.0, etc). in-parallel supports Ruby 1.9+, and breakage of support for other rubies will cause a patch to be rejected.
|
33
|
+
* Make sure that if you have added new functionality of sufficiently high risk, and it can not be covered adequately via unit tests (mocking, requires disk, other classes, etc), you also include acceptance tests in your PR.
|
34
|
+
* Make sure that you have added documentation using [Yard](http://yardoc.org/), new methods/classes without apporpriate documentation will be rejected.
|
35
|
+
* Run the yardoc tool to ensure that your yard documentation is properly formatted and complete
|
36
|
+
* `[bundle exec] yard doc`
|
37
|
+
* Yard docs are great for other developers, but often are difficult to read for users. If your change impacts user-facing functionality, please include changes to the human-readable markdown docs starting at README.md
|
38
|
+
* During the time that you are working on your patch the master in-parallel branch may have changed - you'll want to [rebase](http://git-scm.com/book/en/Git-Branching-Rebasing) before you submit your PR with `git rebase master`. A successful rebase ensures that your patch will cleanly merge into in-parallel.
|
39
|
+
* Submitted patches will be smoke tested through a series of acceptance level tests that ensures basic in-parallel functionality - the results of these tests will be evaluated by a in-parallel team member. Failures associated with the submitted patch will result in the patch being rejected.
|
40
|
+
|
41
|
+
## Submitting Changes
|
42
|
+
|
43
|
+
* Sign the [Contributor License Agreement](http://links.puppet.com/cla).
|
44
|
+
* Push your changes to a topic branch in _your_ fork of the repository.
|
45
|
+
* Submit a pull request to [in-parallel](https://github.com/puppetlabs/in-parallel)
|
46
|
+
* PRs are reviewed as time permits.
|
47
|
+
|
48
|
+
# Additional Resources
|
49
|
+
|
50
|
+
* [More information on contributing](http://links.puppet.com/contribute-to-puppet)
|
51
|
+
* [Contributor License Agreement](http://links.puppet.com/cla)
|
52
|
+
* [General GitHub documentation](http://help.github.com/)
|
53
|
+
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
54
|
+
* Questions? Comments? Contact the in-parallel team at qa-team@puppet.com
|
55
|
+
* The keyword `in-parallel` is monitored and we'll get back to you as quick as we can.
|
data/Gemfile
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
# in the Rakefile, so we require it in all groups
|
4
|
+
gem 'rspec' ,'~> 3.1.0'
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'simplecov'
|
8
|
+
gem 'rake', '>= 0.9.0'
|
9
|
+
#Documentation dependencies
|
10
|
+
gem 'yard' ,'~> 0'
|
11
|
+
gem 'markdown' ,'~> 0'
|
12
|
+
end
|
13
|
+
|
3
14
|
# Specify your gem's dependencies in in_parallel.gemspec
|
4
15
|
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
in-parallel (0.1.12)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activesupport (4.2.6)
|
10
|
+
i18n (~> 0.7)
|
11
|
+
json (~> 1.7, >= 1.7.7)
|
12
|
+
minitest (~> 5.1)
|
13
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
14
|
+
tzinfo (~> 1.1)
|
15
|
+
diff-lcs (1.2.5)
|
16
|
+
docile (1.1.5)
|
17
|
+
i18n (0.7.0)
|
18
|
+
json (1.8.3)
|
19
|
+
kramdown (1.11.1)
|
20
|
+
logutils (0.6.1)
|
21
|
+
markdown (0.4.0)
|
22
|
+
kramdown (>= 0.13.7)
|
23
|
+
props (>= 0.2.0)
|
24
|
+
textutils (>= 0.2.0)
|
25
|
+
minitest (5.9.0)
|
26
|
+
props (1.1.2)
|
27
|
+
rake (10.5.0)
|
28
|
+
rspec (3.1.0)
|
29
|
+
rspec-core (~> 3.1.0)
|
30
|
+
rspec-expectations (~> 3.1.0)
|
31
|
+
rspec-mocks (~> 3.1.0)
|
32
|
+
rspec-core (3.1.7)
|
33
|
+
rspec-support (~> 3.1.0)
|
34
|
+
rspec-expectations (3.1.2)
|
35
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
+
rspec-support (~> 3.1.0)
|
37
|
+
rspec-mocks (3.1.3)
|
38
|
+
rspec-support (~> 3.1.0)
|
39
|
+
rspec-support (3.1.2)
|
40
|
+
rubyzip (1.2.0)
|
41
|
+
simplecov (0.11.2)
|
42
|
+
docile (~> 1.1.0)
|
43
|
+
json (~> 1.8)
|
44
|
+
simplecov-html (~> 0.10.0)
|
45
|
+
simplecov-html (0.10.0)
|
46
|
+
textutils (1.4.0)
|
47
|
+
activesupport
|
48
|
+
logutils (>= 0.6.1)
|
49
|
+
props (>= 1.1.2)
|
50
|
+
rubyzip (>= 1.0.0)
|
51
|
+
thread_safe (0.3.5)
|
52
|
+
tzinfo (1.2.2)
|
53
|
+
thread_safe (~> 0.1)
|
54
|
+
yard (0.9.0)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
in-parallel!
|
61
|
+
markdown (~> 0)
|
62
|
+
rake (>= 0.9.0)
|
63
|
+
rspec (~> 3.1.0)
|
64
|
+
simplecov
|
65
|
+
yard (~> 0)
|
66
|
+
|
67
|
+
BUNDLED WITH
|
68
|
+
1.12.3
|
data/HISTORY.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# experimental_in-parallel_bump_and_tag_master - History
|
2
|
+
## Tags
|
3
|
+
* [LATEST - 8 Aug, 2016 (d026c624)](#LATEST)
|
4
|
+
* [0.1.13 - 8 Aug, 2016 (26d19934)](#0.1.13)
|
5
|
+
|
6
|
+
## Details
|
7
|
+
### <a name = "LATEST">LATEST - 8 Aug, 2016 (d026c624)
|
8
|
+
|
9
|
+
* (GEM) update in-parallel version to 0.1.14 (d026c624)
|
10
|
+
|
11
|
+
* Merge pull request #14 from samwoods1/add_jjb_pipelines (269bc350)
|
12
|
+
|
13
|
+
|
14
|
+
```
|
15
|
+
Merge pull request #14 from samwoods1/add_jjb_pipelines
|
16
|
+
|
17
|
+
(maint) Consistent naming of in-parallel
|
18
|
+
```
|
19
|
+
* (maint) Consistent naming of in-parallel (6fbb442d)
|
20
|
+
|
21
|
+
### <a name = "0.1.13">0.1.13 - 8 Aug, 2016 (26d19934)
|
22
|
+
|
23
|
+
* Initial release.
|
data/MAINTAINERS.md
CHANGED
@@ -6,3 +6,4 @@ will be reviewing & merging PRs to the project.
|
|
6
6
|
| Name | Github | Email |
|
7
7
|
|:--------------:|:---------------------------------------------------:|:---------------------------:|
|
8
8
|
| Sam Woods | [samwoods1](https://github.com/samwoods1) | <sam.woods@puppet.com> |
|
9
|
+
| Zach Reichert | [zreichert](https://github.com/zreichert) | <zach.reichert@puppet.com> |
|
data/README.md
CHANGED
@@ -1,20 +1,33 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
A lightweight Ruby library with very simple syntax, making use of Process.fork to execute code in parallel.
|
3
3
|
|
4
|
-
|
4
|
+
# Use Cases
|
5
|
+
Many other Ruby libraries that simplify parallel execution support one primary use case - crunching through a large queue of small, similar tasks as quickly and efficiently as possible. This library primarily supports the use case of executing a few larger and unrelated tasks in parallel, automatically managing the stdout and passing return values back to the main process. This library was created to be used by Puppet's Beaker test framework to enable parallel execution of some of the framework's tasks, and allow users to execute code in parallel within their tests.
|
5
6
|
|
6
7
|
If you are looking for something that excels at executing a large queue of tasks in parallel as efficiently as possible, you should take a look at the [parallel](https://github.com/grosser/parallel) project.
|
7
8
|
|
8
|
-
|
9
|
+
# Install
|
10
|
+
```gem install in-parallel```
|
11
|
+
|
12
|
+
# Usage
|
13
|
+
```include InParallel``` to use as a mix-in
|
14
|
+
|
15
|
+
The methods below allow you to fork processes to execute multiple methods or blocks within an enumerable in parallel. They all have this common behavior:
|
9
16
|
|
17
|
+
1. STDOUT is captured for each forked process and logged all at once when the process completes or is terminated.
|
18
|
+
1. By default execution of processes in parallel will wait until execution of all processes are complete before continuing (with the exception of run_in_background).
|
19
|
+
1. You can specify the parameter kill_all_on_error=true if you want to immediately exit all forked processes when an error executing any of the forked processes occurs.
|
20
|
+
1. When the forked process raises an exception or exits with a non zero exit code, an exception will be raised in the main process.
|
21
|
+
1. Terminating the main process with 'ctrl-c' or killing the process in some other way will immediately cause all forked processes to be killed and log their STDOUT up to that point.
|
22
|
+
1. If the result of the method or block can be marshalled, it will be returned as though it was executed within the same process. If the result cannot be marshalled a warning is produced and the return value will be nil.
|
23
|
+
1. NOTE: results of methods within run_in_parallel can be assigned to instance or class variables, but not local variables. See examples below.
|
24
|
+
1. Will timeout (stop execution and raise an exception) based on a global timeout value, or timeout parameter.
|
25
|
+
|
26
|
+
## Methods
|
10
27
|
### run_in_parallel(timeout=nil, kill_all_on_error = false, &block)
|
11
28
|
1. Each method in a block will be executed in parallel (unless the method is defined in Kernel or BaseObject).
|
12
29
|
1. Any methods further down the stack won't be affected, only the ones directly within the block.
|
13
|
-
|
14
|
-
3. Log STDOUT and STDERR chunked per process to the console so that it is easy to see what happened in which process.
|
15
|
-
4. Waits for each process in realtime and logs immediately upon completion of each process
|
16
|
-
5. If an exception is raised by a child process, it will optionally (kill_all_on_error) be re-raised in the primary process and kill all other still running child processes. The default will wait for all processes to complete execution before re-raising any unhandled exception from the child processes.
|
17
|
-
6. Times out by default at 30 minutes. Timeout default can be changed with InParallel::InParallelExecutor.parallel_default_timeout=X, or you can set the timeout param when calling the method
|
30
|
+
1. Waits for each process in realtime and logs immediately upon completion of each process
|
18
31
|
|
19
32
|
```ruby
|
20
33
|
def method_with_param(name)
|
@@ -40,8 +53,7 @@ If you are looking for something that excels at executing a large queue of tasks
|
|
40
53
|
|
41
54
|
puts "#{@result_1}, #{@result_2[:foo]}"
|
42
55
|
```
|
43
|
-
|
44
|
-
STDOUT would be:
|
56
|
+
stdout:
|
45
57
|
```
|
46
58
|
Forked process for 'method_with_param' - PID = '49398'
|
47
59
|
Forked process for 'method_without_param' - PID = '49399'
|
@@ -55,41 +67,19 @@ hello world
|
|
55
67
|
------ Completed output for method_without_param - 49399
|
56
68
|
hello world, bar
|
57
69
|
```
|
58
|
-
|
59
70
|
### Enumerable.each_in_parallel(identifier=nil, timeout=(InParallel::InParallelExecutor.timeout), kill_all_on_error = false, &block)
|
60
71
|
1. This is very similar to other solutions, except that it directly extends the Enumerable class with an each_in_parallel method, giving you the ability to pretty simply spawn a process for any item in an array or map.
|
61
|
-
|
62
|
-
|
63
|
-
4. If an exception is raised by a child process, it will optionally (kill_all_on_error) be re-raised in the primary process and kill all other still running child processes. The default will wait for all processes to complete execution before re-raising any unhandled exception from the child processes.
|
64
|
-
5. Times out by default at 30 minutes. Timeout default can be changed with InParallel::InParallelExecutor.parallel_default_timeout=X, or you can set the timeout param when calling the method
|
72
|
+
1. Identifies the block location (or caller location if the block does not have a source_location) in the console log to make it clear which block is being executed
|
73
|
+
1. Identifier param is only for logging, otherwise it will use the block source location.
|
65
74
|
|
66
75
|
```ruby
|
67
76
|
["foo", "bar", "baz"].each_in_parallel { |item| puts item }
|
68
|
-
|
69
|
-
```
|
70
|
-
STDOUT:
|
71
|
-
```
|
72
|
-
'each_in_parallel' spawned process for '/Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>'' - PID = '51600'
|
73
|
-
'each_in_parallel' spawned process for '/Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>'' - PID = '51601'
|
74
|
-
'each_in_parallel' spawned process for '/Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>'' - PID = '51602'
|
75
|
-
|
76
|
-
------ Begin output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51600
|
77
|
-
foo
|
78
|
-
------ Completed output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51600
|
79
|
-
|
80
|
-
------ Begin output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51601
|
81
|
-
bar
|
82
|
-
------ Completed output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51601
|
83
|
-
|
84
|
-
------ Begin output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51602
|
85
|
-
baz
|
86
|
-
------ Completed output for /Users/samwoods/parallel_test/test.rb:77:in `block (2 levels) in <top (required)>' - 51602
|
87
77
|
```
|
88
78
|
|
89
79
|
### run_in_background(ignore_results = true, &block)
|
90
80
|
1. This does basically the same thing as run_in_parallel, except it does not wait for execution of all processes to complete, it returns immediately.
|
91
|
-
|
92
|
-
|
81
|
+
1. You can optionally ignore results completely (default) or delay evaluating the results until later
|
82
|
+
1. You can run multiple blocks in the background and then at some later point evaluate all of the results
|
93
83
|
|
94
84
|
```ruby
|
95
85
|
TMP_FILE = '/tmp/test_file.txt'
|
@@ -108,7 +98,8 @@ baz
|
|
108
98
|
sleep(3)
|
109
99
|
# Should exist once the delay from create_file_with_delay is done
|
110
100
|
puts(File.exists?(TMP_FILE)) # true
|
111
|
-
|
101
|
+
```
|
102
|
+
```ruby
|
112
103
|
# Example 2 - delay results
|
113
104
|
run_in_background(false) { @result = create_file_with_delay(TMP_FILE) }
|
114
105
|
|
@@ -128,5 +119,21 @@ baz
|
|
128
119
|
|
129
120
|
### wait_for_processes(timeout=nil, kill_all_on_error = false)
|
130
121
|
1. Used only after run_in_background with ignore_results=false
|
131
|
-
|
132
|
-
|
122
|
+
1. Optional args for timeout and kill_all_on_error
|
123
|
+
1. See run_in_background for examples
|
124
|
+
|
125
|
+
## Global Options
|
126
|
+
You can get or set the following values to set global defaults. These defaults can also be specified per execution by supplying the values as parameters to the parallel methods.
|
127
|
+
```
|
128
|
+
# How many seconds to wait between logging a 'Waiting for child processes.' message. Defaults to 30 seconds
|
129
|
+
parallel_signal_interval
|
130
|
+
|
131
|
+
# How many seconds to wait before timing out a forked child process and raising an exception. Defaults to 30 minutes.
|
132
|
+
parallel_default_timeout
|
133
|
+
|
134
|
+
# The log level to log output.
|
135
|
+
# NOTE: The entire contents of STDOUT for forked processes will be printed to console regardless of
|
136
|
+
# the log level set here.
|
137
|
+
@logger.log_level
|
138
|
+
```
|
139
|
+
|
data/Rakefile
CHANGED
data/lib/in_parallel.rb
CHANGED
@@ -313,7 +313,7 @@ module InParallel
|
|
313
313
|
# All methods within the block should show up as missing (unless defined in :Kernel)
|
314
314
|
def method_missing(method_sym, *args, &block)
|
315
315
|
if InParallelExecutor.main_pid == ::Process.pid
|
316
|
-
out = InParallelExecutor._execute_in_parallel("'#{method_sym.to_s}' #{
|
316
|
+
out = InParallelExecutor._execute_in_parallel("'#{method_sym.to_s}' #{caller[0].to_s}",
|
317
317
|
@object.eval('self')) { send(method_sym, *args, &block) }
|
318
318
|
out[:tmp_result]
|
319
319
|
end
|
data/lib/parallel_enumerable.rb
CHANGED
@@ -10,7 +10,7 @@ module Enumerable
|
|
10
10
|
# @return [Array<Object>] results - the return value of each block execution.
|
11
11
|
def each_in_parallel(identifier=nil, timeout=(InParallel::InParallelExecutor.parallel_default_timeout), kill_all_on_error = false, &block)
|
12
12
|
if InParallel::InParallelExecutor.fork_supported? && count > 1
|
13
|
-
identifier ||= "#{
|
13
|
+
identifier ||= "#{caller[0]}"
|
14
14
|
each do |item|
|
15
15
|
InParallel::InParallelExecutor._execute_in_parallel(identifier) { block.call(item) }
|
16
16
|
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require_relative('../lib/in_parallel')
|
3
|
+
include InParallel
|
4
|
+
TMP_FILE = Dir.mktmpdir + 'test_file.txt'
|
5
|
+
|
6
|
+
class SingletonTest
|
7
|
+
def initialize
|
8
|
+
@test_data = [1, 2, 3]
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_test_data
|
12
|
+
@test_data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class SingletonWrapper
|
17
|
+
def initialize
|
18
|
+
@instance_var = get_singleton_class
|
19
|
+
singleton_class.class_eval do
|
20
|
+
@@x = "foo"
|
21
|
+
@x = 'bar'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_instance_var
|
26
|
+
@instance_var
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_wrapper
|
31
|
+
SingletonWrapper.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_singleton_class
|
35
|
+
test = SingletonTest.new
|
36
|
+
|
37
|
+
def test.someval
|
38
|
+
"someval"
|
39
|
+
end
|
40
|
+
|
41
|
+
return test
|
42
|
+
end
|
43
|
+
|
44
|
+
# Helper functions for the unit tests
|
45
|
+
def method_with_param(param)
|
46
|
+
puts "foo"
|
47
|
+
puts "bar + #{param} \n"
|
48
|
+
return "bar + #{param}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_without_param
|
52
|
+
ret_val = { :foo => "bar" }
|
53
|
+
puts ret_val
|
54
|
+
return ret_val
|
55
|
+
end
|
56
|
+
|
57
|
+
def simple_puts(my_string)
|
58
|
+
puts my_string
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_file_with_delay(file_path, wait=2)
|
62
|
+
sleep wait
|
63
|
+
File.open(file_path, 'w') { |f| f.write('contents') }
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_pid
|
68
|
+
return Process.pid
|
69
|
+
end
|
70
|
+
|
71
|
+
def raise_an_error
|
72
|
+
raise StandardError.new('An error occurred')
|
73
|
+
end
|
74
|
+
|
75
|
+
#Tests
|
76
|
+
describe '.run_in_parallel' do
|
77
|
+
before do
|
78
|
+
File.delete(TMP_FILE) if File.exists?(TMP_FILE)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should run methods in another process' do
|
82
|
+
run_in_parallel do
|
83
|
+
@result = get_pid
|
84
|
+
@result2 = get_pid
|
85
|
+
end
|
86
|
+
|
87
|
+
expect(@result).to_not eq(Process.pid)
|
88
|
+
expect(@result2).to_not eq(Process.pid)
|
89
|
+
expect(@result).to_not eq(@result2)
|
90
|
+
end
|
91
|
+
it 'should return correct values' do
|
92
|
+
start_time = Time.now
|
93
|
+
|
94
|
+
run_in_parallel do
|
95
|
+
@result_from_test = method_with_param('blah')
|
96
|
+
@result_2 = method_without_param
|
97
|
+
end
|
98
|
+
# return values for instance variables should be set correctly
|
99
|
+
expect(@result_from_test).to eq 'bar + blah'
|
100
|
+
# should be able to return objects (not just strings)
|
101
|
+
expect(@result_2).to eq({ :foo => "bar" })
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return a singleton class value" do
|
105
|
+
|
106
|
+
run_in_parallel { @result = get_singleton_class }
|
107
|
+
|
108
|
+
expect(@result.get_test_data).to eq([1, 2, 3])
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should return an object with an instance variable set to an object containing singleton methods" do
|
112
|
+
run_in_parallel { @result = get_wrapper }
|
113
|
+
expect(@result.get_instance_var.get_test_data).to eq([1, 2, 3])
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should raise an exception and return immediately with kill_all_on_error and one of the processes errors." do
|
117
|
+
expect { run_in_parallel(nil, true) do
|
118
|
+
@result = get_singleton_class
|
119
|
+
@result_2 = raise_an_error
|
120
|
+
@result_3 = create_file_with_delay(TMP_FILE)
|
121
|
+
end }.to raise_error StandardError
|
122
|
+
|
123
|
+
expect(@result_3).to_not eq(true)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise an exception and let all processes complete when one of the processes errors." do
|
127
|
+
expect { run_in_parallel(nil, false) do
|
128
|
+
@result = get_singleton_class
|
129
|
+
@result_2 = raise_an_error
|
130
|
+
@result_3 = create_file_with_delay(TMP_FILE)
|
131
|
+
end }.to raise_error StandardError
|
132
|
+
|
133
|
+
expect(@result_3).to eq(true)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should not run in parallel if forking is not supported" do
|
137
|
+
InParallel::InParallelExecutor.class_variable_set(:@@supported, nil)
|
138
|
+
expect(Process).to receive(:respond_to?).with(:fork).and_return(false).once
|
139
|
+
expect(InParallel::InParallelExecutor.logger).to receive(:warn).with("Warning: Fork is not supported on this OS, executing block normally")
|
140
|
+
|
141
|
+
run_in_parallel do
|
142
|
+
@result_from_test = method_with_param('blah')
|
143
|
+
@result_2 = get_pid
|
144
|
+
end
|
145
|
+
|
146
|
+
expect(@result_from_test).to eq 'bar + blah'
|
147
|
+
expect(@result_2).to eq Process.pid
|
148
|
+
end
|
149
|
+
|
150
|
+
# it "should chunk stdout per process" do
|
151
|
+
# expect {run_in_parallel {
|
152
|
+
# simple_puts('foobar')
|
153
|
+
# }}.to output(/------ Begin output for simple_puts.*foobar.*------ Completed output for simple_puts/).to_stdout
|
154
|
+
# end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '.run_in_background' do
|
158
|
+
before do
|
159
|
+
File.delete(TMP_FILE) if File.exists?(TMP_FILE)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should run in the background' do
|
163
|
+
run_in_background { @result = create_file_with_delay(TMP_FILE) }
|
164
|
+
|
165
|
+
start = Time.now
|
166
|
+
# Should not exist immediately upon block completion
|
167
|
+
expect(File.exists? TMP_FILE).to eq false
|
168
|
+
# Give this some time to complete since it takes longer on the vmpooler vms
|
169
|
+
file_exists = false
|
170
|
+
while Time.now < start + 10 do
|
171
|
+
if File.exists? TMP_FILE
|
172
|
+
file_exists = true
|
173
|
+
break
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# Should exist once the delay in create_file_with_delay is done
|
177
|
+
expect(file_exists).to eq true
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should allow you to get results if ignore_results is false' do
|
181
|
+
@block_result = run_in_background(false) { @result = create_file_with_delay(TMP_FILE) }
|
182
|
+
wait_for_processes
|
183
|
+
# We should get the correct value assigned for the method result
|
184
|
+
expect(@result).to eq true
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '.wait_for_processes' do
|
190
|
+
after do
|
191
|
+
InParallel::InParallelExecutor.parallel_default_timeout = 1200
|
192
|
+
end
|
193
|
+
it 'should timeout when the default timeout value is hit' do
|
194
|
+
@block_result = run_in_background(false) do
|
195
|
+
@result = create_file_with_delay(TMP_FILE, 30)
|
196
|
+
end
|
197
|
+
InParallel::InParallelExecutor.parallel_default_timeout = 0.1
|
198
|
+
expect { wait_for_processes }.to raise_error RuntimeError
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should timeout when a specified timeout value is hit' do
|
202
|
+
@block_result = run_in_background(false) do
|
203
|
+
@result = create_file_with_delay(TMP_FILE, 30)
|
204
|
+
@result2 = method_without_param
|
205
|
+
end
|
206
|
+
expect { wait_for_processes(0.1) }.to raise_error RuntimeError
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '.each_in_parallel' do
|
211
|
+
it 'should run each iteration in a separate process' do
|
212
|
+
pids = [1, 2, 3].each_in_parallel { Process.pid }
|
213
|
+
expect(pids.detect { |pid| pids.count(pid) > 1 }).to be_nil
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should return correct values' do
|
217
|
+
start_time = Time.now
|
218
|
+
items = ['foo', 'bar', 'baz', 'blah', 'foobar'].each_in_parallel do |item|
|
219
|
+
sleep(Random.rand(1.0))
|
220
|
+
item
|
221
|
+
end
|
222
|
+
# return values should be an array of the returned items in the last line of the block, in correct order
|
223
|
+
expect(['foo', 'bar', 'baz', 'blah', 'foobar']).to eq(items)
|
224
|
+
# time should be less than combined delay in the 3 block calls
|
225
|
+
expect(expect(Time.now - start_time).to be < 5)
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'should run each iteration of a map in parallel' do
|
229
|
+
items = ['foo', 'bar', 'baz'].map.each_in_parallel do |item|
|
230
|
+
puts item
|
231
|
+
item
|
232
|
+
end
|
233
|
+
# return values should be an array of the returned items in the last line of the block, in correct order
|
234
|
+
expect(items).to eq(['foo', 'bar', 'baz'])
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should not run in parallel if there is only 1 item in the enumerator' do
|
238
|
+
expect(InParallel::InParallelExecutor.logger).to_not receive(:info).with(/Forked process for/)
|
239
|
+
expect(["foo"].map.each_in_parallel { Process.pid }[0]).to eq(Process.pid)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'should allow you to specify the method_sym' do
|
243
|
+
allow(InParallel::InParallelExecutor.logger).to receive(:info).with(anything())
|
244
|
+
expect(InParallel::InParallelExecutor.logger).to receive(:info).with(/Forked process for my_method/).exactly(3).times
|
245
|
+
|
246
|
+
[1, 2, 3].each_in_parallel('my_method') { |item|
|
247
|
+
puts item
|
248
|
+
}
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
metadata
CHANGED
@@ -1,72 +1,42 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: in-parallel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- samwoods1
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.11'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
41
|
-
description: The other Ruby librarys that do parallel execution all support one primary
|
42
|
-
use case - crunching through a large queue of small tasks as quickly and efficiently
|
43
|
-
as possible. This library primarily supports the use case of needing to run a few
|
44
|
-
larger tasks in parallel and managing the stdout to make it easy to understand which
|
45
|
-
processes are logging what. This library was created to be used by the Beaker test
|
46
|
-
framework to enable parallel execution of some of the framework's tasks, and allow
|
47
|
-
people within thier tests to execute code in parallel when wanted. This solution
|
48
|
-
does not check to see how many processors you have, it just forks as many processes
|
49
|
-
as you ask for. That means that it will handle a handful of parallel processes well,
|
50
|
-
but could definitely overload your system with ruby processes if you try to spin
|
51
|
-
up a LOT of processes. If you're looking for something simple and light-weight and
|
52
|
-
on either linux or mac (forking processes is not supported on Windows), then this
|
53
|
-
solution could be what you want.
|
11
|
+
date: 2016-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Many other Ruby libraries that simplify parallel execution support one
|
14
|
+
primary use case - crunching through a large queue of small, similar tasks as quickly
|
15
|
+
and efficiently as possible. This library primarily supports the use case of executing
|
16
|
+
a few larger and unrelated tasks in parallel, automatically managing the stdout
|
17
|
+
and passing return values back to the main process. This library was created to
|
18
|
+
be used by Puppet's Beaker test framework to enable parallel execution of some of
|
19
|
+
the framework's tasks, and allow users to execute code in parallel within their
|
20
|
+
tests.
|
54
21
|
email:
|
55
22
|
- sam.woods@puppetlabs.com
|
56
23
|
executables: []
|
57
24
|
extensions: []
|
58
25
|
extra_rdoc_files: []
|
59
26
|
files:
|
27
|
+
- CONTRIBUTING.md
|
60
28
|
- Gemfile
|
29
|
+
- Gemfile.lock
|
30
|
+
- HISTORY.md
|
61
31
|
- LICENSE
|
62
32
|
- MAINTAINERS.md
|
63
33
|
- README.md
|
64
34
|
- Rakefile
|
65
|
-
-
|
66
|
-
- in_parallel/version.rb
|
35
|
+
- lib/in-parallel/version.rb
|
67
36
|
- lib/in_parallel.rb
|
68
37
|
- lib/parallel_enumerable.rb
|
69
38
|
- lib/parallel_logger.rb
|
39
|
+
- spec/in-paralell_spec.rb
|
70
40
|
homepage: https://github.com/puppetlabs/in-parallel
|
71
41
|
licenses:
|
72
42
|
- MIT
|
data/in_parallel.gemspec
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require_relative 'in_parallel/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "in-parallel"
|
8
|
-
spec.version = InParallel::VERSION
|
9
|
-
spec.authors = ["samwoods1"]
|
10
|
-
spec.email = ["sam.woods@puppetlabs.com"]
|
11
|
-
|
12
|
-
spec.summary = "A lightweight library to execute a handful of tasks in parallel with simple syntax"
|
13
|
-
spec.description = "The other Ruby librarys that do parallel execution all support one primary use case " +
|
14
|
-
"- crunching through a large queue of small tasks as quickly and efficiently as possible. This library " +
|
15
|
-
"primarily supports the use case of needing to run a few larger tasks in parallel and managing the " +
|
16
|
-
"stdout to make it easy to understand which processes are logging what. This library was created to be " +
|
17
|
-
"used by the Beaker test framework to enable parallel execution of some of the framework's tasks, and " +
|
18
|
-
"allow people within thier tests to execute code in parallel when wanted. This solution does not check " +
|
19
|
-
"to see how many processors you have, it just forks as many processes as you ask for. That means that it " +
|
20
|
-
"will handle a handful of parallel processes well, but could definitely overload your system with ruby " +
|
21
|
-
"processes if you try to spin up a LOT of processes. If you're looking for something simple and " +
|
22
|
-
"light-weight and on either linux or mac (forking processes is not supported on Windows), then this " +
|
23
|
-
"solution could be what you want."
|
24
|
-
spec.homepage = "https://github.com/puppetlabs/in-parallel"
|
25
|
-
spec.license = "MIT"
|
26
|
-
|
27
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
-
spec.bindir = "exe"
|
29
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
-
spec.require_paths = ["lib"]
|
31
|
-
|
32
|
-
spec.add_development_dependency "bundler", "~> 1.11"
|
33
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
34
|
-
end
|
data/in_parallel/version.rb
DELETED