puma 6.5.0 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63fdc6ae2fa73c81ae2f7b46eab301f0fa4f3ad7363f6299736b349fcf3421f8
4
- data.tar.gz: 80573e037561faed9663644fcf281f717b7e60c1b8a41f3215bb530ac53a9320
3
+ metadata.gz: 94e6b5d56525a92e2e279d59467572a254c7c03b9dc14a3e91ec5eba67b54620
4
+ data.tar.gz: 55e78ab10a5d222cce09dc89a92fbbd15d8e8ae7abbec95ca309e2903f68bd97
5
5
  SHA512:
6
- metadata.gz: 18fa6b3bb2dd0b2397cc92feead8728c6582fb94190d0a669147a9cd391eb0d61927427cc843f7d4385de46b281d1461d648edc818a01e495fc5341dbdda3d30
7
- data.tar.gz: 47877aca488f7c25549a0bcc07541e789a1de9d582c9e686949e311c258442128821d7187c478ffd7421acde1db8e88ed45ddb9f150b4396326f63404b78b6ee
6
+ metadata.gz: 750f9ec059b9710eb5ae739dc04ad600fff8386a40dc3ef83500189caacf8b7413d01eb60d751befd1aa077f3565e60617d13ceba8b9a0c29912abf410b700ff
7
+ data.tar.gz: 1a56b1696333ae86e643bd045ebe83f6a1700ac5df948113a443326e867ce17bf3f4b7e05e01cda6b99605130a2b77ccf49d2ae153ad2e879971c7238d652548
data/History.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 6.6.0 / 2025-01-29
2
+
3
+ * Features
4
+ * Option to turn off SIGUSR2 trapping ([#3570], [#3567])
5
+ * Shorten `ThreadPool` trimmer and reaper thread names ([#3383])
6
+ * Add after_refork hook ([#3386])
7
+ * Add busy threads stat ([#3517])
8
+ * Add a debug log before running each type of hook ([#3375])
9
+ * Allow alternative schemes in Binder ([#3348], [#3302])
10
+ * Avoid spawning `Threadpool#trim` thread if pool size is fixed ([#3384])
11
+
12
+ * Bugfixes
13
+ * Change `HttpParserError` to be subclass of `StandardError` ([#3590], [#3552])
14
+ * added test cases
15
+ * fix update phased restart symlink folder
16
+
17
+ * Performance
18
+ * Only ping worker 0 during phased restart if using fork worker ([#3568])
19
+
20
+ * Refactor
21
+ * Fix multi-delimiter split to get status app token ([#3505])
22
+ * Change ping to use const ([#3595])
23
+ * Fixup use of Puma::Const::PipeRequest constants ([#3565])
24
+ * Update DSL hook processing logic to be consistent ([#3376])
25
+
1
26
  ## 6.5.0 / 2024-11-23
2
27
 
3
28
  * Features
@@ -2125,6 +2150,22 @@ be added back in a future date when a java Puma::MiniSSL is added.
2125
2150
  * Bugfixes
2126
2151
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
2127
2152
 
2153
+ [#3570]:https://github.com/puma/puma/pull/3570 "PR by @mohamedhafez, merged 2024-12-30"
2154
+ [#3567]:https://github.com/puma/puma/issues/3567 "Issue by @mohamedhafez, closed 2024-12-30"
2155
+ [#3383]:https://github.com/puma/puma/pull/3383 "PR by @joshuay03, merged 2024-11-29"
2156
+ [#3386]:https://github.com/puma/puma/pull/3386 "PR by @Drakula2k, merged 2024-11-27"
2157
+ [#3517]:https://github.com/puma/puma/pull/3517 "PR by @jjb, merged 2024-11-26"
2158
+ [#3375]:https://github.com/puma/puma/pull/3375 "PR by @joshuay03, merged 2024-11-23"
2159
+ [#3348]:https://github.com/puma/puma/pull/3348 "PR by @tomurb, merged 2024-11-23"
2160
+ [#3302]:https://github.com/puma/puma/issues/3302 "Issue by @benburkert, closed 2024-11-23"
2161
+ [#3384]:https://github.com/puma/puma/pull/3384 "PR by @joshuay03, merged 2024-11-23"
2162
+ [#3590]:https://github.com/puma/puma/pull/3590 "PR by @MSP-Greg, merged 2025-01-01"
2163
+ [#3552]:https://github.com/puma/puma/issues/3552 "Issue by @utay, closed 2025-01-01"
2164
+ [#3568]:https://github.com/puma/puma/pull/3568 "PR by @joshuay03, merged 2024-12-11"
2165
+ [#3505]:https://github.com/puma/puma/pull/3505 "PR by @AnthonyClark, merged 2025-01-27"
2166
+ [#3595]:https://github.com/puma/puma/pull/3595 "PR by @nateberkopec, merged 2025-01-07"
2167
+ [#3565]:https://github.com/puma/puma/pull/3565 "PR by @MSP-Greg, merged 2024-11-28"
2168
+ [#3376]:https://github.com/puma/puma/pull/3376 "PR by @joshuay03, merged 2024-11-23"
2128
2169
  [#3407]:https://github.com/puma/puma/pull/3407 "PR by @JacobEvelyn, merged 2024-11-05"
2129
2170
  [#3439]:https://github.com/puma/puma/pull/3439 "PR by @codergeek121, merged 2024-11-04"
2130
2171
  [#3437]:https://github.com/puma/puma/issues/3437 "Issue by @rafaelfranca, closed 2024-11-04"
@@ -2709,14 +2750,14 @@ be added back in a future date when a java Puma::MiniSSL is added.
2709
2750
  [#782]:https://github.com/puma/puma/issues/782 "Issue by @Tonkpils, closed 2016-07-19"
2710
2751
  [#1010]:https://github.com/puma/puma/issues/1010 "Issue by @mneumark, closed 2016-07-19"
2711
2752
  [#959]:https://github.com/puma/puma/issues/959 "Issue by @mwpastore, closed 2016-04-22"
2712
- [#840]:https://github.com/puma/puma/issues/840 "Issue by @maxkwallace, closed 2016-04-07"
2753
+ [#840]:https://github.com/puma/puma/issues/840 "Issue by @marisawallace, closed 2016-04-07"
2713
2754
  [#1007]:https://github.com/puma/puma/pull/1007 "PR by @willnet, merged 2016-06-24"
2714
2755
  [#1014]:https://github.com/puma/puma/pull/1014 "PR by @szymon-jez, merged 2016-07-11"
2715
2756
  [#1015]:https://github.com/puma/puma/pull/1015 "PR by @bf4, merged 2016-07-19"
2716
2757
  [#1017]:https://github.com/puma/puma/pull/1017 "PR by @jorihardman, merged 2016-07-19"
2717
2758
  [#954]:https://github.com/puma/puma/pull/954 "PR by @jf, merged 2016-04-12"
2718
2759
  [#955]:https://github.com/puma/puma/pull/955 "PR by @jf, merged 2016-04-22"
2719
- [#956]:https://github.com/puma/puma/pull/956 "PR by @maxkwallace, merged 2016-04-12"
2760
+ [#956]:https://github.com/puma/puma/pull/956 "PR by @marisawallace, merged 2016-04-12"
2720
2761
  [#960]:https://github.com/puma/puma/pull/960 "PR by @kmayer, merged 2016-04-15"
2721
2762
  [#969]:https://github.com/puma/puma/pull/969 "PR by @frankwong15, merged 2016-05-10"
2722
2763
  [#970]:https://github.com/puma/puma/pull/970 "PR by @willnet, merged 2016-04-26"
data/README.md CHANGED
@@ -159,7 +159,7 @@ on_worker_boot do
159
159
  end
160
160
  ```
161
161
 
162
- In addition, there is an `on_refork` hook which is used only in [`fork_worker` mode](docs/fork_worker.md),
162
+ In addition, there is an `on_refork` and `after_refork` hooks which are used only in [`fork_worker` mode](docs/fork_worker.md),
163
163
  when the worker 0 child process forks a grandchild worker:
164
164
 
165
165
  ```ruby
@@ -169,6 +169,13 @@ on_refork do
169
169
  end
170
170
  ```
171
171
 
172
+ ```ruby
173
+ after_refork do
174
+ # Used only when fork_worker mode is enabled. Add code to run inside the Puma worker 0
175
+ # child process after it forks a grandchild worker.
176
+ end
177
+ ```
178
+
172
179
  Importantly, note the following considerations when Ruby forks a child process:
173
180
 
174
181
  1. File descriptors such as network sockets **are** copied from the parent to the forked
@@ -186,6 +193,7 @@ Therefore, we recommend the following:
186
193
  2. If (1) is not possible, use `before_fork` and `on_refork` to disconnect the parent's socket
187
194
  connections when forking, so that they are not accidentally copied to the child process.
188
195
  3. Use `on_worker_boot` to restart any background threads on the forked child.
196
+ 4. Use `after_refork` to restart any background threads on the parent.
189
197
 
190
198
  #### Master process lifecycle hooks
191
199
 
data/docs/fork_worker.md CHANGED
@@ -24,7 +24,13 @@ The `fork_worker` option allows your application to be initialized only once for
24
24
 
25
25
  ### Usage Considerations
26
26
 
27
- - `fork_worker` introduces a new `on_refork` configuration hook. If you were using the `before_fork` hook previously, we generally recommend to copy its logic to `on_refork`. Note that `fork_worker` triggers the `before_fork` configuration hook *only* when initially forking the master process to worker 0, and triggers the `on_refork` hook on all subsequent forks from worker 0 to additional workers.
27
+ - `fork_worker` introduces new `on_refork` and `after_refork` configuration hooks. Note the following:
28
+ - When initially forking the parent process to the worker 0 child, `before_fork` will trigger on the parent process and `on_worker_boot` will trigger on the worker 0 child as normal.
29
+ - When forking the worker 0 child to grandchild workers, `on_refork` and `after_refork` will trigger on the worker 0 child, and `on_worker_boot` will trigger on each grandchild worker.
30
+ - For clarity, `before_fork` does not trigger on worker 0, and `after_refork` does not trigger on the grandchild.
31
+ - As a general migration guide:
32
+ - Copy any logic within your existing `before_fork` hook to the `on_refork` hook.
33
+ - Consider to copy logic from your `on_worker_boot` hook to the `after_refork` hook, if it is needed to reset the state of worker 0 after it forks.
28
34
 
29
35
  ### Limitations
30
36
 
data/docs/java_options.md CHANGED
@@ -12,6 +12,7 @@ Moreover, default values may be used in case of invalid inputs.
12
12
  | PUMA_QUERY_STRING_MAX_LENGTH | 1024 * 10 | Positive natural number |
13
13
  | PUMA_REQUEST_PATH_MAX_LENGTH | 8192 | Positive natural number |
14
14
  | PUMA_REQUEST_URI_MAX_LENGTH | 1024 * 12 | Positive natural number |
15
+ | PUMA_SKIP_SIGUSR2 | nil | n/a |
15
16
 
16
17
  ## Examples
17
18
 
@@ -46,3 +47,8 @@ foo@bar:~ curl "http://localhost:9292${path}"
46
47
  Hello World
47
48
  ```
48
49
 
50
+ ### Java Flight Recorder Compatibility
51
+
52
+ Unfortunately Java Flight Recorder uses `SIGUSR2` internally. If you wish to
53
+ use JFR, turn off Puma's trapping of `SIGUSR2` by setting the environment variable
54
+ `PUMA_SKIP_SIGUSR2` to any value.
data/docs/plugins.md CHANGED
@@ -36,3 +36,7 @@ object that is useful for additional configuration.
36
36
 
37
37
  Public methods in [`Puma::Plugin`](../lib/puma/plugin.rb) are treated as a
38
38
  public API for plugins.
39
+
40
+ ## Binder hooks
41
+
42
+ There's `Puma::Binder#before_parse` method that allows to add proc to run before the body of `Puma::Binder#parse`. Example of usage can be found in [that repository](https://github.com/anchordotdev/puma-acme/blob/v0.1.3/lib/puma/acme/plugin.rb#L97-L118) (`before_parse_hook` could be renamed `before_parse`, making monkey patching of [binder.rb](https://github.com/anchordotdev/puma-acme/blob/v0.1.3/lib/puma/acme/binder.rb) is unnecessary).
data/docs/stats.md CHANGED
@@ -55,10 +55,14 @@ end
55
55
 
56
56
  When Puma runs in single mode, these stats are available at the top level. When Puma runs in cluster mode, these stats are available within the `worker_status` array in a hash labeled `last_status`, in an array of hashes where one hash represents each worker.
57
57
 
58
- * backlog: requests that are waiting for an available thread to be available. if this is above 0, you need more capacity [always true?]
58
+ * backlog: requests that are waiting for an available thread to be available. if this is frequently above 0, you need more capacity.
59
59
  * running: how many threads are spawned. A spawned thread may be busy processing a request or waiting for a new request. If `min_threads` and `max_threads` are set to the same number,
60
60
  this will be a never-changing number (other than rare cases when a thread dies, etc).
61
- * pool_capacity: the number of requests that the server is capable of taking right now. For example, if the number is 5, then it means there are 5 threads sitting idle ready to take a request. If one request comes in, then the value would be 4 until it finishes processing. If the minimum threads allowed is zero, this number will still have a maximum value of the maximum threads allowed.
61
+ * busy_threads: `running` - `how many threads are waiting to receive work` + `how many requests are waiting for a thread to pick them up`.
62
+ this is a "wholistic" stat reflecting the overall current state of work to be done and the capacity to do it.
63
+ * pool_capacity: `how many threads are waiting to receive work` + `max_threads` - `running`. In a typical configuration where `min_threads`
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 `busy_theads`, which is usually a more useful stat.
62
66
  * max_threads: the maximum number of threads Puma is configured to spool per worker
63
67
  * requests_count: the number of requests this worker has served since starting
64
68
 
@@ -77,7 +77,7 @@ public class Http11 extends RubyObject {
77
77
 
78
78
  public static void createHttp11(Ruby runtime) {
79
79
  RubyModule mPuma = runtime.defineModule("Puma");
80
- mPuma.defineClassUnder("HttpParserError",runtime.getClass("IOError"),runtime.getClass("IOError").getAllocator());
80
+ mPuma.defineClassUnder("HttpParserError",runtime.getClass("StandardError"),runtime.getClass("StandardError").getAllocator());
81
81
 
82
82
  RubyClass cHttpParser = mPuma.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
83
83
  cHttpParser.defineAnnotatedMethods(Http11.class);
@@ -475,7 +475,7 @@ void Init_puma_http11(void)
475
475
  DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
476
476
  DEF_GLOBAL(request_path, "REQUEST_PATH");
477
477
 
478
- eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
478
+ eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eStandardError);
479
479
  rb_global_variable(&eHttpParserError);
480
480
 
481
481
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
@@ -80,7 +80,7 @@ module Puma
80
80
 
81
81
  def authenticate(env)
82
82
  return true unless @auth_token
83
- env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
83
+ env['QUERY_STRING'].to_s.split(/[&;]/).include? "token=#{@auth_token}"
84
84
  end
85
85
 
86
86
  def rack_response(status, body, content_type='application/json')
data/lib/puma/binder.rb CHANGED
@@ -142,7 +142,14 @@ module Puma
142
142
  end
143
143
  end
144
144
 
145
+ def before_parse(&block)
146
+ @before_parse ||= []
147
+ @before_parse << block if block
148
+ @before_parse
149
+ end
150
+
145
151
  def parse(binds, log_writer = nil, log_msg = 'Listening')
152
+ before_parse.each(&:call)
146
153
  log_writer ||= @log_writer
147
154
  binds.each do |str|
148
155
  uri = URI.parse str
@@ -88,25 +88,27 @@ module Puma
88
88
  server.begin_restart(true)
89
89
  @config.run_hooks(:before_refork, nil, @log_writer, @hook_data)
90
90
  end
91
+ elsif idx == -2 # refork cycle is done
92
+ @config.run_hooks(:after_refork, nil, @log_writer, @hook_data)
91
93
  elsif idx == 0 # restart server
92
94
  restart_server << true << false
93
95
  else # fork worker
94
96
  worker_pids << pid = spawn_worker(idx)
95
- @worker_write << "#{Puma::Const::PipeRequest::FORK}#{pid}:#{idx}\n" rescue nil
97
+ @worker_write << "#{PIPE_FORK}#{pid}:#{idx}\n" rescue nil
96
98
  end
97
99
  end
98
100
  end
99
101
  end
100
102
 
101
103
  Signal.trap "SIGTERM" do
102
- @worker_write << "#{Puma::Const::PipeRequest::EXTERNAL_TERM}#{Process.pid}\n" rescue nil
104
+ @worker_write << "#{PIPE_EXTERNAL_TERM}#{Process.pid}\n" rescue nil
103
105
  restart_server.clear
104
106
  server.stop
105
107
  restart_server << false
106
108
  end
107
109
 
108
110
  begin
109
- @worker_write << "#{Puma::Const::PipeRequest::BOOT}#{Process.pid}:#{index}\n"
111
+ @worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
110
112
  rescue SystemCallError, IOError
111
113
  Puma::Util.purge_interrupt_queue
112
114
  STDERR.puts "Master seems to have exited, exiting."
@@ -122,7 +124,7 @@ module Puma
122
124
 
123
125
  stat_thread ||= Thread.new(@worker_write) do |io|
124
126
  Puma.set_thread_name "stat pld"
125
- base_payload = "p#{Process.pid}"
127
+ base_payload = "#{PIPE_PING}#{Process.pid}"
126
128
 
127
129
  while true
128
130
  begin
@@ -131,7 +133,8 @@ module Puma
131
133
  t = server.pool_capacity || 0
132
134
  m = server.max_threads || 0
133
135
  rc = server.requests_count || 0
134
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads":#{m}, "requests_count":#{rc} }\n!
136
+ bt = server.busy_threads || 0
137
+ payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads":#{m}, "requests_count":#{rc}, "busy_threads":#{bt} }\n!
135
138
  io << payload
136
139
  rescue IOError
137
140
  Puma::Util.purge_interrupt_queue
@@ -147,7 +150,7 @@ module Puma
147
150
  # exiting until any background operations are completed
148
151
  @config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
149
152
  ensure
150
- @worker_write << "#{Puma::Const::PipeRequest::TERM}#{Process.pid}\n" rescue nil
153
+ @worker_write << "#{PIPE_TERM}#{Process.pid}\n" rescue nil
151
154
  @worker_write.close
152
155
  end
153
156
 
@@ -51,7 +51,7 @@ module Puma
51
51
  @term
52
52
  end
53
53
 
54
- STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*) }/
54
+ STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*), "busy_threads":(?<busy_threads>\d*) }/
55
55
  private_constant :STATUS_PATTERN
56
56
 
57
57
  def ping!(status)
data/lib/puma/cluster.rb CHANGED
@@ -87,6 +87,10 @@ module Puma
87
87
 
88
88
  if @options[:fork_worker] && all_workers_in_phase?
89
89
  @fork_writer << "0\n"
90
+
91
+ if worker_at(0).phase > 0
92
+ @fork_writer << "-2\n"
93
+ end
90
94
  end
91
95
  end
92
96
 
@@ -456,7 +460,7 @@ module Puma
456
460
  req = read.read_nonblock(1)
457
461
  next unless req
458
462
 
459
- if req == Puma::Const::PipeRequest::WAKEUP
463
+ if req == PIPE_WAKEUP
460
464
  @next_check = Time.now
461
465
  next
462
466
  end
@@ -464,7 +468,7 @@ module Puma
464
468
  result = read.gets
465
469
  pid = result.to_i
466
470
 
467
- if req == Puma::Const::PipeRequest::BOOT || req == Puma::Const::PipeRequest::FORK
471
+ if req == PIPE_BOOT || req == PIPE_FORK
468
472
  pid, idx = result.split(':').map(&:to_i)
469
473
  w = worker_at idx
470
474
  w.pid = pid if w.pid.nil?
@@ -472,22 +476,22 @@ module Puma
472
476
 
473
477
  if w = @workers.find { |x| x.pid == pid }
474
478
  case req
475
- when Puma::Const::PipeRequest::BOOT
479
+ when PIPE_BOOT
476
480
  w.boot!
477
481
  log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
478
482
  @next_check = Time.now
479
483
  workers_not_booted -= 1
480
- when Puma::Const::PipeRequest::EXTERNAL_TERM
484
+ when PIPE_EXTERNAL_TERM
481
485
  # external term, see worker method, Signal.trap "SIGTERM"
482
486
  w.term!
483
- when Puma::Const::PipeRequest::TERM
487
+ when PIPE_TERM
484
488
  w.term unless w.term?
485
- when Puma::Const::PipeRequest::PING
489
+ when PIPE_PING
486
490
  status = result.sub(/^\d+/,'').chomp
487
491
  w.ping!(status)
488
492
  @events.fire(:ping!, w)
489
493
 
490
- if in_phased_restart && workers_not_booted.positive? && w0 = worker_at(0)
494
+ if in_phased_restart && @options[:fork_worker] && workers_not_booted.positive? && w0 = worker_at(0)
491
495
  w0.ping!(status)
492
496
  @events.fire(:ping!, w0)
493
497
  end
@@ -497,7 +501,7 @@ module Puma
497
501
  debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
498
502
  booted = true
499
503
  end
500
- when Puma::Const::PipeRequest::IDLE
504
+ when PIPE_IDLE
501
505
  if idle_workers[pid]
502
506
  idle_workers.delete pid
503
507
  else
@@ -318,6 +318,8 @@ module Puma
318
318
  # @param arg [Launcher, Int] `:on_restart` passes Launcher
319
319
  #
320
320
  def run_hooks(key, arg, log_writer, hook_data = nil)
321
+ log_writer.debug "Running #{key} hooks"
322
+
321
323
  @options.all_of(key).each do |b|
322
324
  begin
323
325
  if Array === b
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 = "6.5.0"
104
- CODE_NAME = "Sky's Version"
103
+ PUMA_VERSION = VERSION = "6.6.0"
104
+ CODE_NAME = "Return to Forever"
105
105
 
106
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
107
 
@@ -294,14 +294,15 @@ module Puma
294
294
 
295
295
  PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
296
296
 
297
+ # All constants are prefixed with `PIPE_` to avoid name collisions.
297
298
  module PipeRequest
298
- WAKEUP = "!"
299
- BOOT = "b"
300
- FORK = "f"
301
- EXTERNAL_TERM = "e"
302
- TERM = "t"
303
- PING = "p"
304
- IDLE = "i"
299
+ PIPE_WAKEUP = "!"
300
+ PIPE_BOOT = "b"
301
+ PIPE_FORK = "f"
302
+ PIPE_EXTERNAL_TERM = "e"
303
+ PIPE_TERM = "t"
304
+ PIPE_PING = "p"
305
+ PIPE_IDLE = "i"
305
306
  end
306
307
  end
307
308
  end
data/lib/puma/dsl.rb CHANGED
@@ -47,6 +47,7 @@ module Puma
47
47
  # | on_worker_boot | :before_worker_boot | inside, before |
48
48
  # | on_worker_shutdown | :before_worker_shutdown | inside, after |
49
49
  # | on_refork | :before_refork | inside |
50
+ # | after_refork | :after_refork | inside |
50
51
  #
51
52
  class DSL
52
53
  ON_WORKER_KEY = [String, Symbol].freeze
@@ -438,8 +439,7 @@ module Puma
438
439
  # end
439
440
  #
440
441
  def on_restart(&block)
441
- @options[:on_restart] ||= []
442
- @options[:on_restart] << block
442
+ process_hook :on_restart, nil, block, 'on_restart'
443
443
  end
444
444
 
445
445
  # Command to use to restart Puma. This should be just how to
@@ -731,8 +731,7 @@ module Puma
731
731
  def before_fork(&block)
732
732
  warn_if_in_single_mode('before_fork')
733
733
 
734
- @options[:before_fork] ||= []
735
- @options[:before_fork] << block
734
+ process_hook :before_fork, nil, block, 'before_fork'
736
735
  end
737
736
 
738
737
  # Code to run in a worker when it boots to setup
@@ -855,9 +854,32 @@ module Puma
855
854
  # @version 5.0.0
856
855
  #
857
856
  def on_refork(key = nil, &block)
857
+ warn_if_in_single_mode('on_refork')
858
+
858
859
  process_hook :before_refork, key, block, 'on_refork'
859
860
  end
860
861
 
862
+ # When `fork_worker` is enabled, code to run in Worker 0
863
+ # after all other workers are re-forked from this process,
864
+ # after the server has temporarily stopped serving requests
865
+ # (once per complete refork cycle).
866
+ #
867
+ # This can be used to re-open any connections to remote servers
868
+ # (database, Redis, ...) that were closed via on_refork.
869
+ #
870
+ # This can be called multiple times to add several hooks.
871
+ #
872
+ # @note Cluster mode with `fork_worker` enabled only.
873
+ #
874
+ # @example
875
+ # after_refork do
876
+ # puts 'After refork...'
877
+ # end
878
+ #
879
+ def after_refork(key = nil, &block)
880
+ process_hook :after_refork, key, block, 'after_refork'
881
+ end
882
+
861
883
  # Provide a block to be executed just before a thread is added to the thread
862
884
  # pool. Be careful: while the block executes, thread creation is delayed, and
863
885
  # probably a request will have to wait too! The new thread will not be added to
@@ -876,8 +898,7 @@ module Puma
876
898
  # end
877
899
  #
878
900
  def on_thread_start(&block)
879
- @options[:before_thread_start] ||= []
880
- @options[:before_thread_start] << block
901
+ process_hook :before_thread_start, nil, block, 'on_thread_start'
881
902
  end
882
903
 
883
904
  # Provide a block to be executed after a thread is trimmed from the thread
@@ -901,8 +922,7 @@ module Puma
901
922
  # end
902
923
  #
903
924
  def on_thread_exit(&block)
904
- @options[:before_thread_exit] ||= []
905
- @options[:before_thread_exit] << block
925
+ process_hook :before_thread_exit, nil, block, 'on_thread_exit'
906
926
  end
907
927
 
908
928
  # Code to run out-of-band when the worker is idle.
@@ -1415,8 +1435,8 @@ module Puma
1415
1435
  if workers_val == 0
1416
1436
  log_string =
1417
1437
  "Warning: You specified code to run in a `#{hook_name}` block, " \
1418
- "but Puma is not configured to run in cluster mode (worker count > 0 ), " \
1419
- "so your `#{hook_name}` block did not run"
1438
+ "but Puma is not configured to run in cluster mode (worker count > 0), " \
1439
+ "so your `#{hook_name}` block will not run."
1420
1440
 
1421
1441
  LogWriter.stdio.log(log_string)
1422
1442
  end
data/lib/puma/launcher.rb CHANGED
@@ -167,6 +167,13 @@ module Puma
167
167
  log "* phased-restart called but not available, restarting normally."
168
168
  return restart
169
169
  end
170
+
171
+ if @options.file_options[:tag].nil?
172
+ dir = File.realdirpath(@restart_dir)
173
+ @options[:tag] = File.basename(dir)
174
+ set_process_title
175
+ end
176
+
170
177
  true
171
178
  end
172
179
 
@@ -412,12 +419,14 @@ module Puma
412
419
  end
413
420
 
414
421
  def setup_signals
415
- begin
416
- Signal.trap "SIGUSR2" do
417
- restart
422
+ unless ENV["PUMA_SKIP_SIGUSR2"]
423
+ begin
424
+ Signal.trap "SIGUSR2" do
425
+ restart
426
+ end
427
+ rescue Exception
428
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
418
429
  end
419
- rescue Exception
420
- log "*** SIGUSR2 not implemented, signal based restart unavailable!"
421
430
  end
422
431
 
423
432
  unless Puma.jruby?
data/lib/puma/runner.rb CHANGED
@@ -8,6 +8,9 @@ module Puma
8
8
  # serve requests. This class spawns a new instance of `Puma::Server` via
9
9
  # a call to `start_server`.
10
10
  class Runner
11
+
12
+ include ::Puma::Const::PipeRequest
13
+
11
14
  def initialize(launcher)
12
15
  @launcher = launcher
13
16
  @log_writer = launcher.log_writer
@@ -27,7 +30,7 @@ module Puma
27
30
  def wakeup!
28
31
  return unless @wakeup
29
32
 
30
- @wakeup.write Puma::Const::PipeRequest::WAKEUP unless @wakeup.closed?
33
+ @wakeup.write PIPE_WAKEUP unless @wakeup.closed?
31
34
 
32
35
  rescue SystemCallError, IOError
33
36
  Puma::Util.purge_interrupt_queue
@@ -137,10 +137,7 @@ module Puma
137
137
  ENV.delete("NOTIFY_SOCKET") if unset_env
138
138
 
139
139
  begin
140
- Addrinfo.unix(sock, :DGRAM).connect do |s|
141
- s.close_on_exec = true
142
- s.write(state)
143
- end
140
+ Addrinfo.unix(sock, :DGRAM).connect { |s| s.write state }
144
141
  rescue StandardError => e
145
142
  raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
146
143
  end
data/lib/puma/server.rb CHANGED
@@ -233,6 +233,11 @@ module Puma
233
233
  @thread_pool&.pool_capacity
234
234
  end
235
235
 
236
+ # @!attribute [r] busy_threads
237
+ def busy_threads
238
+ @thread_pool&.busy_threads
239
+ end
240
+
236
241
  # Runs the server.
237
242
  #
238
243
  # If +background+ is true (the default) then a thread is spun
@@ -253,9 +258,8 @@ module Puma
253
258
  @reactor.run
254
259
  end
255
260
 
256
-
257
261
  @thread_pool.auto_reap! if options[:reaping_time]
258
- @thread_pool.auto_trim! if options[:auto_trim_time]
262
+ @thread_pool.auto_trim! if @min_threads != @max_threads && options[:auto_trim_time]
259
263
 
260
264
  @check, @notify = Puma::Util.pipe unless @notify
261
265
 
@@ -340,7 +344,7 @@ module Puma
340
344
  @idle_timeout_reached = true
341
345
 
342
346
  if @clustered
343
- @worker_write << "#{PipeRequest::IDLE}#{Process.pid}\n" rescue nil
347
+ @worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
344
348
  next
345
349
  else
346
350
  @log_writer.log "- Idle timeout reached"
@@ -353,7 +357,7 @@ module Puma
353
357
 
354
358
  if @idle_timeout_reached && @clustered
355
359
  @idle_timeout_reached = false
356
- @worker_write << "#{PipeRequest::IDLE}#{Process.pid}\n" rescue nil
360
+ @worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
357
361
  end
358
362
 
359
363
  ios.first.each do |sock|
@@ -646,7 +650,7 @@ module Puma
646
650
 
647
651
  # List of methods invoked by #stats.
648
652
  # @version 5.0.0
649
- STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
653
+ STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count, :busy_threads].freeze
650
654
 
651
655
  # Returns a hash of stats about the running server for reporting purposes.
652
656
  # @version 5.0.0
@@ -91,7 +91,8 @@ module Puma
91
91
  with_mutex do
92
92
  { backlog: @todo.size,
93
93
  running: @spawned,
94
- pool_capacity: @waiting + (@max - @spawned)
94
+ pool_capacity: @waiting + (@max - @spawned),
95
+ busy_threads: @spawned - @waiting + @todo.size
95
96
  }
96
97
  end
97
98
  end
@@ -369,12 +370,12 @@ module Puma
369
370
  end
370
371
 
371
372
  def auto_trim!(timeout=@auto_trim_time)
372
- @auto_trim = Automaton.new(self, timeout, "#{@name} threadpool trimmer", :trim)
373
+ @auto_trim = Automaton.new(self, timeout, "#{@name} tp trim", :trim)
373
374
  @auto_trim.start!
374
375
  end
375
376
 
376
377
  def auto_reap!(timeout=@reaping_time)
377
- @reaper = Automaton.new(self, timeout, "#{@name} threadpool reaper", :reap)
378
+ @reaper = Automaton.new(self, timeout, "#{@name} tp reap", :reap)
378
379
  @reaper.start!
379
380
  end
380
381
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.5.0
4
+ version: 6.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-22 00:00:00.000000000 Z
10
+ date: 2025-01-28 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: nio4r
@@ -132,7 +131,6 @@ metadata:
132
131
  homepage_uri: https://puma.io
133
132
  source_code_uri: https://github.com/puma/puma
134
133
  rubygems_mfa_required: 'true'
135
- post_install_message:
136
134
  rdoc_options: []
137
135
  require_paths:
138
136
  - lib
@@ -147,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
145
  - !ruby/object:Gem::Version
148
146
  version: '0'
149
147
  requirements: []
150
- rubygems_version: 3.5.22
151
- signing_key:
148
+ rubygems_version: 3.6.3
152
149
  specification_version: 4
153
150
  summary: A Ruby/Rack web server built for parallelism.
154
151
  test_files: []