sisyphus 0.2.4 → 0.2.5
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/.rspec +2 -0
- data/lib/sisyphus/forking_execution_strategy.rb +23 -38
- data/lib/sisyphus/master.rb +20 -44
- data/lib/sisyphus/simple_execution_strategy.rb +2 -9
- data/lib/sisyphus/worker.rb +40 -13
- data/lib/sisyphus/worker_pool.rb +36 -0
- data/lib/sisyphus.rb +0 -2
- data/lib/version.rb +1 -1
- data/sisyphus.gemspec +2 -0
- data/spec/sisyphus/forking_execution_strategy_spec.rb +34 -58
- data/spec/sisyphus/master_spec.rb +44 -129
- data/spec/sisyphus/simple_execution_strategy_spec.rb +9 -13
- data/spec/sisyphus/worker_pool_spec.rb +82 -0
- data/spec/sisyphus/worker_spec.rb +80 -20
- data/spec/spec_helper.rb +17 -0
- metadata +19 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5b0d7aeac8761a528b7d084dcf254c284d541f1aabf3c728e35e5f25233fa8fe
|
|
4
|
+
data.tar.gz: 2358bceac26513eeab3f712138bbcd98cfda0fa9e3f3d38a1cdbbabacb762487
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66317b7e08c7c1de9125e1300811e808641fc76dc29230bcdff93ec66e1a1a1f8841b4565cdbd54709517501f283b81dbcb5e1ba4ceaeefe5f5ccfa7566437bd
|
|
7
|
+
data.tar.gz: cc2fb6929498e3c6ab44eddbd6aa7439cfef3357fdff42e11998f25941c2c44a522b6a7375800fa35068ddd2964bf2869e0f81b01cdafc08ed2546088299f327
|
data/.rspec
ADDED
|
@@ -1,56 +1,23 @@
|
|
|
1
1
|
module Sisyphus
|
|
2
2
|
class ForkingExecutionStrategy
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def initialize(logger)
|
|
7
|
-
@logger = logger
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def execute(job, error_handler = ->{})
|
|
4
|
+
def execute(job, error_handler = ->(process_name, error) {})
|
|
11
5
|
if @child_pid = fork
|
|
12
|
-
|
|
6
|
+
ChildProcess.new(@child_pid).success?
|
|
13
7
|
else
|
|
14
|
-
perform job
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class ChildProcess
|
|
19
|
-
attr_reader :pid
|
|
20
|
-
|
|
21
|
-
def initialize(pid)
|
|
22
|
-
@pid = pid
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def success?
|
|
26
|
-
status.success?
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def status
|
|
32
|
-
_, status = ::Process.waitpid2 pid
|
|
33
|
-
status
|
|
8
|
+
perform job, error_handler
|
|
34
9
|
end
|
|
35
10
|
end
|
|
36
11
|
|
|
37
12
|
private
|
|
38
13
|
|
|
39
|
-
def
|
|
40
|
-
child_process.success?
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def child_process
|
|
44
|
-
ChildProcess.new(@child_pid)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def perform(job)
|
|
14
|
+
def perform(job, error_handler)
|
|
48
15
|
self.process_name = "Child of worker #{::Process.ppid}"
|
|
49
16
|
begin
|
|
50
17
|
job.perform
|
|
51
18
|
exit! 0
|
|
52
19
|
rescue ::Exception => e
|
|
53
|
-
|
|
20
|
+
error_handler.call process_name, e
|
|
54
21
|
exit! 1
|
|
55
22
|
end
|
|
56
23
|
end
|
|
@@ -63,5 +30,23 @@ module Sisyphus
|
|
|
63
30
|
$0 = name
|
|
64
31
|
end
|
|
65
32
|
|
|
33
|
+
class ChildProcess
|
|
34
|
+
attr_reader :pid
|
|
35
|
+
|
|
36
|
+
def initialize(pid)
|
|
37
|
+
@pid = pid
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def success?
|
|
41
|
+
status.success?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def status
|
|
45
|
+
_, status = ::Process.waitpid2 pid
|
|
46
|
+
status
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
66
51
|
end
|
|
67
52
|
end
|
data/lib/sisyphus/master.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
require 'timeout'
|
|
2
|
-
require_relative './forking_execution_strategy'
|
|
3
2
|
require_relative './worker'
|
|
3
|
+
require_relative './forking_execution_strategy'
|
|
4
4
|
require_relative './null_logger'
|
|
5
|
+
require_relative './worker_pool'
|
|
5
6
|
|
|
6
7
|
module Sisyphus
|
|
7
8
|
class Master
|
|
@@ -9,13 +10,15 @@ module Sisyphus
|
|
|
9
10
|
|
|
10
11
|
HANDLED_SIGNALS = [:INT, :TTIN, :TTOU]
|
|
11
12
|
|
|
12
|
-
attr_reader :logger, :job, :number_of_workers
|
|
13
|
+
attr_reader :logger, :job, :number_of_workers, :execution_strategy
|
|
13
14
|
|
|
14
15
|
def initialize(job, options = {})
|
|
15
16
|
self.number_of_workers = options.fetch :workers, 0
|
|
16
17
|
@logger = options.fetch(:logger) { NullLogger.new }
|
|
17
|
-
@execution_strategy = options.fetch(:execution_strategy) { ForkingExecutionStrategy }
|
|
18
|
-
|
|
18
|
+
@execution_strategy = options.fetch(:execution_strategy) { ForkingExecutionStrategy.new }
|
|
19
|
+
|
|
20
|
+
@worker_pool = options.fetch(:worker_pool) { WorkerPool.new self }
|
|
21
|
+
|
|
19
22
|
@job = job
|
|
20
23
|
|
|
21
24
|
self_reader, self_writer = IO.pipe
|
|
@@ -27,33 +30,15 @@ module Sisyphus
|
|
|
27
30
|
def start
|
|
28
31
|
trap_signals
|
|
29
32
|
number_of_workers.times do
|
|
30
|
-
spawn_worker
|
|
33
|
+
@worker_pool.spawn_worker
|
|
31
34
|
sleep rand(1000).fdiv(1000)
|
|
32
35
|
end
|
|
33
36
|
puts "Sisyphus::Master started with PID: #{Process.pid}"
|
|
34
37
|
watch_for_output
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
if wpid = fork
|
|
40
|
-
writer.close
|
|
41
|
-
workers << { pid: wpid, reader: reader }
|
|
42
|
-
else
|
|
43
|
-
reader.close
|
|
44
|
-
self.process_name = "Worker #{Process.pid}"
|
|
45
|
-
worker = create_worker(writer)
|
|
46
|
-
start_worker worker
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def start_worker(worker)
|
|
51
|
-
worker.setup
|
|
52
|
-
worker.start
|
|
53
|
-
rescue Exception => e
|
|
54
|
-
worker.error_handler.call
|
|
55
|
-
logger.warn(process_name) { e }
|
|
56
|
-
exit! 0
|
|
40
|
+
def create_worker
|
|
41
|
+
Worker.new(job, execution_strategy, logger)
|
|
57
42
|
end
|
|
58
43
|
|
|
59
44
|
def stop_worker(wpid)
|
|
@@ -70,7 +55,7 @@ module Sisyphus
|
|
|
70
55
|
Timeout.timeout(30) do
|
|
71
56
|
watch_for_shutdown while worker_count > 0
|
|
72
57
|
end
|
|
73
|
-
rescue e
|
|
58
|
+
rescue Timeout::Error => e
|
|
74
59
|
p "Timeout reached:", e
|
|
75
60
|
end
|
|
76
61
|
end
|
|
@@ -81,20 +66,15 @@ module Sisyphus
|
|
|
81
66
|
|
|
82
67
|
private
|
|
83
68
|
|
|
84
|
-
attr_reader :workers
|
|
85
69
|
attr_writer :number_of_workers
|
|
86
70
|
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def execution_strategy
|
|
92
|
-
@execution_strategy.new logger
|
|
71
|
+
def workers
|
|
72
|
+
@worker_pool.workers
|
|
93
73
|
end
|
|
94
74
|
|
|
95
75
|
def watch_for_shutdown
|
|
96
76
|
wpid, _ = Process.wait2
|
|
97
|
-
worker =
|
|
77
|
+
worker = workers.find { |w| w.fetch(:pid) == wpid }
|
|
98
78
|
worker.fetch(:reader).close
|
|
99
79
|
workers.delete worker
|
|
100
80
|
wpid
|
|
@@ -112,7 +92,7 @@ module Sisyphus
|
|
|
112
92
|
end
|
|
113
93
|
|
|
114
94
|
def process_signal_queue
|
|
115
|
-
handle_signal(
|
|
95
|
+
handle_signal(signal_queue.shift) until signal_queue.empty?
|
|
116
96
|
end
|
|
117
97
|
|
|
118
98
|
def process_pipes(pipes)
|
|
@@ -131,7 +111,7 @@ module Sisyphus
|
|
|
131
111
|
end
|
|
132
112
|
|
|
133
113
|
def respawn_worker(wpid)
|
|
134
|
-
spawn_worker
|
|
114
|
+
@worker_pool.spawn_worker
|
|
135
115
|
stop_worker wpid
|
|
136
116
|
watch_for_shutdown
|
|
137
117
|
end
|
|
@@ -161,7 +141,7 @@ module Sisyphus
|
|
|
161
141
|
end
|
|
162
142
|
|
|
163
143
|
def queue_signal(signal)
|
|
164
|
-
|
|
144
|
+
signal_queue << signal
|
|
165
145
|
@selfpipe[:writer].write_nonblock('.')
|
|
166
146
|
rescue Errno::EAGAIN
|
|
167
147
|
# Ignore
|
|
@@ -191,7 +171,7 @@ module Sisyphus
|
|
|
191
171
|
|
|
192
172
|
def handle_ttin
|
|
193
173
|
self.number_of_workers += 1
|
|
194
|
-
spawn_worker
|
|
174
|
+
@worker_pool.spawn_worker
|
|
195
175
|
end
|
|
196
176
|
|
|
197
177
|
def handle_ttou
|
|
@@ -209,12 +189,8 @@ module Sisyphus
|
|
|
209
189
|
@stopping
|
|
210
190
|
end
|
|
211
191
|
|
|
212
|
-
def
|
|
213
|
-
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def process_name
|
|
217
|
-
$0
|
|
192
|
+
def signal_queue
|
|
193
|
+
Thread.main[:signal_queue]
|
|
218
194
|
end
|
|
219
195
|
end
|
|
220
196
|
end
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
module Sisyphus
|
|
2
2
|
class SimpleExecutionStrategy
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def initialize(logger)
|
|
7
|
-
@logger = logger
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def execute(job, error_handler = ->{})
|
|
4
|
+
def execute(job, error_handler = ->(name, error) {})
|
|
11
5
|
job.perform
|
|
12
6
|
rescue Exception => e
|
|
13
|
-
|
|
14
|
-
error_handler.call
|
|
7
|
+
error_handler.call process_name, e
|
|
15
8
|
end
|
|
16
9
|
|
|
17
10
|
private
|
data/lib/sisyphus/worker.rb
CHANGED
|
@@ -2,25 +2,34 @@ module Sisyphus
|
|
|
2
2
|
class Worker
|
|
3
3
|
UNCAUGHT_ERROR = '.'
|
|
4
4
|
|
|
5
|
-
attr_reader :execution_strategy, :job, :output
|
|
5
|
+
attr_reader :logger, :execution_strategy, :job, :output, :to_master
|
|
6
6
|
|
|
7
|
-
def initialize(job,
|
|
7
|
+
def initialize(job, execution_strategy, logger)
|
|
8
8
|
@job = job
|
|
9
|
-
@output =
|
|
9
|
+
@to_master, @output = IO.pipe
|
|
10
10
|
@execution_strategy = execution_strategy
|
|
11
|
+
@logger = logger
|
|
12
|
+
@set_up = false
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
+
def start
|
|
16
|
+
setup
|
|
17
|
+
run
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
def
|
|
20
|
+
def setup
|
|
18
21
|
trap_signals
|
|
22
|
+
job.setup if job.respond_to? :setup
|
|
23
|
+
setup_done
|
|
24
|
+
rescue Exception => e
|
|
25
|
+
error_handler.call "Setup", e
|
|
26
|
+
end
|
|
19
27
|
|
|
28
|
+
def run
|
|
20
29
|
loop do
|
|
21
30
|
break if stopped?
|
|
22
31
|
perform_job
|
|
23
|
-
end
|
|
32
|
+
end if set_up?
|
|
24
33
|
|
|
25
34
|
exit! 0
|
|
26
35
|
end
|
|
@@ -30,27 +39,45 @@ module Sisyphus
|
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
def error_handler
|
|
33
|
-
-> {
|
|
42
|
+
-> (name, error) {
|
|
43
|
+
return if stopped?
|
|
34
44
|
begin
|
|
35
|
-
|
|
45
|
+
logger.warn(name) { error }
|
|
46
|
+
output.write UNCAUGHT_ERROR
|
|
36
47
|
rescue Errno::EAGAIN, Errno::EINTR
|
|
37
48
|
# Ignore
|
|
38
49
|
end
|
|
39
50
|
}
|
|
40
51
|
end
|
|
41
52
|
|
|
53
|
+
def stop
|
|
54
|
+
@stopped = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def atfork_parent
|
|
58
|
+
output.close
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def atfork_child
|
|
62
|
+
to_master.close
|
|
63
|
+
end
|
|
64
|
+
|
|
42
65
|
private
|
|
43
66
|
|
|
67
|
+
def set_up?
|
|
68
|
+
@set_up
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def setup_done
|
|
72
|
+
@set_up = true
|
|
73
|
+
end
|
|
74
|
+
|
|
44
75
|
def trap_signals
|
|
45
76
|
Signal.trap('INT') do
|
|
46
77
|
stop
|
|
47
78
|
end
|
|
48
79
|
end
|
|
49
80
|
|
|
50
|
-
def stop
|
|
51
|
-
@stopped = true
|
|
52
|
-
end
|
|
53
|
-
|
|
54
81
|
def stopped?
|
|
55
82
|
@stopped
|
|
56
83
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative './worker'
|
|
2
|
+
|
|
3
|
+
module Sisyphus
|
|
4
|
+
class WorkerPool
|
|
5
|
+
|
|
6
|
+
attr_reader :workers, :worker_factory
|
|
7
|
+
|
|
8
|
+
def initialize(worker_factory)
|
|
9
|
+
@worker_factory = worker_factory
|
|
10
|
+
@workers = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def spawn_worker
|
|
14
|
+
worker = create_worker
|
|
15
|
+
if wpid = fork
|
|
16
|
+
worker.atfork_parent
|
|
17
|
+
workers << { pid: wpid, reader: worker.to_master }
|
|
18
|
+
else
|
|
19
|
+
worker.atfork_child
|
|
20
|
+
self.process_name = "Worker #{Process.pid}"
|
|
21
|
+
worker.start
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def create_worker
|
|
28
|
+
worker_factory.create_worker
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_name=(name)
|
|
32
|
+
$0 = name
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/sisyphus.rb
CHANGED
data/lib/version.rb
CHANGED
data/sisyphus.gemspec
CHANGED
|
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
+
spec.required_ruby_version = '>= 1.9.3'
|
|
22
|
+
|
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
23
25
|
spec.add_development_dependency "rspec", "~> 2.14"
|
|
@@ -1,50 +1,37 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
1
2
|
require_relative '../../lib/sisyphus/forking_execution_strategy'
|
|
2
3
|
|
|
3
4
|
module Sisyphus
|
|
4
5
|
describe ForkingExecutionStrategy do
|
|
5
6
|
|
|
6
|
-
let(:logger) { double :logger }
|
|
7
7
|
let(:job) { double :job }
|
|
8
|
-
let(:error_handler) {
|
|
9
|
-
let(:strategy) { ForkingExecutionStrategy.new
|
|
8
|
+
let(:error_handler) { ->(name, raised_error) {} }
|
|
9
|
+
let(:strategy) { ForkingExecutionStrategy.new }
|
|
10
10
|
let(:child_pid) { 1 }
|
|
11
|
+
let(:status) { double :process_status }
|
|
11
12
|
|
|
12
13
|
it 'forks on execution' do
|
|
13
|
-
allow(
|
|
14
|
+
allow(Process).to receive(:waitpid2).with(child_pid) { [child_pid, status] }
|
|
15
|
+
allow(status).to receive(:success?) { true }
|
|
14
16
|
expect(strategy).to receive(:fork) { child_pid }
|
|
15
17
|
strategy.execute job
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
context 'in the parent process' do
|
|
19
21
|
|
|
20
|
-
let(:child_process) { double :child_process }
|
|
21
|
-
|
|
22
22
|
before :each do
|
|
23
23
|
allow(strategy).to receive(:fork) { child_pid }
|
|
24
|
+
allow(status).to receive(:success?) { true }
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
it '
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
strategy.execute job, error_handler
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'does not call error_handler if execution is successful' do
|
|
33
|
-
allow(strategy).to receive(:success?) { true }
|
|
34
|
-
expect(error_handler).not_to receive(:call)
|
|
35
|
-
strategy.execute job, error_handler
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it 'instantiates a child_process' do
|
|
39
|
-
allow(child_process).to receive(:success?) { true }
|
|
40
|
-
expect(ForkingExecutionStrategy::ChildProcess).to receive(:new).with(child_pid) { child_process }
|
|
41
|
-
strategy.execute job, error_handler
|
|
27
|
+
it 'waits for the execution to finish' do
|
|
28
|
+
expect(Process).to receive(:waitpid2).with(child_pid) { [child_pid, status] }
|
|
29
|
+
strategy.execute(job, error_handler)
|
|
42
30
|
end
|
|
43
31
|
|
|
44
|
-
it '
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
strategy.execute job, error_handler
|
|
32
|
+
it 'gets the status of the child process' do
|
|
33
|
+
allow(Process).to receive(:waitpid2) { [child_pid, status] }
|
|
34
|
+
expect(strategy.execute job, error_handler).to eq(true)
|
|
48
35
|
end
|
|
49
36
|
|
|
50
37
|
end
|
|
@@ -75,47 +62,36 @@ module Sisyphus
|
|
|
75
62
|
strategy.execute job
|
|
76
63
|
end
|
|
77
64
|
|
|
78
|
-
it '
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
allow(strategy).to receive(:process_name) { process_name }
|
|
82
|
-
allow(job).to receive(:perform) { raise Exception, exception_message }
|
|
83
|
-
expect(logger).to receive(:warn).with(process_name)
|
|
84
|
-
strategy.execute job
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it 'exits with a 1 status if job is performed and it fails' do
|
|
88
|
-
process_name = "foobarbaz"
|
|
89
|
-
allow(strategy).to receive(:process_name) { process_name }
|
|
90
|
-
allow(job).to receive(:perform) { raise "foo" }
|
|
91
|
-
allow(logger).to receive(:warn)
|
|
92
|
-
expect(strategy).to receive(:exit!).with(1)
|
|
93
|
-
strategy.execute job
|
|
65
|
+
it 'does not call error_handler if execution is successful' do
|
|
66
|
+
allow(strategy).to receive(:exit!).with(0)
|
|
67
|
+
strategy.execute job, ->(n, e) { fail "Should not be called" }
|
|
94
68
|
end
|
|
95
69
|
|
|
96
|
-
|
|
70
|
+
context 'when the job#perform fails' do
|
|
97
71
|
|
|
98
|
-
|
|
72
|
+
let(:process_name) { "foobarbaz" }
|
|
73
|
+
let(:exception) { Exception.new("foo") }
|
|
99
74
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
75
|
+
before :each do
|
|
76
|
+
allow(strategy).to receive(:process_name) { process_name }
|
|
77
|
+
allow(job).to receive(:perform).and_raise(exception)
|
|
78
|
+
end
|
|
103
79
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
[pid, status]
|
|
80
|
+
it 'exits with a 1 status if job is performed and it fails' do
|
|
81
|
+
expect(strategy).to receive(:exit!).with(1)
|
|
82
|
+
strategy.execute job, error_handler
|
|
108
83
|
end
|
|
109
|
-
child_process.success?
|
|
110
|
-
end
|
|
111
84
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
85
|
+
it 'calls error_handler if execution is unsuccessful' do
|
|
86
|
+
allow(strategy).to receive(:exit!)
|
|
87
|
+
strategy.execute job, ->(name, raised_error) {
|
|
88
|
+
expect(name).to eq(process_name)
|
|
89
|
+
expect(raised_error).to eq(exception)
|
|
90
|
+
}
|
|
116
91
|
end
|
|
117
|
-
|
|
92
|
+
|
|
118
93
|
end
|
|
94
|
+
|
|
119
95
|
end
|
|
120
96
|
|
|
121
97
|
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
1
2
|
require_relative '../../lib/sisyphus/master'
|
|
2
3
|
|
|
3
4
|
module Sisyphus
|
|
4
5
|
describe Master do
|
|
5
|
-
subject(:master) { Master.new job }
|
|
6
|
+
subject(:master) { Master.new job, worker_pool: worker_pool }
|
|
7
|
+
|
|
8
|
+
let(:worker_pool) { double :worker_pool }
|
|
6
9
|
|
|
7
10
|
before(:each) {
|
|
8
11
|
allow(master).to receive(:puts)
|
|
@@ -12,186 +15,98 @@ module Sisyphus
|
|
|
12
15
|
let(:job) { double(:job) }
|
|
13
16
|
let(:pipes) { [double(:reader_pipe), double(:writer_pipe)] }
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
before :each do
|
|
25
|
-
allow(master).to receive(:fork) { nil }
|
|
26
|
-
allow(IO).to receive(:pipe) { pipes }
|
|
27
|
-
allow(pipes.first).to receive(:close)
|
|
28
|
-
allow(Process).to receive(:pid) { 666 }
|
|
29
|
-
allow(master).to receive(:exit!)
|
|
30
|
-
allow(Worker).to receive(:new) { worker }
|
|
31
|
-
allow(worker).to receive(:setup)
|
|
32
|
-
allow(worker).to receive(:start)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'should setup the worker' do
|
|
36
|
-
expect(worker).to receive(:setup)
|
|
37
|
-
master.spawn_worker
|
|
38
|
-
end
|
|
18
|
+
it 'creates a worker' do
|
|
19
|
+
execution_strategy = double :execution_strategy
|
|
20
|
+
master = Master.new job
|
|
21
|
+
allow(master).to receive(:execution_strategy) { execution_strategy }
|
|
22
|
+
worker = master.create_worker
|
|
23
|
+
expect(worker.job).to eq(job)
|
|
24
|
+
expect(worker.execution_strategy).to eq(execution_strategy)
|
|
25
|
+
end
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
expect(master).to receive(:process_name=).with("Worker #{666}")
|
|
42
|
-
master.spawn_worker
|
|
43
|
-
end
|
|
27
|
+
describe 'when it has running workers' do
|
|
44
28
|
|
|
45
|
-
|
|
46
|
-
expect(worker).to receive(:start)
|
|
47
|
-
master.spawn_worker
|
|
48
|
-
end
|
|
29
|
+
let(:workers) { double :workers }
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
master.spawn_worker
|
|
55
|
-
end
|
|
31
|
+
it 'stops all workers when receiving stop_all' do
|
|
32
|
+
allow(worker_pool).to receive(:workers) { workers }
|
|
33
|
+
allow(workers).to receive(:each).and_yield({ pid: 666 }).and_yield({ pid: 667 })
|
|
34
|
+
allow(workers).to receive(:length).and_return(2, 1, 0)
|
|
56
35
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
master.spawn_worker
|
|
60
|
-
end
|
|
36
|
+
expect(master).to receive(:stop_worker).with(666).ordered
|
|
37
|
+
expect(master).to receive(:stop_worker).with(667).ordered
|
|
61
38
|
|
|
62
|
-
|
|
63
|
-
let(:logger) { double(:logger) }
|
|
64
|
-
|
|
65
|
-
it 'should log the exception' do
|
|
66
|
-
allow(worker).to receive(:error_handler) { ->{} }
|
|
67
|
-
allow(master).to receive(:logger) { logger }
|
|
68
|
-
allow(worker).to receive(:setup) { raise :raised_by_spec }
|
|
69
|
-
expect(logger).to receive(:warn)
|
|
70
|
-
master.spawn_worker
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it 'should write to the writer pipe' do
|
|
74
|
-
allow(master).to receive(:logger) { logger }
|
|
75
|
-
allow(worker).to receive(:setup) { raise :raised_by_spec }
|
|
76
|
-
allow(logger).to receive(:warn)
|
|
77
|
-
expect(worker).to receive(:error_handler) { ->{} }
|
|
78
|
-
master.spawn_worker
|
|
79
|
-
end
|
|
80
|
-
end
|
|
39
|
+
master.stop_all
|
|
81
40
|
end
|
|
82
41
|
|
|
83
|
-
describe '
|
|
42
|
+
describe 'and it receives stop_worker message' do
|
|
84
43
|
before :each do
|
|
85
|
-
allow(
|
|
86
|
-
allow(
|
|
87
|
-
allow(pipes.last).to receive(:close)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'increases worker_count' do
|
|
91
|
-
master.spawn_worker
|
|
92
|
-
expect(master.worker_count).to eq(1)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it 'should open a pipe' do
|
|
96
|
-
expect(IO).to receive(:pipe) { pipes }
|
|
97
|
-
master.spawn_worker
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it 'should close the writer pipe' do
|
|
101
|
-
expect(pipes.last).to receive(:close)
|
|
102
|
-
master.spawn_worker
|
|
44
|
+
allow(worker_pool).to receive(:workers) { workers }
|
|
45
|
+
allow(workers).to receive(:find) { |&block| block.call({ pid: 666 }) }
|
|
103
46
|
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
describe 'when it has running workers' do
|
|
108
|
-
before :each do
|
|
109
|
-
pipes.each { |p| allow(p).to receive(:close) }
|
|
110
|
-
allow(IO).to receive(:pipe) { pipes }
|
|
111
|
-
allow(master).to receive(:fork) { 666 }
|
|
112
|
-
master.spawn_worker
|
|
113
|
-
allow(Process).to receive(:kill).with('INT', 666)
|
|
114
|
-
allow(Process).to receive(:waitpid2).with(666)
|
|
115
|
-
end
|
|
116
47
|
|
|
117
|
-
describe 'and it receives stop_worker message' do
|
|
118
48
|
it 'kills a child with the INT signal' do
|
|
119
49
|
expect(Process).to receive(:kill).with('INT', 666)
|
|
120
50
|
master.stop_worker(666)
|
|
121
51
|
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it 'stops all workers when receiving stop_all' do
|
|
125
|
-
allow(Process).to receive(:kill).with('INT', 666)
|
|
126
|
-
allow(Process).to receive(:wait2) { 666 }
|
|
127
|
-
|
|
128
|
-
expect(master).to receive(:stop_worker).with(666).exactly(master.worker_count).times.and_call_original
|
|
129
52
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
describe 'when there are no running workers' do
|
|
135
|
-
describe 'and it receives stop_worker' do
|
|
136
|
-
it 'raises an error' do
|
|
137
|
-
expect { master.stop_worker(666) }.not_to raise_error
|
|
53
|
+
it 'kills nothing if no worker corresponds to the pid' do
|
|
54
|
+
expect(Process).not_to receive(:kill).with('INT', 667)
|
|
55
|
+
master.stop_worker(667)
|
|
138
56
|
end
|
|
139
57
|
end
|
|
140
58
|
|
|
141
|
-
describe 'and it receives stop_all' do
|
|
142
|
-
it 'does nothing' do
|
|
143
|
-
expect(master).not_to receive(:stop_worker)
|
|
144
|
-
master.stop_all
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
59
|
end
|
|
148
60
|
|
|
149
61
|
it 'starts the specified number of workers when started' do
|
|
150
|
-
master = Master.new nil, workers: 3
|
|
62
|
+
master = Master.new nil, workers: 3, worker_pool: worker_pool
|
|
151
63
|
allow(master).to receive(:puts)
|
|
152
64
|
allow(master).to receive(:watch_for_output)
|
|
153
|
-
|
|
65
|
+
allow(master).to receive(:sleep)
|
|
66
|
+
expect(worker_pool).to receive(:spawn_worker).exactly(3).times
|
|
154
67
|
master.start
|
|
155
68
|
end
|
|
156
69
|
|
|
157
70
|
describe 'when number of workers is zero' do
|
|
158
|
-
let(:master) { Master.new nil, workers: 0 }
|
|
71
|
+
let(:master) { Master.new nil, workers: 0, worker_pool: worker_pool }
|
|
159
72
|
|
|
160
73
|
before(:each) { allow(master).to receive(:puts) }
|
|
161
74
|
|
|
162
75
|
it 'should not start workers' do
|
|
76
|
+
allow(master).to receive(:trap_signals)
|
|
163
77
|
allow(master).to receive(:watch_for_output)
|
|
164
|
-
expect(
|
|
78
|
+
expect(worker_pool).not_to receive(:spawn_worker)
|
|
165
79
|
master.start
|
|
166
80
|
end
|
|
167
81
|
end
|
|
168
82
|
|
|
169
83
|
it 'attaches a signal handler when started' do
|
|
84
|
+
allow(worker_pool).to receive(:spawn_worker)
|
|
85
|
+
allow(master).to receive(:watch_for_output)
|
|
86
|
+
|
|
170
87
|
expect(Signal).to receive(:trap).with(:TTIN)
|
|
171
88
|
expect(Signal).to receive(:trap).with(:INT)
|
|
172
89
|
expect(Signal).to receive(:trap).with(:TTOU)
|
|
173
|
-
|
|
174
|
-
allow(master).to receive(:watch_for_output)
|
|
90
|
+
|
|
175
91
|
master.start
|
|
176
92
|
end
|
|
177
93
|
|
|
178
94
|
it 'should watch for output' do
|
|
179
|
-
allow(
|
|
95
|
+
allow(worker_pool).to receive(:spawn_worker)
|
|
96
|
+
allow(master).to receive(:trap_signals)
|
|
180
97
|
expect(master).to receive(:watch_for_output)
|
|
181
98
|
master.start
|
|
182
99
|
end
|
|
183
100
|
|
|
184
101
|
it 'can resolve a wpid from a reader pipe' do
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
allow(
|
|
188
|
-
|
|
189
|
-
master.spawn_worker
|
|
190
|
-
|
|
191
|
-
expect(master.send(:worker_pid, pipes.first)).to eq(666)
|
|
102
|
+
pipe = double :pipe
|
|
103
|
+
allow(pipe).to receive(:fileno) { 123 }
|
|
104
|
+
allow(worker_pool).to receive(:workers) { [{ pid: 666, reader: pipe }] }
|
|
105
|
+
expect(master.send(:worker_pid, pipe)).to eq(666)
|
|
192
106
|
end
|
|
193
107
|
|
|
194
108
|
it 'raises if it can\'t resolve a wpid from a reader pipe' do
|
|
109
|
+
allow(worker_pool).to receive(:workers) { [] }
|
|
195
110
|
expect { master.send(:worker_pid, pipes.first) }.to raise_error("Unknown worker pipe")
|
|
196
111
|
end
|
|
197
112
|
end
|
|
@@ -1,32 +1,28 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
1
2
|
require_relative '../../lib/sisyphus/simple_execution_strategy'
|
|
2
3
|
|
|
3
4
|
module Sisyphus
|
|
4
5
|
describe SimpleExecutionStrategy do
|
|
5
6
|
|
|
6
|
-
let(:logger) { double :logger }
|
|
7
7
|
let(:job) { double :job }
|
|
8
|
-
let(:strategy) { SimpleExecutionStrategy.new
|
|
8
|
+
let(:strategy) { SimpleExecutionStrategy.new }
|
|
9
9
|
|
|
10
10
|
it 'should perform the job when executed' do
|
|
11
11
|
expect(job).to receive(:perform)
|
|
12
12
|
strategy.execute job
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
it 'should
|
|
15
|
+
it 'should call the error_handler if the job fails' do
|
|
16
16
|
error_message = "This is a horrible failure.. The Universe is probably ending!"
|
|
17
|
+
error = Exception.new(error_message)
|
|
17
18
|
process_name = "uber awesome process name"
|
|
18
|
-
allow(job).to receive(:perform) { fail
|
|
19
|
+
allow(job).to receive(:perform) { fail error }
|
|
19
20
|
allow(strategy).to receive(:process_name) { process_name }
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
strategy.execute job, ->(name, raised_error) {
|
|
22
|
+
expect(name).to eq(process_name)
|
|
23
|
+
expect(raised_error).to eq(error)
|
|
24
|
+
}
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
it 'should call the error_handler if the job fails' do
|
|
25
|
-
allow(job).to receive(:perform) { fail "foo" }
|
|
26
|
-
allow(logger).to receive(:warn)
|
|
27
|
-
error_handler = double :error_handler
|
|
28
|
-
expect(error_handler).to receive(:call)
|
|
29
|
-
strategy.execute job, error_handler
|
|
30
|
-
end
|
|
31
27
|
end
|
|
32
28
|
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require_relative '../../lib/sisyphus/master'
|
|
3
|
+
require_relative '../../lib/sisyphus/worker_pool'
|
|
4
|
+
|
|
5
|
+
module Sisyphus
|
|
6
|
+
describe WorkerPool do
|
|
7
|
+
|
|
8
|
+
subject(:worker_pool) { WorkerPool.new worker_factory }
|
|
9
|
+
|
|
10
|
+
describe 'when receiving the spawn_worker message' do
|
|
11
|
+
|
|
12
|
+
let(:job) { double :job }
|
|
13
|
+
let(:worker_factory) { Master.new(job) }
|
|
14
|
+
let(:pipes) { [double(:output), double(:input)] }
|
|
15
|
+
|
|
16
|
+
it 'retrieves a worker from the worker_factory' do
|
|
17
|
+
allow(worker_pool).to receive(:fork) { 3267 }
|
|
18
|
+
expect(worker_factory).to receive(:create_worker).and_call_original
|
|
19
|
+
worker_pool.spawn_worker
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'forks' do
|
|
23
|
+
expect(worker_pool).to receive(:fork) { 666 }
|
|
24
|
+
worker_pool.spawn_worker
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe 'in the worker process' do
|
|
28
|
+
|
|
29
|
+
let(:worker) { double :worker }
|
|
30
|
+
|
|
31
|
+
before :each do
|
|
32
|
+
allow(worker_pool).to receive(:fork) { nil }
|
|
33
|
+
allow(worker_pool).to receive(:process_name=)
|
|
34
|
+
allow(worker).to receive(:start)
|
|
35
|
+
allow(worker).to receive(:atfork_child)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'runs Worker#atfork_child' do
|
|
39
|
+
allow(worker_factory).to receive(:create_worker) { worker }
|
|
40
|
+
expect(worker).to receive(:atfork_child).with(no_args)
|
|
41
|
+
worker_pool.spawn_worker
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'renames the process' do
|
|
45
|
+
allow(worker_factory).to receive(:create_worker) { worker }
|
|
46
|
+
expect(worker_pool).to receive(:process_name=).with("Worker #{Process.pid}")
|
|
47
|
+
worker_pool.spawn_worker
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'starts the worker' do
|
|
51
|
+
allow(worker_factory).to receive(:create_worker) { worker }
|
|
52
|
+
allow(worker).to receive(:atfork_child)
|
|
53
|
+
expect(worker).to receive(:start)
|
|
54
|
+
worker_pool.spawn_worker
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe 'in the master process' do
|
|
60
|
+
|
|
61
|
+
before :each do
|
|
62
|
+
allow(worker_pool).to receive(:fork) { Process.pid }
|
|
63
|
+
allow(pipes.last).to receive(:close)
|
|
64
|
+
allow(IO).to receive(:pipe) { pipes }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'closes the input pipe' do
|
|
68
|
+
worker = double :worker
|
|
69
|
+
allow(worker).to receive(:to_master)
|
|
70
|
+
allow(worker_factory).to receive(:create_worker) { worker }
|
|
71
|
+
expect(worker).to receive(:atfork_parent).with(no_args)
|
|
72
|
+
worker_pool.spawn_worker
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'adds the worker pid and output pipe to the list of workers' do
|
|
76
|
+
expect(worker_pool.workers).to receive(:<<).with({ pid: Process.pid, reader: pipes.first })
|
|
77
|
+
worker_pool.spawn_worker
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -1,23 +1,38 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
1
2
|
require_relative '../../lib/sisyphus/worker'
|
|
2
3
|
|
|
3
4
|
module Sisyphus
|
|
4
5
|
describe Worker do
|
|
5
6
|
let(:job) { double :job }
|
|
6
|
-
let(:output) { double :pipe }
|
|
7
7
|
let(:execution_strategy) { double :execution_strategy }
|
|
8
|
-
let(:
|
|
8
|
+
let(:logger) { double :logger }
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
worker.
|
|
10
|
+
subject(:worker) { Worker.new job, execution_strategy, logger }
|
|
11
|
+
|
|
12
|
+
it 'sets up itself and executes run when started' do
|
|
13
|
+
expect(worker).to receive(:setup).ordered
|
|
14
|
+
expect(worker).to receive(:run).ordered
|
|
14
15
|
worker.start
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
it '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
it 'traps INT signals before setting up the job' do
|
|
19
|
+
allow(job).to receive(:respond_to?).with(:setup) { true }
|
|
20
|
+
expect(Signal).to receive(:trap).with('INT').ordered
|
|
21
|
+
expect(job).to receive(:setup).ordered
|
|
22
|
+
worker.setup
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'does not perform the job when it has been stopped' do
|
|
26
|
+
worker.stop
|
|
27
|
+
allow(worker).to receive(:exit!)
|
|
28
|
+
expect(worker).not_to receive(:perform_job)
|
|
29
|
+
worker.run
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'will not run if it has not been set up' do
|
|
33
|
+
expect(worker).not_to receive(:perform_job)
|
|
34
|
+
allow(worker).to receive(:exit!)
|
|
35
|
+
worker.run
|
|
21
36
|
end
|
|
22
37
|
|
|
23
38
|
it 'uses execution_strategy to perform the job' do
|
|
@@ -31,37 +46,82 @@ module Sisyphus
|
|
|
31
46
|
worker.perform_job
|
|
32
47
|
end
|
|
33
48
|
|
|
49
|
+
it 'only closes the output in atfork_parent' do
|
|
50
|
+
expect(worker.output).to receive(:close)
|
|
51
|
+
expect(worker.to_master).not_to receive(:close)
|
|
52
|
+
worker.atfork_parent
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'only closes the to_master in atfork_child' do
|
|
56
|
+
expect(worker.to_master).to receive(:close)
|
|
57
|
+
expect(worker.output).not_to receive(:close)
|
|
58
|
+
worker.atfork_child
|
|
59
|
+
end
|
|
60
|
+
|
|
34
61
|
context 'the error_handler' do
|
|
35
62
|
|
|
36
63
|
it 'writes the UNCAUGHT_ERROR to output' do
|
|
37
|
-
|
|
38
|
-
worker.
|
|
64
|
+
allow(logger).to receive(:warn)
|
|
65
|
+
expect(worker.output).to receive(:write).with Worker::UNCAUGHT_ERROR
|
|
66
|
+
worker.error_handler.call(:name, :error)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'logs the error being thrown' do
|
|
70
|
+
the_name = :the_name
|
|
71
|
+
the_exception = :the_exception
|
|
72
|
+
expect(worker.logger).to receive(:warn) do |name, &block|
|
|
73
|
+
expect(name).to eq(the_name)
|
|
74
|
+
expect(block.call).to eq(the_exception)
|
|
75
|
+
end
|
|
76
|
+
worker.error_handler.call(the_name, the_exception)
|
|
39
77
|
end
|
|
40
78
|
|
|
41
79
|
it 'does not write UNCAUGHT_ERROR to output if the worker is stopped' do
|
|
42
80
|
allow(worker).to receive(:stopped?) { true }
|
|
43
|
-
expect(output).to_not receive(:write)
|
|
44
|
-
worker.error_handler.call
|
|
45
|
-
expect(output).to_not receive(:write)
|
|
46
|
-
worker.error_handler.call
|
|
81
|
+
expect(worker.output).to_not receive(:write)
|
|
82
|
+
worker.error_handler.call(:name, :error)
|
|
83
|
+
expect(worker.output).to_not receive(:write)
|
|
84
|
+
worker.error_handler.call(:name, :error)
|
|
47
85
|
end
|
|
48
86
|
|
|
49
87
|
end
|
|
50
88
|
|
|
51
89
|
context 'when job does not respond to :setup' do
|
|
52
90
|
it 'does not call job.setup' do
|
|
53
|
-
job.
|
|
54
|
-
job.
|
|
91
|
+
allow(job).to receive(:respond_to?).with(:setup) { false }
|
|
92
|
+
expect(job).not_to receive(:setup)
|
|
55
93
|
worker.setup
|
|
56
94
|
end
|
|
57
95
|
end
|
|
58
96
|
|
|
59
97
|
context 'when job responds to :setup' do
|
|
98
|
+
|
|
99
|
+
before :each do
|
|
100
|
+
allow(job).to receive(:respond_to?).with(:setup) { true }
|
|
101
|
+
end
|
|
102
|
+
|
|
60
103
|
it 'sets up the job' do
|
|
61
|
-
job
|
|
62
|
-
job.should_receive :setup
|
|
104
|
+
expect(job).to receive(:setup)
|
|
63
105
|
worker.setup
|
|
64
106
|
end
|
|
107
|
+
|
|
108
|
+
context 'and job#setup raises an exception' do
|
|
109
|
+
|
|
110
|
+
before :each do
|
|
111
|
+
allow(job).to receive(:setup).and_raise(Exception)
|
|
112
|
+
allow(logger).to receive(:warn)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'handles the exception' do
|
|
116
|
+
expect { worker.setup }.not_to raise_error(Exception)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'calls the error_handler in the rescue block' do
|
|
120
|
+
expect(worker.output).to receive(:write).with(Worker::UNCAUGHT_ERROR)
|
|
121
|
+
worker.setup
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
end
|
|
65
125
|
end
|
|
66
126
|
|
|
67
127
|
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
9
|
+
config.run_all_when_everything_filtered = true
|
|
10
|
+
config.filter_run :focus
|
|
11
|
+
|
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
14
|
+
# the seed, which is printed after each run.
|
|
15
|
+
# --seed 1234
|
|
16
|
+
config.order = 'random'
|
|
17
|
+
end
|
metadata
CHANGED
|
@@ -1,55 +1,54 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sisyphus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rasmus Bang Grouleff
|
|
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: bundler
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - ~>
|
|
16
|
+
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
18
|
version: '1.3'
|
|
20
19
|
type: :development
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- - ~>
|
|
23
|
+
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: '1.3'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: rake
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
|
-
- - ~>
|
|
30
|
+
- - "~>"
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
32
|
version: '10.0'
|
|
34
33
|
type: :development
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
|
-
- - ~>
|
|
37
|
+
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '10.0'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
41
|
name: rspec
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
44
43
|
requirements:
|
|
45
|
-
- - ~>
|
|
44
|
+
- - "~>"
|
|
46
45
|
- !ruby/object:Gem::Version
|
|
47
46
|
version: '2.14'
|
|
48
47
|
type: :development
|
|
49
48
|
prerelease: false
|
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
50
|
requirements:
|
|
52
|
-
- - ~>
|
|
51
|
+
- - "~>"
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: '2.14'
|
|
55
54
|
description: A tiny library for spawning worker processes
|
|
@@ -59,7 +58,8 @@ executables: []
|
|
|
59
58
|
extensions: []
|
|
60
59
|
extra_rdoc_files: []
|
|
61
60
|
files:
|
|
62
|
-
- .gitignore
|
|
61
|
+
- ".gitignore"
|
|
62
|
+
- ".rspec"
|
|
63
63
|
- Gemfile
|
|
64
64
|
- Gemfile.lock
|
|
65
65
|
- LICENSE.txt
|
|
@@ -73,38 +73,40 @@ files:
|
|
|
73
73
|
- lib/sisyphus/simple_execution_strategy.rb
|
|
74
74
|
- lib/sisyphus/sleep.rb
|
|
75
75
|
- lib/sisyphus/worker.rb
|
|
76
|
+
- lib/sisyphus/worker_pool.rb
|
|
76
77
|
- lib/version.rb
|
|
77
78
|
- sisyphus.gemspec
|
|
78
79
|
- spec/sisyphus/forking_execution_strategy_spec.rb
|
|
79
80
|
- spec/sisyphus/master_spec.rb
|
|
80
81
|
- spec/sisyphus/simple_execution_strategy_spec.rb
|
|
82
|
+
- spec/sisyphus/worker_pool_spec.rb
|
|
81
83
|
- spec/sisyphus/worker_spec.rb
|
|
84
|
+
- spec/spec_helper.rb
|
|
82
85
|
homepage: https://github.com/rbgrouleff/sisyphus
|
|
83
86
|
licenses:
|
|
84
87
|
- Apache License 2.0
|
|
85
88
|
metadata: {}
|
|
86
|
-
post_install_message:
|
|
87
89
|
rdoc_options: []
|
|
88
90
|
require_paths:
|
|
89
91
|
- lib
|
|
90
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
93
|
requirements:
|
|
92
|
-
- -
|
|
94
|
+
- - ">="
|
|
93
95
|
- !ruby/object:Gem::Version
|
|
94
|
-
version:
|
|
96
|
+
version: 1.9.3
|
|
95
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
98
|
requirements:
|
|
97
|
-
- -
|
|
99
|
+
- - ">="
|
|
98
100
|
- !ruby/object:Gem::Version
|
|
99
101
|
version: '0'
|
|
100
102
|
requirements: []
|
|
101
|
-
|
|
102
|
-
rubygems_version: 2.1.5
|
|
103
|
-
signing_key:
|
|
103
|
+
rubygems_version: 3.7.2
|
|
104
104
|
specification_version: 4
|
|
105
105
|
summary: A tiny library for spawning worker processes
|
|
106
106
|
test_files:
|
|
107
107
|
- spec/sisyphus/forking_execution_strategy_spec.rb
|
|
108
108
|
- spec/sisyphus/master_spec.rb
|
|
109
109
|
- spec/sisyphus/simple_execution_strategy_spec.rb
|
|
110
|
+
- spec/sisyphus/worker_pool_spec.rb
|
|
110
111
|
- spec/sisyphus/worker_spec.rb
|
|
112
|
+
- spec/spec_helper.rb
|