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.

@@ -96,35 +96,29 @@ module Puma
96
96
  @socket.flush
97
97
  end
98
98
 
99
- def close
100
- begin
101
- # Try to setup (so that we can then close them) any
102
- # partially initialized sockets.
103
- while @engine.init?
104
- # Don't let this socket hold this loop forever.
105
- # If it can't send more packets within 1s, then
106
- # give up.
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
- @engine.inject notify
123
- done = @engine.shutdown
108
+ def should_drop_bytes?
109
+ @engine.init? || !@engine.shutdown
110
+ end
124
111
 
125
- break if done
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
@@ -32,4 +32,3 @@ Puma::Plugin.create do
32
32
  end
33
33
  end
34
34
  end
35
-
@@ -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
@@ -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
- # Stops the acceptor thread and then causes the worker threads to finish
897
- # off the request queue before finally exiting.
898
- #
899
- def stop(sync=false)
899
+ def notify_safely(message)
900
900
  begin
901
- @notify << STOP_COMMAND
901
+ @notify << message
902
902
  rescue IOError
903
- # The server, in another thread, is shutting down
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
- begin
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
- begin
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)
@@ -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' % @spawned if Thread.current.respond_to?(:name=)
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
@@ -84,6 +84,8 @@ module Rack
84
84
  end
85
85
  private
86
86
  def self.set_host_port_to_config(host, port, config)
87
+ config.clear_binds! if host || port
88
+
87
89
  if host && (host[0,1] == '.' || host[0,1] == '/')
88
90
  config.bind "unix://#{host}"
89
91
  elsif host && host =~ /^ssl:\/\//
@@ -7,3 +7,7 @@ See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon.
7
7
  ## Upstart
8
8
 
9
9
  See `/tools/jungle/upstart` for Ubuntu's upstart scripts.
10
+
11
+ ## Systemd
12
+
13
+ See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md).
@@ -31,7 +31,7 @@ st = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\nCon
31
31
  puts "length: #{content.length}"
32
32
 
33
33
  threads = []
34
- ARGV[1].to_i.times do
34
+ ARGV[1].to_i.times do
35
35
  t = Thread.new do
36
36
  size = 100
37
37
  puts ">>>> #{size} sized chunks"
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.9.1
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-06-03 00:00:00.000000000 Z
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. In order to get the best throughput, it is highly recommended that
44
- you use a Ruby implementation with real threads like Rubinius or JRuby.
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
@@ -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**:
@@ -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!