puma 5.1.1 → 5.3.1
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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +131 -10
- data/README.md +24 -2
- data/docs/architecture.md +22 -18
- data/docs/compile_options.md +6 -6
- data/docs/deployment.md +2 -2
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +2 -2
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +1 -1
- data/docs/stats.md +142 -0
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/extconf.rb +14 -0
- data/ext/puma_http11/http11_parser.c +19 -21
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +162 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/puma_http11.c +2 -2
- data/lib/puma.rb +34 -8
- data/lib/puma/binder.rb +50 -43
- data/lib/puma/client.rb +5 -3
- data/lib/puma/cluster.rb +40 -8
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +3 -3
- data/lib/puma/control_cli.rb +5 -1
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +56 -4
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +2 -3
- data/lib/puma/launcher.rb +4 -3
- data/lib/puma/minissl.rb +48 -17
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/reactor.rb +7 -2
- data/lib/puma/request.rb +9 -4
- data/lib/puma/runner.rb +8 -3
- data/lib/puma/server.rb +46 -112
- data/lib/puma/thread_pool.rb +4 -3
- data/lib/rack/handler/puma.rb +1 -0
- metadata +6 -3
@@ -182,8 +182,6 @@ static final int puma_parser_start = 1;
|
|
182
182
|
static final int puma_parser_first_final = 46;
|
183
183
|
static final int puma_parser_error = 0;
|
184
184
|
|
185
|
-
static final int puma_parser_en_main = 1;
|
186
|
-
|
187
185
|
|
188
186
|
// line 62 "ext/puma_http11/http11_parser.java.rl"
|
189
187
|
|
@@ -212,12 +210,12 @@ static final int puma_parser_en_main = 1;
|
|
212
210
|
cs = 0;
|
213
211
|
|
214
212
|
|
215
|
-
// line
|
213
|
+
// line 214 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
216
214
|
{
|
217
215
|
cs = puma_parser_start;
|
218
216
|
}
|
219
217
|
|
220
|
-
// line
|
218
|
+
// line 88 "ext/puma_http11/http11_parser.java.rl"
|
221
219
|
|
222
220
|
body_start = 0;
|
223
221
|
content_len = 0;
|
@@ -244,7 +242,7 @@ static final int puma_parser_en_main = 1;
|
|
244
242
|
parser.buffer = buffer;
|
245
243
|
|
246
244
|
|
247
|
-
// line
|
245
|
+
// line 246 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
248
246
|
{
|
249
247
|
int _klen;
|
250
248
|
int _trans = 0;
|
@@ -400,7 +398,7 @@ case 1:
|
|
400
398
|
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
|
401
399
|
}
|
402
400
|
break;
|
403
|
-
// line
|
401
|
+
// line 402 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
404
402
|
}
|
405
403
|
}
|
406
404
|
}
|
@@ -420,7 +418,7 @@ case 5:
|
|
420
418
|
break; }
|
421
419
|
}
|
422
420
|
|
423
|
-
// line
|
421
|
+
// line 114 "ext/puma_http11/http11_parser.java.rl"
|
424
422
|
|
425
423
|
parser.cs = cs;
|
426
424
|
parser.nread += (p - off);
|
@@ -41,8 +41,8 @@ static VALUE global_request_path;
|
|
41
41
|
|
42
42
|
/** Defines common length and error messages for input length validation. */
|
43
43
|
#define QUOTE(s) #s
|
44
|
-
#define
|
45
|
-
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the "
|
44
|
+
#define EXPLAIN_MAX_LENGTH_VALUE(s) QUOTE(s)
|
45
|
+
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
|
46
46
|
|
47
47
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
48
48
|
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
data/lib/puma.rb
CHANGED
@@ -19,6 +19,40 @@ module Puma
|
|
19
19
|
autoload :Server, 'puma/server'
|
20
20
|
autoload :Launcher, 'puma/launcher'
|
21
21
|
|
22
|
+
# at present, MiniSSL::Engine is only defined in extension code (puma_http11),
|
23
|
+
# not in minissl.rb
|
24
|
+
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
|
25
|
+
|
26
|
+
HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
|
27
|
+
|
28
|
+
if HAS_SSL
|
29
|
+
require 'puma/minissl'
|
30
|
+
else
|
31
|
+
module MiniSSL
|
32
|
+
# this class is defined so that it exists when Puma is compiled
|
33
|
+
# without ssl support, as Server and Reactor use it in rescue statements.
|
34
|
+
class SSLError < StandardError ; end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ssl?
|
39
|
+
HAS_SSL
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.abstract_unix_socket?
|
43
|
+
@abstract_unix ||=
|
44
|
+
if HAS_UNIX_SOCKET
|
45
|
+
begin
|
46
|
+
::UNIXServer.new("\0puma.temp.unix").close
|
47
|
+
true
|
48
|
+
rescue ArgumentError # darwin
|
49
|
+
false
|
50
|
+
end
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
22
56
|
# @!attribute [rw] stats_object=
|
23
57
|
def self.stats_object=(val)
|
24
58
|
@get_stats = val
|
@@ -40,12 +74,4 @@ module Puma
|
|
40
74
|
return unless Thread.current.respond_to?(:name=)
|
41
75
|
Thread.current.name = "puma #{name}"
|
42
76
|
end
|
43
|
-
|
44
|
-
unless HAS_SSL
|
45
|
-
module MiniSSL
|
46
|
-
# this class is defined so that it exists when Puma is compiled
|
47
|
-
# without ssl support, as Server and Reactor use it in rescue statements.
|
48
|
-
class SSLError < StandardError ; end
|
49
|
-
end
|
50
|
-
end
|
51
77
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -13,7 +13,7 @@ module Puma
|
|
13
13
|
require 'puma/minissl'
|
14
14
|
require 'puma/minissl/context_builder'
|
15
15
|
|
16
|
-
# Odd bug in 'pure Ruby' nio4r
|
16
|
+
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
17
|
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
18
|
# The bug was that it did not require openssl.
|
19
19
|
# @todo remove when Ruby 2.3 support is dropped
|
@@ -160,6 +160,7 @@ module Puma
|
|
160
160
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
161
161
|
logger.log "* Activated #{str}"
|
162
162
|
else
|
163
|
+
ios_len = @ios.length
|
163
164
|
params = Util.parse_query uri.query
|
164
165
|
|
165
166
|
opt = params.key?('low_latency')
|
@@ -167,14 +168,8 @@ module Puma
|
|
167
168
|
|
168
169
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
169
170
|
|
170
|
-
@ios.each do |i|
|
171
|
-
|
172
|
-
addr = if i.local_address.ipv6?
|
173
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
174
|
-
else
|
175
|
-
i.local_address.ip_unpack.join(':')
|
176
|
-
end
|
177
|
-
|
171
|
+
@ios[ios_len..-1].each do |i|
|
172
|
+
addr = loc_addr_str i
|
178
173
|
logger.log "* #{log_msg} on http://#{addr}"
|
179
174
|
end
|
180
175
|
end
|
@@ -182,11 +177,19 @@ module Puma
|
|
182
177
|
@listeners << [str, io] if io
|
183
178
|
when "unix"
|
184
179
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
180
|
+
abstract = false
|
181
|
+
if str.start_with? 'unix://@'
|
182
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
183
|
+
abstract = true
|
184
|
+
path = "@#{path}"
|
185
|
+
end
|
185
186
|
|
186
187
|
if fd = @inherited_fds.delete(str)
|
188
|
+
@unix_paths << path unless abstract
|
187
189
|
io = inherit_unix_listener path, fd
|
188
190
|
logger.log "* Inherited #{str}"
|
189
191
|
elsif sock = @activated_sockets.delete([ :unix, path ])
|
192
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
190
193
|
io = inherit_unix_listener path, sock
|
191
194
|
logger.log "* Activated #{str}"
|
192
195
|
else
|
@@ -210,6 +213,7 @@ module Puma
|
|
210
213
|
end
|
211
214
|
end
|
212
215
|
|
216
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
213
217
|
io = add_unix_listener path, umask, mode, backlog
|
214
218
|
logger.log "* #{log_msg} on #{str}"
|
215
219
|
end
|
@@ -229,8 +233,13 @@ module Puma
|
|
229
233
|
io = inherit_ssl_listener sock, ctx
|
230
234
|
logger.log "* Activated #{str}"
|
231
235
|
else
|
236
|
+
ios_len = @ios.length
|
232
237
|
io = add_ssl_listener uri.host, uri.port, ctx
|
233
|
-
|
238
|
+
|
239
|
+
@ios[ios_len..-1].each do |i|
|
240
|
+
addr = loc_addr_str i
|
241
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
242
|
+
end
|
234
243
|
end
|
235
244
|
|
236
245
|
@listeners << [str, io] if io
|
@@ -258,14 +267,18 @@ module Puma
|
|
258
267
|
end
|
259
268
|
|
260
269
|
# Also close any unused activated sockets
|
261
|
-
@activated_sockets.
|
262
|
-
|
263
|
-
|
264
|
-
sock.
|
265
|
-
|
270
|
+
unless @activated_sockets.empty?
|
271
|
+
fds = @ios.map(&:to_i)
|
272
|
+
@activated_sockets.each do |key, sock|
|
273
|
+
next if fds.include? sock.to_i
|
274
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
275
|
+
begin
|
276
|
+
sock.close
|
277
|
+
rescue SystemCallError
|
278
|
+
end
|
279
|
+
# We have to unlink a unix socket path that's not being used
|
280
|
+
File.unlink key[1] if key.first == :unix
|
266
281
|
end
|
267
|
-
# We have to unlink a unix socket path that's not being used
|
268
|
-
File.unlink key[1] if key[0] == :unix
|
269
282
|
end
|
270
283
|
end
|
271
284
|
|
@@ -297,11 +310,7 @@ module Puma
|
|
297
310
|
end
|
298
311
|
|
299
312
|
def inherit_tcp_listener(host, port, fd)
|
300
|
-
|
301
|
-
s = fd
|
302
|
-
else
|
303
|
-
s = TCPServer.for_fd(fd)
|
304
|
-
end
|
313
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
305
314
|
|
306
315
|
@ios << s
|
307
316
|
s
|
@@ -339,11 +348,8 @@ module Puma
|
|
339
348
|
def inherit_ssl_listener(fd, ctx)
|
340
349
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
341
350
|
|
342
|
-
|
343
|
-
|
344
|
-
else
|
345
|
-
s = TCPServer.for_fd(fd)
|
346
|
-
end
|
351
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
352
|
+
|
347
353
|
ssl = MiniSSL::Server.new(s, ctx)
|
348
354
|
|
349
355
|
env = @proto_env.dup
|
@@ -358,8 +364,6 @@ module Puma
|
|
358
364
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
359
365
|
#
|
360
366
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
361
|
-
@unix_paths << path unless File.exist? path
|
362
|
-
|
363
367
|
# Let anyone connect by default
|
364
368
|
umask ||= 0
|
365
369
|
|
@@ -376,8 +380,7 @@ module Puma
|
|
376
380
|
raise "There is already a server bound to: #{path}"
|
377
381
|
end
|
378
382
|
end
|
379
|
-
|
380
|
-
s = UNIXServer.new(path)
|
383
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
381
384
|
s.listen backlog
|
382
385
|
@ios << s
|
383
386
|
ensure
|
@@ -396,13 +399,8 @@ module Puma
|
|
396
399
|
end
|
397
400
|
|
398
401
|
def inherit_unix_listener(path, fd)
|
399
|
-
|
402
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
400
403
|
|
401
|
-
if fd.kind_of? TCPServer
|
402
|
-
s = fd
|
403
|
-
else
|
404
|
-
s = UNIXServer.for_fd fd
|
405
|
-
end
|
406
404
|
@ios << s
|
407
405
|
|
408
406
|
env = @proto_env.dup
|
@@ -413,24 +411,24 @@ module Puma
|
|
413
411
|
end
|
414
412
|
|
415
413
|
def close_listeners
|
416
|
-
listeners.each do |l, io|
|
417
|
-
io.close unless io.closed?
|
418
|
-
uri = URI.parse
|
414
|
+
@listeners.each do |l, io|
|
415
|
+
io.close unless io.closed?
|
416
|
+
uri = URI.parse l
|
419
417
|
next unless uri.scheme == 'unix'
|
420
418
|
unix_path = "#{uri.host}#{uri.path}"
|
421
|
-
File.unlink unix_path if unix_paths.include? unix_path
|
419
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
422
420
|
end
|
423
421
|
end
|
424
422
|
|
425
423
|
def redirects_for_restart
|
426
|
-
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
424
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
427
425
|
redirects[:close_others] = true
|
428
426
|
redirects
|
429
427
|
end
|
430
428
|
|
431
429
|
# @version 5.0.0
|
432
430
|
def redirects_for_restart_env
|
433
|
-
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
431
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
434
432
|
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
435
433
|
end
|
436
434
|
end
|
@@ -444,6 +442,15 @@ module Puma
|
|
444
442
|
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
445
443
|
end
|
446
444
|
|
445
|
+
def loc_addr_str(io)
|
446
|
+
loc_addr = io.to_io.local_address
|
447
|
+
if loc_addr.ipv6?
|
448
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
449
|
+
else
|
450
|
+
loc_addr.ip_unpack.join(':')
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
447
454
|
# @version 5.0.0
|
448
455
|
def socket_activation_fd(int)
|
449
456
|
int + 3 # 3 is the magic number you add to follow the SA protocol
|
data/lib/puma/client.rb
CHANGED
@@ -126,7 +126,7 @@ module Puma
|
|
126
126
|
@parsed_bytes = 0
|
127
127
|
@ready = false
|
128
128
|
@body_remain = 0
|
129
|
-
@peerip = nil
|
129
|
+
@peerip = nil if @remote_addr_header
|
130
130
|
@in_last_chunk = false
|
131
131
|
|
132
132
|
if @buffer
|
@@ -295,6 +295,7 @@ module Puma
|
|
295
295
|
|
296
296
|
if remain > MAX_BODY
|
297
297
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
298
|
+
@body.unlink
|
298
299
|
@body.binmode
|
299
300
|
@tempfile = @body
|
300
301
|
else
|
@@ -374,7 +375,7 @@ module Puma
|
|
374
375
|
end
|
375
376
|
|
376
377
|
if decode_chunk(chunk)
|
377
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
378
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
378
379
|
return true
|
379
380
|
end
|
380
381
|
end
|
@@ -386,12 +387,13 @@ module Puma
|
|
386
387
|
@prev_chunk = ""
|
387
388
|
|
388
389
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
390
|
+
@body.unlink
|
389
391
|
@body.binmode
|
390
392
|
@tempfile = @body
|
391
393
|
@chunked_content_length = 0
|
392
394
|
|
393
395
|
if decode_chunk(body)
|
394
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
396
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
395
397
|
return true
|
396
398
|
end
|
397
399
|
end
|
data/lib/puma/cluster.rb
CHANGED
@@ -43,6 +43,7 @@ module Puma
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def start_phased_restart
|
46
|
+
@events.fire_on_restart!
|
46
47
|
@phase += 1
|
47
48
|
log "- Starting phased worker restart, phase: #{@phase}"
|
48
49
|
|
@@ -317,7 +318,7 @@ module Puma
|
|
317
318
|
|
318
319
|
stop_workers
|
319
320
|
stop
|
320
|
-
|
321
|
+
@events.fire_on_stopped!
|
321
322
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
322
323
|
exit 0 # Clean exit, workers were stopped
|
323
324
|
end
|
@@ -332,16 +333,22 @@ module Puma
|
|
332
333
|
# This is aligned with the output from Runner, see Runner#output_header
|
333
334
|
log "* Workers: #{@options[:workers]}"
|
334
335
|
|
335
|
-
# Threads explicitly marked as fork safe will be ignored.
|
336
|
-
# Used in Rails, but may be used by anyone.
|
337
|
-
before = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
338
|
-
|
339
336
|
if preload?
|
337
|
+
# Threads explicitly marked as fork safe will be ignored. Used in Rails,
|
338
|
+
# but may be used by anyone. Note that we need to explicit
|
339
|
+
# Process::Waiter check here because there's a bug in Ruby 2.6 and below
|
340
|
+
# where calling thread_variable_get on a Process::Waiter will segfault.
|
341
|
+
# We can drop that clause once those versions of Ruby are no longer
|
342
|
+
# supported.
|
343
|
+
fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
|
344
|
+
|
345
|
+
before = Thread.list.reject(&fork_safe)
|
346
|
+
|
340
347
|
log "* Restarts: (\u2714) hot (\u2716) phased"
|
341
348
|
log "* Preloading application"
|
342
349
|
load_and_bind
|
343
350
|
|
344
|
-
after = Thread.list.reject
|
351
|
+
after = Thread.list.reject(&fork_safe)
|
345
352
|
|
346
353
|
if after.size > before.size
|
347
354
|
threads = (after - before)
|
@@ -382,6 +389,8 @@ module Puma
|
|
382
389
|
|
383
390
|
log "Use Ctrl-C to stop"
|
384
391
|
|
392
|
+
single_worker_warning
|
393
|
+
|
385
394
|
redirect_io
|
386
395
|
|
387
396
|
Plugins.fire_background
|
@@ -403,12 +412,16 @@ module Puma
|
|
403
412
|
|
404
413
|
begin
|
405
414
|
booted = false
|
415
|
+
in_phased_restart = false
|
416
|
+
workers_not_booted = @options[:workers]
|
406
417
|
|
407
418
|
while @status == :run
|
408
419
|
begin
|
409
420
|
if @phased_restart
|
410
421
|
start_phased_restart
|
411
422
|
@phased_restart = false
|
423
|
+
in_phased_restart = true
|
424
|
+
workers_not_booted = @options[:workers]
|
412
425
|
end
|
413
426
|
|
414
427
|
check_workers
|
@@ -434,8 +447,9 @@ module Puma
|
|
434
447
|
case req
|
435
448
|
when "b"
|
436
449
|
w.boot!
|
437
|
-
log "- Worker #{w.index} (PID: #{pid}) booted, phase: #{w.phase}"
|
450
|
+
log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
|
438
451
|
@next_check = Time.now
|
452
|
+
workers_not_booted -= 1
|
439
453
|
when "e"
|
440
454
|
# external term, see worker method, Signal.trap "SIGTERM"
|
441
455
|
w.instance_variable_set :@term, true
|
@@ -453,6 +467,10 @@ module Puma
|
|
453
467
|
log "! Out-of-sync worker list, no #{pid} worker"
|
454
468
|
end
|
455
469
|
end
|
470
|
+
if in_phased_restart && workers_not_booted.zero?
|
471
|
+
@events.fire_on_booted!
|
472
|
+
in_phased_restart = false
|
473
|
+
end
|
456
474
|
|
457
475
|
rescue Interrupt
|
458
476
|
@status = :stop
|
@@ -470,6 +488,15 @@ module Puma
|
|
470
488
|
|
471
489
|
private
|
472
490
|
|
491
|
+
def single_worker_warning
|
492
|
+
return if @options[:workers] != 1 || @options[:silence_single_worker_warning]
|
493
|
+
|
494
|
+
log "! WARNING: Detected running cluster mode with 1 worker."
|
495
|
+
log "! Running Puma in cluster mode with a single worker is often a misconfiguration."
|
496
|
+
log "! Consider running Puma in single-mode (workers = 0) in order to reduce memory overhead."
|
497
|
+
log "! Set the `silence_single_worker_warning` option to silence this warning message."
|
498
|
+
end
|
499
|
+
|
473
500
|
# loops thru @workers, removing workers that exited, and calling
|
474
501
|
# `#term` if needed
|
475
502
|
def wait_workers
|
@@ -499,7 +526,12 @@ module Puma
|
|
499
526
|
def timeout_workers
|
500
527
|
@workers.each do |w|
|
501
528
|
if !w.term? && w.ping_timeout <= Time.now
|
502
|
-
|
529
|
+
details = if w.booted?
|
530
|
+
"(worker failed to check in within #{@options[:worker_timeout]} seconds)"
|
531
|
+
else
|
532
|
+
"(worker failed to boot within #{@options[:worker_boot_timeout]} seconds)"
|
533
|
+
end
|
534
|
+
log "! Terminating timed out worker #{details}: #{w.pid}"
|
503
535
|
w.kill
|
504
536
|
end
|
505
537
|
end
|