puma 5.0.2-java → 5.2.0-java

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +667 -567
  3. data/README.md +51 -21
  4. data/bin/puma-wild +3 -9
  5. data/docs/compile_options.md +19 -0
  6. data/docs/deployment.md +6 -7
  7. data/docs/fork_worker.md +2 -0
  8. data/docs/jungle/README.md +0 -4
  9. data/docs/jungle/rc.d/puma +2 -2
  10. data/docs/kubernetes.md +66 -0
  11. data/docs/nginx.md +1 -1
  12. data/docs/plugins.md +1 -1
  13. data/docs/restart.md +46 -23
  14. data/docs/stats.md +142 -0
  15. data/docs/systemd.md +25 -3
  16. data/ext/puma_http11/ext_help.h +1 -1
  17. data/ext/puma_http11/extconf.rb +18 -5
  18. data/ext/puma_http11/http11_parser.c +45 -47
  19. data/ext/puma_http11/http11_parser.java.rl +1 -1
  20. data/ext/puma_http11/http11_parser.rl +1 -1
  21. data/ext/puma_http11/mini_ssl.c +199 -119
  22. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
  23. data/ext/puma_http11/puma_http11.c +25 -12
  24. data/lib/puma.rb +2 -2
  25. data/lib/puma/app/status.rb +44 -46
  26. data/lib/puma/binder.rb +69 -25
  27. data/lib/puma/cli.rb +4 -0
  28. data/lib/puma/client.rb +26 -79
  29. data/lib/puma/cluster.rb +37 -202
  30. data/lib/puma/cluster/worker.rb +176 -0
  31. data/lib/puma/cluster/worker_handle.rb +86 -0
  32. data/lib/puma/configuration.rb +21 -8
  33. data/lib/puma/const.rb +11 -3
  34. data/lib/puma/control_cli.rb +73 -70
  35. data/lib/puma/dsl.rb +100 -22
  36. data/lib/puma/error_logger.rb +10 -3
  37. data/lib/puma/events.rb +18 -3
  38. data/lib/puma/json.rb +96 -0
  39. data/lib/puma/launcher.rb +57 -15
  40. data/lib/puma/minissl.rb +47 -16
  41. data/lib/puma/minissl/context_builder.rb +6 -0
  42. data/lib/puma/null_io.rb +4 -0
  43. data/lib/puma/puma_http11.jar +0 -0
  44. data/lib/puma/queue_close.rb +26 -0
  45. data/lib/puma/reactor.rb +85 -363
  46. data/lib/puma/request.rb +451 -0
  47. data/lib/puma/runner.rb +17 -23
  48. data/lib/puma/server.rb +164 -553
  49. data/lib/puma/single.rb +2 -2
  50. data/lib/puma/state_file.rb +5 -3
  51. data/lib/puma/systemd.rb +46 -0
  52. data/lib/puma/util.rb +11 -0
  53. metadata +11 -6
  54. data/docs/jungle/upstart/README.md +0 -61
  55. data/docs/jungle/upstart/puma-manager.conf +0 -31
  56. data/docs/jungle/upstart/puma.conf +0 -69
  57. data/lib/puma/accept_nonblock.rb +0 -29
data/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  # Puma: A Ruby Web Server Built For Concurrency
6
6
 
7
- [![Actions Build Status](https://github.com/puma/puma/workflows/CI/badge.svg?branch=master)](https://github.com/puma/puma/actions)
7
+ [![Actions MRI](https://github.com/puma/puma/workflows/MRI/badge.svg?branch=master)](https://github.com/puma/puma/actions?query=workflow%3AMRI)
8
+ [![Actions non MRI](https://github.com/puma/puma/workflows/non_MRI/badge.svg?branch=master)](https://github.com/puma/puma/actions?query=workflow%3Anon_MRI)
8
9
  [![Code Climate](https://codeclimate.com/github/puma/puma.svg)](https://codeclimate.com/github/puma/puma)
9
10
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=puma&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=puma&package-manager=bundler&version-scheme=semver)
10
11
  [![StackOverflow](https://img.shields.io/badge/stackoverflow-Puma-blue.svg)]( https://stackoverflow.com/questions/tagged/puma )
@@ -63,20 +64,30 @@ You can run your Sinatra application with Puma from the command line like this:
63
64
  $ ruby app.rb -s Puma
64
65
  ```
65
66
 
66
- Or you can configure your Sinatra application to always use Puma:
67
+ In order to actually configure Puma using a config file, like `puma.rb`, however, you need to use the `puma` executable. To do this, you must add a rackup file to your Sinatra app:
67
68
 
68
69
  ```ruby
69
- require 'sinatra'
70
- configure { set :server, :puma }
70
+ # config.ru
71
+ require './app'
72
+ run Sinatra::Application
73
+ ```
74
+
75
+ You can then start your application using:
76
+
77
+ ```
78
+ $ bundle exec puma
71
79
  ```
72
80
 
73
81
  ## Configuration
74
82
 
75
- Puma provides numerous options. Consult `puma -h` (or `puma --help`) for a full list of CLI options, or see [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb).
83
+ Puma provides numerous options. Consult `puma -h` (or `puma --help`) for a full list of CLI options, or see `Puma::DSL` or [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb).
76
84
 
77
85
  You can also find several configuration examples as part of the
78
86
  [test](https://github.com/puma/puma/tree/master/test/config) suite.
79
87
 
88
+ For debugging purposes, you can set the environment variable `PUMA_LOG_CONFIG` with a value
89
+ and the loaded configuration will be printed as part of the boot process.
90
+
80
91
  ### Thread Pool
81
92
 
82
93
  Puma uses a thread pool. You can set the minimum and maximum number of threads that are available in the pool with the `-t` (or `--threads`) flag:
@@ -135,12 +146,12 @@ before_fork do
135
146
  end
136
147
  ```
137
148
 
138
- Preloading can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app copies the code of master into the workers.
149
+ Preloading can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and `preload_app!` copies the code of master into the workers.
139
150
 
140
151
  ### Error handling
141
152
 
142
153
  If puma encounters an error outside of the context of your application, it will respond with a 500 and a simple
143
- textual error message (see `lowlevel_error` in [this file](https://github.com/puma/puma/blob/master/lib/puma/server.rb)).
154
+ textual error message (see `Puma::Server#lowlevel_error` or [server.rb](https://github.com/puma/puma/blob/master/lib/puma/server.rb)).
144
155
  You can specify custom behavior for this scenario. For example, you can report the error to your third-party
145
156
  error-tracking service (in this example, [rollbar](https://rollbar.com)):
146
157
 
@@ -193,7 +204,7 @@ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_fil
193
204
  $ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA'
194
205
  ```
195
206
 
196
- See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites.
207
+ See https://www.openssl.org/docs/man1.1.1/man1/ciphers.html for cipher filter format and full list of cipher suites.
197
208
 
198
209
  Disable TLS v1 with the `no_tlsv1` option:
199
210
 
@@ -201,6 +212,23 @@ Disable TLS v1 with the `no_tlsv1` option:
201
212
  $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true'
202
213
  ```
203
214
 
215
+ #### Controlling OpenSSL Verification Flags
216
+
217
+ To enable verification flags offered by OpenSSL, use `verification_flags` (not available for JRuby):
218
+
219
+ ```
220
+ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_flags=PARTIAL_CHAIN'
221
+ ```
222
+
223
+ You can also set multiple verification flags (by separating them with coma):
224
+
225
+ ```
226
+ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_flags=PARTIAL_CHAIN,CRL_CHECK'
227
+ ```
228
+
229
+ List of available flags: `USE_CHECK_TIME`, `CRL_CHECK`, `CRL_CHECK_ALL`, `IGNORE_CRITICAL`, `X509_STRICT`, `ALLOW_PROXY_CERTS`, `POLICY_CHECK`, `EXPLICIT_POLICY`, `INHIBIT_ANY`, `INHIBIT_MAP`, `NOTIFY_POLICY`, `EXTENDED_CRL_SUPPORT`, `USE_DELTAS`, `CHECK_SS_SIGNATURE`, `TRUSTED_FIRST`, `SUITEB_128_LOS_ONLY`, `SUITEB_192_LOS`, `SUITEB_128_LOS`, `PARTIAL_CHAIN`, `NO_ALT_CHAINS`, `NO_CHECK_TIME`
230
+ (see https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_hostflags.html#VERIFICATION-FLAGS).
231
+
204
232
  ### Control/Status Server
205
233
 
206
234
  Puma has a built-in status and control app that can be used to query and control Puma.
@@ -209,7 +237,7 @@ Puma has a built-in status and control app that can be used to query and control
209
237
  $ puma --control-url tcp://127.0.0.1:9293 --control-token foo
210
238
  ```
211
239
 
212
- Puma will start the control server on localhost port 9293. All requests to the control server will need to include control token (in this case, `token=foo`) as a query parameter. This allows for simple authentication. Check out [status.rb](https://github.com/puma/puma/blob/master/lib/puma/app/status.rb) to see what the status app has available.
240
+ Puma will start the control server on localhost port 9293. All requests to the control server will need to include control token (in this case, `token=foo`) as a query parameter. This allows for simple authentication. Check out `Puma::App::Status` or [status.rb](https://github.com/puma/puma/blob/master/lib/puma/app/status.rb) to see what the status app has available.
213
241
 
214
242
  You can also interact with the control server via `pumactl`. This command will restart Puma:
215
243
 
@@ -227,7 +255,7 @@ You can also provide a configuration file with the `-C` (or `--config`) flag:
227
255
  $ puma -C /path/to/config
228
256
  ```
229
257
 
230
- If no configuration file is specified, Puma will look for a configuration file at `config/puma.rb`. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` or the `RAILS_ENV` environment variables, Puma looks for configuration at `config/puma/<environment_name>.rb`.
258
+ If no configuration file is specified, Puma will look for a configuration file at `config/puma.rb`. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` or the `RAILS_ENV` environment variables, Puma first looks for configuration at `config/puma/<environment_name>.rb`, and then falls back to `config/puma.rb`.
231
259
 
232
260
  If you want to prevent Puma from looking for a configuration file in those locations, provide a dash as the argument to the `-C` (or `--config`) flag:
233
261
 
@@ -235,19 +263,19 @@ If you want to prevent Puma from looking for a configuration file in those locat
235
263
  $ puma -C "-"
236
264
  ```
237
265
 
238
- The other side-effects of setting the environment are whether to show stack traces (in `development` or `test`), and setting RACK_ENV may potentially affect middleware looking for this value to change their behavior. The default puma RACK_ENV value is `development`. You can see all config default values [here](https://github.com/puma/puma/blob/12d1706ddc71b89ed2ee26275e31c788e94ff541/lib/puma/configuration.rb#L170).
266
+ The other side-effects of setting the environment are whether to show stack traces (in `development` or `test`), and setting RACK_ENV may potentially affect middleware looking for this value to change their behavior. The default puma RACK_ENV value is `development`. You can see all config default values in `Puma::Configuration#puma_default_options` or [configuration.rb](https://github.com/puma/puma/blob/61c6213fbab/lib/puma/configuration.rb#L182-L204).
239
267
 
240
- Check out [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb) to see all available options.
268
+ Check out `Puma::DSL` or [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb) to see all available options.
241
269
 
242
270
  ## Restart
243
271
 
244
272
  Puma includes the ability to restart itself. When available (MRI, Rubinius, JRuby), Puma performs a "hot restart". This is the same functionality available in *Unicorn* and *NGINX* which keep the server sockets open between restarts. This makes sure that no pending requests are dropped while the restart is taking place.
245
273
 
246
- For more, see the [restart documentation](https://github.com/puma/puma/blob/master/docs/restart.md).
274
+ For more, see the [Restart documentation](docs/restart.md).
247
275
 
248
276
  ## Signals
249
277
 
250
- Puma responds to several signals. A detailed guide to using UNIX signals with Puma can be found in the [signals documentation](https://github.com/puma/puma/blob/master/docs/signals.md).
278
+ Puma responds to several signals. A detailed guide to using UNIX signals with Puma can be found in the [Signals documentation](docs/signals.md).
251
279
 
252
280
  ## Platform Constraints
253
281
 
@@ -255,6 +283,7 @@ Some platforms do not support all Puma features.
255
283
 
256
284
  * **JRuby**, **Windows**: server sockets are not seamless on restart, they must be closed and reopened. These platforms have no way to pass descriptors into a new process that is exposed to Ruby. Also, cluster mode is not supported due to a lack of fork(2).
257
285
  * **Windows**: Cluster mode is not supported due to a lack of fork(2).
286
+ * **Kubernetes**: The way Kubernetes handles pod shutdowns interacts poorly with server processes implementing graceful shutdown, like Puma. See the [kubernetes section of the documentation](docs/kubernetes.md) for more details.
258
287
 
259
288
  ## Known Bugs
260
289
 
@@ -273,18 +302,21 @@ end
273
302
 
274
303
  Puma has support for Capistrano with an [external gem](https://github.com/seuros/capistrano-puma).
275
304
 
276
- It is common to use process monitors with Puma. Modern process monitors like systemd or upstart
305
+ It is common to use process monitors with Puma. Modern process monitors like systemd or rc.d
277
306
  provide continuous monitoring and restarts for increased
278
307
  reliability in production environments:
279
308
 
280
- * [docs/jungle](https://github.com/puma/puma/tree/master/docs/jungle) for rc.d and upstart
281
- * [docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md)
309
+ * [rc.d](docs/jungle/rc.d/README.md)
310
+ * [systemd](docs/systemd.md)
311
+
312
+ Community guides:
313
+
314
+ * [Deploying Puma on OpenBSD using relayd and httpd](https://gist.github.com/anon987654321/4532cf8d6c59c1f43ec8973faa031103)
282
315
 
283
316
  ## Community Extensions
284
317
 
285
318
  ### Plugins
286
319
 
287
- * [puma-heroku](https://github.com/puma/puma-heroku) — default Puma configuration for running on Heroku
288
320
  * [puma-metrics](https://github.com/harmjanblok/puma-metrics) — export Puma metrics to Prometheus
289
321
  * [puma-plugin-statsd](https://github.com/yob/puma-plugin-statsd) — send Puma metrics to statsd
290
322
  * [puma-plugin-systemd](https://github.com/sj26/puma-plugin-systemd) — deeper integration with systemd for notify, status and watchdog
@@ -295,9 +327,7 @@ reliability in production environments:
295
327
 
296
328
  ## Contributing
297
329
 
298
- Find details for contributing in the [contribution guide].
299
-
300
- [contribution guide]: https://github.com/puma/puma/blob/master/CONTRIBUTING.md
330
+ Find details for contributing in the [contribution guide](CONTRIBUTING.md).
301
331
 
302
332
  ## License
303
333
 
@@ -5,24 +5,18 @@
5
5
 
6
6
  require 'rubygems'
7
7
 
8
- gems = ARGV.shift
8
+ cli_arg = ARGV.shift
9
9
 
10
10
  inc = ""
11
11
 
12
- if gems == "-I"
12
+ if cli_arg == "-I"
13
13
  inc = ARGV.shift
14
14
  $LOAD_PATH.concat inc.split(":")
15
- gems = ARGV.shift
16
- end
17
-
18
- gems.split(",").each do |s|
19
- name, ver = s.split(":",2)
20
- gem name, ver
21
15
  end
22
16
 
23
17
  module Puma; end
24
18
 
25
- Puma.const_set("WILD_ARGS", ["-I", inc, gems])
19
+ Puma.const_set("WILD_ARGS", ["-I", inc])
26
20
 
27
21
  require 'puma/cli'
28
22
 
@@ -0,0 +1,19 @@
1
+ # Compile Options
2
+
3
+ There are some `cflags` provided to change Puma's default configuration for its C extension.
4
+
5
+ ## Query String, `PUMA_QUERY_STRING_MAX_LENGTH`
6
+
7
+ By default, the max length of `QUERY_STRING` is `1024 * 10`. But you may want to adjust it to allow accept larger queries in GET requests.
8
+
9
+ For manual install, pass the `PUMA_QUERY_STRING_MAX_LENGTH` option like this:
10
+
11
+ ```
12
+ gem install puma -- --with-cflags="-D PUMA_QUERY_STRING_MAX_LENGTH=64000"
13
+ ```
14
+
15
+ For Bundler, use its configuration system:
16
+
17
+ ```
18
+ bundle config build.puma "--with-cflags='-D PUMA_QUERY_STRING_MAX_LENGTH=64000'"
19
+ ```
@@ -1,4 +1,4 @@
1
- # Deployment engineering for puma
1
+ # Deployment engineering for Puma
2
2
 
3
3
  Puma is software that is expected to be run in a deployed environment eventually.
4
4
  You can certainly use it as your dev server only, but most people look to use
@@ -7,12 +7,11 @@ it in their production deployments as well.
7
7
  To that end, this is meant to serve as a foundation of wisdom how to do that
8
8
  in a way that increases happiness and decreases downtime.
9
9
 
10
- ## Specifying puma
10
+ ## Specifying Puma
11
11
 
12
12
  Most people want to do this by putting `gem "puma"` into their Gemfile, so we'll
13
13
  go ahead and assume that. Go add it now... we'll wait.
14
14
 
15
-
16
15
  Welcome back!
17
16
 
18
17
  ## Single vs Cluster mode
@@ -20,7 +19,7 @@ Welcome back!
20
19
  Puma was originally conceived as a thread-only webserver, but grew the ability to
21
20
  also use processes in version 2.
22
21
 
23
- To run puma in single mode (e.g. for a development environment) you will need to
22
+ To run `puma` in single mode (e.g. for a development environment) you will need to
24
23
  set the number of workers to 0, anything above will run in cluster mode.
25
24
 
26
25
  Here are some rules of thumb for cluster mode:
@@ -30,7 +29,7 @@ Here are some rules of thumb for cluster mode:
30
29
  * Use cluster mode and set the number of workers to 1.5x the number of cpu cores
31
30
  in the machine, minimum 2.
32
31
  * Set the number of threads to desired concurrent requests / number of workers.
33
- Puma defaults to 16 and that's a decent number.
32
+ Puma defaults to 5 and that's a decent number.
34
33
 
35
34
  #### Migrating from Unicorn
36
35
 
@@ -82,7 +81,7 @@ thread to become available.
82
81
 
83
82
  Daemonization was removed in Puma 5.0. For alternatives, continue reading.
84
83
 
85
- I prefer to not daemonize my servers and use something like `runit` or `upstart` to
84
+ I prefer to not daemonize my servers and use something like `runit` or `systemd` to
86
85
  monitor them as child processes. This gives them fast response to crashes and
87
86
  makes it easy to figure out what is going on. Additionally, unlike `unicorn`,
88
87
  puma does not require daemonization to do zero-downtime restarts.
@@ -92,7 +91,7 @@ task and thus want it to live on past the `cap deploy`. To these people I say:
92
91
  You need to be using a process monitor. Nothing is making sure puma stays up in
93
92
  this scenario! You're just waiting for something weird to happen, puma to die,
94
93
  and to get paged at 3am. Do yourself a favor, at least the process monitoring
95
- your OS comes with, be it `sysvinit`, `upstart`, or `systemd`. Or branch out
94
+ your OS comes with, be it `sysvinit` or `systemd`. Or branch out
96
95
  and use `runit` or hell, even `monit`.
97
96
 
98
97
  ## Restarting
@@ -24,6 +24,8 @@ Similar to the `preload_app!` option, the `fork_worker` option allows your appli
24
24
 
25
25
  ### Limitations
26
26
 
27
+ - Not compatible with the `preload_app!` option
28
+
27
29
  - This mode is still very experimental so there may be bugs or edge-cases, particularly around expected behavior of existing hooks. Please open a [bug report](https://github.com/puma/puma/issues/new?template=bug_report.md) if you encounter any issues.
28
30
 
29
31
  - In order to fork new workers cleanly, worker 0 shuts down its server and stops serving requests so there are no open file descriptors or other kinds of shared global state between processes, and to maximize copy-on-write efficiency across the newly-forked workers. This may temporarily reduce total capacity of the cluster during a phased restart / refork.
@@ -1,9 +1,5 @@
1
1
  # Puma as a service
2
2
 
3
- ## Upstart
4
-
5
- See `/docs/jungle/upstart` for Ubuntu's upstart scripts.
6
-
7
3
  ## Systemd
8
4
 
9
5
  See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md).
@@ -23,7 +23,7 @@ puma_start()
23
23
  rb_ver=$(/usr/local/bin/jq -r ".servers[$i].ruby_version" /usr/local/etc/puma.conf)
24
24
  case $rb_env in
25
25
  "rbenv")
26
- su - $user -c "cd $dir && rbenv shell $rb_ver && bundle exec puma -C $dir/config/puma.rb -d"
26
+ cd $dir && rbenv shell $rb_ver && /usr/sbin/daemon -u $user bundle exec puma -C $dir/config/puma.rb
27
27
  ;;
28
28
  *)
29
29
  ;;
@@ -48,7 +48,7 @@ puma_restart()
48
48
  rb_ver=$(/usr/local/bin/jq -r ".servers[$i].ruby_version" /usr/local/etc/puma.conf)
49
49
  case $rb_env in
50
50
  "rbenv")
51
- su - $user -c "cd $dir && pkill ruby && rbenv shell $ruby_version && bundle exec puma -C $dir/config/puma.rb -d"
51
+ cd $dir && rbenv shell $rb_ver && /usr/sbin/daemon -u $user bundle exec puma -C $dir/config/puma.rb
52
52
  ;;
53
53
  *)
54
54
  ;;
@@ -0,0 +1,66 @@
1
+ # Kubernetes
2
+
3
+ ## Running Puma in Kubernetes
4
+
5
+ In general running Puma in Kubernetes works as-is, no special configuration is needed beyond what you would write anyway to get a new Kubernetes Deployment going. There is one known interaction between the way Kubernetes handles pod termination and how Puma handles `SIGINT`, where some request might be sent to Puma after it has already entered graceful shutdown mode and is no longer accepting requests. This can lead to dropped requests during rolling deploys. A workaround for this is listed at the end of this article.
6
+
7
+ ## Basic setup
8
+
9
+ Assuming you already have a running cluster and docker image repository, you can run a simple Puma app with the following example Dockerfile and Deployment specification. These are meant as examples only and are deliberately very minimal to the point of skipping many options that are recommended for running in production, like healthchecks and envvar configuration with ConfigMaps. In general you should check the [Kubernetes documentation](https://kubernetes.io/docs/home/) and [Docker documentation](https://docs.docker.com/) for a more comprehensive overview of the available options.
10
+
11
+ A basic Dockerfile example:
12
+ ```
13
+ FROM ruby:2.5.1-alpine # can be updated to newer ruby versions
14
+ RUN apk update && apk add build-base # and any other packages you need
15
+
16
+ # Only rebuild gem bundle if Gemfile changes
17
+ COPY Gemfile Gemfile.lock ./
18
+ RUN bundle install
19
+
20
+ # Copy over the rest of the files
21
+ COPY . .
22
+
23
+ # Open up port and start the service
24
+ EXPOSE 9292
25
+ CMD bundle exec rackup -o 0.0.0.0
26
+ ```
27
+
28
+ A sample `deployment.yaml`:
29
+ ```
30
+ ---
31
+ apiVersion: apps/v1
32
+ kind: Deployment
33
+ metadata:
34
+ name: my-awesome-puma-app
35
+ spec:
36
+ selector:
37
+ matchLabels:
38
+ app: my-awesome-puma-app
39
+ template:
40
+ metadata:
41
+ labels:
42
+ app: my-awesome-puma-app
43
+ service: my-awesome-puma-app
44
+ spec:
45
+ containers:
46
+ - name: my-awesome-puma-app
47
+ image: <your image here>
48
+ ports:
49
+ - containerPort: 9292
50
+ ```
51
+
52
+ ## Graceful shutdown and pod termination
53
+
54
+ For some high-throughput systems, it is possible that some HTTP requests will return responses with response codes in the 5XX range during a rolling deploy to a new version. This is caused by [the way that Kubernetes terminates a pod during rolling deploys](https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-terminating-with-grace):
55
+
56
+ 1. The replication controller determines a pod should be shut down.
57
+ 2. The Pod is set to the “Terminating” State and removed from the endpoints list of all Services, so that it receives no more requests.
58
+ 3. The pods pre-stop hook get called. The default for this is to send `SIGTERM` to the process inside the pod.
59
+ 4. The pod has up to `terminationGracePeriodSeconds` (default: 30 seconds) to gracefully shut down. Puma will do this (after it receives SIGTERM) by closing down the socket that accepts new requests and finishing any requests already running before exiting the Puma process.
60
+ 5. If the pod is still running after `terminationGracePeriodSeconds` has elapsed, the pod receives `SIGKILL` to make sure the process inside it stops. After that, the container exits and all other Kubernetes objects associated with it are cleaned up.
61
+
62
+ There is a subtle race condition between step 2 and 3: The replication controller does not synchronously remove the pod from the Services AND THEN call the pre-stop hook of the pod, but rather it asynchronously sends "remove this pod from your endpoints" requests to the Services and then immediately proceeds to invoke the pods' pre-stop hook. If the Service controller (typically something like nginx or haproxy) receives this request handles this request "too" late (due to internal lag or network latency between the replication and Service controllers) then it is possible that the Service controller will send one or more requests to a Puma process which has already shut down its listening socket. These requests will then fail with 5XX error codes.
63
+
64
+ The way Kubernetes works this way, rather than handling step 2 synchronously, is due to the CAP theorem: in a distributed system there is no way to guarantuee that any message will arrive promptly. In particular, waiting for all Service controllers to report back might get stuck for an indefinite time if one of them has already been terminated or if there has been a net split. A way to work around this is to add a sleep to the pre-stop hook of the same time as the `terminationGracePeriodSeconds` time. This will allow the Puma process to keep serving new requests during the entire grace period, although it will no longer receive new requests after all Service controllers have propagated the removal of the pod from their endpoint lists. Then, after `terminationGracePeriodSeconds`, the pod receives `SIGKILL` and closes down. If your process can't handle SIGKILL properly, for example because it needs to release locks in different services, you can also sleep for a shorter period (and/or increase `terminationGracePeriodSeconds`) as long as the time slept is longer than the time that your Service controllers take to propagate the pod removal. The downside of this workaround is that all pods will take at minimum the amount of time slept to shut down and this will increase the time required for your rolling deploy.
65
+
66
+ More discussions and links to relevant articles can be found in https://github.com/puma/puma/issues/2343.
@@ -31,7 +31,7 @@ server {
31
31
 
32
32
  location / {
33
33
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
34
- proxy_set_header Host $http_host;
34
+ proxy_set_header Host $host;
35
35
 
36
36
  # If the file exists as a static file serve it directly without
37
37
  # running all the other rewrite tests on it
@@ -8,7 +8,7 @@ operations.
8
8
  * [tmp\_restart](https://github.com/puma/puma/blob/master/lib/puma/plugin/tmp_restart.rb):
9
9
  Restarts the server if the file `tmp/restart.txt` is touched
10
10
  * [heroku](https://github.com/puma/puma-heroku/blob/master/lib/puma/plugin/heroku.rb):
11
- Packages up the default configuration used by puma on Heroku
11
+ Packages up the default configuration used by puma on Heroku (being sunset with the release of Puma 5.0)
12
12
 
13
13
  Plugins are activated in a puma configuration file (such as `config/puma.rb'`)
14
14
  by adding `plugin "name"`, such as `plugin "heroku"`.
@@ -1,41 +1,64 @@
1
- # Restarts
1
+ Puma provides three distinct kinds of restart operations, each for different use cases. Hot restarts and phased restarts are described here. The third kind of restart operation is called "refork" and is described in the documentation for [`fork_worker`](fork_worker.md).
2
2
 
3
- To perform a restart, there are 3 builtin mechanisms:
3
+ ## Hot restart
4
4
 
5
- * Send the `puma` process the `SIGUSR2` signal (normal restart)
6
- * Send the `puma` process the `SIGUSR1` signal (restart in phases (a "rolling restart"), cluster mode only)
7
- * Use the status server and issue `/restart`
5
+ To perform a "hot" restart, Puma performs an `exec` operation to start the process up again, so no memory is shared between the old process and the new process. As a result, it is safe to issue a restart any place where you would manually stop Puma and start it again. In particular, it is safe to upgrade Puma itself using a hot restart.
8
6
 
9
- No code is shared between the current and restarted process, so it should be safe to issue a restart any place where you would manually stop Puma and start it again.
7
+ If the new process is unable to load, it will simply exit. You should therefore run Puma under a process monitor when using it in production.
10
8
 
11
- If the new process is unable to load, it will simply exit. You should therefore run Puma under a process monitor (see below) when using it in production.
9
+ ### How-to
12
10
 
13
- ### Normal vs Hot vs Phased Restart
11
+ Any of the following will cause a Puma server to perform a hot restart:
14
12
 
15
- A hot restart means that no requests will be lost while deploying your new code, since the server socket is kept open between restarts.
13
+ * Send the `puma` process the `SIGUSR2` signal
14
+ * Issue a `GET` request to the Puma status/control server with the path `/restart`
15
+ * Issue `pumactl restart` (this uses the control server method if available, otherwise sends the `SIGUSR2` signal to the process)
16
16
 
17
- But beware, hot restart does not mean that the incoming requests won’t hang for multiple seconds while your new code has not fully deployed. If you need a zero downtime and zero hanging requests deploy, you must use phased restart.
17
+ ### Supported configurations
18
18
 
19
- When you run pumactl phased-restart, Puma kills workers one-by-one, meaning that at least another worker is still available to serve requests, which lead to zero hanging requests (yay!).
19
+ * Works in cluster mode and in single mode
20
+ * Supported on all platforms
20
21
 
21
- But again beware, upgrading an application sometimes involves upgrading the database schema. With phased restart, there may be a moment during the deployment where processes belonging to the previous version and processes belonging to the new version both exist at the same time. Any database schema upgrades you perform must therefore be backwards-compatible with the old application version.
22
+ ### Client experience
22
23
 
23
- If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (`pumactl restart`). That way, no code is shared while deploying (in that case, `preload_app!` might help for quicker deployment, see ["Clustered Mode" in the README](../README.md#clustered-mode)).
24
+ * All platforms: for clients with an in-flight request, those clients will be served responses before the connection is closed gracefully. Puma gracefully disconnects any idle HTTP persistent connections before restarting.
25
+ * On MRI or TruffleRuby on Linux and BSD: Clients who connect just before the server restarts may experience increased latency while the server stops and starts again, but their connections will not be closed prematurely.
26
+ * On Windows and on JRuby: Clients who connect just before a restart may experience "connection reset" errors.
24
27
 
25
- **Note**: Hot and phased restarts are only available on MRI, not on JRuby. They are also unavailable on Windows servers.
28
+ ### Additional notes
26
29
 
27
- ### Release Directory
30
+ * Only one version of the application is running at a time.
31
+ * `on_restart` is invoked just before the server shuts down. This can be used to clean up resources (like long-lived database connections) gracefully. Since Ruby 2.0, it is not typically necessary to explicitly close file descriptors on restart. This is because any file descriptor opened by Ruby will have the `FD_CLOEXEC` flag set, meaning that file descriptors are closed on `exec`. `on_restart` is useful, though, if your application needs to perform any more graceful protocol-specific shutdown procedures before closing connections.
28
32
 
29
- If your symlink releases into a common working directory (i.e., `/current` from Capistrano), Puma won't pick up your new changes when running phased restarts without additional configuration. You should set your working directory within Puma's config to specify the directory it should use. This is a change from earlier versions of Puma (< 2.15) that would infer the directory for you.
33
+ ## Phased restart
30
34
 
31
- ```ruby
32
- # config/puma.rb
35
+ Phased restarts replace all running workers in a Puma cluster. This is a useful way to gracefully upgrade the application that Puma is serving. A phased restart works by first killing an old worker, then starting a new worker, waiting until the new worker has successfully started before proceeding to the next worker. This process continues until all workers have been replaced. The master process is not restarted.
33
36
 
34
- directory '/var/www/current'
35
- ```
37
+ ### How-to
36
38
 
37
- ### Cleanup Code
39
+ Any of the following will cause a Puma server to perform a phased restart:
38
40
 
39
- Puma isn't able to understand all the resources that your app may use, so it provides a hook in the configuration file you pass to `-C` called `on_restart`. The block passed to `on_restart` will be called, unsurprisingly, just before Puma restarts itself.
41
+ * Send the `puma` process the `SIGUSR1` signal
42
+ * Issue a `GET` request to the Puma status/control server with the path `/phased-restart`
43
+ * Issue `pumactl phased-restart` (this uses the control server method if available, otherwise sends the `SIGUSR1` signal to the process)
40
44
 
41
- You should place code to close global log files, redis connections, etc. in this block so that their file descriptors don't leak into the restarted process. Failure to do so will result in slowly running out of descriptors and eventually obscure crashes as the server is restarted many times.
45
+ ### Supported configurations
46
+
47
+ * Works in cluster mode only
48
+ * To support upgrading the application that Puma is serving, ensure `prune_bundler` is enabled and that `preload_app` is disabled (it is disabled by default).
49
+ * Supported on all platforms where cluster mode is supported
50
+
51
+ ### Client experience
52
+
53
+ * In-flight requests are always served responses before the connection is closed gracefully
54
+ * Idle persistent connections are gracefully disconnected
55
+ * New connections are not lost, and clients will not experience any increase in latency (as long as the number of configured workers is greater than one)
56
+
57
+ ### Additional notes
58
+
59
+ * When a phased restart begins, the Puma master process changes its current working directory to the directory specified by the `directory` option. If `directory` is set to symlink, this is automatically re-evaluated, so this mechanism can be used to upgrade the application.
60
+ * On a single server, it's possible that two versions of the application are running concurrently during a phased restart.
61
+ * `on_restart` is not invoked
62
+ * Phased restarts can be slow for Puma clusters with many workers. Hot restarts often complete more quickly, but at the cost of increased latency during the restart.
63
+ * Phased restarts cannot be used to upgrade any gems loaded by the Puma master process, including `puma` itself, anything in `extra_runtime_dependencies`, or dependencies thereof. Upgrading other gems is safe.
64
+ * If you remove the gems from old releases as part of your deployment strategy, there are additional considerations. Do not put any gems into `extra_runtime_dependencies` that have native extensions or have dependencies that have native extensions (one common example is `puma_worker_killer` and its dependency on `ffi`). Workers will fail on boot during a phased restart. The underlying issue is recorded in [an issue on the rubygems project](https://github.com/rubygems/rubygems/issues/4004). Hot restarts are your only option here if you need these dependencies.