raptor 0.26.0 → 0.27.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +57 -66
- data/lib/raptor/cli.rb +3 -0
- data/lib/raptor/cluster.rb +47 -11
- data/lib/raptor/embedded.rb +81 -15
- data/lib/raptor/plugin.rb +1 -0
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/cluster.rbs +16 -5
- data/sig/generated/raptor/embedded.rbs +26 -1
- data/sig/generated/raptor/plugin.rbs +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f72ac9941a9da99f3fe49f3464ac925ff6304cf196836502a480220eb99d48f
|
|
4
|
+
data.tar.gz: 7a72e415e3663c54ac3c4f2fcfd13481356b3357693298c082f2b2eda3727182
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf330c4d82552ee29b56b4bf0998355d3faad012147a5d34b9ade39d1f333ef4cc46b9d0e81c9b96516462fa920c3efe0c849fb716cdb158261cc3a6aff5014c
|
|
7
|
+
data.tar.gz: 9ff03e07f24109d0c1a0af66c309b6a49fa8f45c6db0aecd0a1c8507082d2e558a67bf72c01bf6c59f83c2aa379a41e8046f2b91f2d17deb8ecd59c5e44b162f
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ run proc { |_env| [200, { "content-type" => "text/plain" }, ["Hello, World!"]] }
|
|
|
37
37
|
```
|
|
38
38
|
> bundle exec raptor -w 10 -t 3 hello_world.ru
|
|
39
39
|
[Raptor 39704|Main|Main] Cluster initializing:
|
|
40
|
-
[Raptor 39704|Main|Main] ├─ Version: 0.
|
|
40
|
+
[Raptor 39704|Main|Main] ├─ Version: 0.27.0
|
|
41
41
|
[Raptor 39704|Main|Main] ├─ Ruby Version: ruby 4.0.9 (2026-10-23 revision 3e93b23afc) +YJIT +PRISM [arm64-darwin25]
|
|
42
42
|
[Raptor 39704|Main|Main] ├─ Environment: development
|
|
43
43
|
[Raptor 39704|Main|Main] ├─ Master PID: 39704
|
|
@@ -148,8 +148,11 @@ working directory. Pass `-c PATH` to point at a specific file.
|
|
|
148
148
|
worker_shutdown_timeout: 30,
|
|
149
149
|
refork_after: 1000, # `nil` on non-Linux
|
|
150
150
|
before_fork: [],
|
|
151
|
+
after_fork: [],
|
|
151
152
|
before_worker_boot: [],
|
|
152
153
|
before_worker_shutdown: [],
|
|
154
|
+
after_worker_drain: [],
|
|
155
|
+
after_worker_exit: [],
|
|
153
156
|
before_refork: [],
|
|
154
157
|
plugins: [],
|
|
155
158
|
stats_file: "tmp/raptor.json",
|
|
@@ -191,21 +194,9 @@ Raptor::Plugin.create(:stats_logger) do
|
|
|
191
194
|
end
|
|
192
195
|
```
|
|
193
196
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
`start(cluster)` runs once before the initial workers fork. `cluster.options` exposes configuration, including
|
|
198
|
-
plugin-specific keys. Append callbacks to its `before_fork`, `before_worker_boot`, `before_worker_shutdown`, and
|
|
199
|
-
`before_refork` arrays during startup. If your plugin opens a listener, close the worker's inherited copy with
|
|
200
|
-
`cluster.options[:before_worker_boot] << proc { @listener.close }` so only the master serves it.
|
|
201
|
-
|
|
202
|
-
`cluster.stats` returns per-worker snapshots. `cluster.stats_hash` returns the same aggregate structure as `GET /stats`,
|
|
203
|
-
including boot status, busy threads, capacity, backlog, and request counts. Both remain available while workers drain.
|
|
204
|
-
|
|
205
|
-
Override `stop` to close listeners and flush exporters. Raptor calls it after workers exit, in reverse plugin order,
|
|
206
|
-
then stops and joins threads created with `in_background`. These callbacks also run before hot restart and when startup
|
|
207
|
-
fails, so `stop` must handle partially initialized resources. Worker replacement and phased restart keep the same
|
|
208
|
-
master plugin instances running.
|
|
197
|
+
`cluster.options` exposes configuration and lifecycle hooks. `cluster.stats` and `cluster.stats_hash` provide worker
|
|
198
|
+
and aggregate statistics. Override `stop` to release plugin resources after workers exit, including during hot restart.
|
|
199
|
+
See the [API documentation](https://joshuay03.github.io/raptor/Raptor/Plugin.html) for lifecycle details.
|
|
209
200
|
|
|
210
201
|
## Bindings
|
|
211
202
|
|
|
@@ -274,7 +265,7 @@ Set `control_url` to a Unix socket URL such as `unix:///tmp/raptor-control.sock`
|
|
|
274
265
|
|
|
275
266
|
## (Micro) Benchmarks
|
|
276
267
|
|
|
277
|
-
Raptor 0.
|
|
268
|
+
Raptor 0.27.0 vs Puma 8.0.2 vs Falcon 0.57.0. Each case has 3 fresh-process samples, interleaved in seeded shuffled
|
|
278
269
|
rounds. Each sample warms up for 3s and measures for 15s. Metrics are independent medians. ± is (max − min) / 2 /
|
|
279
270
|
median, not a confidence interval. Failure counts below each table sum all samples, so a failed run cannot disappear in
|
|
280
271
|
a median. Throughput counts successful ordinary requests only.
|
|
@@ -302,90 +293,90 @@ accounting for shared pages instead of counting them fully in every worker.
|
|
|
302
293
|
|
|
303
294
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
304
295
|
| ------ | -------------- | --------------- | ------------ | --------------- | ------ | ------------- | ---------- | ------- |
|
|
305
|
-
| Raptor | Fixed (3) | - | 3.
|
|
306
|
-
| Raptor | Scaling (3–16) | - | 8.
|
|
307
|
-
| Puma | Fixed (3) | Fixed (3) | 1.
|
|
308
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 8.
|
|
309
|
-
| Falcon | Fibers | Fixed (3) |
|
|
310
|
-
| Falcon | Fibers | Scaling (3–16) |
|
|
296
|
+
| Raptor | Fixed (3) | - | 3.35k ±1.2% | - | 49.00 | - | 2.421 | 337.8 |
|
|
297
|
+
| Raptor | Scaling (3–16) | - | 8.21k ±0.6% | - | 22.09 | - | 0.468 | 323.6 |
|
|
298
|
+
| Puma | Fixed (3) | Fixed (3) | 1.53k ±0.7% | 54.4% lower | 120.94 | 146.8% higher | 0.969 | 272.0 |
|
|
299
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 8.15k ±0.5% | 0.7% lower | 22.23 | 0.6% higher | 0.467 | 319.5 |
|
|
300
|
+
| Falcon | Fibers | Fixed (3) | 11.07k ±0.5% | 230.3% higher | 15.44 | 68.5% lower | 0.439 | 446.6 |
|
|
301
|
+
| Falcon | Fibers | Scaling (3–16) | 11.07k ±0.5% | 34.8% higher | 15.44 | 30.1% lower | 0.439 | 446.6 |
|
|
311
302
|
|
|
312
303
|
### IO over HTTP/1.1 keep-alive
|
|
313
304
|
|
|
314
305
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
315
306
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
316
|
-
| Raptor | Fixed (3) | - | 3.
|
|
317
|
-
| Raptor | Scaling (3–16) | - | 7.39k ±
|
|
318
|
-
| Raptor | Fixed (16) | - | 7.
|
|
319
|
-
| Puma | Fixed (3) | Fixed (3) | 1.
|
|
320
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 7.
|
|
321
|
-
| Puma | Fixed (16) | Fixed (16) | 7.
|
|
322
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
323
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
324
|
-
| Falcon | Fibers | Fixed (16) | 7.
|
|
307
|
+
| Raptor | Fixed (3) | - | 3.09k ±6.5% | - | 51.42 | - | 2.346 | 348.9 |
|
|
308
|
+
| Raptor | Scaling (3–16) | - | 7.39k ±0.6% | - | 24.67 | - | 0.465 | 344.2 |
|
|
309
|
+
| Raptor | Fixed (16) | - | 7.48k ±0.5% | - | 24.30 | - | 0.453 | 344.1 |
|
|
310
|
+
| Puma | Fixed (3) | Fixed (3) | 1.49k ±0.4% | 51.7% lower | 100.59 | 95.6% higher | 1.009 | 282.5 |
|
|
311
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 7.39k ±0.4% | 0.0% higher | 24.58 | 0.4% lower | 0.434 | 310.0 |
|
|
312
|
+
| Puma | Fixed (16) | Fixed (16) | 7.52k ±0.4% | 0.5% higher | 24.16 | 0.6% lower | 0.428 | 316.3 |
|
|
313
|
+
| Falcon | Fibers | Fixed (3) | 7.34k ±2.1% | 137.4% higher | 24.78 | 51.8% lower | 0.382 | 329.6 |
|
|
314
|
+
| Falcon | Fibers | Scaling (3–16) | 7.34k ±2.1% | 0.7% lower | 24.78 | 0.4% higher | 0.382 | 329.6 |
|
|
315
|
+
| Falcon | Fibers | Fixed (16) | 7.34k ±2.1% | 1.9% lower | 24.78 | 2.0% higher | 0.382 | 329.6 |
|
|
325
316
|
|
|
326
317
|
### IO over HTTP/2
|
|
327
318
|
|
|
328
|
-
| Server | Mode | Raptor baseline | req/s
|
|
329
|
-
| ------ | -------------- | --------------- |
|
|
330
|
-
| Raptor | Fixed (3) | - | 1.61k ±
|
|
331
|
-
| Raptor | Scaling (3–16) | - |
|
|
332
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
333
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
319
|
+
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
320
|
+
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
321
|
+
| Raptor | Fixed (3) | - | 1.61k ±0.3% | - | 131.78 | - | 0.901 | 410.2 |
|
|
322
|
+
| Raptor | Scaling (3–16) | - | 7.24k ±8.6% | - | 30.21 | - | 0.563 | 940.4 |
|
|
323
|
+
| Falcon | Fibers | Fixed (3) | 7.82k ±1.7% | 386.4% higher | 25.75 | 80.5% lower | 0.426 | 526.6 |
|
|
324
|
+
| Falcon | Fibers | Scaling (3–16) | 7.82k ±1.7% | 8.0% higher | 25.75 | 14.7% lower | 0.426 | 526.6 |
|
|
334
325
|
|
|
335
326
|
### CPU over HTTP/1.1
|
|
336
327
|
|
|
337
328
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
338
329
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
339
|
-
| Raptor | Fixed (3) | - | 3.
|
|
340
|
-
| Raptor | Scaling (3–16) | - | 3.
|
|
341
|
-
| Puma | Fixed (3) | Fixed (3) | 3.
|
|
342
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 3.
|
|
343
|
-
| Falcon | Fibers | Fixed (3) | 3.
|
|
344
|
-
| Falcon | Fibers | Scaling (3–16) | 3.
|
|
330
|
+
| Raptor | Fixed (3) | - | 3.40k ±1.5% | - | 66.74 | - | 2.519 | 511.8 |
|
|
331
|
+
| Raptor | Scaling (3–16) | - | 3.45k ±3.9% | - | 62.67 | - | 2.389 | 504.6 |
|
|
332
|
+
| Puma | Fixed (3) | Fixed (3) | 3.24k ±0.9% | 4.7% lower | 57.00 | 14.6% lower | 2.457 | 444.5 |
|
|
333
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 3.09k ±2.7% | 10.4% lower | 49.54 | 21.0% lower | 2.182 | 437.4 |
|
|
334
|
+
| Falcon | Fibers | Fixed (3) | 3.53k ±0.8% | 3.8% higher | 47.04 | 29.5% lower | 2.430 | 432.3 |
|
|
335
|
+
| Falcon | Fibers | Scaling (3–16) | 3.53k ±0.8% | 2.2% higher | 47.04 | 24.9% lower | 2.430 | 432.3 |
|
|
345
336
|
|
|
346
337
|
### CPU over HTTP/1.1 keep-alive
|
|
347
338
|
|
|
348
339
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
349
340
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
350
|
-
| Raptor | Fixed (3) | - | 3.
|
|
351
|
-
| Raptor | Scaling (3–16) | - | 2.
|
|
352
|
-
| Puma | Fixed (3) | Fixed (3) | 3.
|
|
353
|
-
| Puma | Scaling (3–16) | Scaling (3–16) |
|
|
354
|
-
| Falcon | Fibers | Fixed (3) | 3.17k ±
|
|
355
|
-
| Falcon | Fibers | Scaling (3–16) | 3.17k ±
|
|
341
|
+
| Raptor | Fixed (3) | - | 3.12k ±0.3% | - | 50.35 | - | 2.534 | 483.3 |
|
|
342
|
+
| Raptor | Scaling (3–16) | - | 2.99k ±2.1% | - | 50.79 | - | 2.532 | 502.5 |
|
|
343
|
+
| Puma | Fixed (3) | Fixed (3) | 3.49k ±6.5% | 11.7% higher | 51.75 | 2.8% higher | 2.319 | 383.4 |
|
|
344
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 3.00k ±3.8% | 0.5% higher | 64.39 | 26.8% higher | 2.224 | 470.8 |
|
|
345
|
+
| Falcon | Fibers | Fixed (3) | 3.17k ±0.2% | 1.7% higher | 67.10 | 33.3% higher | 2.763 | 529.6 |
|
|
346
|
+
| Falcon | Fibers | Scaling (3–16) | 3.17k ±0.2% | 6.1% higher | 67.10 | 32.1% higher | 2.763 | 529.6 |
|
|
356
347
|
|
|
357
348
|
### CPU over HTTP/2
|
|
358
349
|
|
|
359
350
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
360
351
|
| ------ | -------------- | --------------- | ------------ | --------------- | ------ | ------------- | ---------- | ------- |
|
|
361
|
-
| Raptor | Fixed (3) | - | 3.
|
|
362
|
-
| Raptor | Scaling (3–16) | - | 3.
|
|
363
|
-
| Falcon | Fibers | Fixed (3) | 2.
|
|
364
|
-
| Falcon | Fibers | Scaling (3–16) | 2.
|
|
352
|
+
| Raptor | Fixed (3) | - | 3.32k ±3.7% | - | 70.25 | - | 2.374 | 583.2 |
|
|
353
|
+
| Raptor | Scaling (3–16) | - | 3.31k ±0.9% | - | 60.05 | - | 2.347 | 562.3 |
|
|
354
|
+
| Falcon | Fibers | Fixed (3) | 2.36k ±18.9% | 29.0% lower | 127.17 | 81.0% higher | 2.399 | 561.8 |
|
|
355
|
+
| Falcon | Fibers | Scaling (3–16) | 2.36k ±18.9% | 28.8% lower | 127.17 | 111.8% higher | 2.399 | 561.8 |
|
|
365
356
|
|
|
366
357
|
### 64 KiB body over HTTP/1.1 keep-alive
|
|
367
358
|
|
|
368
359
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
369
360
|
| ------ | -------------- | --------------- | ------------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
370
|
-
| Raptor | Scaling (3–16) | - | 94.
|
|
371
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 88.
|
|
372
|
-
| Falcon | Fibers | Scaling (3–16) | 103.
|
|
361
|
+
| Raptor | Scaling (3–16) | - | 94.57k ±1.5% | - | 3.70 | - | 0.051 | 592.0 |
|
|
362
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 88.51k ±0.7% | 6.4% lower | 3.67 | 0.6% lower | 0.054 | 1201.7 |
|
|
363
|
+
| Falcon | Fibers | Scaling (3–16) | 103.00k ±5.1% | 8.9% higher | 3.84 | 3.9% higher | 0.051 | 341.1 |
|
|
373
364
|
|
|
374
365
|
### IO + SSE (native) over HTTP/1.1 keep-alive
|
|
375
366
|
|
|
376
367
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
377
368
|
| ------------- | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
378
|
-
| Raptor | Fixed (3) | - |
|
|
379
|
-
| Raptor | Scaling (3–16) | - | 7.
|
|
380
|
-
| Puma | Fixed (3) | Fixed (3) | 0.
|
|
381
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 7.
|
|
382
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
383
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
384
|
-
| Raptor (enum) | Fixed (3) | - | 0.
|
|
369
|
+
| Raptor | Fixed (3) | - | 2.74k ±6.2% | - | 59.25 | - | 2.298 | 341.5 |
|
|
370
|
+
| Raptor | Scaling (3–16) | - | 7.41k ±1.2% | - | 24.53 | - | 0.471 | 345.4 |
|
|
371
|
+
| Puma | Fixed (3) | Fixed (3) | 0.34k ±5.4% | 87.5% lower | 361.94 | 510.8% higher | 1.715 | 263.9 |
|
|
372
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 7.34k ±1.0% | 1.0% lower | 24.70 | 0.7% higher | 0.430 | 310.9 |
|
|
373
|
+
| Falcon | Fibers | Fixed (3) | 7.30k ±1.0% | 165.9% higher | 24.88 | 58.0% lower | 0.379 | 356.6 |
|
|
374
|
+
| Falcon | Fibers | Scaling (3–16) | 7.30k ±1.0% | 1.6% lower | 24.88 | 1.5% higher | 0.379 | 356.6 |
|
|
375
|
+
| Raptor (enum) | Fixed (3) | - | 0.88k ±0.3% | - | 66.74 | - | 8.142 | 382.6 |
|
|
385
376
|
|
|
386
|
-
Puma, Fixed (3) had
|
|
377
|
+
Puma, Fixed (3) had 37 timeouts, 1 stalled stream across all samples.
|
|
387
378
|
|
|
388
|
-
Raptor (enum), Fixed (3) had
|
|
379
|
+
Raptor (enum), Fixed (3) had 16 timeouts across all samples.
|
|
389
380
|
|
|
390
381
|
> ruby 4.1.0dev (2026-11-10T00:02:04Z HEAD 7a07e456e4) +YJIT +PRISM [aarch64-linux]
|
|
391
382
|
> 10 workers, 120 HTTP/1.1 client connections, 40 HTTP/2 connections × 3 streams.
|
data/lib/raptor/cli.rb
CHANGED
|
@@ -67,8 +67,11 @@ module Raptor
|
|
|
67
67
|
worker_shutdown_timeout: 30,
|
|
68
68
|
refork_after: (RUBY_PLATFORM.include?("linux") ? 1000 : nil),
|
|
69
69
|
before_fork: [].freeze,
|
|
70
|
+
after_fork: [].freeze,
|
|
70
71
|
before_worker_boot: [].freeze,
|
|
71
72
|
before_worker_shutdown: [].freeze,
|
|
73
|
+
after_worker_drain: [].freeze,
|
|
74
|
+
after_worker_exit: [].freeze,
|
|
72
75
|
before_refork: [].freeze,
|
|
73
76
|
plugins: [].freeze,
|
|
74
77
|
stats_file: "tmp/raptor.json",
|
data/lib/raptor/cluster.rb
CHANGED
|
@@ -80,8 +80,11 @@ module Raptor
|
|
|
80
80
|
# @rbs @worker_drain_timeout: Integer
|
|
81
81
|
# @rbs @worker_shutdown_timeout: Integer
|
|
82
82
|
# @rbs @before_fork: Array[Proc]
|
|
83
|
+
# @rbs @after_fork: Array[Proc]
|
|
83
84
|
# @rbs @before_worker_boot: Array[Proc]
|
|
84
85
|
# @rbs @before_worker_shutdown: Array[Proc]
|
|
86
|
+
# @rbs @after_worker_drain: Array[Proc]
|
|
87
|
+
# @rbs @after_worker_exit: Array[Proc]
|
|
85
88
|
# @rbs @before_refork: Array[Proc]
|
|
86
89
|
# @rbs @stats_file: String?
|
|
87
90
|
# @rbs @control_server: ControlServer?
|
|
@@ -97,6 +100,7 @@ module Raptor
|
|
|
97
100
|
# @rbs @app: untyped
|
|
98
101
|
# @rbs @shutdown: bool
|
|
99
102
|
# @rbs @workers: Hash[Integer, Integer]
|
|
103
|
+
# @rbs @worker_indices: Hash[Integer, Integer]
|
|
100
104
|
# @rbs @timed_out: Set[Integer]
|
|
101
105
|
# @rbs @stats: Stats
|
|
102
106
|
# @rbs @started_at: Float
|
|
@@ -146,9 +150,12 @@ module Raptor
|
|
|
146
150
|
# @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
|
|
147
151
|
# @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
|
|
148
152
|
# @option options [Integer, Array<Integer>, nil] :refork_after request-count threshold(s) at which a warm worker is promoted to a fork source for phased refork; nil or 0 disables. Requires `PR_SET_CHILD_SUBREAPER` (Linux)
|
|
149
|
-
# @option options [Array<Proc>] :before_fork procs called in the master before
|
|
153
|
+
# @option options [Array<Proc>] :before_fork procs called in the master before each direct worker fork, not seed forks
|
|
154
|
+
# @option options [Array<Proc>] :after_fork procs called in the master after each direct fork attempt, including failures
|
|
150
155
|
# @option options [Array<Proc>] :before_worker_boot procs called with the worker index before it begins serving
|
|
151
156
|
# @option options [Array<Proc>] :before_worker_shutdown procs called with the worker index before its graceful shutdown
|
|
157
|
+
# @option options [Array<Proc>] :after_worker_drain procs called with the worker index after draining, before exit or seed promotion
|
|
158
|
+
# @option options [Array<Proc>] :after_worker_exit procs called in the master after reaping, with the worker index, pid, and Process::Status
|
|
152
159
|
# @option options [Array<Proc>] :before_refork procs called in a worker before it transitions to a seed
|
|
153
160
|
# @option options [Array<String, Symbol>] :plugins plugins to load and start in order
|
|
154
161
|
# @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
|
|
@@ -178,8 +185,11 @@ module Raptor
|
|
|
178
185
|
@worker_drain_timeout = options[:worker_drain_timeout]
|
|
179
186
|
@worker_shutdown_timeout = options[:worker_shutdown_timeout]
|
|
180
187
|
@before_fork = options[:before_fork] = Array(options[:before_fork]).dup
|
|
188
|
+
@after_fork = options[:after_fork] = Array(options[:after_fork]).dup
|
|
181
189
|
@before_worker_boot = options[:before_worker_boot] = Array(options[:before_worker_boot]).dup
|
|
182
190
|
@before_worker_shutdown = options[:before_worker_shutdown] = Array(options[:before_worker_shutdown]).dup
|
|
191
|
+
@after_worker_drain = options[:after_worker_drain] = Array(options[:after_worker_drain]).dup
|
|
192
|
+
@after_worker_exit = options[:after_worker_exit] = Array(options[:after_worker_exit]).dup
|
|
183
193
|
@before_refork = options[:before_refork] = Array(options[:before_refork]).dup
|
|
184
194
|
@stats_file = options[:stats_file]
|
|
185
195
|
@control_server = ControlServer.new(options[:control_url]) { stats_hash } if options[:control_url]
|
|
@@ -208,6 +218,7 @@ module Raptor
|
|
|
208
218
|
|
|
209
219
|
@shutdown = false
|
|
210
220
|
@workers = {}
|
|
221
|
+
@worker_indices = {}
|
|
211
222
|
@timed_out = Set.new
|
|
212
223
|
@stats = Stats.new(@worker_count)
|
|
213
224
|
@started_at = Process.clock_gettime(Process::CLOCK_REALTIME)
|
|
@@ -438,15 +449,34 @@ module Raptor
|
|
|
438
449
|
pid = spawn_worker_via_seed(index)
|
|
439
450
|
if pid
|
|
440
451
|
@workers[index] = pid
|
|
452
|
+
@worker_indices[pid] = index
|
|
441
453
|
return
|
|
442
454
|
end
|
|
443
455
|
Log.warn "Seed (#{@seed_pid}) failed to fork worker #{index}, falling back to direct fork"
|
|
444
456
|
@seed_pid = nil
|
|
445
457
|
end
|
|
446
458
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
459
|
+
master_pid = Process.pid
|
|
460
|
+
begin
|
|
461
|
+
@before_fork.each(&:call)
|
|
462
|
+
pid = fork { run_worker(index, @phase) }
|
|
463
|
+
@workers[index] = pid
|
|
464
|
+
@worker_indices[pid] = index
|
|
465
|
+
ensure
|
|
466
|
+
if Process.pid == master_pid
|
|
467
|
+
error = $!
|
|
468
|
+
@after_fork.each do |hook|
|
|
469
|
+
hook.call
|
|
470
|
+
rescue => hook_error
|
|
471
|
+
if error
|
|
472
|
+
Log.rescued_error(hook_error)
|
|
473
|
+
else
|
|
474
|
+
error = hook_error
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
raise error if error
|
|
478
|
+
end
|
|
479
|
+
end
|
|
450
480
|
end
|
|
451
481
|
|
|
452
482
|
# Asks the seed to fork a new worker at the given index, returning the
|
|
@@ -511,19 +541,20 @@ module Raptor
|
|
|
511
541
|
#
|
|
512
542
|
# @rbs (Integer pid, Process::Status status) -> void
|
|
513
543
|
def reap_pid(pid, status)
|
|
544
|
+
index = @worker_indices.delete(pid)
|
|
545
|
+
return unless index
|
|
546
|
+
|
|
547
|
+
active = @workers[index] == pid
|
|
514
548
|
if pid == @seed_pid
|
|
515
549
|
Log.info "Seed (#{pid}) exited, #{exit_description(status)}"
|
|
516
550
|
@seed_pid = nil
|
|
517
|
-
return
|
|
518
551
|
end
|
|
519
552
|
|
|
520
|
-
|
|
521
|
-
return unless index
|
|
522
|
-
|
|
523
|
-
@workers.delete(index)
|
|
553
|
+
@workers.delete(index) if active
|
|
524
554
|
@timed_out.delete(pid)
|
|
555
|
+
@after_worker_exit.each { |hook| hook.parameters.empty? ? hook.call : hook.call(index, pid, status) }
|
|
525
556
|
|
|
526
|
-
|
|
557
|
+
if active && !@shutdown
|
|
527
558
|
Log.warn "Restarting worker #{index} (#{pid}), #{exit_description(status)}"
|
|
528
559
|
spawn_worker(index)
|
|
529
560
|
end
|
|
@@ -655,7 +686,11 @@ module Raptor
|
|
|
655
686
|
pids = @workers.values + [@seed_pid].compact
|
|
656
687
|
Log.warn "Force-killing #{pids.size} process(es) after #{@worker_shutdown_timeout}s"
|
|
657
688
|
pids.each { |pid| Process.kill("KILL", pid) rescue nil }
|
|
658
|
-
pids.each
|
|
689
|
+
pids.each do |pid|
|
|
690
|
+
_, status = Process.wait2(pid)
|
|
691
|
+
reap_pid(pid, status)
|
|
692
|
+
rescue Errno::ECHILD
|
|
693
|
+
end
|
|
659
694
|
end
|
|
660
695
|
|
|
661
696
|
# Kills workers that have stopped checking in. A booted worker that
|
|
@@ -903,6 +938,7 @@ module Raptor
|
|
|
903
938
|
end
|
|
904
939
|
|
|
905
940
|
worker.shutdown(close_listeners: !promote_to_seed)
|
|
941
|
+
@after_worker_drain.each { |hook| hook.parameters.empty? ? hook.call : hook.call(index) }
|
|
906
942
|
# A seed never accepts again, and the workers it forks bind their own.
|
|
907
943
|
bpf_listeners.each(&:close) if promote_to_seed
|
|
908
944
|
stats_thread.join
|
data/lib/raptor/embedded.rb
CHANGED
|
@@ -61,15 +61,7 @@ module Raptor
|
|
|
61
61
|
@binder = Binder.new(@binds, socket_backlog: @options[:socket_backlog])
|
|
62
62
|
@port = @binder.server_port
|
|
63
63
|
@addresses = @binder.addresses
|
|
64
|
-
|
|
65
|
-
@app,
|
|
66
|
-
listeners: @binder.listeners,
|
|
67
|
-
port: @port,
|
|
68
|
-
options: @options,
|
|
69
|
-
http1_ractors: @options[:http1][:ractors] || Cluster.default_http1_ractor_count(1),
|
|
70
|
-
http2_ractors: @options[:http2][:ractors] || Cluster.default_http2_ractor_count(1)
|
|
71
|
-
)
|
|
72
|
-
@worker.start
|
|
64
|
+
start_worker
|
|
73
65
|
rescue
|
|
74
66
|
@stopped = true
|
|
75
67
|
@binder&.close
|
|
@@ -89,6 +81,57 @@ module Raptor
|
|
|
89
81
|
join
|
|
90
82
|
end
|
|
91
83
|
|
|
84
|
+
# Drains serving work while retaining the bound listeners for resume.
|
|
85
|
+
# Connections may queue, but requests are not served while paused.
|
|
86
|
+
# Existing keep-alive connections close. Repeated pauses have no effect.
|
|
87
|
+
#
|
|
88
|
+
# Pause before master forks and resume in the parent. Close the inherited
|
|
89
|
+
# instance before other worker boot hooks, and shut down when the plugin stops.
|
|
90
|
+
#
|
|
91
|
+
# @example From a plugin's start callback
|
|
92
|
+
# @server = Raptor::Embedded.new(app, port: 9293).start
|
|
93
|
+
# cluster.options[:before_fork] << proc { @server.pause }
|
|
94
|
+
# cluster.options[:after_fork] << proc { @server.resume }
|
|
95
|
+
# cluster.options[:before_worker_boot].unshift(proc { @server.shutdown })
|
|
96
|
+
#
|
|
97
|
+
# @rbs () -> Embedded
|
|
98
|
+
def pause
|
|
99
|
+
with_lifecycle do
|
|
100
|
+
raise ThreadError, "server is not running" unless @started && !@stopped
|
|
101
|
+
|
|
102
|
+
begin
|
|
103
|
+
@worker&.shutdown(close_listeners: false)
|
|
104
|
+
@worker = nil
|
|
105
|
+
rescue
|
|
106
|
+
@stopped = true
|
|
107
|
+
@binder&.close
|
|
108
|
+
raise
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
self
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Resumes serving on the retained listeners. Has no effect while running.
|
|
116
|
+
# Shutdown remains terminal. Startup failures release the listeners and propagate.
|
|
117
|
+
#
|
|
118
|
+
# @rbs () -> Embedded
|
|
119
|
+
def resume
|
|
120
|
+
with_lifecycle do
|
|
121
|
+
raise ThreadError, "server has not been started or has been stopped" unless @started && !@stopped
|
|
122
|
+
|
|
123
|
+
begin
|
|
124
|
+
start_worker unless @worker
|
|
125
|
+
rescue
|
|
126
|
+
@stopped = true
|
|
127
|
+
@binder&.close
|
|
128
|
+
raise
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
|
|
92
135
|
# Drains requests and closes the server's sockets, threads, and Ractors.
|
|
93
136
|
# Calling shutdown more than once has no effect.
|
|
94
137
|
#
|
|
@@ -106,20 +149,43 @@ module Raptor
|
|
|
106
149
|
end
|
|
107
150
|
end
|
|
108
151
|
|
|
109
|
-
# Waits
|
|
152
|
+
# Waits across pauses until termination and propagates serving failures.
|
|
110
153
|
#
|
|
111
154
|
# @rbs () -> void
|
|
112
155
|
def join
|
|
113
|
-
|
|
114
|
-
raise ThreadError, "server has not been started" unless
|
|
115
|
-
|
|
116
|
-
|
|
156
|
+
started = with_lifecycle { @started }
|
|
157
|
+
raise ThreadError, "server has not been started" unless started
|
|
158
|
+
|
|
159
|
+
loop do
|
|
160
|
+
worker, stopped = with_lifecycle { [@worker, @stopped] }
|
|
161
|
+
break if stopped
|
|
162
|
+
|
|
163
|
+
if worker
|
|
164
|
+
worker.join
|
|
165
|
+
break if with_lifecycle { @worker.equal?(worker) }
|
|
166
|
+
else
|
|
167
|
+
@lifecycle_available.wait { @worker || @stopped }
|
|
168
|
+
end
|
|
169
|
+
end
|
|
117
170
|
ensure
|
|
118
|
-
shutdown if
|
|
171
|
+
shutdown if started
|
|
119
172
|
end
|
|
120
173
|
|
|
121
174
|
private
|
|
122
175
|
|
|
176
|
+
# @rbs () -> void
|
|
177
|
+
def start_worker
|
|
178
|
+
@worker = Worker.new(
|
|
179
|
+
@app,
|
|
180
|
+
listeners: @binder.listeners,
|
|
181
|
+
port: @port,
|
|
182
|
+
options: @options,
|
|
183
|
+
http1_ractors: @options[:http1][:ractors] || Cluster.default_http1_ractor_count(1),
|
|
184
|
+
http2_ractors: @options[:http2][:ractors] || Cluster.default_http2_ractor_count(1)
|
|
185
|
+
)
|
|
186
|
+
@worker.start
|
|
187
|
+
end
|
|
188
|
+
|
|
123
189
|
# @rbs () -> void
|
|
124
190
|
def log_initialization
|
|
125
191
|
environment = @options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
data/lib/raptor/plugin.rb
CHANGED
|
@@ -53,6 +53,7 @@ module Raptor
|
|
|
53
53
|
|
|
54
54
|
# Starts in the master before workers fork. Override to open resources
|
|
55
55
|
# and append callbacks to the cluster's lifecycle hook arrays.
|
|
56
|
+
# See {Cluster#initialize} for hooks and {Embedded#pause} for fork-safe Rack listeners.
|
|
56
57
|
#
|
|
57
58
|
# @rbs (Cluster cluster) -> void
|
|
58
59
|
def start(cluster)
|
data/lib/raptor/version.rb
CHANGED
|
@@ -72,20 +72,22 @@ module Raptor
|
|
|
72
72
|
|
|
73
73
|
@before_refork: Array[Proc]
|
|
74
74
|
|
|
75
|
+
@after_worker_exit: Array[Proc]
|
|
76
|
+
|
|
77
|
+
@after_worker_drain: Array[Proc]
|
|
78
|
+
|
|
75
79
|
@before_worker_shutdown: Array[Proc]
|
|
76
80
|
|
|
77
81
|
@before_worker_boot: Array[Proc]
|
|
78
82
|
|
|
83
|
+
@after_fork: Array[Proc]
|
|
84
|
+
|
|
79
85
|
@before_fork: Array[Proc]
|
|
80
86
|
|
|
81
87
|
@worker_shutdown_timeout: Integer
|
|
82
88
|
|
|
83
89
|
@worker_drain_timeout: Integer
|
|
84
90
|
|
|
85
|
-
@worker_timeout: Integer
|
|
86
|
-
|
|
87
|
-
@worker_boot_timeout: Integer
|
|
88
|
-
|
|
89
91
|
@bpf_active: bool
|
|
90
92
|
|
|
91
93
|
@resp_w: IO?
|
|
@@ -122,10 +124,16 @@ module Raptor
|
|
|
122
124
|
|
|
123
125
|
@timed_out: Set[Integer]
|
|
124
126
|
|
|
127
|
+
@worker_indices: Hash[Integer, Integer]
|
|
128
|
+
|
|
125
129
|
@workers: Hash[Integer, Integer]
|
|
126
130
|
|
|
127
131
|
@shutdown: bool
|
|
128
132
|
|
|
133
|
+
@worker_timeout: Integer
|
|
134
|
+
|
|
135
|
+
@worker_boot_timeout: Integer
|
|
136
|
+
|
|
129
137
|
@environment: String
|
|
130
138
|
|
|
131
139
|
@cpu_affinity: bool
|
|
@@ -169,9 +177,12 @@ module Raptor
|
|
|
169
177
|
# @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
|
|
170
178
|
# @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
|
|
171
179
|
# @option options [Integer, Array<Integer>, nil] :refork_after request-count threshold(s) at which a warm worker is promoted to a fork source for phased refork; nil or 0 disables. Requires `PR_SET_CHILD_SUBREAPER` (Linux)
|
|
172
|
-
# @option options [Array<Proc>] :before_fork procs called in the master before
|
|
180
|
+
# @option options [Array<Proc>] :before_fork procs called in the master before each direct worker fork, not seed forks
|
|
181
|
+
# @option options [Array<Proc>] :after_fork procs called in the master after each direct fork attempt, including failures
|
|
173
182
|
# @option options [Array<Proc>] :before_worker_boot procs called with the worker index before it begins serving
|
|
174
183
|
# @option options [Array<Proc>] :before_worker_shutdown procs called with the worker index before its graceful shutdown
|
|
184
|
+
# @option options [Array<Proc>] :after_worker_drain procs called with the worker index after draining, before exit or seed promotion
|
|
185
|
+
# @option options [Array<Proc>] :after_worker_exit procs called in the master after reaping, with the worker index, pid, and Process::Status
|
|
175
186
|
# @option options [Array<Proc>] :before_refork procs called in a worker before it transitions to a seed
|
|
176
187
|
# @option options [Array<String, Symbol>] :plugins plugins to load and start in order
|
|
177
188
|
# @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
|
|
@@ -46,19 +46,44 @@ module Raptor
|
|
|
46
46
|
# @rbs () -> void
|
|
47
47
|
def run: () -> void
|
|
48
48
|
|
|
49
|
+
# Drains serving work while retaining the bound listeners for resume.
|
|
50
|
+
# Connections may queue, but requests are not served while paused.
|
|
51
|
+
# Existing keep-alive connections close. Repeated pauses have no effect.
|
|
52
|
+
#
|
|
53
|
+
# Pause before master forks and resume in the parent. Close the inherited
|
|
54
|
+
# instance before other worker boot hooks, and shut down when the plugin stops.
|
|
55
|
+
#
|
|
56
|
+
# @example From a plugin's start callback
|
|
57
|
+
# @server = Raptor::Embedded.new(app, port: 9293).start
|
|
58
|
+
# cluster.options[:before_fork] << proc { @server.pause }
|
|
59
|
+
# cluster.options[:after_fork] << proc { @server.resume }
|
|
60
|
+
# cluster.options[:before_worker_boot].unshift(proc { @server.shutdown })
|
|
61
|
+
#
|
|
62
|
+
# @rbs () -> Embedded
|
|
63
|
+
def pause: () -> Embedded
|
|
64
|
+
|
|
65
|
+
# Resumes serving on the retained listeners. Has no effect while running.
|
|
66
|
+
# Shutdown remains terminal. Startup failures release the listeners and propagate.
|
|
67
|
+
#
|
|
68
|
+
# @rbs () -> Embedded
|
|
69
|
+
def resume: () -> Embedded
|
|
70
|
+
|
|
49
71
|
# Drains requests and closes the server's sockets, threads, and Ractors.
|
|
50
72
|
# Calling shutdown more than once has no effect.
|
|
51
73
|
#
|
|
52
74
|
# @rbs () -> void
|
|
53
75
|
def shutdown: () -> void
|
|
54
76
|
|
|
55
|
-
# Waits
|
|
77
|
+
# Waits across pauses until termination and propagates serving failures.
|
|
56
78
|
#
|
|
57
79
|
# @rbs () -> void
|
|
58
80
|
def join: () -> void
|
|
59
81
|
|
|
60
82
|
private
|
|
61
83
|
|
|
84
|
+
# @rbs () -> void
|
|
85
|
+
def start_worker: () -> void
|
|
86
|
+
|
|
62
87
|
# @rbs () -> void
|
|
63
88
|
def log_initialization: () -> void
|
|
64
89
|
|
|
@@ -28,6 +28,7 @@ module Raptor
|
|
|
28
28
|
|
|
29
29
|
# Starts in the master before workers fork. Override to open resources
|
|
30
30
|
# and append callbacks to the cluster's lifecycle hook arrays.
|
|
31
|
+
# See {Cluster#initialize} for hooks and {Embedded#pause} for fork-safe Rack listeners.
|
|
31
32
|
#
|
|
32
33
|
# @rbs (Cluster cluster) -> void
|
|
33
34
|
def start: (Cluster cluster) -> void
|