delayed_job_worker_pool 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -2
- data/README.md +4 -2
- data/lib/delayed_job_worker_pool/version.rb +1 -1
- data/lib/delayed_job_worker_pool/worker_pool.rb +67 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b57aa82422ba29a08e43da4d5f3b46e5841d2ea0
|
4
|
+
data.tar.gz: 342039b94feef2d751f62470642ce7b584b6f7e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfed1c0e3a15bed2524aebad47ff861a1130617397aa64ae4fa533d106926c1f3dd0f02a3654576a97d06430c5909ec612d8ea4151d312872bda5b8be706ad40
|
7
|
+
data.tar.gz: 9cd73cb2cc25a036c14559f3e1bb61e76262d09346fe1f2713c6a05b656fe95db6e3064bad9eaecdaa4e110b57119c1a71e6b36885f81f1710966a3aa0a53718
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -8,9 +8,11 @@
|
|
8
8
|
[travis]: http://travis-ci.org/salsify/delayed_job_worker_pool
|
9
9
|
[codeclimate]: https://codeclimate.com/github/salsify/delayed_job_worker_pool
|
10
10
|
|
11
|
-
[Delayed Job's](https://github.com/collectiveidea/delayed_job) built-in worker pooling daemonizes all worker processes. This is great for certain environments but not so great for environments like Heroku that really want your processes to run in the foreground. Delayed Job Worker Pool runs a pool of Delayed Job workers **without** daemonizing them.
|
11
|
+
[Delayed Job's](https://github.com/collectiveidea/delayed_job) built-in worker pooling daemonizes all worker processes. This is great for certain environments but not so great for environments like Heroku that really want your processes to run in the foreground. Delayed Job Worker Pool runs a pool of Delayed Job workers **without** daemonizing them.
|
12
|
+
|
13
|
+
[Salsify](http://salsify.com) is currently using Delayed Job Worker Pool to run multiple Delayed Job workers on a single Heroku PX dyno. Read more about our experience using this gem on our [blog](http://blog.salsify.com/engineering/delayed-job-worker-pooling).
|
12
14
|
|
13
|
-
This gem only works with MRI on Linux/MacOS X
|
15
|
+
**This gem only works with MRI on Linux/MacOS X.**
|
14
16
|
|
15
17
|
## Installation
|
16
18
|
|
@@ -1,8 +1,13 @@
|
|
1
1
|
module DelayedJobWorkerPool
|
2
2
|
class WorkerPool
|
3
|
+
SIGNALS = ['TERM', 'INT'].map(&:freeze).freeze
|
4
|
+
|
3
5
|
def initialize(options = {})
|
4
6
|
@options = options
|
5
7
|
@worker_pids = []
|
8
|
+
@pending_signals = []
|
9
|
+
@pending_signal_read_pipe, @pending_signal_write_pipe = create_pipe(inheritable: false)
|
10
|
+
@master_alive_read_pipe, @master_alive_write_pipe = create_pipe(inheritable: true)
|
6
11
|
self.shutting_down = false
|
7
12
|
end
|
8
13
|
|
@@ -18,7 +23,6 @@ module DelayedJobWorkerPool
|
|
18
23
|
|
19
24
|
log_uninheritable_threads
|
20
25
|
|
21
|
-
create_master_alive_pipe
|
22
26
|
num_workers.times { fork_worker }
|
23
27
|
|
24
28
|
monitor_workers
|
@@ -31,16 +35,22 @@ module DelayedJobWorkerPool
|
|
31
35
|
|
32
36
|
private
|
33
37
|
|
34
|
-
attr_reader :options, :worker_pids, :master_alive_read_pipe, :master_alive_write_pipe
|
38
|
+
attr_reader :options, :worker_pids, :master_alive_read_pipe, :master_alive_write_pipe,
|
39
|
+
:pending_signals, :pending_signal_read_pipe, :pending_signal_write_pipe
|
35
40
|
attr_accessor :shutting_down
|
36
41
|
|
37
42
|
def install_signal_handlers
|
38
|
-
|
39
|
-
|
43
|
+
SIGNALS.each do |signal|
|
44
|
+
trap(signal) do
|
45
|
+
pending_signals << signal
|
46
|
+
pending_signal_write_pipe.write_nonblock('.')
|
47
|
+
end
|
40
48
|
end
|
49
|
+
end
|
41
50
|
|
42
|
-
|
43
|
-
|
51
|
+
def uninstall_signal_handlers
|
52
|
+
SIGNALS.each do |signal|
|
53
|
+
trap(signal, 'DEFAULT')
|
44
54
|
end
|
45
55
|
end
|
46
56
|
|
@@ -55,10 +65,6 @@ module DelayedJobWorkerPool
|
|
55
65
|
end
|
56
66
|
end
|
57
67
|
|
58
|
-
def create_master_alive_pipe
|
59
|
-
@master_alive_read_pipe, @master_alive_write_pipe = IO.pipe
|
60
|
-
end
|
61
|
-
|
62
68
|
def load_app
|
63
69
|
DelayedJobWorkerPool::Application.load
|
64
70
|
end
|
@@ -73,16 +79,32 @@ module DelayedJobWorkerPool
|
|
73
79
|
end
|
74
80
|
|
75
81
|
def monitor_workers
|
76
|
-
|
77
|
-
|
82
|
+
while has_workers?
|
83
|
+
if has_pending_signal?
|
84
|
+
shutdown(pending_signals.pop)
|
85
|
+
elsif (wait_result = Process.wait2(-1, Process::WNOHANG))
|
86
|
+
handle_dead_worker(wait_result.first, wait_result.last)
|
87
|
+
else
|
88
|
+
wait_for_signal(1)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
78
92
|
|
79
|
-
|
93
|
+
def handle_dead_worker(worker_pid, status)
|
94
|
+
return unless worker_pids.include?(worker_pid)
|
80
95
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
96
|
+
log("Worker #{worker_pid} exited with status #{status.to_i}")
|
97
|
+
worker_pids.delete(worker_pid)
|
98
|
+
invoke_callback(:after_worker_shutdown, worker_info(worker_pid))
|
99
|
+
fork_worker unless shutting_down
|
100
|
+
end
|
101
|
+
|
102
|
+
def has_workers?
|
103
|
+
!worker_pids.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_pending_signal?
|
107
|
+
!pending_signals.empty?
|
86
108
|
end
|
87
109
|
|
88
110
|
def invoke_callback(callback_name, *args)
|
@@ -99,6 +121,8 @@ module DelayedJobWorkerPool
|
|
99
121
|
def run_worker
|
100
122
|
master_alive_write_pipe.close
|
101
123
|
|
124
|
+
uninstall_signal_handlers
|
125
|
+
|
102
126
|
Thread.new do
|
103
127
|
IO.select([master_alive_read_pipe])
|
104
128
|
log('Detected dead master. Shutting down worker.')
|
@@ -135,6 +159,31 @@ module DelayedJobWorkerPool
|
|
135
159
|
options.except(:workers, :preload_app, *DelayedJobWorkerPool::DSL::CALLBACK_SETTINGS).merge(name: worker_name(worker_pid))
|
136
160
|
end
|
137
161
|
|
162
|
+
def create_pipe(inheritable: true)
|
163
|
+
read, write = IO.pipe
|
164
|
+
unless inheritable
|
165
|
+
make_file_descriptor_uninheritable(read)
|
166
|
+
make_file_descriptor_uninheritable(write)
|
167
|
+
end
|
168
|
+
[read, write]
|
169
|
+
end
|
170
|
+
|
171
|
+
def make_file_descriptor_uninheritable(io)
|
172
|
+
io.fcntl(Fcntl::F_SETFD)
|
173
|
+
end
|
174
|
+
|
175
|
+
def wait_for_signal(timeout)
|
176
|
+
if IO.select([pending_signal_read_pipe], [], [], timeout)
|
177
|
+
drain_pipe(pending_signal_read_pipe)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def drain_pipe(pipe)
|
182
|
+
loop { pipe.read_nonblock(16) }
|
183
|
+
rescue IO::WaitReadable
|
184
|
+
# We've drained the pipe
|
185
|
+
end
|
186
|
+
|
138
187
|
def log(message)
|
139
188
|
puts(message)
|
140
189
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job_worker_pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Turkel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: delayed_job
|