sisyphus 0.2.3 → 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/Gemfile.lock +1 -1
- data/README.md +5 -2
- data/Rakefile +3 -1
- data/lib/sisyphus/forking_execution_strategy.rb +52 -0
- data/lib/sisyphus/master.rb +44 -49
- data/lib/sisyphus/simple_execution_strategy.rb +17 -0
- data/lib/sisyphus/sleep.rb +1 -1
- data/lib/sisyphus/worker.rb +47 -33
- data/lib/sisyphus/worker_pool.rb +36 -0
- data/lib/sisyphus.rb +1 -1
- data/lib/version.rb +1 -1
- data/sisyphus.gemspec +2 -0
- data/spec/sisyphus/forking_execution_strategy_spec.rb +98 -0
- data/spec/sisyphus/master_spec.rb +57 -137
- data/spec/sisyphus/simple_execution_strategy_spec.rb +28 -0
- data/spec/sisyphus/worker_pool_spec.rb +82 -0
- data/spec/sisyphus/worker_spec.rb +84 -82
- data/spec/spec_helper.rb +17 -0
- metadata +25 -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
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -32,6 +32,10 @@ Getting started
|
|
|
32
32
|
master or workers encounter. The logger should quack like a
|
|
33
33
|
`Logger` instance from the Ruby stdlib. Exceptions are logged with the
|
|
34
34
|
`Logger::WARN` level.
|
|
35
|
+
* `:execution_strategy` which can either be `Sisyphus::SimpleExecutionStrategy` or
|
|
36
|
+
`Sisyphus::ForkingExecutionStrategy`. This is the strategy used by
|
|
37
|
+
workers when performing the job. The default is
|
|
38
|
+
`Sisyphus::ForkingExecutionStrategy`.
|
|
35
39
|
4. You can start workers by doing one of the following things:
|
|
36
40
|
* Send the `start` message to the master, if the `options` hash was
|
|
37
41
|
provided. This starts a run loop which monitors workers and
|
|
@@ -63,8 +67,6 @@ Things missing
|
|
|
63
67
|
Sisyphus is still very much in its infancy, though the ambition isn't to build a [Resque] [resque] clone, but
|
|
64
68
|
instead build as small a tool with as few features as possible.
|
|
65
69
|
|
|
66
|
-
[resque]: https://github.com/resque/resque
|
|
67
|
-
|
|
68
70
|
There are, however, still features that are missing:
|
|
69
71
|
|
|
70
72
|
- Force killing workers
|
|
@@ -76,6 +78,7 @@ There are, however, still features that are missing:
|
|
|
76
78
|
- Some sort of reaping of worker processes
|
|
77
79
|
- Documentation
|
|
78
80
|
|
|
81
|
+
[resque]: https://github.com/resque/resque
|
|
79
82
|
[unicorn]: http://unicorn.bogomips.org/
|
|
80
83
|
|
|
81
84
|
Contributing
|
data/Rakefile
CHANGED
|
@@ -5,8 +5,10 @@ task :sleeper do
|
|
|
5
5
|
gem 'sisyphus'
|
|
6
6
|
require 'sisyphus'
|
|
7
7
|
require 'sisyphus/sleep'
|
|
8
|
+
require 'logger'
|
|
8
9
|
|
|
10
|
+
logger = Logger.new(STDOUT)
|
|
9
11
|
job = Sisyphus::Sleep.new
|
|
10
|
-
master = Sisyphus::Master.new job, workers: 2
|
|
12
|
+
master = Sisyphus::Master.new job, workers: 2, logger: logger
|
|
11
13
|
master.start
|
|
12
14
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Sisyphus
|
|
2
|
+
class ForkingExecutionStrategy
|
|
3
|
+
|
|
4
|
+
def execute(job, error_handler = ->(process_name, error) {})
|
|
5
|
+
if @child_pid = fork
|
|
6
|
+
ChildProcess.new(@child_pid).success?
|
|
7
|
+
else
|
|
8
|
+
perform job, error_handler
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def perform(job, error_handler)
|
|
15
|
+
self.process_name = "Child of worker #{::Process.ppid}"
|
|
16
|
+
begin
|
|
17
|
+
job.perform
|
|
18
|
+
exit! 0
|
|
19
|
+
rescue ::Exception => e
|
|
20
|
+
error_handler.call process_name, e
|
|
21
|
+
exit! 1
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def process_name
|
|
26
|
+
$0
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def process_name=(name)
|
|
30
|
+
$0 = name
|
|
31
|
+
end
|
|
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
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/sisyphus/master.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'timeout'
|
|
2
2
|
require_relative './worker'
|
|
3
|
+
require_relative './forking_execution_strategy'
|
|
3
4
|
require_relative './null_logger'
|
|
5
|
+
require_relative './worker_pool'
|
|
4
6
|
|
|
5
7
|
module Sisyphus
|
|
6
8
|
class Master
|
|
@@ -8,12 +10,15 @@ module Sisyphus
|
|
|
8
10
|
|
|
9
11
|
HANDLED_SIGNALS = [:INT, :TTIN, :TTOU]
|
|
10
12
|
|
|
11
|
-
attr_reader :logger
|
|
13
|
+
attr_reader :logger, :job, :number_of_workers, :execution_strategy
|
|
12
14
|
|
|
13
15
|
def initialize(job, options = {})
|
|
14
|
-
|
|
15
|
-
@logger = options.fetch
|
|
16
|
-
@
|
|
16
|
+
self.number_of_workers = options.fetch :workers, 0
|
|
17
|
+
@logger = options.fetch(:logger) { NullLogger.new }
|
|
18
|
+
@execution_strategy = options.fetch(:execution_strategy) { ForkingExecutionStrategy.new }
|
|
19
|
+
|
|
20
|
+
@worker_pool = options.fetch(:worker_pool) { WorkerPool.new self }
|
|
21
|
+
|
|
17
22
|
@job = job
|
|
18
23
|
|
|
19
24
|
self_reader, self_writer = IO.pipe
|
|
@@ -24,60 +29,54 @@ module Sisyphus
|
|
|
24
29
|
|
|
25
30
|
def start
|
|
26
31
|
trap_signals
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
number_of_workers.times do
|
|
33
|
+
@worker_pool.spawn_worker
|
|
29
34
|
sleep rand(1000).fdiv(1000)
|
|
30
35
|
end
|
|
31
36
|
puts "Sisyphus::Master started with PID: #{Process.pid}"
|
|
32
37
|
watch_for_output
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
if wpid = fork
|
|
38
|
-
writer.close
|
|
39
|
-
@workers << { pid: wpid, reader: reader }
|
|
40
|
-
else
|
|
41
|
-
reader.close
|
|
42
|
-
self.process_name = "Worker #{Process.pid}"
|
|
43
|
-
begin
|
|
44
|
-
worker = Worker.new(@job, writer, logger)
|
|
45
|
-
worker.setup
|
|
46
|
-
worker.start
|
|
47
|
-
rescue Exception => e
|
|
48
|
-
writer.write Worker::UNCAUGHT_ERROR
|
|
49
|
-
logger.warn(process_name) { e }
|
|
50
|
-
exit! 0
|
|
51
|
-
end
|
|
52
|
-
end
|
|
40
|
+
def create_worker
|
|
41
|
+
Worker.new(job, execution_strategy, logger)
|
|
53
42
|
end
|
|
54
43
|
|
|
55
44
|
def stop_worker(wpid)
|
|
56
|
-
if
|
|
45
|
+
if workers.find { |w| w.fetch(:pid) == wpid }
|
|
57
46
|
Process.kill 'INT', wpid rescue Errno::ESRCH # Ignore if the process is already gone
|
|
58
47
|
end
|
|
59
48
|
end
|
|
60
49
|
|
|
61
50
|
def stop_all
|
|
62
|
-
|
|
51
|
+
workers.each do |worker|
|
|
63
52
|
stop_worker worker.fetch(:pid)
|
|
64
53
|
end
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
begin
|
|
55
|
+
Timeout.timeout(30) do
|
|
56
|
+
watch_for_shutdown while worker_count > 0
|
|
57
|
+
end
|
|
58
|
+
rescue Timeout::Error => e
|
|
59
|
+
p "Timeout reached:", e
|
|
67
60
|
end
|
|
68
61
|
end
|
|
69
62
|
|
|
70
63
|
def worker_count
|
|
71
|
-
|
|
64
|
+
workers.length
|
|
72
65
|
end
|
|
73
66
|
|
|
74
67
|
private
|
|
75
68
|
|
|
69
|
+
attr_writer :number_of_workers
|
|
70
|
+
|
|
71
|
+
def workers
|
|
72
|
+
@worker_pool.workers
|
|
73
|
+
end
|
|
74
|
+
|
|
76
75
|
def watch_for_shutdown
|
|
77
76
|
wpid, _ = Process.wait2
|
|
78
|
-
worker =
|
|
77
|
+
worker = workers.find { |w| w.fetch(:pid) == wpid }
|
|
79
78
|
worker.fetch(:reader).close
|
|
80
|
-
|
|
79
|
+
workers.delete worker
|
|
81
80
|
wpid
|
|
82
81
|
rescue Errno::ECHILD
|
|
83
82
|
end
|
|
@@ -93,7 +92,7 @@ module Sisyphus
|
|
|
93
92
|
end
|
|
94
93
|
|
|
95
94
|
def process_signal_queue
|
|
96
|
-
handle_signal(
|
|
95
|
+
handle_signal(signal_queue.shift) until signal_queue.empty?
|
|
97
96
|
end
|
|
98
97
|
|
|
99
98
|
def process_pipes(pipes)
|
|
@@ -107,26 +106,26 @@ module Sisyphus
|
|
|
107
106
|
|
|
108
107
|
def process_output(pipes)
|
|
109
108
|
pipes.each do |pipe|
|
|
110
|
-
|
|
109
|
+
respawn_worker worker_pid(pipe) unless stopping?
|
|
111
110
|
end
|
|
112
111
|
end
|
|
113
112
|
|
|
114
|
-
def
|
|
115
|
-
|
|
113
|
+
def respawn_worker(wpid)
|
|
114
|
+
@worker_pool.spawn_worker
|
|
116
115
|
stop_worker wpid
|
|
117
116
|
watch_for_shutdown
|
|
118
117
|
end
|
|
119
118
|
|
|
120
119
|
def worker_pipes
|
|
121
120
|
if worker_count > 0
|
|
122
|
-
|
|
121
|
+
workers.map { |w| w.fetch(:reader) }
|
|
123
122
|
else
|
|
124
123
|
[]
|
|
125
124
|
end
|
|
126
125
|
end
|
|
127
126
|
|
|
128
127
|
def worker_pid(reader)
|
|
129
|
-
if worker =
|
|
128
|
+
if worker = workers.find { |w| w.fetch(:reader).fileno == reader.fileno }
|
|
130
129
|
worker.fetch(:pid)
|
|
131
130
|
else
|
|
132
131
|
raise 'Unknown worker pipe'
|
|
@@ -142,7 +141,7 @@ module Sisyphus
|
|
|
142
141
|
end
|
|
143
142
|
|
|
144
143
|
def queue_signal(signal)
|
|
145
|
-
|
|
144
|
+
signal_queue << signal
|
|
146
145
|
@selfpipe[:writer].write_nonblock('.')
|
|
147
146
|
rescue Errno::EAGAIN
|
|
148
147
|
# Ignore
|
|
@@ -171,14 +170,14 @@ module Sisyphus
|
|
|
171
170
|
end
|
|
172
171
|
|
|
173
172
|
def handle_ttin
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
self.number_of_workers += 1
|
|
174
|
+
@worker_pool.spawn_worker
|
|
176
175
|
end
|
|
177
176
|
|
|
178
177
|
def handle_ttou
|
|
179
|
-
if
|
|
180
|
-
|
|
181
|
-
stop_worker(
|
|
178
|
+
if number_of_workers > 0
|
|
179
|
+
self.number_of_workers -= 1
|
|
180
|
+
stop_worker(workers.first.fetch(:pid))
|
|
182
181
|
end
|
|
183
182
|
end
|
|
184
183
|
|
|
@@ -190,12 +189,8 @@ module Sisyphus
|
|
|
190
189
|
@stopping
|
|
191
190
|
end
|
|
192
191
|
|
|
193
|
-
def
|
|
194
|
-
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def process_name
|
|
198
|
-
$0
|
|
192
|
+
def signal_queue
|
|
193
|
+
Thread.main[:signal_queue]
|
|
199
194
|
end
|
|
200
195
|
end
|
|
201
196
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Sisyphus
|
|
2
|
+
class SimpleExecutionStrategy
|
|
3
|
+
|
|
4
|
+
def execute(job, error_handler = ->(name, error) {})
|
|
5
|
+
job.perform
|
|
6
|
+
rescue Exception => e
|
|
7
|
+
error_handler.call process_name, e
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def process_name
|
|
13
|
+
$0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/sisyphus/sleep.rb
CHANGED
data/lib/sisyphus/worker.rb
CHANGED
|
@@ -2,49 +2,74 @@ module Sisyphus
|
|
|
2
2
|
class Worker
|
|
3
3
|
UNCAUGHT_ERROR = '.'
|
|
4
4
|
|
|
5
|
-
attr_reader :logger
|
|
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
|
+
@execution_strategy = execution_strategy
|
|
10
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
|
|
27
36
|
|
|
28
|
-
private
|
|
29
|
-
|
|
30
37
|
def perform_job
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
execution_strategy.execute job, error_handler
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error_handler
|
|
42
|
+
-> (name, error) {
|
|
43
|
+
return if stopped?
|
|
33
44
|
begin
|
|
34
|
-
|
|
45
|
+
logger.warn(name) { error }
|
|
46
|
+
output.write UNCAUGHT_ERROR
|
|
35
47
|
rescue Errno::EAGAIN, Errno::EINTR
|
|
36
48
|
# Ignore
|
|
37
49
|
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
}
|
|
51
|
+
end
|
|
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
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def set_up?
|
|
68
|
+
@set_up
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def setup_done
|
|
72
|
+
@set_up = true
|
|
48
73
|
end
|
|
49
74
|
|
|
50
75
|
def trap_signals
|
|
@@ -53,20 +78,9 @@ module Sisyphus
|
|
|
53
78
|
end
|
|
54
79
|
end
|
|
55
80
|
|
|
56
|
-
def stop
|
|
57
|
-
@stopped = true
|
|
58
|
-
end
|
|
59
|
-
|
|
60
81
|
def stopped?
|
|
61
82
|
@stopped
|
|
62
83
|
end
|
|
63
84
|
|
|
64
|
-
def process_name=(name)
|
|
65
|
-
$0 = name
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def process_name
|
|
69
|
-
$0
|
|
70
|
-
end
|
|
71
85
|
end
|
|
72
86
|
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"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require_relative '../../lib/sisyphus/forking_execution_strategy'
|
|
3
|
+
|
|
4
|
+
module Sisyphus
|
|
5
|
+
describe ForkingExecutionStrategy do
|
|
6
|
+
|
|
7
|
+
let(:job) { double :job }
|
|
8
|
+
let(:error_handler) { ->(name, raised_error) {} }
|
|
9
|
+
let(:strategy) { ForkingExecutionStrategy.new }
|
|
10
|
+
let(:child_pid) { 1 }
|
|
11
|
+
let(:status) { double :process_status }
|
|
12
|
+
|
|
13
|
+
it 'forks on execution' do
|
|
14
|
+
allow(Process).to receive(:waitpid2).with(child_pid) { [child_pid, status] }
|
|
15
|
+
allow(status).to receive(:success?) { true }
|
|
16
|
+
expect(strategy).to receive(:fork) { child_pid }
|
|
17
|
+
strategy.execute job
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'in the parent process' do
|
|
21
|
+
|
|
22
|
+
before :each do
|
|
23
|
+
allow(strategy).to receive(:fork) { child_pid }
|
|
24
|
+
allow(status).to receive(:success?) { true }
|
|
25
|
+
end
|
|
26
|
+
|
|
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)
|
|
30
|
+
end
|
|
31
|
+
|
|
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)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'in the execution process' do
|
|
40
|
+
|
|
41
|
+
let(:ppid) { 2 }
|
|
42
|
+
|
|
43
|
+
before :each do
|
|
44
|
+
allow(strategy).to receive(:fork) { nil }
|
|
45
|
+
allow(job).to receive(:perform)
|
|
46
|
+
allow(strategy).to receive(:exit!)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'updates the process name' do
|
|
50
|
+
allow(Process).to receive(:ppid) { ppid }
|
|
51
|
+
expect(strategy).to receive(:process_name=).with("Child of worker #{ppid}")
|
|
52
|
+
strategy.execute job
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'performs the job' do
|
|
56
|
+
expect(job).to receive(:perform)
|
|
57
|
+
strategy.execute job
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'exits with a 0 status if job is performed without failing' do
|
|
61
|
+
expect(strategy).to receive(:exit!).with(0)
|
|
62
|
+
strategy.execute job
|
|
63
|
+
end
|
|
64
|
+
|
|
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" }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when the job#perform fails' do
|
|
71
|
+
|
|
72
|
+
let(:process_name) { "foobarbaz" }
|
|
73
|
+
let(:exception) { Exception.new("foo") }
|
|
74
|
+
|
|
75
|
+
before :each do
|
|
76
|
+
allow(strategy).to receive(:process_name) { process_name }
|
|
77
|
+
allow(job).to receive(:perform).and_raise(exception)
|
|
78
|
+
end
|
|
79
|
+
|
|
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
|
|
83
|
+
end
|
|
84
|
+
|
|
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
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|