ntl-orchestra 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/README.md +2 -2
- data/lib/orchestra/thread_pool.rb +32 -40
- data/lib/orchestra/version.rb +1 -1
- data/test/integration/multithreading_test.rb +7 -3
- data/test/unit/thread_pool_test.rb +20 -16
- metadata +54 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7a3c2a8f906bac033bc49fd31c5c785d61fcc665
|
4
|
+
data.tar.gz: b7909a5e4a439b36f0e133a98356a176fe31f31d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b29d023f1ec08bd492365f926d328aa2bc8568d92e67bfb030dbe9807935a465efbc15eb4a95da256aac47590ab0a23c103e07196a4d2a2526e832b46db15ea0
|
7
|
+
data.tar.gz: 191c785e486a990ea5a30284f14098174de61f0f55dfb416b5ca5fdee5d4ad95a7dfff89225ff4b95fdd8ca13bffacef488253e162deb37fdfc4f6a46f3afcd4
|
data/README.md
CHANGED
@@ -296,7 +296,7 @@ Orchestra::Operation.new do
|
|
296
296
|
end
|
297
297
|
```
|
298
298
|
|
299
|
-
The third is a minor variation of the second. The only difference is that the operation will always return `true`. `finally` makes sense for operations that perform side effects (e.g. Command objects),
|
299
|
+
The third is a minor variation of the second. The only difference is that the operation will always return `true`. `finally` makes sense for operations that perform side effects (e.g. Command objects), whereas `result` will make sense for queries.
|
300
300
|
|
301
301
|
```ruby
|
302
302
|
Orchestra::Operation.new do
|
@@ -493,7 +493,7 @@ Embedded performances will inherit the observers of the outer operation.
|
|
493
493
|
|
494
494
|
## Recording and playing back services
|
495
495
|
|
496
|
-
The final main feature of Orchestra is the ability to record the service calls throughout an operation. These recordings can then be used to replay operations. This could be helpful, for instance, to attach to exceptions in your exception logging service so that programmers can replay failed performances on their development environments. In addition, these recordings could be used to drive integration testing. Thus, instead of using separate tools such as
|
496
|
+
The final main feature of Orchestra is the ability to record the service calls throughout an operation. These recordings can then be used to replay operations. This could be helpful, for instance, to attach to exceptions in your exception logging service so that programmers can replay failed performances on their development environments. In addition, these recordings could be used to drive integration testing. Thus, instead of using separate tools such as ActiveRecord fixtures, FactoryGirl, and VCR for every service dependency, you can test your operations with one single setup artifact.
|
497
497
|
|
498
498
|
You can record a performance on any `Conductor` by calling `#record` instead of `#perform`:
|
499
499
|
|
@@ -15,7 +15,7 @@ module Orchestra
|
|
15
15
|
def initialize args = {}
|
16
16
|
@timeout, _ = Util.extract_key_args args, :timeout_ms => 1000
|
17
17
|
@threads = Set.new
|
18
|
-
@
|
18
|
+
@dying = Queue.new
|
19
19
|
@pool_lock = Mutex.new
|
20
20
|
@queue = Queue.new
|
21
21
|
@jobs = {}
|
@@ -70,34 +70,49 @@ module Orchestra
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def update event, *;
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def with_timeout &block
|
77
|
-
Timeout.timeout Rational(timeout, 1000), &block
|
73
|
+
return unless event == :failed
|
74
|
+
reap_thread
|
78
75
|
end
|
79
76
|
|
80
77
|
private
|
81
78
|
|
82
79
|
def add_thread!
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
80
|
+
old_count = queue.num_waiting
|
81
|
+
thr = Thread.new &method(:thread_loop)
|
82
|
+
Thread.pass while thr.status == 'run'
|
83
|
+
@threads << thr
|
87
84
|
true
|
88
85
|
end
|
89
86
|
|
90
|
-
def reap_dead_pool
|
91
|
-
@dead_pool.each &:join
|
92
|
-
@dead_pool.clear
|
93
|
-
end
|
94
|
-
|
95
87
|
def remove_thread!
|
96
|
-
|
97
|
-
|
88
|
+
queue << :terminate
|
89
|
+
reap_thread
|
98
90
|
true
|
99
91
|
end
|
100
92
|
|
93
|
+
def reap_thread
|
94
|
+
thread = @dying.pop
|
95
|
+
@threads.delete thread
|
96
|
+
thread.join
|
97
|
+
end
|
98
|
+
|
99
|
+
def thread_loop
|
100
|
+
Thread.current.abort_on_exception = false
|
101
|
+
until (job = queue.pop) == :terminate
|
102
|
+
job.perform
|
103
|
+
Thread.pass
|
104
|
+
end
|
105
|
+
rescue => error
|
106
|
+
add_thread!
|
107
|
+
job.set_error error
|
108
|
+
ensure
|
109
|
+
@dying << Thread.current
|
110
|
+
end
|
111
|
+
|
112
|
+
def while_locked &block
|
113
|
+
@pool_lock.synchronize &block
|
114
|
+
end
|
115
|
+
|
101
116
|
class Job
|
102
117
|
include Observable
|
103
118
|
|
@@ -136,28 +151,5 @@ module Orchestra
|
|
136
151
|
Failed = Module.new
|
137
152
|
end
|
138
153
|
|
139
|
-
def thread_loop
|
140
|
-
Thread.current.abort_on_exception = false
|
141
|
-
until (job = queue.pop) == :terminate
|
142
|
-
job.perform
|
143
|
-
end
|
144
|
-
rescue => error
|
145
|
-
add_thread!
|
146
|
-
job.set_error error
|
147
|
-
ensure
|
148
|
-
@threads.delete Thread.current
|
149
|
-
@dead_pool << Thread.current
|
150
|
-
end
|
151
|
-
|
152
|
-
def wait_for_thread_count_to_change
|
153
|
-
old_count = queue.num_waiting
|
154
|
-
yield
|
155
|
-
ensure
|
156
|
-
Thread.pass while queue.num_waiting == old_count
|
157
|
-
end
|
158
|
-
|
159
|
-
def while_locked &block
|
160
|
-
@pool_lock.synchronize do with_timeout &block end
|
161
|
-
end
|
162
154
|
end
|
163
155
|
end
|
data/lib/orchestra/version.rb
CHANGED
@@ -20,15 +20,19 @@ class MultithreadingTest < Minitest::Test
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_multithreading
|
23
|
-
list = (1..
|
23
|
+
list = (1..1000).to_a
|
24
24
|
|
25
25
|
thread_ids = @conductor.perform @operation, :list => list
|
26
26
|
|
27
|
-
|
27
|
+
assert_equal(
|
28
|
+
@conductor.thread_count,
|
29
|
+
thread_ids.uniq.size,
|
30
|
+
"performance must be spread across threads",
|
31
|
+
)
|
28
32
|
end
|
29
33
|
|
30
34
|
def test_exception_during_multithreading
|
31
|
-
list = (1..
|
35
|
+
list = (1..50).to_a
|
32
36
|
list[23] = :blow_up
|
33
37
|
|
34
38
|
assert_raises CustomError do
|
@@ -2,6 +2,7 @@ class ThreadPoolTest < Minitest::Test
|
|
2
2
|
def setup
|
3
3
|
@thread_pool = Orchestra::ThreadPool.new
|
4
4
|
@thread_pool.count = 5
|
5
|
+
@iterations = Integer(ENV['THREAD_POOL_ITERATIONS'] || 50)
|
5
6
|
end
|
6
7
|
|
7
8
|
def teardown
|
@@ -14,7 +15,7 @@ class ThreadPoolTest < Minitest::Test
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def test_shutting_down
|
17
|
-
|
18
|
+
@iterations.times do |idx|
|
18
19
|
assert_equal 5, @thread_pool.count
|
19
20
|
begin
|
20
21
|
@thread_pool.shutdown
|
@@ -28,23 +29,26 @@ class ThreadPoolTest < Minitest::Test
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def test_adjusting_thread_count_is_robust
|
31
|
-
iterate = lambda { |delta|
|
32
|
+
iterate = lambda { |delta, idx|
|
32
33
|
old_count = @thread_pool.count
|
33
34
|
new_count = old_count + delta
|
34
|
-
expected_status = ['sleep'] * new_count
|
35
35
|
@thread_pool.count = new_count
|
36
|
-
assert_equal
|
36
|
+
assert_equal(
|
37
|
+
new_count,
|
38
|
+
@thread_pool.count,
|
39
|
+
"going from #{old_count} ⇒ #{new_count}; size is #{@thread_pool.count}, idx=#{idx}",
|
40
|
+
)
|
37
41
|
}
|
38
42
|
|
39
|
-
# Add
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
# Add/remove @iteration times
|
44
|
+
@iterations.times do |idx|
|
45
|
+
iterate.call 1, idx
|
46
|
+
iterate.call -1, idx
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
def test_performing_work
|
47
|
-
|
51
|
+
@iterations.times do
|
48
52
|
result = @thread_pool.perform do :deadbeef end
|
49
53
|
|
50
54
|
assert_equal :deadbeef, result
|
@@ -52,22 +56,22 @@ class ThreadPoolTest < Minitest::Test
|
|
52
56
|
end
|
53
57
|
|
54
58
|
def test_enqueueing_work
|
55
|
-
jobs =
|
59
|
+
jobs = @iterations.times.map do |num|
|
56
60
|
@thread_pool.enqueue do (num + 1) * 2 end
|
57
61
|
end
|
58
62
|
|
59
63
|
result = jobs.map &:wait
|
60
64
|
|
61
|
-
assert_equal
|
62
|
-
assert_equal 2,
|
63
|
-
assert_equal
|
65
|
+
assert_equal @iterations, result.uniq.size
|
66
|
+
assert_equal 2, result.first
|
67
|
+
assert_equal @iterations * 2, result.last
|
64
68
|
end
|
65
69
|
|
66
70
|
def test_handling_exceptions
|
67
71
|
Thread.current.abort_on_exception = false
|
68
72
|
old_thread_count = @thread_pool.count
|
69
73
|
|
70
|
-
input =
|
74
|
+
input = @iterations.times.to_a
|
71
75
|
input << nil
|
72
76
|
|
73
77
|
10.times do
|
@@ -78,7 +82,7 @@ class ThreadPoolTest < Minitest::Test
|
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
|
-
assert_equal
|
85
|
+
assert_equal old_thread_count, @thread_pool.count
|
82
86
|
end
|
83
87
|
|
84
88
|
def test_observing_jobs
|
metadata
CHANGED
@@ -1,80 +1,66 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ntl-orchestra
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
|
-
authors:
|
6
|
+
authors:
|
7
7
|
- ntl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
|
12
|
+
date: 2014-12-04 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- &id003
|
18
|
+
- ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: "0"
|
21
|
+
prerelease: false
|
14
22
|
name: invokr
|
15
|
-
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
23
|
+
version_requirements: *id001
|
20
24
|
type: :runtime
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: "1.6"
|
21
31
|
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
32
|
name: bundler
|
29
|
-
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.6'
|
33
|
+
version_requirements: *id002
|
34
34
|
type: :development
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- *id003
|
35
39
|
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.6'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
40
|
name: pry
|
43
|
-
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
41
|
+
version_requirements: *id004
|
48
42
|
type: :development
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "10.0"
|
49
49
|
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
50
|
name: rake
|
57
|
-
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '10.0'
|
51
|
+
version_requirements: *id005
|
62
52
|
type: :development
|
63
|
-
|
64
|
-
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '10.0'
|
69
|
-
description: Orchestra is an orchestration framework for designing complex operations
|
70
|
-
in an object oriented fashion.
|
71
|
-
email:
|
53
|
+
description: Orchestra is an orchestration framework for designing complex operations in an object oriented fashion.
|
54
|
+
email:
|
72
55
|
- nathanladd+github@gmail.com
|
73
56
|
executables: []
|
57
|
+
|
74
58
|
extensions: []
|
59
|
+
|
75
60
|
extra_rdoc_files: []
|
76
|
-
|
77
|
-
|
61
|
+
|
62
|
+
files:
|
63
|
+
- .gitignore
|
78
64
|
- Gemfile
|
79
65
|
- LICENSE.txt
|
80
66
|
- README.md
|
@@ -117,30 +103,29 @@ files:
|
|
117
103
|
- test/unit/util_test.rb
|
118
104
|
- tmp/.keep
|
119
105
|
homepage: https://github.com/ntl/orchestra
|
120
|
-
licenses:
|
106
|
+
licenses:
|
121
107
|
- MIT
|
122
108
|
metadata: {}
|
109
|
+
|
123
110
|
post_install_message:
|
124
111
|
rdoc_options: []
|
125
|
-
|
112
|
+
|
113
|
+
require_paths:
|
126
114
|
- lib
|
127
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
requirements:
|
134
|
-
- - ">="
|
135
|
-
- !ruby/object:Gem::Version
|
136
|
-
version: '0'
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- *id003
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- *id003
|
137
121
|
requirements: []
|
122
|
+
|
138
123
|
rubyforge_project:
|
139
124
|
rubygems_version: 2.2.2
|
140
125
|
signing_key:
|
141
126
|
specification_version: 4
|
142
127
|
summary: Orchestrate complex operations with ease.
|
143
|
-
test_files:
|
128
|
+
test_files:
|
144
129
|
- test/examples/fizz_buzz.rb
|
145
130
|
- test/examples/invitation_service.rb
|
146
131
|
- test/integration/multithreading_test.rb
|