puma 3.9.1 → 3.10.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 +4 -4
- data/History.md +29 -0
- data/README.md +124 -229
- data/docs/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +102 -37
- data/ext/puma_http11/http11_parser.c +130 -146
- data/ext/puma_http11/http11_parser.rl +9 -9
- data/ext/puma_http11/mini_ssl.c +2 -2
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
- data/lib/puma/app/status.rb +8 -0
- data/lib/puma/binder.rb +6 -5
- data/lib/puma/client.rb +1 -0
- data/lib/puma/cluster.rb +8 -2
- data/lib/puma/configuration.rb +3 -2
- data/lib/puma/const.rb +3 -2
- data/lib/puma/control_cli.rb +2 -2
- data/lib/puma/dsl.rb +10 -0
- data/lib/puma/jruby_restart.rb +0 -1
- data/lib/puma/launcher.rb +31 -14
- data/lib/puma/minissl.rb +19 -25
- data/lib/puma/plugin/tmp_restart.rb +0 -1
- data/lib/puma/reactor.rb +3 -0
- data/lib/puma/server.rb +25 -19
- data/lib/puma/thread_pool.rb +2 -2
- data/lib/rack/handler/puma.rb +2 -0
- data/tools/jungle/README.md +4 -0
- data/tools/trickletest.rb +1 -1
- metadata +9 -57
- data/.github/issue_template.md +0 -20
- data/DEPLOYMENT.md +0 -91
- data/Gemfile +0 -14
- data/Manifest.txt +0 -78
- data/Rakefile +0 -165
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/puma.gemspec +0 -20
data/lib/puma/minissl.rb
CHANGED
@@ -96,35 +96,29 @@ module Puma
|
|
96
96
|
@socket.flush
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
return unless IO.select([@socket], nil, nil, 1)
|
108
|
-
begin
|
109
|
-
read_nonblock(1024)
|
110
|
-
rescue Errno::EAGAIN
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
done = @engine.shutdown
|
115
|
-
|
116
|
-
while true
|
117
|
-
enc = @engine.extract
|
118
|
-
@socket.write enc
|
119
|
-
|
120
|
-
notify = @socket.sysread(1024)
|
99
|
+
def read_and_drop(timeout = 1)
|
100
|
+
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
101
|
+
read_nonblock(1024)
|
102
|
+
:drop
|
103
|
+
rescue Errno::EAGAIN
|
104
|
+
# do nothing
|
105
|
+
:eagain
|
106
|
+
end
|
121
107
|
|
122
|
-
|
123
|
-
|
108
|
+
def should_drop_bytes?
|
109
|
+
@engine.init? || !@engine.shutdown
|
110
|
+
end
|
124
111
|
|
125
|
-
|
112
|
+
def close
|
113
|
+
begin
|
114
|
+
# Read any drop any partially initialized sockets and any received bytes during shutdown.
|
115
|
+
# Don't let this socket hold this loop forever.
|
116
|
+
# If it can't send more packets within 1s, then give up.
|
117
|
+
while should_drop_bytes?
|
118
|
+
return if read_and_drop(1) == :timeout
|
126
119
|
end
|
127
120
|
rescue IOError, SystemCallError
|
121
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
128
122
|
# nothing
|
129
123
|
ensure
|
130
124
|
@socket.close
|
data/lib/puma/reactor.rb
CHANGED
@@ -28,6 +28,7 @@ module Puma
|
|
28
28
|
begin
|
29
29
|
ready = IO.select sockets, nil, nil, @sleep_for
|
30
30
|
rescue IOError => e
|
31
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
31
32
|
if sockets.any? { |socket| socket.closed? }
|
32
33
|
STDERR.puts "Error in select: #{e.message} (#{e.class})"
|
33
34
|
STDERR.puts e.backtrace
|
@@ -195,6 +196,7 @@ module Puma
|
|
195
196
|
begin
|
196
197
|
@trigger << "c"
|
197
198
|
rescue IOError
|
199
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
198
200
|
end
|
199
201
|
end
|
200
202
|
|
@@ -202,6 +204,7 @@ module Puma
|
|
202
204
|
begin
|
203
205
|
@trigger << "!"
|
204
206
|
rescue IOError
|
207
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
205
208
|
end
|
206
209
|
|
207
210
|
@thread.join
|
data/lib/puma/server.rb
CHANGED
@@ -64,12 +64,11 @@ module Puma
|
|
64
64
|
@thread_pool = nil
|
65
65
|
|
66
66
|
@persistent_timeout = options.fetch(:persistent_timeout, PERSISTENT_TIMEOUT)
|
67
|
+
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
67
68
|
|
68
69
|
@binder = Binder.new(events)
|
69
70
|
@own_binder = true
|
70
71
|
|
71
|
-
@first_data_timeout = FIRST_DATA_TIMEOUT
|
72
|
-
|
73
72
|
@leak_stack_on_error = true
|
74
73
|
|
75
74
|
@options = options
|
@@ -111,6 +110,7 @@ module Puma
|
|
111
110
|
begin
|
112
111
|
socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
|
113
112
|
rescue IOError, SystemCallError
|
113
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
@@ -118,6 +118,7 @@ module Puma
|
|
118
118
|
begin
|
119
119
|
socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
|
120
120
|
rescue IOError, SystemCallError
|
121
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
@@ -128,6 +129,7 @@ module Puma
|
|
128
129
|
begin
|
129
130
|
tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
|
130
131
|
rescue IOError, SystemCallError
|
132
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
131
133
|
@precheck_closing = false
|
132
134
|
false
|
133
135
|
else
|
@@ -491,6 +493,7 @@ module Puma
|
|
491
493
|
begin
|
492
494
|
client.close if close_socket
|
493
495
|
rescue IOError, SystemCallError
|
496
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
494
497
|
# Already closed
|
495
498
|
rescue StandardError => e
|
496
499
|
@events.unknown_error self, e, "Client"
|
@@ -893,35 +896,38 @@ module Puma
|
|
893
896
|
end
|
894
897
|
end
|
895
898
|
|
896
|
-
|
897
|
-
# off the request queue before finally exiting.
|
898
|
-
#
|
899
|
-
def stop(sync=false)
|
899
|
+
def notify_safely(message)
|
900
900
|
begin
|
901
|
-
@notify <<
|
901
|
+
@notify << message
|
902
902
|
rescue IOError
|
903
|
-
|
903
|
+
# The server, in another thread, is shutting down
|
904
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
905
|
+
rescue RuntimeError => e
|
906
|
+
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
907
|
+
if e.message.include?('IOError')
|
908
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
909
|
+
else
|
910
|
+
raise e
|
911
|
+
end
|
904
912
|
end
|
913
|
+
end
|
914
|
+
private :notify_safely
|
905
915
|
|
916
|
+
# Stops the acceptor thread and then causes the worker threads to finish
|
917
|
+
# off the request queue before finally exiting.
|
918
|
+
|
919
|
+
def stop(sync=false)
|
920
|
+
notify_safely(STOP_COMMAND)
|
906
921
|
@thread.join if @thread && sync
|
907
922
|
end
|
908
923
|
|
909
924
|
def halt(sync=false)
|
910
|
-
|
911
|
-
@notify << HALT_COMMAND
|
912
|
-
rescue IOError
|
913
|
-
# The server, in another thread, is shutting down
|
914
|
-
end
|
915
|
-
|
925
|
+
notify_safely(HALT_COMMAND)
|
916
926
|
@thread.join if @thread && sync
|
917
927
|
end
|
918
928
|
|
919
929
|
def begin_restart
|
920
|
-
|
921
|
-
@notify << RESTART_COMMAND
|
922
|
-
rescue IOError
|
923
|
-
# The server, in another thread, is shutting down
|
924
|
-
end
|
930
|
+
notify_safely(RESTART_COMMAND)
|
925
931
|
end
|
926
932
|
|
927
933
|
def fast_write(io, str)
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -71,9 +71,9 @@ module Puma
|
|
71
71
|
def spawn_thread
|
72
72
|
@spawned += 1
|
73
73
|
|
74
|
-
th = Thread.new do
|
74
|
+
th = Thread.new(@spawned) do |spawned|
|
75
75
|
# Thread name is new in Ruby 2.3
|
76
|
-
Thread.current.name = 'puma %03i' %
|
76
|
+
Thread.current.name = 'puma %03i' % spawned if Thread.current.respond_to?(:name=)
|
77
77
|
todo = @todo
|
78
78
|
block = @block
|
79
79
|
mutex = @mutex
|
data/lib/rack/handler/puma.rb
CHANGED
data/tools/jungle/README.md
CHANGED
data/tools/trickletest.rb
CHANGED
metadata
CHANGED
@@ -1,47 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rdoc
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '4.0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '4.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: hoe
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '3.15'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '3.15'
|
11
|
+
date: 2017-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
41
13
|
description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
|
42
14
|
for Ruby/Rack applications. Puma is intended for use in both development and production
|
43
|
-
environments.
|
44
|
-
|
15
|
+
environments. It's great for highly concurrent Ruby implementations such as Rubinius
|
16
|
+
and JRuby as well as as providing process worker support to support CRuby well.
|
45
17
|
email:
|
46
18
|
- evan@phx.io
|
47
19
|
executables:
|
@@ -49,33 +21,17 @@ executables:
|
|
49
21
|
- pumactl
|
50
22
|
extensions:
|
51
23
|
- ext/puma_http11/extconf.rb
|
52
|
-
extra_rdoc_files:
|
53
|
-
- ".github/issue_template.md"
|
54
|
-
- DEPLOYMENT.md
|
55
|
-
- History.md
|
56
|
-
- Manifest.txt
|
57
|
-
- README.md
|
58
|
-
- Release.md
|
59
|
-
- docs/nginx.md
|
60
|
-
- docs/signals.md
|
61
|
-
- docs/systemd.md
|
62
|
-
- tools/jungle/README.md
|
63
|
-
- tools/jungle/init.d/README.md
|
64
|
-
- tools/jungle/upstart/README.md
|
24
|
+
extra_rdoc_files: []
|
65
25
|
files:
|
66
|
-
- ".github/issue_template.md"
|
67
|
-
- DEPLOYMENT.md
|
68
|
-
- Gemfile
|
69
26
|
- History.md
|
70
27
|
- LICENSE
|
71
|
-
- Manifest.txt
|
72
28
|
- README.md
|
73
|
-
- Rakefile
|
74
|
-
- Release.md
|
75
29
|
- bin/puma
|
76
30
|
- bin/puma-wild
|
77
31
|
- bin/pumactl
|
78
32
|
- docs/nginx.md
|
33
|
+
- docs/plugins.md
|
34
|
+
- docs/restart.md
|
79
35
|
- docs/signals.md
|
80
36
|
- docs/systemd.md
|
81
37
|
- ext/puma_http11/PumaHttp11Service.java
|
@@ -92,7 +48,6 @@ files:
|
|
92
48
|
- ext/puma_http11/org/jruby/puma/Http11Parser.java
|
93
49
|
- ext/puma_http11/org/jruby/puma/MiniSSL.java
|
94
50
|
- ext/puma_http11/puma_http11.c
|
95
|
-
- gemfiles/2.1-Gemfile
|
96
51
|
- lib/puma.rb
|
97
52
|
- lib/puma/accept_nonblock.rb
|
98
53
|
- lib/puma/app/status.rb
|
@@ -132,7 +87,6 @@ files:
|
|
132
87
|
- lib/puma/thread_pool.rb
|
133
88
|
- lib/puma/util.rb
|
134
89
|
- lib/rack/handler/puma.rb
|
135
|
-
- puma.gemspec
|
136
90
|
- tools/jungle/README.md
|
137
91
|
- tools/jungle/init.d/README.md
|
138
92
|
- tools/jungle/init.d/puma
|
@@ -146,9 +100,7 @@ licenses:
|
|
146
100
|
- BSD-3-Clause
|
147
101
|
metadata: {}
|
148
102
|
post_install_message:
|
149
|
-
rdoc_options:
|
150
|
-
- "--main"
|
151
|
-
- README.md
|
103
|
+
rdoc_options: []
|
152
104
|
require_paths:
|
153
105
|
- lib
|
154
106
|
required_ruby_version: !ruby/object:Gem::Requirement
|
data/.github/issue_template.md
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
### Steps to reproduce
|
2
|
-
|
3
|
-
1) ...
|
4
|
-
|
5
|
-
2) ...
|
6
|
-
|
7
|
-
3) ...
|
8
|
-
|
9
|
-
### Expected behavior
|
10
|
-
|
11
|
-
Tell us what should happen ...
|
12
|
-
|
13
|
-
### Actual behavior
|
14
|
-
|
15
|
-
Tell us what happens instead ...
|
16
|
-
|
17
|
-
### System configuration
|
18
|
-
|
19
|
-
**Ruby version**:
|
20
|
-
**Rails version**:
|
data/DEPLOYMENT.md
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
# Deployment engineering for puma
|
2
|
-
|
3
|
-
Puma is software that is expected to be run in a deployed environment eventually.
|
4
|
-
You can certainly use it as your dev server only, but most people look to use
|
5
|
-
it in their production deployments as well.
|
6
|
-
|
7
|
-
To that end, this is meant to serve as a foundation of wisdom how to do that
|
8
|
-
in a way that increases happiness and decreases downtime.
|
9
|
-
|
10
|
-
## Specifying puma
|
11
|
-
|
12
|
-
Most people want to do this by putting `gem "puma"` into their Gemfile, so we'll
|
13
|
-
go ahead and assume that. Go add it now... we'll wait.
|
14
|
-
|
15
|
-
|
16
|
-
Welcome back!
|
17
|
-
|
18
|
-
## Single vs Cluster mode
|
19
|
-
|
20
|
-
Puma was originally conceived as a thread-only webserver, but grew the ability to
|
21
|
-
also use processes in version 2.
|
22
|
-
|
23
|
-
Here are some rules of thumb:
|
24
|
-
|
25
|
-
### MRI
|
26
|
-
|
27
|
-
* Use cluster mode and set the number of workers to 1.5x the number of cpu cores
|
28
|
-
in the machine, minimum 2.
|
29
|
-
* Set the number of threads to desired concurrent requests / number of workers.
|
30
|
-
Puma defaults to 16 and that's a decent number.
|
31
|
-
|
32
|
-
#### Migrating from Unicorn
|
33
|
-
|
34
|
-
* If you're migrating from unicorn though, here are some settings to start with:
|
35
|
-
* Set workers to half the number of unicorn workers you're using
|
36
|
-
* Set threads to 2
|
37
|
-
* Enjoy 50% memory savings
|
38
|
-
* As you grow more confident in the thread safety of your app, you can tune the
|
39
|
-
workers down and the threads up.
|
40
|
-
|
41
|
-
#### Worker utilization
|
42
|
-
|
43
|
-
**How do you know if you're got enough (or too many workers)?**
|
44
|
-
|
45
|
-
A good question. Due to MRI's GIL, only one thread can be executing Ruby code at a time.
|
46
|
-
But since so many apps are waiting on IO from DBs, etc., they can utilize threads
|
47
|
-
to make better use of the process.
|
48
|
-
|
49
|
-
The rule of thumb is you never want processes that are pegged all the time. This
|
50
|
-
means that there is more work to do that the process can get through. On the other
|
51
|
-
hand, if you have processes that sit around doing nothing, then they're just eating
|
52
|
-
up resources.
|
53
|
-
|
54
|
-
Watching your CPU utilization over time and aim for about 70% on average. This means
|
55
|
-
you've got capacity still but aren't starving threads.
|
56
|
-
|
57
|
-
## Daemonizing
|
58
|
-
|
59
|
-
I prefer to not daemonize my servers and use something like `runit` or `upstart` to
|
60
|
-
monitor them as child processes. This gives them fast response to crashes and
|
61
|
-
makes it easy to figure out what is going on. Additionally, unlike `unicorn`,
|
62
|
-
puma does not require daemonization to do zero-downtime restarts.
|
63
|
-
|
64
|
-
I see people using daemonization because they start puma directly via capistrano
|
65
|
-
task and thus want it to live on past the `cap deploy`. To this people I said:
|
66
|
-
You need to be using a process monitor. Nothing is making sure puma stays up in
|
67
|
-
this scenario! You're just waiting for something weird to happen, puma to die,
|
68
|
-
and to get paged at 3am. Do yourself a favor, at least the process monitoring
|
69
|
-
your OS comes with, be it `sysvinit`, `upstart`, or `systemd`. Or branch out
|
70
|
-
and use `runit` or hell, even `monit`.
|
71
|
-
|
72
|
-
## Restarting
|
73
|
-
|
74
|
-
You probably will want to deploy some new code at some point, and you'd like
|
75
|
-
puma to start running that new code. Minimizing the amount of time the server
|
76
|
-
is unavailable would be nice as well. Here's how to do it:
|
77
|
-
|
78
|
-
1. Don't use `preload!`. This dirties the master process and means it will have
|
79
|
-
to shutdown all the workers and re-exec itself to get your new code. It is not compatible with phased-restart and `prune_bundler` as well.
|
80
|
-
|
81
|
-
1. Use `prune_bundler`. This makes it so that the cluster master will detach itself
|
82
|
-
from a Bundler context on start. This allows the cluster workers to load your app
|
83
|
-
and start a brand new Bundler context within the worker only. This means your
|
84
|
-
master remains pristine and can live on between new releases of your code.
|
85
|
-
|
86
|
-
1. Use phased-restart (`SIGUSR1` or `pumactl phased-restart`). This tells the master
|
87
|
-
to kill off one worker at a time and restart them in your new code. This minimizes
|
88
|
-
downtime and staggers the restart nicely. **WARNING** This means that both your
|
89
|
-
old code and your new code will be running concurrently. Most deployment solutions
|
90
|
-
already cause that, but it's worth warning you about it again. Be careful with your
|
91
|
-
migrations, etc!
|