puma 6.6.1 → 7.0.4
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 +4 -4
- data/History.md +130 -4
- data/README.md +11 -24
- data/docs/fork_worker.md +5 -5
- data/docs/kubernetes.md +6 -4
- data/docs/restart.md +2 -2
- data/docs/signals.md +9 -9
- data/docs/stats.md +2 -1
- data/ext/puma_http11/extconf.rb +2 -17
- data/ext/puma_http11/mini_ssl.c +18 -8
- data/ext/puma_http11/org/jruby/puma/Http11.java +9 -1
- data/ext/puma_http11/puma_http11.c +23 -11
- data/lib/puma/binder.rb +10 -8
- data/lib/puma/cli.rb +3 -5
- data/lib/puma/client.rb +31 -31
- data/lib/puma/cluster/worker.rb +9 -10
- data/lib/puma/cluster/worker_handle.rb +36 -5
- data/lib/puma/cluster.rb +12 -13
- data/lib/puma/cluster_accept_loop_delay.rb +92 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +88 -43
- data/lib/puma/const.rb +9 -10
- data/lib/puma/control_cli.rb +6 -2
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +108 -81
- data/lib/puma/error_logger.rb +3 -1
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +28 -29
- data/lib/puma/minissl.rb +0 -1
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -4
- data/lib/puma/request.rb +33 -24
- data/lib/puma/runner.rb +8 -17
- data/lib/puma/server.rb +114 -68
- data/lib/puma/single.rb +4 -1
- data/lib/puma/thread_pool.rb +37 -81
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +2 -2
- metadata +3 -2
data/lib/puma/cli.rb
CHANGED
@@ -39,10 +39,8 @@ module Puma
|
|
39
39
|
@control_url = nil
|
40
40
|
@control_options = {}
|
41
41
|
|
42
|
-
setup_options env
|
43
|
-
|
44
42
|
begin
|
45
|
-
|
43
|
+
setup_options env
|
46
44
|
|
47
45
|
if file = @argv.shift
|
48
46
|
@conf.configure do |user_config, file_config|
|
@@ -93,7 +91,7 @@ module Puma
|
|
93
91
|
#
|
94
92
|
|
95
93
|
def setup_options(env = ENV)
|
96
|
-
@conf = Configuration.new({}, {events: @events}, env) do |user_config, file_config|
|
94
|
+
@conf = Configuration.new({}, { events: @events }, env) do |user_config, file_config|
|
97
95
|
@parser = OptionParser.new do |o|
|
98
96
|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
99
97
|
user_config.bind arg
|
@@ -240,7 +238,7 @@ module Puma
|
|
240
238
|
$stdout.puts o
|
241
239
|
exit 0
|
242
240
|
end
|
243
|
-
end
|
241
|
+
end.parse! @argv
|
244
242
|
end
|
245
243
|
end
|
246
244
|
end
|
data/lib/puma/client.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class IO
|
4
|
-
# We need to use this for a jruby work around on both 1.8 and 1.9.
|
5
|
-
# So this either creates the constant (on 1.8), or harmlessly
|
6
|
-
# reopens it (on 1.9).
|
7
|
-
module WaitReadable
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
3
|
require_relative 'detect'
|
12
4
|
require_relative 'io_buffer'
|
13
5
|
require 'tempfile'
|
@@ -64,6 +56,11 @@ module Puma
|
|
64
56
|
|
65
57
|
TE_ERR_MSG = 'Invalid Transfer-Encoding'
|
66
58
|
|
59
|
+
# See:
|
60
|
+
# https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.1.1
|
61
|
+
# https://httpwg.org/specs/rfc9112.html#rfc.section.6.1
|
62
|
+
STRIP_OWS = /\A[ \t]+|[ \t]+\z/
|
63
|
+
|
67
64
|
# The object used for a request with no body. All requests with
|
68
65
|
# no body share this one object since it has no state.
|
69
66
|
EmptyBody = NullIO.new
|
@@ -111,7 +108,8 @@ module Puma
|
|
111
108
|
end
|
112
109
|
|
113
110
|
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
114
|
-
:tempfile, :io_buffer, :http_content_length_limit_exceeded
|
111
|
+
:tempfile, :io_buffer, :http_content_length_limit_exceeded,
|
112
|
+
:requests_served
|
115
113
|
|
116
114
|
attr_writer :peerip, :http_content_length_limit
|
117
115
|
|
@@ -133,9 +131,9 @@ module Puma
|
|
133
131
|
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
|
134
132
|
end
|
135
133
|
|
136
|
-
# For the hijack protocol
|
137
|
-
#
|
138
|
-
def
|
134
|
+
# For the full hijack protocol, `env['rack.hijack']` is set to
|
135
|
+
# `client.method :full_hijack`
|
136
|
+
def full_hijack
|
139
137
|
@hijacked = true
|
140
138
|
env[HIJACK_IO] ||= @io
|
141
139
|
end
|
@@ -150,11 +148,12 @@ module Puma
|
|
150
148
|
end
|
151
149
|
|
152
150
|
# Number of seconds until the timeout elapses.
|
151
|
+
# @!attribute [r] timeout
|
153
152
|
def timeout
|
154
153
|
[@timeout_at - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0].max
|
155
154
|
end
|
156
155
|
|
157
|
-
def reset
|
156
|
+
def reset
|
158
157
|
@parser.reset
|
159
158
|
@io_buffer.reset
|
160
159
|
@read_header = true
|
@@ -166,7 +165,10 @@ module Puma
|
|
166
165
|
@peerip = nil if @remote_addr_header
|
167
166
|
@in_last_chunk = false
|
168
167
|
@http_content_length_limit_exceeded = false
|
168
|
+
end
|
169
169
|
|
170
|
+
# only used with back-to-back requests contained in the buffer
|
171
|
+
def process_back_to_back_requests
|
170
172
|
if @buffer
|
171
173
|
return false unless try_to_parse_proxy_protocol
|
172
174
|
|
@@ -178,25 +180,20 @@ module Puma
|
|
178
180
|
raise HttpParserError,
|
179
181
|
"HEADER is longer than allowed, aborting client early."
|
180
182
|
end
|
181
|
-
|
182
|
-
return false
|
183
|
-
else
|
184
|
-
begin
|
185
|
-
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
186
|
-
return try_to_finish
|
187
|
-
end
|
188
|
-
rescue IOError
|
189
|
-
# swallow it
|
190
|
-
end
|
191
183
|
end
|
192
184
|
end
|
193
185
|
|
186
|
+
# if a client sends back-to-back requests, the buffer may contain one or more
|
187
|
+
# of them.
|
188
|
+
def has_back_to_back_requests?
|
189
|
+
!(@buffer.nil? || @buffer.empty?)
|
190
|
+
end
|
191
|
+
|
194
192
|
def close
|
195
193
|
tempfile_close
|
196
194
|
begin
|
197
195
|
@io.close
|
198
196
|
rescue IOError, Errno::EBADF
|
199
|
-
Puma::Util.purge_interrupt_queue
|
200
197
|
end
|
201
198
|
end
|
202
199
|
|
@@ -291,8 +288,10 @@ module Puma
|
|
291
288
|
|
292
289
|
def eagerly_finish
|
293
290
|
return true if @ready
|
294
|
-
|
295
|
-
|
291
|
+
while @to_io.wait_readable(0) # rubocop: disable Style/WhileUntilModifier
|
292
|
+
return true if try_to_finish
|
293
|
+
end
|
294
|
+
false
|
296
295
|
end
|
297
296
|
|
298
297
|
def finish(timeout)
|
@@ -412,17 +411,18 @@ module Puma
|
|
412
411
|
if te
|
413
412
|
te_lwr = te.downcase
|
414
413
|
if te.include? ','
|
415
|
-
te_ary = te_lwr.split
|
414
|
+
te_ary = te_lwr.split(',').each { |te| te.gsub!(STRIP_OWS, "") }
|
416
415
|
te_count = te_ary.count CHUNKED
|
417
416
|
te_valid = te_ary[0..-2].all? { |e| ALLOWED_TRANSFER_ENCODING.include? e }
|
418
|
-
if
|
419
|
-
@env.delete TRANSFER_ENCODING2
|
420
|
-
return setup_chunked_body body
|
421
|
-
elsif te_count >= 1
|
417
|
+
if te_count > 1
|
422
418
|
raise HttpParserError , "#{TE_ERR_MSG}, multiple chunked: '#{te}'"
|
419
|
+
elsif te_ary.last != CHUNKED
|
420
|
+
raise HttpParserError , "#{TE_ERR_MSG}, last value must be chunked: '#{te}'"
|
423
421
|
elsif !te_valid
|
424
422
|
raise HttpParserError501, "#{TE_ERR_MSG}, unknown value: '#{te}'"
|
425
423
|
end
|
424
|
+
@env.delete TRANSFER_ENCODING2
|
425
|
+
return setup_chunked_body body
|
426
426
|
elsif te_lwr == CHUNKED
|
427
427
|
@env.delete TRANSFER_ENCODING2
|
428
428
|
return setup_chunked_body body
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -110,7 +110,6 @@ module Puma
|
|
110
110
|
begin
|
111
111
|
@worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
|
112
112
|
rescue SystemCallError, IOError
|
113
|
-
Puma::Util.purge_interrupt_queue
|
114
113
|
STDERR.puts "Master seems to have exited, exiting."
|
115
114
|
return
|
116
115
|
end
|
@@ -128,16 +127,16 @@ module Puma
|
|
128
127
|
|
129
128
|
while true
|
130
129
|
begin
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
io << payload
|
130
|
+
payload = base_payload.dup
|
131
|
+
|
132
|
+
hsh = server.stats
|
133
|
+
hsh.each do |k, v|
|
134
|
+
payload << %Q! "#{k}":#{v || 0},!
|
135
|
+
end
|
136
|
+
# sub call properly adds 'closing' string
|
137
|
+
io << payload.sub(/,\z/, " }\n")
|
138
|
+
server.reset_max
|
139
139
|
rescue IOError
|
140
|
-
Puma::Util.purge_interrupt_queue
|
141
140
|
break
|
142
141
|
end
|
143
142
|
sleep @options[:worker_check_interval]
|
@@ -4,13 +4,15 @@ module Puma
|
|
4
4
|
class Cluster < Runner
|
5
5
|
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
6
|
|
7
|
-
|
8
7
|
# This class represents a worker process from the perspective of the puma
|
9
8
|
# master process. It contains information about the process and its health
|
10
9
|
# and it exposes methods to control the process via IPC. It does not
|
11
10
|
# include the actual logic executed by the worker process itself. For that,
|
12
11
|
# see Puma::Cluster::Worker.
|
13
12
|
class WorkerHandle # :nodoc:
|
13
|
+
# array of stat 'max' keys
|
14
|
+
WORKER_MAX_KEYS = [:backlog_max, :reactor_max]
|
15
|
+
|
14
16
|
def initialize(idx, pid, phase, options)
|
15
17
|
@index = idx
|
16
18
|
@pid = pid
|
@@ -23,6 +25,7 @@ module Puma
|
|
23
25
|
@last_checkin = Time.now
|
24
26
|
@last_status = {}
|
25
27
|
@term = false
|
28
|
+
@worker_max = Array.new WORKER_MAX_KEYS.length, 0
|
26
29
|
end
|
27
30
|
|
28
31
|
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
@@ -51,12 +54,40 @@ module Puma
|
|
51
54
|
@term
|
52
55
|
end
|
53
56
|
|
54
|
-
STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*), "busy_threads":(?<busy_threads>\d*) }/
|
55
|
-
private_constant :STATUS_PATTERN
|
56
|
-
|
57
57
|
def ping!(status)
|
58
|
+
hsh = {}
|
59
|
+
k, v = nil, nil
|
60
|
+
status.tr('}{"', '').strip.split(", ") do |kv|
|
61
|
+
cntr = 0
|
62
|
+
kv.split(':') do |t|
|
63
|
+
if cntr == 0
|
64
|
+
k = t
|
65
|
+
cntr = 1
|
66
|
+
else
|
67
|
+
v = t
|
68
|
+
end
|
69
|
+
end
|
70
|
+
hsh[k.to_sym] = v.to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
# check stat max values, we can't signal workers to reset the max values,
|
74
|
+
# so we do so here
|
75
|
+
WORKER_MAX_KEYS.each_with_index do |key, idx|
|
76
|
+
next unless hsh[key]
|
77
|
+
|
78
|
+
if hsh[key] < @worker_max[idx]
|
79
|
+
hsh[key] = @worker_max[idx]
|
80
|
+
else
|
81
|
+
@worker_max[idx] = hsh[key]
|
82
|
+
end
|
83
|
+
end
|
58
84
|
@last_checkin = Time.now
|
59
|
-
@last_status =
|
85
|
+
@last_status = hsh
|
86
|
+
end
|
87
|
+
|
88
|
+
# Resets max values to zero. Called whenever `Cluster#stats` is called
|
89
|
+
def reset_max
|
90
|
+
WORKER_MAX_KEYS.length.times { |idx| @worker_max[idx] = 0 }
|
60
91
|
end
|
61
92
|
|
62
93
|
# @see Puma::Cluster#check_workers
|
data/lib/puma/cluster.rb
CHANGED
@@ -22,6 +22,7 @@ module Puma
|
|
22
22
|
@workers = []
|
23
23
|
@next_check = Time.now
|
24
24
|
|
25
|
+
@worker_max = [] # keeps track of 'max' stat values
|
25
26
|
@phased_restart = false
|
26
27
|
end
|
27
28
|
|
@@ -45,8 +46,7 @@ module Puma
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def start_phased_restart(refork = false)
|
48
|
-
@events.
|
49
|
-
|
49
|
+
@events.fire_before_restart!
|
50
50
|
@phase += 1
|
51
51
|
if refork
|
52
52
|
log "- Starting worker refork, phase: #{@phase}"
|
@@ -268,11 +268,14 @@ module Puma
|
|
268
268
|
end
|
269
269
|
|
270
270
|
# Inside of a child process, this will return all zeroes, as @workers is only populated in
|
271
|
-
# the master process.
|
271
|
+
# the master process. Calling this also resets stat 'max' values to zero.
|
272
272
|
# @!attribute [r] stats
|
273
|
+
# @return [Hash]
|
274
|
+
|
273
275
|
def stats
|
274
276
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
275
277
|
worker_status = @workers.map do |w|
|
278
|
+
w.reset_max
|
276
279
|
{
|
277
280
|
started_at: utc_iso8601(w.started_at),
|
278
281
|
pid: w.pid,
|
@@ -283,7 +286,6 @@ module Puma
|
|
283
286
|
last_status: w.last_status,
|
284
287
|
}
|
285
288
|
end
|
286
|
-
|
287
289
|
{
|
288
290
|
started_at: utc_iso8601(@started_at),
|
289
291
|
workers: @workers.size,
|
@@ -352,7 +354,7 @@ module Puma
|
|
352
354
|
|
353
355
|
stop_workers
|
354
356
|
stop
|
355
|
-
@events.
|
357
|
+
@events.fire_after_stopped!
|
356
358
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
357
359
|
exit 0 # Clean exit, workers were stopped
|
358
360
|
end
|
@@ -369,12 +371,8 @@ module Puma
|
|
369
371
|
|
370
372
|
if preload?
|
371
373
|
# Threads explicitly marked as fork safe will be ignored. Used in Rails,
|
372
|
-
# but may be used by anyone.
|
373
|
-
|
374
|
-
# where calling thread_variable_get on a Process::Waiter will segfault.
|
375
|
-
# We can drop that clause once those versions of Ruby are no longer
|
376
|
-
# supported.
|
377
|
-
fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
|
374
|
+
# but may be used by anyone.
|
375
|
+
fork_safe = ->(t) { t.thread_variable_get(:fork_safe) }
|
378
376
|
|
379
377
|
before = Thread.list.reject(&fork_safe)
|
380
378
|
|
@@ -423,6 +421,7 @@ module Puma
|
|
423
421
|
|
424
422
|
log "Use Ctrl-C to stop"
|
425
423
|
|
424
|
+
warn_ruby_mn_threads
|
426
425
|
single_worker_warning
|
427
426
|
|
428
427
|
redirect_io
|
@@ -511,7 +510,7 @@ module Puma
|
|
511
510
|
end
|
512
511
|
|
513
512
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
514
|
-
@events.
|
513
|
+
@events.fire_after_booted!
|
515
514
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
516
515
|
booted = true
|
517
516
|
end
|
@@ -528,7 +527,7 @@ module Puma
|
|
528
527
|
end
|
529
528
|
|
530
529
|
if in_phased_restart && workers_not_booted.zero?
|
531
|
-
@events.
|
530
|
+
@events.fire_after_booted!
|
532
531
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
533
532
|
in_phased_restart = false
|
534
533
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
# Calculate a delay value for sleeping when running in clustered mode
|
5
|
+
#
|
6
|
+
# The main reason this is a class is so it can be unit tested independently.
|
7
|
+
# This makes modification easier in the future if we can encode properties of the
|
8
|
+
# delay into a test instead of relying on end-to-end testing only.
|
9
|
+
#
|
10
|
+
# This is an imprecise mechanism to address specific goals:
|
11
|
+
#
|
12
|
+
# - Evenly distribute requests across all workers at start
|
13
|
+
# - Evenly distribute CPU resources across all workers
|
14
|
+
#
|
15
|
+
# ## Goal: Distribute requests across workers at start
|
16
|
+
#
|
17
|
+
# There was a perf bug in Puma where one worker would wake up slightly before the rest and accept
|
18
|
+
# all the requests on the socket even though it didn't have enough resources to process all of them.
|
19
|
+
# This was originally fixed by never calling accept when a worker had more requests than threads
|
20
|
+
# already https://github.com/puma/puma/pull/3678/files/2736ebddb3fc8528e5150b5913fba251c37a8bf7#diff-a95f46e7ce116caddc9b9a9aa81004246d5210d5da5f4df90a818c780630166bL251-L291
|
21
|
+
#
|
22
|
+
# With the introduction of true keepalive support, there are two ways a request can come in:
|
23
|
+
# - A new request from a new client comes into the socket and it must be "accept"-d
|
24
|
+
# - A keepalive request is served and the connection is retained. Another request is then accepted
|
25
|
+
#
|
26
|
+
# Ideally the server handles requests in the order they come in, and ideally it doesn't accept more requests than it can handle.
|
27
|
+
# These goals are contradictory, because when the server is at maximum capacity due to keepalive connections, it could mean we
|
28
|
+
# block all new requests, even if those came in before the new request on the older keepalive connection.
|
29
|
+
#
|
30
|
+
# ## Distribute CPU resources across all workers
|
31
|
+
#
|
32
|
+
# - This issue was opened https://github.com/puma/puma/issues/2078
|
33
|
+
#
|
34
|
+
# There are several entangled issues and it's not exactly clear the root cause, but the observable outcome
|
35
|
+
# was that performance was better with a small sleep, and that eventually became the default.
|
36
|
+
#
|
37
|
+
# An attempt to describe why this works is here: https://github.com/puma/puma/issues/2078#issuecomment-3287032470.
|
38
|
+
#
|
39
|
+
# Summarizing: The delay is for tuning the rate at which "accept" is called on the socket.
|
40
|
+
# Puma works by calling "accept" nonblock on the socket in a loop. When there are multiple workers,
|
41
|
+
# (processes) then they will "race" to accept a request at roughly the same rate. However if one
|
42
|
+
# worker has all threads busy processing requests, then accepting a new request might "steal" it from
|
43
|
+
# a less busy worker. If a worker has no work to do, it should loop as fast as possible.
|
44
|
+
#
|
45
|
+
# ## Solution(s): Distribute requests across workers at start
|
46
|
+
#
|
47
|
+
# For now, both goals are framed as "load balancing" across workers (processes) and achieved through
|
48
|
+
# the same mechanism of sleeping longer to delay busier workers. Rather than the prior Puma 6.x
|
49
|
+
# and earlier behavior of using a binary on/off sleep value, we increase it an amound proportional
|
50
|
+
# to the load the server is under. Capping the maximum delay to the scenario where all threads are busy
|
51
|
+
# and the todo list has reached a multiplier of the maximum number of threads.
|
52
|
+
#
|
53
|
+
# Private: API may change unexpectedly
|
54
|
+
class ClusterAcceptLoopDelay
|
55
|
+
attr_reader :max_threads, :max_delay
|
56
|
+
|
57
|
+
# Initialize happens once, `call` happens often. Push global calculations here
|
58
|
+
def initialize(
|
59
|
+
# Number of workers in the cluster
|
60
|
+
workers: ,
|
61
|
+
# Maximum delay in seconds i.e. 0.005 is 5 microseconds
|
62
|
+
max_delay: # In seconds i.e. 0.005 is 5 microseconds
|
63
|
+
|
64
|
+
)
|
65
|
+
@on = max_delay > 0 && workers >= 2
|
66
|
+
@max_delay = max_delay.to_f
|
67
|
+
|
68
|
+
# Reach maximum delay when `max_threads * overload_multiplier` is reached in the system
|
69
|
+
@overload_multiplier = 25.0
|
70
|
+
end
|
71
|
+
|
72
|
+
def on?
|
73
|
+
@on
|
74
|
+
end
|
75
|
+
|
76
|
+
# We want the extreme values of this delay to be known (minimum and maximum) as well as
|
77
|
+
# a predictable curve between the two. i.e. no step functions or hard cliffs.
|
78
|
+
#
|
79
|
+
# Return value is always numeric. Returns 0 if there should be no delay
|
80
|
+
def calculate(
|
81
|
+
# Number of threads working right now, plus number of requests in the todo list
|
82
|
+
busy_threads_plus_todo:,
|
83
|
+
# Maximum number of threads in the pool, note that the busy threads (alone) may go over this value at times
|
84
|
+
# if the pool needs to be reaped. The busy thread plus todo count may go over this value by a large amount
|
85
|
+
max_threads:
|
86
|
+
)
|
87
|
+
max_value = @overload_multiplier * max_threads
|
88
|
+
# Approaches max delay when `busy_threads_plus_todo` approaches `max_value`
|
89
|
+
return max_delay * busy_threads_plus_todo.clamp(0, max_value) / max_value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -29,13 +29,13 @@ module Puma
|
|
29
29
|
|
30
30
|
CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
|
31
31
|
# Util::HeaderHash allows mixed
|
32
|
-
HTTP_VERSION = Const::HTTP_VERSION
|
33
32
|
HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
|
34
33
|
PATH_INFO = Const::PATH_INFO
|
35
34
|
QUERY_STRING = Const::QUERY_STRING
|
36
35
|
REMOTE_ADDR = Const::REMOTE_ADDR
|
37
36
|
REMOTE_USER = 'REMOTE_USER'
|
38
37
|
REQUEST_METHOD = Const::REQUEST_METHOD
|
38
|
+
SERVER_PROTOCOL = Const::SERVER_PROTOCOL
|
39
39
|
|
40
40
|
def initialize(app, logger=nil)
|
41
41
|
@app = app
|
@@ -70,7 +70,7 @@ module Puma
|
|
70
70
|
env[REQUEST_METHOD],
|
71
71
|
env[PATH_INFO],
|
72
72
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
73
|
-
env[
|
73
|
+
env[SERVER_PROTOCOL],
|
74
74
|
now - began_at ]
|
75
75
|
|
76
76
|
write(msg)
|
@@ -87,7 +87,7 @@ module Puma
|
|
87
87
|
env[REQUEST_METHOD],
|
88
88
|
env[PATH_INFO],
|
89
89
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
90
|
-
env[
|
90
|
+
env[SERVER_PROTOCOL],
|
91
91
|
status.to_s[0..3],
|
92
92
|
length,
|
93
93
|
now - began_at ]
|