puma 7.0.4 → 7.1.0
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 +23 -0
- data/README.md +7 -2
- data/docs/kubernetes.md +2 -2
- data/docs/stats.md +1 -1
- data/lib/puma/client.rb +21 -25
- data/lib/puma/cluster/worker_handle.rb +2 -2
- data/lib/puma/cluster.rb +9 -7
- data/lib/puma/cluster_accept_loop_delay.rb +17 -18
- data/lib/puma/configuration.rb +1 -0
- data/lib/puma/const.rb +2 -2
- data/lib/puma/dsl.rb +25 -4
- data/lib/puma/launcher.rb +29 -24
- data/lib/puma/server.rb +34 -30
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +10 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb282989c2e0ee20b007c5b0cdf231caf106816210101572346098f6863502c7
|
4
|
+
data.tar.gz: f4217f8221a64fcd6f5bf2c2249ed8dc6d512ba167e93ef4273b0accb574d26d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2070bae758f22fc8b0784c6b426a7177d65e9039fd2d1969d1241e8f101e46a04afdc5f93f5b6646eb0d9fb15fd6b47f87fb0cee478c3c58e840d3ae852b754a
|
7
|
+
data.tar.gz: 6d35fd83049a36ec8017fb859899fddd0305b90977a14f707f551b1e802d822100bf10126ce43598bf23bbf17deb581135955f61028ee56d0ba3e161718f9d79
|
data/History.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 7.1.0 / 2025-10-16
|
2
|
+
|
3
|
+
* Features
|
4
|
+
* Introduce `after_worker_shutdown` hook ([#3707])
|
5
|
+
* Reintroduce keepalive "fast inline" behavior. Provides faster (8x on JRuby & 1.4x on Ruby) pipeline processing ([#3794])
|
6
|
+
|
7
|
+
* Bugfixes
|
8
|
+
* Skip reading zero bytes when request body is buffered ([#3795])
|
9
|
+
* Fix `PUMA_LOG_CONFIG=1` logging twice with prune_bundler enabled ([#3778])
|
10
|
+
* Fix prune_bundler not showing in `PUMA_LOG_CONFIG=1` output ([#3779])
|
11
|
+
* Guard ThreadPool method call, which may be nil during shutdown ([#3791], [#3790])
|
12
|
+
* Set `Thread.current.puma_server` in Thread init code, not every request ([#3774])
|
13
|
+
* Fix race condition while deleting pidfile ([#3657])
|
14
|
+
|
1
15
|
## 7.0.4 / 2025-09-23
|
2
16
|
|
3
17
|
* Bugfixes
|
@@ -2245,6 +2259,15 @@ be added back in a future date when a java Puma::MiniSSL is added.
|
|
2245
2259
|
* Bugfixes
|
2246
2260
|
* Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
|
2247
2261
|
|
2262
|
+
[#3707]:https://github.com/puma/puma/pull/3707 "PR by @nerdrew, merged 2025-10-02"
|
2263
|
+
[#3794]:https://github.com/puma/puma/pull/3794 "PR by @schneems, merged 2025-10-16"
|
2264
|
+
[#3795]:https://github.com/puma/puma/pull/3795 "PR by @MSP-Greg, merged 2025-10-16"
|
2265
|
+
[#3778]:https://github.com/puma/puma/pull/3778 "PR by @joshuay03, merged 2025-10-16"
|
2266
|
+
[#3779]:https://github.com/puma/puma/pull/3779 "PR by @joshuay03, merged 2025-10-16"
|
2267
|
+
[#3791]:https://github.com/puma/puma/pull/3791 "PR by @MSP-Greg, merged 2025-10-09"
|
2268
|
+
[#3790]:https://github.com/puma/puma/issues/3790 "Issue by @eric-wtfoxtrot, closed 2025-10-09"
|
2269
|
+
[#3774]:https://github.com/puma/puma/pull/3774 "PR by @MSP-Greg, merged 2025-10-16"
|
2270
|
+
[#3657]:https://github.com/puma/puma/pull/3657 "PR by @marksmith, merged 2025-10-16"
|
2248
2271
|
[#3703]:https://github.com/puma/puma/pull/3703 "PR by @marshall-lee, merged 2025-09-20"
|
2249
2272
|
[#3742]:https://github.com/puma/puma/pull/3742 "PR by @kenballus, merged 2025-09-18"
|
2250
2273
|
[#3754]:https://github.com/puma/puma/pull/3754 "PR by @byroot, merged 2025-09-18"
|
data/README.md
CHANGED
@@ -142,8 +142,8 @@ Preloading can’t be used with phased restart, since phased restart kills and r
|
|
142
142
|
|
143
143
|
#### Cluster mode hooks
|
144
144
|
|
145
|
-
When using clustered mode, Puma's configuration DSL provides `before_fork` and `
|
146
|
-
hooks to run code when the master process forks
|
145
|
+
When using clustered mode, Puma's configuration DSL provides `before_fork`, `before_worker_boot`, and `after_worker_shutdown`
|
146
|
+
hooks to run code when the master process forks, the child workers are booted, and after each child worker exits respectively.
|
147
147
|
|
148
148
|
It is recommended to use these hooks with `preload_app!`, otherwise constants loaded by your
|
149
149
|
application (such as `Rails`) will not be available inside the hooks.
|
@@ -157,6 +157,11 @@ end
|
|
157
157
|
before_worker_boot do
|
158
158
|
# Add code to run inside the Puma worker process after forking.
|
159
159
|
end
|
160
|
+
|
161
|
+
after_worker_shutdown do |worker_handle|
|
162
|
+
# Add code to run inside the Puma master process after a worker exits. `worker.process_status` can be used to get the
|
163
|
+
# `Process::Status` of the exited worker.
|
164
|
+
end
|
160
165
|
```
|
161
166
|
|
162
167
|
In addition, there is an `before_refork` and `after_refork` hooks which are used only in [`fork_worker` mode](docs/fork_worker.md),
|
data/docs/kubernetes.md
CHANGED
@@ -10,7 +10,7 @@ Assuming you already have a running cluster and docker image repository, you can
|
|
10
10
|
|
11
11
|
A basic Dockerfile example:
|
12
12
|
|
13
|
-
```
|
13
|
+
```Dockerfile
|
14
14
|
FROM ruby:3.4.5-alpine # can be updated to newer ruby versions
|
15
15
|
RUN apk update && apk add build-base # and any other packages you need
|
16
16
|
|
@@ -28,7 +28,7 @@ CMD bundle exec rackup -o 0.0.0.0
|
|
28
28
|
|
29
29
|
A sample `deployment.yaml`:
|
30
30
|
|
31
|
-
```
|
31
|
+
```yaml
|
32
32
|
---
|
33
33
|
apiVersion: apps/v1
|
34
34
|
kind: Deployment
|
data/docs/stats.md
CHANGED
@@ -62,7 +62,7 @@ When Puma runs in single mode, these stats are available at the top level. When
|
|
62
62
|
this is a "wholistic" stat reflecting the overall current state of work to be done and the capacity to do it.
|
63
63
|
* pool_capacity: `how many threads are waiting to receive work` + `max_threads` - `running`. In a typical configuration where `min_threads`
|
64
64
|
and `max_threads` are configured to the same number, this is simply `how many threads are waiting to receive work`. This number exists only as a stat
|
65
|
-
and is not used for any internal decisions, unlike `
|
65
|
+
and is not used for any internal decisions, unlike `busy_threads`, which is usually a more useful stat.
|
66
66
|
* max_threads: the maximum number of threads Puma is configured to spool per worker
|
67
67
|
* requests_count: the number of requests this worker has served since starting
|
68
68
|
* reactor_max: the maximum observed number of requests held in Puma's "reactor" which is used for asyncronously buffering request bodies. This stat is reset on every call, so it's the maximum value observed since the last stat call.
|
data/lib/puma/client.rb
CHANGED
@@ -500,40 +500,36 @@ module Puma
|
|
500
500
|
# after this
|
501
501
|
remain = @body_remain
|
502
502
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
503
|
+
# don't bother with reading zero bytes
|
504
|
+
unless remain.zero?
|
505
|
+
begin
|
506
|
+
chunk = @io.read_nonblock(remain.clamp(0, CHUNK_SIZE), @read_buffer)
|
507
|
+
rescue IO::WaitReadable
|
508
|
+
return false
|
509
|
+
rescue SystemCallError, IOError
|
510
|
+
raise ConnectionError, "Connection error detected during read"
|
511
|
+
end
|
508
512
|
|
509
|
-
|
510
|
-
chunk
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
513
|
+
# No chunk means a closed socket
|
514
|
+
unless chunk
|
515
|
+
@body.close
|
516
|
+
@buffer = nil
|
517
|
+
set_ready
|
518
|
+
raise EOFError
|
519
|
+
end
|
516
520
|
|
517
|
-
|
518
|
-
unless chunk
|
519
|
-
@body.close
|
520
|
-
@buffer = nil
|
521
|
-
set_ready
|
522
|
-
raise EOFError
|
521
|
+
remain -= @body.write(chunk)
|
523
522
|
end
|
524
523
|
|
525
|
-
remain -= @body.write(chunk)
|
526
|
-
|
527
524
|
if remain <= 0
|
528
525
|
@body.rewind
|
529
526
|
@buffer = nil
|
530
527
|
set_ready
|
531
|
-
|
528
|
+
true
|
529
|
+
else
|
530
|
+
@body_remain = remain
|
531
|
+
false
|
532
532
|
end
|
533
|
-
|
534
|
-
@body_remain = remain
|
535
|
-
|
536
|
-
false
|
537
533
|
end
|
538
534
|
|
539
535
|
def read_chunked_body
|
@@ -28,10 +28,10 @@ module Puma
|
|
28
28
|
@worker_max = Array.new WORKER_MAX_KEYS.length, 0
|
29
29
|
end
|
30
30
|
|
31
|
-
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
31
|
+
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at, :process_status
|
32
32
|
|
33
33
|
# @version 5.0.0
|
34
|
-
attr_writer :pid, :phase
|
34
|
+
attr_writer :pid, :phase, :process_status
|
35
35
|
|
36
36
|
def booted?
|
37
37
|
@stage == :booted
|
data/lib/puma/cluster.rb
CHANGED
@@ -23,7 +23,7 @@ module Puma
|
|
23
23
|
@next_check = Time.now
|
24
24
|
|
25
25
|
@worker_max = [] # keeps track of 'max' stat values
|
26
|
-
@
|
26
|
+
@pending_phased_restart = false
|
27
27
|
end
|
28
28
|
|
29
29
|
# Returns the list of cluster worker handles.
|
@@ -238,7 +238,7 @@ module Puma
|
|
238
238
|
def phased_restart(refork = false)
|
239
239
|
return false if @options[:preload_app] && !refork
|
240
240
|
|
241
|
-
@
|
241
|
+
@pending_phased_restart = refork ? :refork : true
|
242
242
|
wakeup!
|
243
243
|
|
244
244
|
true
|
@@ -456,11 +456,11 @@ module Puma
|
|
456
456
|
break
|
457
457
|
end
|
458
458
|
|
459
|
-
if @
|
460
|
-
start_phased_restart(@
|
459
|
+
if @pending_phased_restart
|
460
|
+
start_phased_restart(@pending_phased_restart == :refork)
|
461
461
|
|
462
|
-
in_phased_restart = @
|
463
|
-
@
|
462
|
+
in_phased_restart = @pending_phased_restart
|
463
|
+
@pending_phased_restart = false
|
464
464
|
|
465
465
|
workers_not_booted = @options[:workers]
|
466
466
|
# worker 0 is not restarted on refork
|
@@ -583,7 +583,9 @@ module Puma
|
|
583
583
|
# `Process.wait2(-1)` from detecting a terminated process: https://bugs.ruby-lang.org/issues/19837.
|
584
584
|
# 2. When `fork_worker` is enabled, some worker may not be direct children,
|
585
585
|
# but grand children. Because of this they won't be reaped by `Process.wait2(-1)`.
|
586
|
-
if reaped_children.delete(w.pid) || Process.
|
586
|
+
if (status = reaped_children.delete(w.pid) || Process.wait2(w.pid, Process::WNOHANG)&.last)
|
587
|
+
w.process_status = status
|
588
|
+
@config.run_hooks(:after_worker_shutdown, w, @log_writer)
|
587
589
|
true
|
588
590
|
else
|
589
591
|
w.term if w.term?
|
@@ -20,48 +20,47 @@ module Puma
|
|
20
20
|
# already https://github.com/puma/puma/pull/3678/files/2736ebddb3fc8528e5150b5913fba251c37a8bf7#diff-a95f46e7ce116caddc9b9a9aa81004246d5210d5da5f4df90a818c780630166bL251-L291
|
21
21
|
#
|
22
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"-
|
23
|
+
# - A new request from a new client comes into the socket and it must be "accept"-ed
|
24
24
|
# - A keepalive request is served and the connection is retained. Another request is then accepted
|
25
25
|
#
|
26
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
27
|
# These goals are contradictory, because when the server is at maximum capacity due to keepalive connections, it could mean we
|
28
28
|
# block all new requests, even if those came in before the new request on the older keepalive connection.
|
29
29
|
#
|
30
|
-
# ## Distribute CPU resources across all workers
|
30
|
+
# ## Goal: Distribute CPU resources across all workers
|
31
31
|
#
|
32
32
|
# - This issue was opened https://github.com/puma/puma/issues/2078
|
33
33
|
#
|
34
|
-
# There are several entangled issues and it's not exactly clear the root cause, but the observable outcome
|
34
|
+
# There are several entangled issues and it's not exactly clear what the root cause is, but the observable outcome
|
35
35
|
# was that performance was better with a small sleep, and that eventually became the default.
|
36
36
|
#
|
37
37
|
# An attempt to describe why this works is here: https://github.com/puma/puma/issues/2078#issuecomment-3287032470.
|
38
38
|
#
|
39
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)
|
40
|
+
# Puma works by calling "accept" nonblock on the socket in a loop. When there are multiple workers
|
41
|
+
# (processes), they will "race" to accept a request at roughly the same rate. However, if one
|
42
42
|
# worker has all threads busy processing requests, then accepting a new request might "steal" it from
|
43
43
|
# a less busy worker. If a worker has no work to do, it should loop as fast as possible.
|
44
44
|
#
|
45
|
-
# ## Solution
|
45
|
+
# ## Solution: Distribute requests across workers at start
|
46
46
|
#
|
47
47
|
# For now, both goals are framed as "load balancing" across workers (processes) and achieved through
|
48
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
|
50
|
-
# to the load the server is under
|
49
|
+
# and earlier behavior of using a binary on/off sleep value, we increase it an amount proportional
|
50
|
+
# to the load the server is under, capping the maximum delay to the scenario where all threads are busy
|
51
51
|
# and the todo list has reached a multiplier of the maximum number of threads.
|
52
52
|
#
|
53
53
|
# Private: API may change unexpectedly
|
54
54
|
class ClusterAcceptLoopDelay
|
55
|
-
attr_reader :
|
55
|
+
attr_reader :max_delay
|
56
56
|
|
57
|
-
# Initialize happens once, `call` happens often.
|
57
|
+
# Initialize happens once, `call` happens often. Perform global calculations here.
|
58
58
|
def initialize(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
)
|
59
|
+
# Number of workers in the cluster
|
60
|
+
workers: ,
|
61
|
+
# Maximum delay in seconds i.e. 0.005 is 5 milliseconds
|
62
|
+
max_delay:
|
63
|
+
)
|
65
64
|
@on = max_delay > 0 && workers >= 2
|
66
65
|
@max_delay = max_delay.to_f
|
67
66
|
|
@@ -76,12 +75,12 @@ module Puma
|
|
76
75
|
# We want the extreme values of this delay to be known (minimum and maximum) as well as
|
77
76
|
# a predictable curve between the two. i.e. no step functions or hard cliffs.
|
78
77
|
#
|
79
|
-
# Return value is always numeric. Returns 0 if there should be no delay
|
78
|
+
# Return value is always numeric. Returns 0 if there should be no delay.
|
80
79
|
def calculate(
|
81
80
|
# Number of threads working right now, plus number of requests in the todo list
|
82
81
|
busy_threads_plus_todo:,
|
83
82
|
# 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
|
83
|
+
# if the pool needs to be reaped. The busy thread plus todo count may go over this value by a large amount.
|
85
84
|
max_threads:
|
86
85
|
)
|
87
86
|
max_value = @overload_multiplier * max_threads
|
data/lib/puma/configuration.rb
CHANGED
@@ -155,6 +155,7 @@ module Puma
|
|
155
155
|
out_of_band: [],
|
156
156
|
# Number of seconds for another request within a persistent session.
|
157
157
|
persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
|
158
|
+
prune_bundler: false,
|
158
159
|
queue_requests: true,
|
159
160
|
rackup: 'config.ru'.freeze,
|
160
161
|
raise_exception_on_sigterm: true,
|
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "7.0
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "7.1.0"
|
104
|
+
CODE_NAME = "Neon Witch"
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
107
107
|
|
data/lib/puma/dsl.rb
CHANGED
@@ -656,7 +656,8 @@ module Puma
|
|
656
656
|
@options[:state] = path.to_s
|
657
657
|
end
|
658
658
|
|
659
|
-
# Use +permission+ to restrict permissions for the state file.
|
659
|
+
# Use +permission+ to restrict permissions for the state file. By convention,
|
660
|
+
# +permission+ is an octal number (e.g. `0640` or `0o640`).
|
660
661
|
#
|
661
662
|
# @example
|
662
663
|
# state_permission 0600
|
@@ -818,6 +819,20 @@ module Puma
|
|
818
819
|
|
819
820
|
alias_method :after_worker_boot, :after_worker_fork
|
820
821
|
|
822
|
+
# Code to run in the master right after a worker has stopped. The worker's
|
823
|
+
# index and Process::Status are passed as arguments.
|
824
|
+
#
|
825
|
+
# @note Cluster mode only.
|
826
|
+
#
|
827
|
+
# @example
|
828
|
+
# after_worker_shutdown do |worker_handle|
|
829
|
+
# puts 'Worker crashed' unless worker_handle.process_status.success?
|
830
|
+
# end
|
831
|
+
#
|
832
|
+
def after_worker_shutdown(&block)
|
833
|
+
process_hook :after_worker_shutdown, nil, block, cluster_only: true
|
834
|
+
end
|
835
|
+
|
821
836
|
# Code to run after puma is booted (works for both single and cluster modes).
|
822
837
|
#
|
823
838
|
# @example
|
@@ -1207,13 +1222,19 @@ module Puma
|
|
1207
1222
|
end
|
1208
1223
|
|
1209
1224
|
|
1210
|
-
#
|
1211
|
-
#
|
1225
|
+
# Maximum delay of worker accept loop.
|
1226
|
+
#
|
1227
|
+
# Attempts to route traffic to less-busy workers by causing a busy worker to delay
|
1228
|
+
# listening on the socket, allowing workers which are not processing as many
|
1212
1229
|
# requests to pick up new requests first.
|
1213
1230
|
#
|
1214
1231
|
# The default is 0.005 seconds.
|
1215
1232
|
#
|
1216
|
-
#
|
1233
|
+
# To turn off this feature, set the value to 0.
|
1234
|
+
#
|
1235
|
+
# @note Cluster mode with >= 2 workers only.
|
1236
|
+
#
|
1237
|
+
# @note Interpreters with forking support only.
|
1217
1238
|
#
|
1218
1239
|
# @see Puma::Server#handle_servers
|
1219
1240
|
# @see Puma::ThreadPool#wait_for_less_busy_worker
|
data/lib/puma/launcher.rb
CHANGED
@@ -42,26 +42,39 @@ module Puma
|
|
42
42
|
# end
|
43
43
|
# Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
|
44
44
|
def initialize(conf, launcher_args={})
|
45
|
-
|
46
|
-
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
47
|
-
@events = launcher_args[:events] || Events.new
|
48
|
-
@argv = launcher_args[:argv] || []
|
49
|
-
@original_argv = @argv.dup
|
50
|
-
@config = conf
|
51
|
-
|
52
|
-
env = launcher_args.delete(:env) || ENV
|
45
|
+
## Minimal initialization for a potential early restart (e.g. when pruning bundle)
|
53
46
|
|
47
|
+
@config = conf
|
54
48
|
@config.clamp
|
49
|
+
|
55
50
|
@options = @config.options
|
56
51
|
|
52
|
+
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
53
|
+
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
54
|
+
@log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
|
55
|
+
@log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
|
57
56
|
@options[:log_writer] = @log_writer
|
58
57
|
@options[:logger] = @log_writer if clustered?
|
59
58
|
|
59
|
+
@events = launcher_args[:events] || Events.new
|
60
|
+
|
61
|
+
@argv = launcher_args[:argv] || []
|
62
|
+
@original_argv = @argv.dup
|
63
|
+
|
64
|
+
## End minimal initialization
|
65
|
+
|
66
|
+
generate_restart_data
|
67
|
+
Dir.chdir(@restart_dir)
|
68
|
+
|
69
|
+
prune_bundler!
|
70
|
+
|
71
|
+
env = launcher_args.delete(:env) || ENV
|
72
|
+
|
60
73
|
# Advertise the Configuration
|
61
74
|
Puma.cli_config = @config if defined?(Puma.cli_config)
|
62
75
|
log_config if env['PUMA_LOG_CONFIG']
|
63
76
|
|
64
|
-
@binder
|
77
|
+
@binder = Binder.new(@log_writer, @options)
|
65
78
|
@binder.create_inherited_fds(env).each { |k| env.delete k }
|
66
79
|
@binder.create_activated_fds(env).each { |k| env.delete k }
|
67
80
|
|
@@ -81,21 +94,10 @@ module Puma
|
|
81
94
|
)
|
82
95
|
end
|
83
96
|
|
84
|
-
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
85
|
-
@log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
|
86
|
-
|
87
|
-
@log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
|
88
|
-
|
89
|
-
generate_restart_data
|
90
|
-
|
91
97
|
if clustered? && !Puma.forkable?
|
92
98
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
93
99
|
end
|
94
100
|
|
95
|
-
Dir.chdir(@restart_dir)
|
96
|
-
|
97
|
-
prune_bundler!
|
98
|
-
|
99
101
|
@environment = @options[:environment] if @options[:environment]
|
100
102
|
set_rack_environment
|
101
103
|
|
@@ -139,7 +141,10 @@ module Puma
|
|
139
141
|
# Delete the configured pidfile
|
140
142
|
def delete_pidfile
|
141
143
|
path = @options[:pidfile]
|
142
|
-
|
144
|
+
begin
|
145
|
+
File.unlink(path) if path
|
146
|
+
rescue Errno::ENOENT
|
147
|
+
end
|
143
148
|
end
|
144
149
|
|
145
150
|
# Begin async shutdown of the server
|
@@ -381,9 +386,9 @@ module Puma
|
|
381
386
|
# using it.
|
382
387
|
@restart_dir = Dir.pwd
|
383
388
|
|
384
|
-
|
385
|
-
|
386
|
-
|
389
|
+
# Use the same trick as unicorn, namely favor PWD because
|
390
|
+
# it will contain an unresolved symlink, useful for when
|
391
|
+
# the pwd is /data/releases/current.
|
387
392
|
elsif dir = ENV['PWD']
|
388
393
|
s_env = File.stat(dir)
|
389
394
|
s_pwd = File.stat(Dir.pwd)
|
data/lib/puma/server.rb
CHANGED
@@ -19,9 +19,6 @@ require 'socket'
|
|
19
19
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
20
20
|
|
21
21
|
module Puma
|
22
|
-
# Add `Thread#puma_server` and `Thread#puma_server=`
|
23
|
-
Thread.attr_accessor(:puma_server)
|
24
|
-
|
25
22
|
# The HTTP Server itself. Serves out a single Rack app.
|
26
23
|
#
|
27
24
|
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
@@ -262,7 +259,7 @@ module Puma
|
|
262
259
|
|
263
260
|
@status = :run
|
264
261
|
|
265
|
-
@thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
|
262
|
+
@thread_pool = ThreadPool.new(thread_name, options, server: self) { |client| process_client client }
|
266
263
|
|
267
264
|
if @queue_requests
|
268
265
|
@reactor = Reactor.new(@io_selector_backend) { |c|
|
@@ -481,9 +478,6 @@ module Puma
|
|
481
478
|
#
|
482
479
|
# Return true if one or more requests were processed.
|
483
480
|
def process_client(client)
|
484
|
-
# Advertise this server into the thread
|
485
|
-
Thread.current.puma_server = self
|
486
|
-
|
487
481
|
close_socket = true
|
488
482
|
|
489
483
|
requests = 0
|
@@ -502,31 +496,41 @@ module Puma
|
|
502
496
|
client.finish(@first_data_timeout)
|
503
497
|
end
|
504
498
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
499
|
+
can_loop = true
|
500
|
+
while can_loop
|
501
|
+
can_loop = false
|
502
|
+
@requests_count += 1
|
503
|
+
case handle_request(client, requests + 1)
|
504
|
+
when false
|
505
|
+
when :async
|
506
|
+
close_socket = false
|
507
|
+
when true
|
508
|
+
requests += 1
|
512
509
|
|
513
|
-
|
510
|
+
client.reset
|
514
511
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
512
|
+
# This indicates data exists in the client read buffer and there may be
|
513
|
+
# additional requests on it, so process them
|
514
|
+
next_request_ready = if client.has_back_to_back_requests?
|
515
|
+
with_force_shutdown(client) { client.process_back_to_back_requests }
|
516
|
+
else
|
517
|
+
with_force_shutdown(client) { client.eagerly_finish }
|
518
|
+
end
|
522
519
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
520
|
+
if next_request_ready
|
521
|
+
# When Puma has spare threads, allow this one to be monopolized
|
522
|
+
# Perf optimization for https://github.com/puma/puma/issues/3788
|
523
|
+
if @thread_pool.waiting > 0
|
524
|
+
can_loop = true
|
525
|
+
else
|
526
|
+
@thread_pool << client
|
527
|
+
close_socket = false
|
528
|
+
end
|
529
|
+
elsif @queue_requests
|
530
|
+
client.set_timeout @persistent_timeout
|
531
|
+
if @reactor.add client
|
532
|
+
close_socket = false
|
533
|
+
end
|
530
534
|
end
|
531
535
|
end
|
532
536
|
end
|
@@ -706,7 +710,7 @@ module Puma
|
|
706
710
|
|
707
711
|
def reset_max
|
708
712
|
@reactor.reactor_max = 0 if @reactor
|
709
|
-
@thread_pool
|
713
|
+
@thread_pool&.reset_max
|
710
714
|
end
|
711
715
|
|
712
716
|
# below are 'delegations' to binder
|
data/lib/puma/state_file.rb
CHANGED
@@ -32,10 +32,11 @@ module Puma
|
|
32
32
|
"#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
35
36
|
if permission
|
36
|
-
File.write path, contents, mode: 'wb:UTF-8'
|
37
|
-
else
|
38
37
|
File.write path, contents, mode: 'wb:UTF-8', perm: permission
|
38
|
+
else
|
39
|
+
File.write path, contents, mode: 'wb:UTF-8'
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -5,6 +5,10 @@ require 'thread'
|
|
5
5
|
require_relative 'io_buffer'
|
6
6
|
|
7
7
|
module Puma
|
8
|
+
|
9
|
+
# Add `Thread#puma_server` and `Thread#puma_server=`
|
10
|
+
Thread.attr_accessor(:puma_server)
|
11
|
+
|
8
12
|
# Internal Docs for A simple thread pool management object.
|
9
13
|
#
|
10
14
|
# Each Puma "worker" has a thread pool to process requests.
|
@@ -33,7 +37,9 @@ module Puma
|
|
33
37
|
# The block passed is the work that will be performed in each
|
34
38
|
# thread.
|
35
39
|
#
|
36
|
-
def initialize(name, options = {}, &block)
|
40
|
+
def initialize(name, options = {}, server: nil, &block)
|
41
|
+
@server = server
|
42
|
+
|
37
43
|
@not_empty = ConditionVariable.new
|
38
44
|
@not_full = ConditionVariable.new
|
39
45
|
@mutex = Mutex.new
|
@@ -134,6 +140,9 @@ module Puma
|
|
134
140
|
trigger_before_thread_start_hooks
|
135
141
|
th = Thread.new(@spawned) do |spawned|
|
136
142
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
143
|
+
# Advertise server into the thread
|
144
|
+
Thread.current.puma_server = @server
|
145
|
+
|
137
146
|
todo = @todo
|
138
147
|
block = @block
|
139
148
|
mutex = @mutex
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0
|
4
|
+
version: 7.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
@@ -132,6 +132,7 @@ metadata:
|
|
132
132
|
homepage_uri: https://puma.io
|
133
133
|
source_code_uri: https://github.com/puma/puma
|
134
134
|
rubygems_mfa_required: 'true'
|
135
|
+
msys2_mingw_dependencies: openssl
|
135
136
|
rdoc_options: []
|
136
137
|
require_paths:
|
137
138
|
- lib
|