puma 4.3.12 → 6.3.1
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 +4 -4
- data/History.md +1729 -521
- data/LICENSE +23 -20
- data/README.md +169 -45
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -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/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +84 -128
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +49 -12
- data/ext/puma_http11/http11_parser.c +46 -48
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +278 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +46 -57
- data/lib/puma/app/status.rb +53 -39
- data/lib/puma/binder.rb +237 -121
- data/lib/puma/cli.rb +34 -34
- data/lib/puma/client.rb +172 -98
- data/lib/puma/cluster/worker.rb +180 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +226 -231
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +114 -87
- data/lib/puma/const.rb +139 -95
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +516 -110
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +164 -155
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +36 -19
- data/lib/puma/minissl.rb +230 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +7 -11
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +93 -368
- data/lib/puma/request.rb +671 -0
- data/lib/puma/runner.rb +92 -75
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +321 -794
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +140 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -7
- data/lib/rack/handler/puma.rb +113 -87
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +33 -24
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
class Cluster < Puma::Runner
|
5
|
+
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
|
+
|
7
|
+
|
8
|
+
# This class is instantiated by the `Puma::Cluster` and represents a single
|
9
|
+
# worker process.
|
10
|
+
#
|
11
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
12
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
13
|
+
# that this inherits from.
|
14
|
+
class Worker < Puma::Runner # :nodoc:
|
15
|
+
attr_reader :index, :master
|
16
|
+
|
17
|
+
def initialize(index:, master:, launcher:, pipes:, server: nil)
|
18
|
+
super(launcher)
|
19
|
+
|
20
|
+
@index = index
|
21
|
+
@master = master
|
22
|
+
@check_pipe = pipes[:check_pipe]
|
23
|
+
@worker_write = pipes[:worker_write]
|
24
|
+
@fork_pipe = pipes[:fork_pipe]
|
25
|
+
@wakeup = pipes[:wakeup]
|
26
|
+
@server = server
|
27
|
+
@hook_data = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
title = "puma: cluster worker #{index}: #{master}"
|
32
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
33
|
+
$0 = title
|
34
|
+
|
35
|
+
Signal.trap "SIGINT", "IGNORE"
|
36
|
+
Signal.trap "SIGCHLD", "DEFAULT"
|
37
|
+
|
38
|
+
Thread.new do
|
39
|
+
Puma.set_thread_name "wrkr check"
|
40
|
+
@check_pipe.wait_readable
|
41
|
+
log "! Detected parent died, dying"
|
42
|
+
exit! 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# If we're not running under a Bundler context, then
|
46
|
+
# report the info about the context we will be using
|
47
|
+
if !ENV['BUNDLE_GEMFILE']
|
48
|
+
if File.exist?("Gemfile")
|
49
|
+
log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
|
50
|
+
elsif File.exist?("gems.rb")
|
51
|
+
log "+ Gemfile in context: #{File.expand_path("gems.rb")}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Invoke any worker boot hooks so they can get
|
56
|
+
# things in shape before booting the app.
|
57
|
+
@config.run_hooks(:before_worker_boot, index, @log_writer, @hook_data)
|
58
|
+
|
59
|
+
begin
|
60
|
+
server = @server ||= start_server
|
61
|
+
rescue Exception => e
|
62
|
+
log "! Unable to start worker"
|
63
|
+
log e
|
64
|
+
log e.backtrace.join("\n ")
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
restart_server = Queue.new << true << false
|
69
|
+
|
70
|
+
fork_worker = @options[:fork_worker] && index == 0
|
71
|
+
|
72
|
+
if fork_worker
|
73
|
+
restart_server.clear
|
74
|
+
worker_pids = []
|
75
|
+
Signal.trap "SIGCHLD" do
|
76
|
+
wakeup! if worker_pids.reject! do |p|
|
77
|
+
Process.wait(p, Process::WNOHANG) rescue true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Thread.new do
|
82
|
+
Puma.set_thread_name "wrkr fork"
|
83
|
+
while (idx = @fork_pipe.gets)
|
84
|
+
idx = idx.to_i
|
85
|
+
if idx == -1 # stop server
|
86
|
+
if restart_server.length > 0
|
87
|
+
restart_server.clear
|
88
|
+
server.begin_restart(true)
|
89
|
+
@config.run_hooks(:before_refork, nil, @log_writer, @hook_data)
|
90
|
+
end
|
91
|
+
elsif idx == 0 # restart server
|
92
|
+
restart_server << true << false
|
93
|
+
else # fork worker
|
94
|
+
worker_pids << pid = spawn_worker(idx)
|
95
|
+
@worker_write << "f#{pid}:#{idx}\n" rescue nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Signal.trap "SIGTERM" do
|
102
|
+
@worker_write << "e#{Process.pid}\n" rescue nil
|
103
|
+
restart_server.clear
|
104
|
+
server.stop
|
105
|
+
restart_server << false
|
106
|
+
end
|
107
|
+
|
108
|
+
begin
|
109
|
+
@worker_write << "b#{Process.pid}:#{index}\n"
|
110
|
+
rescue SystemCallError, IOError
|
111
|
+
Puma::Util.purge_interrupt_queue
|
112
|
+
STDERR.puts "Master seems to have exited, exiting."
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
while restart_server.pop
|
117
|
+
server_thread = server.run
|
118
|
+
|
119
|
+
if @log_writer.debug? && index == 0
|
120
|
+
debug_loaded_extensions "Loaded Extensions - worker 0:"
|
121
|
+
end
|
122
|
+
|
123
|
+
stat_thread ||= Thread.new(@worker_write) do |io|
|
124
|
+
Puma.set_thread_name "stat pld"
|
125
|
+
base_payload = "p#{Process.pid}"
|
126
|
+
|
127
|
+
while true
|
128
|
+
begin
|
129
|
+
b = server.backlog || 0
|
130
|
+
r = server.running || 0
|
131
|
+
t = server.pool_capacity || 0
|
132
|
+
m = server.max_threads || 0
|
133
|
+
rc = server.requests_count || 0
|
134
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
|
135
|
+
io << payload
|
136
|
+
rescue IOError
|
137
|
+
Puma::Util.purge_interrupt_queue
|
138
|
+
break
|
139
|
+
end
|
140
|
+
sleep @options[:worker_check_interval]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
server_thread.join
|
144
|
+
end
|
145
|
+
|
146
|
+
# Invoke any worker shutdown hooks so they can prevent the worker
|
147
|
+
# exiting until any background operations are completed
|
148
|
+
@config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
|
149
|
+
ensure
|
150
|
+
@worker_write << "t#{Process.pid}\n" rescue nil
|
151
|
+
@worker_write.close
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def spawn_worker(idx)
|
157
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer, @hook_data)
|
158
|
+
|
159
|
+
pid = fork do
|
160
|
+
new_worker = Worker.new index: idx,
|
161
|
+
master: master,
|
162
|
+
launcher: @launcher,
|
163
|
+
pipes: { check_pipe: @check_pipe,
|
164
|
+
worker_write: @worker_write },
|
165
|
+
server: @server
|
166
|
+
new_worker.run
|
167
|
+
end
|
168
|
+
|
169
|
+
if !pid
|
170
|
+
log "! Complete inability to spawn new workers detected"
|
171
|
+
log "! Seppuku is the only choice."
|
172
|
+
exit! 1
|
173
|
+
end
|
174
|
+
|
175
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer, @hook_data)
|
176
|
+
pid
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
class Cluster < Runner
|
5
|
+
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
|
+
|
7
|
+
|
8
|
+
# This class represents a worker process from the perspective of the puma
|
9
|
+
# master process. It contains information about the process and its health
|
10
|
+
# and it exposes methods to control the process via IPC. It does not
|
11
|
+
# include the actual logic executed by the worker process itself. For that,
|
12
|
+
# see Puma::Cluster::Worker.
|
13
|
+
class WorkerHandle # :nodoc:
|
14
|
+
def initialize(idx, pid, phase, options)
|
15
|
+
@index = idx
|
16
|
+
@pid = pid
|
17
|
+
@phase = phase
|
18
|
+
@stage = :started
|
19
|
+
@signal = "TERM"
|
20
|
+
@options = options
|
21
|
+
@first_term_sent = nil
|
22
|
+
@started_at = Time.now
|
23
|
+
@last_checkin = Time.now
|
24
|
+
@last_status = {}
|
25
|
+
@term = false
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
29
|
+
|
30
|
+
# @version 5.0.0
|
31
|
+
attr_writer :pid, :phase
|
32
|
+
|
33
|
+
def booted?
|
34
|
+
@stage == :booted
|
35
|
+
end
|
36
|
+
|
37
|
+
def uptime
|
38
|
+
Time.now - started_at
|
39
|
+
end
|
40
|
+
|
41
|
+
def boot!
|
42
|
+
@last_checkin = Time.now
|
43
|
+
@stage = :booted
|
44
|
+
end
|
45
|
+
|
46
|
+
def term!
|
47
|
+
@term = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def term?
|
51
|
+
@term
|
52
|
+
end
|
53
|
+
|
54
|
+
def ping!(status)
|
55
|
+
@last_checkin = Time.now
|
56
|
+
captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
|
57
|
+
@last_status = captures.names.inject({}) do |hash, key|
|
58
|
+
hash[key.to_sym] = captures[key].to_i
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @see Puma::Cluster#check_workers
|
64
|
+
# @version 5.0.0
|
65
|
+
def ping_timeout
|
66
|
+
@last_checkin +
|
67
|
+
(booted? ?
|
68
|
+
@options[:worker_timeout] :
|
69
|
+
@options[:worker_boot_timeout]
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def term
|
74
|
+
begin
|
75
|
+
if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
|
76
|
+
@signal = "KILL"
|
77
|
+
else
|
78
|
+
@term ||= true
|
79
|
+
@first_term_sent ||= Time.now
|
80
|
+
end
|
81
|
+
Process.kill @signal, @pid if @pid
|
82
|
+
rescue Errno::ESRCH
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def kill
|
87
|
+
@signal = 'KILL'
|
88
|
+
term
|
89
|
+
end
|
90
|
+
|
91
|
+
def hup
|
92
|
+
Process.kill "HUP", @pid
|
93
|
+
rescue Errno::ESRCH
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|