puma 3.6.0 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/{History.txt → History.md} +293 -79
- data/README.md +143 -227
- data/docs/architecture.md +36 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +124 -22
- data/ext/puma_http11/extconf.rb +2 -0
- data/ext/puma_http11/http11_parser.c +85 -84
- data/ext/puma_http11/http11_parser.h +1 -0
- data/ext/puma_http11/http11_parser.rl +10 -9
- data/ext/puma_http11/io_buffer.c +7 -7
- data/ext/puma_http11/mini_ssl.c +62 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
- data/ext/puma_http11/puma_http11.c +1 -0
- data/lib/puma.rb +13 -5
- data/lib/puma/app/status.rb +8 -0
- data/lib/puma/binder.rb +21 -14
- data/lib/puma/cli.rb +49 -33
- data/lib/puma/client.rb +39 -4
- data/lib/puma/cluster.rb +51 -11
- data/lib/puma/commonlogger.rb +19 -20
- data/lib/puma/compat.rb +3 -7
- data/lib/puma/configuration.rb +133 -130
- data/lib/puma/const.rb +13 -37
- data/lib/puma/control_cli.rb +38 -35
- data/lib/puma/convenient.rb +3 -3
- data/lib/puma/detect.rb +3 -1
- data/lib/puma/dsl.rb +80 -58
- data/lib/puma/events.rb +6 -8
- data/lib/puma/io_buffer.rb +1 -1
- data/lib/puma/jruby_restart.rb +0 -1
- data/lib/puma/launcher.rb +52 -30
- data/lib/puma/minissl.rb +73 -4
- data/lib/puma/null_io.rb +6 -13
- data/lib/puma/plugin/tmp_restart.rb +1 -2
- data/lib/puma/rack/builder.rb +3 -0
- data/lib/puma/rack/urlmap.rb +9 -8
- data/lib/puma/reactor.rb +135 -0
- data/lib/puma/runner.rb +23 -1
- data/lib/puma/server.rb +117 -34
- data/lib/puma/single.rb +14 -3
- data/lib/puma/thread_pool.rb +67 -20
- data/lib/puma/util.rb +1 -5
- data/lib/rack/handler/puma.rb +58 -17
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +9 -2
- data/tools/jungle/init.d/puma +32 -62
- data/tools/jungle/init.d/run-puma +5 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -1
- metadata +22 -92
- data/Gemfile +0 -13
- data/Manifest.txt +0 -77
- data/Rakefile +0 -158
- data/lib/puma/rack/backports/uri/common_18.rb +0 -59
- data/lib/puma/rack/backports/uri/common_192.rb +0 -55
- data/puma.gemspec +0 -52
data/lib/puma/app/status.rb
CHANGED
@@ -55,6 +55,14 @@ module Puma
|
|
55
55
|
return rack_response(200, OK_STATUS)
|
56
56
|
end
|
57
57
|
|
58
|
+
when /\/gc$/
|
59
|
+
GC.start
|
60
|
+
return rack_response(200, OK_STATUS)
|
61
|
+
|
62
|
+
when /\/gc-stats$/
|
63
|
+
json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
|
64
|
+
return rack_response(200, json)
|
65
|
+
|
58
66
|
when /\/stats$/
|
59
67
|
return rack_response(200, @cli.stats)
|
60
68
|
else
|
data/lib/puma/binder.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'puma/const'
|
2
1
|
require 'uri'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
require 'puma/const'
|
5
|
+
require 'puma/util'
|
3
6
|
|
4
7
|
module Puma
|
5
8
|
class Binder
|
@@ -102,7 +105,7 @@ module Puma
|
|
102
105
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
103
106
|
end
|
104
107
|
|
105
|
-
@listeners << [str, io]
|
108
|
+
@listeners << [str, io] if io
|
106
109
|
when "unix"
|
107
110
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
108
111
|
|
@@ -117,7 +120,7 @@ module Puma
|
|
117
120
|
|
118
121
|
umask = nil
|
119
122
|
mode = nil
|
120
|
-
backlog =
|
123
|
+
backlog = 1024
|
121
124
|
|
122
125
|
if uri.query
|
123
126
|
params = Util.parse_query uri.query
|
@@ -140,11 +143,11 @@ module Puma
|
|
140
143
|
|
141
144
|
@listeners << [str, io]
|
142
145
|
when "ssl"
|
143
|
-
MiniSSL.check
|
144
|
-
|
145
146
|
params = Util.parse_query uri.query
|
146
147
|
require 'puma/minissl'
|
147
148
|
|
149
|
+
MiniSSL.check
|
150
|
+
|
148
151
|
ctx = MiniSSL::Context.new
|
149
152
|
|
150
153
|
if defined?(JRUBY_VERSION)
|
@@ -159,6 +162,7 @@ module Puma
|
|
159
162
|
end
|
160
163
|
|
161
164
|
ctx.keystore_pass = params['keystore-pass']
|
165
|
+
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
162
166
|
else
|
163
167
|
unless params['key']
|
164
168
|
@events.error "Please specify the SSL key via 'key='"
|
@@ -179,6 +183,7 @@ module Puma
|
|
179
183
|
end
|
180
184
|
|
181
185
|
ctx.ca = params['ca'] if params['ca']
|
186
|
+
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
182
187
|
end
|
183
188
|
|
184
189
|
if params['verify_mode']
|
@@ -206,7 +211,7 @@ module Puma
|
|
206
211
|
io = add_ssl_listener uri.host, uri.port, ctx
|
207
212
|
end
|
208
213
|
|
209
|
-
@listeners << [str, io]
|
214
|
+
@listeners << [str, io] if io
|
210
215
|
else
|
211
216
|
logger.error "Invalid URI: #{str}"
|
212
217
|
end
|
@@ -242,9 +247,10 @@ module Puma
|
|
242
247
|
end
|
243
248
|
end
|
244
249
|
|
245
|
-
def
|
246
|
-
|
247
|
-
|
250
|
+
def loopback_addresses
|
251
|
+
Socket.ip_address_list.select do |addrinfo|
|
252
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
253
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
248
254
|
end
|
249
255
|
|
250
256
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -256,7 +262,7 @@ module Puma
|
|
256
262
|
#
|
257
263
|
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
258
264
|
if host == "localhost"
|
259
|
-
|
265
|
+
loopback_addresses.each do |addr|
|
260
266
|
add_tcp_listener addr, port, optimize_for_latency, backlog
|
261
267
|
end
|
262
268
|
return
|
@@ -295,8 +301,8 @@ module Puma
|
|
295
301
|
MiniSSL.check
|
296
302
|
|
297
303
|
if host == "localhost"
|
298
|
-
|
299
|
-
add_ssl_listener addr, port, optimize_for_latency, backlog
|
304
|
+
loopback_addresses.each do |addr|
|
305
|
+
add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
|
300
306
|
end
|
301
307
|
return
|
302
308
|
end
|
@@ -309,6 +315,7 @@ module Puma
|
|
309
315
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
310
316
|
s.listen backlog
|
311
317
|
|
318
|
+
|
312
319
|
ssl = MiniSSL::Server.new s, ctx
|
313
320
|
env = @proto_env.dup
|
314
321
|
env[HTTPS_KEY] = HTTPS
|
@@ -340,7 +347,7 @@ module Puma
|
|
340
347
|
|
341
348
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
342
349
|
#
|
343
|
-
def add_unix_listener(path, umask=nil, mode=nil, backlog=
|
350
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
344
351
|
@unix_paths << path
|
345
352
|
|
346
353
|
# Let anyone connect by default
|
@@ -361,7 +368,7 @@ module Puma
|
|
361
368
|
end
|
362
369
|
|
363
370
|
s = UNIXServer.new(path)
|
364
|
-
s.listen backlog
|
371
|
+
s.listen backlog
|
365
372
|
@ios << s
|
366
373
|
ensure
|
367
374
|
File.umask old_mask
|
data/lib/puma/cli.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'uri'
|
3
3
|
|
4
|
+
require 'puma'
|
5
|
+
require 'puma/configuration'
|
4
6
|
require 'puma/launcher'
|
7
|
+
require 'puma/const'
|
8
|
+
require 'puma/events'
|
5
9
|
|
6
10
|
module Puma
|
7
11
|
class << self
|
@@ -44,21 +48,21 @@ module Puma
|
|
44
48
|
@parser.parse! @argv
|
45
49
|
|
46
50
|
if file = @argv.shift
|
47
|
-
@conf.configure do |
|
48
|
-
|
51
|
+
@conf.configure do |user_config, file_config|
|
52
|
+
file_config.rackup file
|
49
53
|
end
|
50
54
|
end
|
51
55
|
rescue UnsupportedOption
|
52
56
|
exit 1
|
53
57
|
end
|
54
58
|
|
55
|
-
@conf.configure do |
|
59
|
+
@conf.configure do |user_config, file_config|
|
56
60
|
if @stdout || @stderr
|
57
|
-
|
61
|
+
user_config.stdout_redirect @stdout, @stderr, @append
|
58
62
|
end
|
59
63
|
|
60
64
|
if @control_url
|
61
|
-
|
65
|
+
user_config.activate_control_app @control_url, @control_options
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
@@ -80,27 +84,35 @@ module Puma
|
|
80
84
|
raise UnsupportedOption
|
81
85
|
end
|
82
86
|
|
87
|
+
def configure_control_url(command_line_arg)
|
88
|
+
if command_line_arg
|
89
|
+
@control_url = command_line_arg
|
90
|
+
elsif Puma.jruby?
|
91
|
+
unsupported "No default url available on JRuby"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
83
95
|
# Build the OptionParser object to handle the available options.
|
84
96
|
#
|
85
97
|
|
86
98
|
def setup_options
|
87
|
-
@conf = Configuration.new do |
|
99
|
+
@conf = Configuration.new do |user_config, file_config|
|
88
100
|
@parser = OptionParser.new do |o|
|
89
101
|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
90
|
-
|
102
|
+
user_config.bind arg
|
91
103
|
end
|
92
104
|
|
93
105
|
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
94
|
-
|
106
|
+
file_config.load arg
|
95
107
|
end
|
96
108
|
|
97
|
-
o.on "--control URL", "The bind url to use for the control server"
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
109
|
+
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
110
|
+
configure_control_url(arg)
|
111
|
+
end
|
112
|
+
|
113
|
+
# alias --control-url for backwards-compatibility
|
114
|
+
o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
|
115
|
+
configure_control_url(arg)
|
104
116
|
end
|
105
117
|
|
106
118
|
o.on "--control-token TOKEN",
|
@@ -109,21 +121,21 @@ module Puma
|
|
109
121
|
end
|
110
122
|
|
111
123
|
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
112
|
-
|
113
|
-
|
124
|
+
user_config.daemonize
|
125
|
+
user_config.quiet
|
114
126
|
end
|
115
127
|
|
116
128
|
o.on "--debug", "Log lowlevel debugging information" do
|
117
|
-
|
129
|
+
user_config.debug
|
118
130
|
end
|
119
131
|
|
120
132
|
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
121
|
-
|
133
|
+
user_config.directory d
|
122
134
|
end
|
123
135
|
|
124
136
|
o.on "-e", "--environment ENVIRONMENT",
|
125
137
|
"The environment to run the Rack app on (default development)" do |arg|
|
126
|
-
|
138
|
+
user_config.environment arg
|
127
139
|
end
|
128
140
|
|
129
141
|
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
@@ -132,50 +144,54 @@ module Puma
|
|
132
144
|
|
133
145
|
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
134
146
|
"Use -b for more advanced options" do |arg|
|
135
|
-
|
147
|
+
user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
136
148
|
end
|
137
149
|
|
138
150
|
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
139
|
-
|
151
|
+
user_config.pidfile arg
|
140
152
|
end
|
141
153
|
|
142
154
|
o.on "--preload", "Preload the app. Cluster mode only" do
|
143
|
-
|
155
|
+
user_config.preload_app!
|
144
156
|
end
|
145
157
|
|
146
158
|
o.on "--prune-bundler", "Prune out the bundler env if possible" do
|
147
|
-
|
159
|
+
user_config.prune_bundler
|
148
160
|
end
|
149
161
|
|
150
162
|
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
151
|
-
|
163
|
+
user_config.quiet
|
152
164
|
end
|
153
165
|
|
154
166
|
o.on "-v", "--log-requests", "Log requests as they occur" do
|
155
|
-
|
167
|
+
user_config.log_requests
|
156
168
|
end
|
157
169
|
|
158
170
|
o.on "-R", "--restart-cmd CMD",
|
159
171
|
"The puma command to run during a hot restart",
|
160
172
|
"Default: inferred" do |cmd|
|
161
|
-
|
173
|
+
user_config.restart_command cmd
|
162
174
|
end
|
163
175
|
|
164
176
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
165
|
-
|
177
|
+
user_config.state_path arg
|
166
178
|
end
|
167
179
|
|
168
180
|
o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
|
169
181
|
min, max = arg.split(":")
|
170
182
|
if max
|
171
|
-
|
183
|
+
user_config.threads min, max
|
172
184
|
else
|
173
|
-
|
185
|
+
user_config.threads min, min
|
174
186
|
end
|
175
187
|
end
|
176
188
|
|
177
189
|
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
178
|
-
|
190
|
+
user_config.tcp_mode!
|
191
|
+
end
|
192
|
+
|
193
|
+
o.on "--early-hints", "Enable early hints support" do
|
194
|
+
user_config.early_hints
|
179
195
|
end
|
180
196
|
|
181
197
|
o.on "-V", "--version", "Print the version information" do
|
@@ -185,11 +201,11 @@ module Puma
|
|
185
201
|
|
186
202
|
o.on "-w", "--workers COUNT",
|
187
203
|
"Activate cluster mode: How many worker processes to create" do |arg|
|
188
|
-
|
204
|
+
user_config.workers arg
|
189
205
|
end
|
190
206
|
|
191
207
|
o.on "--tag NAME", "Additional text to display in process listing" do |arg|
|
192
|
-
|
208
|
+
user_config.tag arg
|
193
209
|
end
|
194
210
|
|
195
211
|
o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
|
data/lib/puma/client.rb
CHANGED
@@ -21,6 +21,17 @@ module Puma
|
|
21
21
|
|
22
22
|
class ConnectionError < RuntimeError; end
|
23
23
|
|
24
|
+
# An instance of this class represents a unique request from a client.
|
25
|
+
# For example a web request from a browser or from CURL. This
|
26
|
+
#
|
27
|
+
# An instance of `Puma::Client` can be used as if it were an IO object
|
28
|
+
# for example it is passed into `IO.select` inside of the `Puma::Reactor`.
|
29
|
+
# This is accomplished by the `to_io` method which gets called on any
|
30
|
+
# non-IO objects being used with the IO api such as `IO.select.
|
31
|
+
#
|
32
|
+
# Instances of this class are responsible for knowing if
|
33
|
+
# the header and body are fully buffered via the `try_to_finish` method.
|
34
|
+
# They can be used to "time out" a response via the `timeout_at` reader.
|
24
35
|
class Client
|
25
36
|
include Puma::Const
|
26
37
|
extend Puma::Delegation
|
@@ -111,6 +122,7 @@ module Puma
|
|
111
122
|
begin
|
112
123
|
@io.close
|
113
124
|
rescue IOError
|
125
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
114
126
|
end
|
115
127
|
end
|
116
128
|
|
@@ -155,7 +167,8 @@ module Puma
|
|
155
167
|
len = line.strip.to_i(16)
|
156
168
|
if len == 0
|
157
169
|
@body.rewind
|
158
|
-
|
170
|
+
rest = io.read
|
171
|
+
@buffer = rest.empty? ? nil : rest
|
159
172
|
@requests_served += 1
|
160
173
|
@ready = true
|
161
174
|
return true
|
@@ -215,14 +228,20 @@ module Puma
|
|
215
228
|
end
|
216
229
|
|
217
230
|
def setup_body
|
218
|
-
@
|
231
|
+
if @env[HTTP_EXPECT] == CONTINUE
|
232
|
+
# TODO allow a hook here to check the headers before
|
233
|
+
# going forward
|
234
|
+
@io << HTTP_11_100
|
235
|
+
@io.flush
|
236
|
+
end
|
237
|
+
|
219
238
|
@read_header = false
|
220
239
|
|
221
240
|
body = @parser.body
|
222
241
|
|
223
242
|
te = @env[TRANSFER_ENCODING2]
|
224
243
|
|
225
|
-
if te ==
|
244
|
+
if te && CHUNKED.casecmp(te) == 0
|
226
245
|
return setup_chunked_body(body)
|
227
246
|
end
|
228
247
|
|
@@ -276,6 +295,14 @@ module Puma
|
|
276
295
|
raise ConnectionError, "Connection error detected during read"
|
277
296
|
end
|
278
297
|
|
298
|
+
# No data means a closed socket
|
299
|
+
unless data
|
300
|
+
@buffer = nil
|
301
|
+
@requests_served += 1
|
302
|
+
@ready = true
|
303
|
+
raise EOFError
|
304
|
+
end
|
305
|
+
|
279
306
|
if @buffer
|
280
307
|
@buffer << data
|
281
308
|
else
|
@@ -290,7 +317,7 @@ module Puma
|
|
290
317
|
raise HttpParserError,
|
291
318
|
"HEADER is longer than allowed, aborting client early."
|
292
319
|
end
|
293
|
-
|
320
|
+
|
294
321
|
false
|
295
322
|
end
|
296
323
|
|
@@ -305,6 +332,14 @@ module Puma
|
|
305
332
|
raise e
|
306
333
|
end
|
307
334
|
|
335
|
+
# No data means a closed socket
|
336
|
+
unless data
|
337
|
+
@buffer = nil
|
338
|
+
@requests_served += 1
|
339
|
+
@ready = true
|
340
|
+
raise EOFError
|
341
|
+
end
|
342
|
+
|
308
343
|
if @buffer
|
309
344
|
@buffer << data
|
310
345
|
else
|
data/lib/puma/cluster.rb
CHANGED
@@ -1,8 +1,24 @@
|
|
1
1
|
require 'puma/runner'
|
2
|
+
require 'puma/util'
|
3
|
+
require 'puma/plugin'
|
4
|
+
|
2
5
|
require 'time'
|
3
6
|
|
4
7
|
module Puma
|
8
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
9
|
+
# to boot and serve a Ruby application when puma "workers" are needed
|
10
|
+
# i.e. when using multi-processes. For example `$ puma -w 5`
|
11
|
+
#
|
12
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
13
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
14
|
+
# that this inherits from.
|
15
|
+
#
|
16
|
+
# An instance of this class will spawn the number of processes passed in
|
17
|
+
# via the `spawn_workers` method call. Each worker will have it's own
|
18
|
+
# instance of a `Puma::Server`.
|
5
19
|
class Cluster < Runner
|
20
|
+
WORKER_CHECK_INTERVAL = 5
|
21
|
+
|
6
22
|
def initialize(cli, events)
|
7
23
|
super cli, events
|
8
24
|
|
@@ -19,7 +35,7 @@ module Puma
|
|
19
35
|
@workers.each { |x| x.term }
|
20
36
|
|
21
37
|
begin
|
22
|
-
Process.
|
38
|
+
@workers.each { |w| Process.waitpid(w.pid) }
|
23
39
|
rescue Interrupt
|
24
40
|
log "! Cancelled waiting for workers"
|
25
41
|
end
|
@@ -110,6 +126,7 @@ module Puma
|
|
110
126
|
|
111
127
|
def spawn_workers
|
112
128
|
diff = @options[:workers] - @workers.size
|
129
|
+
return if diff < 1
|
113
130
|
|
114
131
|
master = Process.pid
|
115
132
|
|
@@ -135,6 +152,21 @@ module Puma
|
|
135
152
|
end
|
136
153
|
end
|
137
154
|
|
155
|
+
def cull_workers
|
156
|
+
diff = @workers.size - @options[:workers]
|
157
|
+
return if diff < 1
|
158
|
+
|
159
|
+
debug "Culling #{diff.inspect} workers"
|
160
|
+
|
161
|
+
workers_to_cull = @workers[-diff,diff]
|
162
|
+
debug "Workers to cull: #{workers_to_cull.inspect}"
|
163
|
+
|
164
|
+
workers_to_cull.each do |worker|
|
165
|
+
log "- Worker #{worker.index} (pid: #{worker.pid}) terminating"
|
166
|
+
worker.term
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
138
170
|
def next_worker_index
|
139
171
|
all_positions = 0...@options[:workers]
|
140
172
|
occupied_positions = @workers.map { |w| w.index }
|
@@ -149,7 +181,7 @@ module Puma
|
|
149
181
|
def check_workers(force=false)
|
150
182
|
return if !force && @next_check && @next_check >= Time.now
|
151
183
|
|
152
|
-
@next_check = Time.now +
|
184
|
+
@next_check = Time.now + WORKER_CHECK_INTERVAL
|
153
185
|
|
154
186
|
any = false
|
155
187
|
|
@@ -175,6 +207,7 @@ module Puma
|
|
175
207
|
|
176
208
|
@workers.delete_if(&:dead?)
|
177
209
|
|
210
|
+
cull_workers
|
178
211
|
spawn_workers
|
179
212
|
|
180
213
|
if all_workers_booted?
|
@@ -202,12 +235,13 @@ module Puma
|
|
202
235
|
begin
|
203
236
|
@wakeup.write "!" unless @wakeup.closed?
|
204
237
|
rescue SystemCallError, IOError
|
238
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
205
239
|
end
|
206
240
|
end
|
207
241
|
|
208
242
|
def worker(index, master)
|
209
|
-
title
|
210
|
-
title
|
243
|
+
title = "puma: cluster worker #{index}: #{master}"
|
244
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
211
245
|
$0 = title
|
212
246
|
|
213
247
|
Signal.trap "SIGINT", "IGNORE"
|
@@ -245,6 +279,7 @@ module Puma
|
|
245
279
|
begin
|
246
280
|
@worker_write << "b#{Process.pid}\n"
|
247
281
|
rescue SystemCallError, IOError
|
282
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
248
283
|
STDERR.puts "Master seems to have exited, exiting."
|
249
284
|
return
|
250
285
|
end
|
@@ -253,13 +288,16 @@ module Puma
|
|
253
288
|
base_payload = "p#{Process.pid}"
|
254
289
|
|
255
290
|
while true
|
256
|
-
sleep
|
291
|
+
sleep WORKER_CHECK_INTERVAL
|
257
292
|
begin
|
258
|
-
b = server.backlog
|
259
|
-
r = server.running
|
260
|
-
|
293
|
+
b = server.backlog || 0
|
294
|
+
r = server.running || 0
|
295
|
+
t = server.pool_capacity || 0
|
296
|
+
m = server.max_threads || 0
|
297
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
261
298
|
io << payload
|
262
299
|
rescue IOError
|
300
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
263
301
|
break
|
264
302
|
end
|
265
303
|
end
|
@@ -315,7 +353,7 @@ module Puma
|
|
315
353
|
def stats
|
316
354
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
317
355
|
booted_worker_count = @workers.count { |w| w.booted? }
|
318
|
-
worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
356
|
+
worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
319
357
|
%Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
|
320
358
|
end
|
321
359
|
|
@@ -337,7 +375,6 @@ module Puma
|
|
337
375
|
|
338
376
|
Signal.trap "TTOU" do
|
339
377
|
@options[:workers] -= 1 if @options[:workers] >= 2
|
340
|
-
@workers.last.term
|
341
378
|
wakeup!
|
342
379
|
end
|
343
380
|
|
@@ -351,7 +388,10 @@ module Puma
|
|
351
388
|
log "Early termination of worker"
|
352
389
|
exit! 0
|
353
390
|
else
|
391
|
+
stop_workers
|
354
392
|
stop
|
393
|
+
|
394
|
+
raise SignalException, "SIGTERM"
|
355
395
|
end
|
356
396
|
end
|
357
397
|
end
|
@@ -445,7 +485,7 @@ module Puma
|
|
445
485
|
|
446
486
|
force_check = false
|
447
487
|
|
448
|
-
res = IO.select([read], nil, nil,
|
488
|
+
res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
|
449
489
|
|
450
490
|
if res
|
451
491
|
req = read.read_nonblock(1)
|