raptor 0.24.0 → 0.26.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/.buildkite/pipeline.yml +34 -8
- data/AGENTS.md +25 -0
- data/CHANGELOG.md +32 -24
- data/README.md +81 -57
- data/Rakefile +10 -3
- data/docs/raptor-vs-puma.md +4 -2
- data/docs/talk.md +1 -1
- data/lib/rackup/handler/raptor.rb +19 -12
- data/lib/raptor/binder.rb +17 -5
- data/lib/raptor/cli.rb +29 -2
- data/lib/raptor/cluster.rb +15 -130
- data/lib/raptor/embedded.rb +159 -0
- data/lib/raptor/reactor.rb +120 -73
- data/lib/raptor/version.rb +1 -1
- data/lib/raptor/worker.rb +200 -0
- data/lib/raptor.rb +1 -1
- data/sig/generated/rackup/handler/raptor.rbs +8 -8
- data/sig/generated/raptor/cli.rbs +5 -0
- data/sig/generated/raptor/cluster.rbs +8 -32
- data/sig/generated/raptor/embedded.rbs +68 -0
- data/sig/generated/raptor/reactor.rbs +31 -16
- data/sig/generated/raptor/worker.rbs +77 -0
- data/sig/generated/raptor.rbs +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01e94b50f111d86ede3add1b2ff22fe674c4b2da50d748fc81728ac56458d9c4
|
|
4
|
+
data.tar.gz: cff8caa5355ce8f7b4e1d7fb7ec1ae17e8fe5332852b57042831d9a10921276f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9d7fd70b2cea77c2d6b1b232542a9a70d8d4f7a40a88870128af620e5125e24d4578e77885dcbd1df9fc750785547a8a71f87791381358a693a3ee382defdf9
|
|
7
|
+
data.tar.gz: ea9c5cf5e367b17e47bdaeb8e927d5c2214a7d739d6a646b3162c808b3bbc3f82c026e3632a8846f4f978069add42f34cc3727415a8b90a9a321eb20dea93147
|
data/.buildkite/pipeline.yml
CHANGED
|
@@ -18,6 +18,17 @@ steps:
|
|
|
18
18
|
- bundle install
|
|
19
19
|
- bundle exec rake ci
|
|
20
20
|
|
|
21
|
+
- label: ":rails: Rails system tests"
|
|
22
|
+
image: "ruby:4.0"
|
|
23
|
+
env:
|
|
24
|
+
BROWSER_PATH: /usr/bin/chromium
|
|
25
|
+
commands:
|
|
26
|
+
- ruby -v
|
|
27
|
+
- apt-get update && apt-get install -y chromium
|
|
28
|
+
- bundle install
|
|
29
|
+
- bundle exec rails --version
|
|
30
|
+
- bundle exec rake test:rails
|
|
31
|
+
|
|
21
32
|
- label: ":ruby: Ruby 4.0 +GC.stress"
|
|
22
33
|
image: "ruby:4.0"
|
|
23
34
|
env:
|
|
@@ -88,7 +99,7 @@ steps:
|
|
|
88
99
|
queue: macos
|
|
89
100
|
commands:
|
|
90
101
|
- brew install --quiet ruby@4.0
|
|
91
|
-
- export PATH="$(brew --prefix ruby@4.0)/bin
|
|
102
|
+
- export PATH="$(brew --prefix ruby@4.0)/bin:$$PATH"
|
|
92
103
|
- bundle config set --local path .bundle
|
|
93
104
|
- ruby -v
|
|
94
105
|
- bundle install
|
|
@@ -101,12 +112,27 @@ steps:
|
|
|
101
112
|
RUBY_YJIT_ENABLE: "1"
|
|
102
113
|
commands:
|
|
103
114
|
- brew install --quiet ruby@4.0
|
|
104
|
-
- export PATH="$(brew --prefix ruby@4.0)/bin
|
|
115
|
+
- export PATH="$(brew --prefix ruby@4.0)/bin:$$PATH"
|
|
105
116
|
- bundle config set --local path .bundle
|
|
106
117
|
- ruby -v
|
|
107
118
|
- bundle install
|
|
108
119
|
- bundle exec rake ci
|
|
109
120
|
|
|
121
|
+
- label: ":rails: Rails system tests"
|
|
122
|
+
agents:
|
|
123
|
+
queue: macos
|
|
124
|
+
env:
|
|
125
|
+
BROWSER_PATH: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
|
126
|
+
commands:
|
|
127
|
+
- brew install --quiet ruby@4.0
|
|
128
|
+
- export PATH="$(brew --prefix ruby@4.0)/bin:$$PATH"
|
|
129
|
+
- test -x "$$BROWSER_PATH" || brew install --cask --quiet google-chrome
|
|
130
|
+
- bundle config set --local path .bundle
|
|
131
|
+
- ruby -v
|
|
132
|
+
- bundle install
|
|
133
|
+
- bundle exec rails --version
|
|
134
|
+
- bundle exec rake test:rails
|
|
135
|
+
|
|
110
136
|
- label: ":ruby: Ruby 4.0 +GC.stress"
|
|
111
137
|
agents:
|
|
112
138
|
queue: macos
|
|
@@ -114,7 +140,7 @@ steps:
|
|
|
114
140
|
RUBY_GC_STRESS: "1"
|
|
115
141
|
commands:
|
|
116
142
|
- brew install --quiet ruby@4.0
|
|
117
|
-
- export PATH="$(brew --prefix ruby@4.0)/bin
|
|
143
|
+
- export PATH="$(brew --prefix ruby@4.0)/bin:$$PATH"
|
|
118
144
|
- bundle config set --local path .bundle
|
|
119
145
|
- ruby -v
|
|
120
146
|
- bundle install
|
|
@@ -128,7 +154,7 @@ steps:
|
|
|
128
154
|
RUBY_GC_STRESS: "1"
|
|
129
155
|
commands:
|
|
130
156
|
- brew install --quiet ruby@4.0
|
|
131
|
-
- export PATH="$(brew --prefix ruby@4.0)/bin
|
|
157
|
+
- export PATH="$(brew --prefix ruby@4.0)/bin:$$PATH"
|
|
132
158
|
- bundle config set --local path .bundle
|
|
133
159
|
- ruby -v
|
|
134
160
|
- bundle install
|
|
@@ -141,7 +167,7 @@ steps:
|
|
|
141
167
|
commands:
|
|
142
168
|
- brew unlink ruby
|
|
143
169
|
- brew install --HEAD --quiet ruby
|
|
144
|
-
- export PATH="$(brew --prefix ruby)/bin
|
|
170
|
+
- export PATH="$(brew --prefix ruby)/bin:$$PATH"
|
|
145
171
|
- bundle config set --local path .bundle
|
|
146
172
|
- ruby -v
|
|
147
173
|
- bundle install
|
|
@@ -156,7 +182,7 @@ steps:
|
|
|
156
182
|
commands:
|
|
157
183
|
- brew unlink ruby
|
|
158
184
|
- brew install --HEAD --quiet ruby
|
|
159
|
-
- export PATH="$(brew --prefix ruby)/bin
|
|
185
|
+
- export PATH="$(brew --prefix ruby)/bin:$$PATH"
|
|
160
186
|
- bundle config set --local path .bundle
|
|
161
187
|
- ruby -v
|
|
162
188
|
- bundle install
|
|
@@ -171,7 +197,7 @@ steps:
|
|
|
171
197
|
commands:
|
|
172
198
|
- brew unlink ruby
|
|
173
199
|
- brew install --HEAD --quiet ruby
|
|
174
|
-
- export PATH="$(brew --prefix ruby)/bin
|
|
200
|
+
- export PATH="$(brew --prefix ruby)/bin:$$PATH"
|
|
175
201
|
- bundle config set --local path .bundle
|
|
176
202
|
- ruby -v
|
|
177
203
|
- bundle install
|
|
@@ -187,7 +213,7 @@ steps:
|
|
|
187
213
|
commands:
|
|
188
214
|
- brew unlink ruby
|
|
189
215
|
- brew install --HEAD --quiet ruby
|
|
190
|
-
- export PATH="$(brew --prefix ruby)/bin
|
|
216
|
+
- export PATH="$(brew --prefix ruby)/bin:$$PATH"
|
|
191
217
|
- bundle config set --local path .bundle
|
|
192
218
|
- ruby -v
|
|
193
219
|
- bundle install
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Repository conventions
|
|
2
|
+
|
|
3
|
+
## Code organisation
|
|
4
|
+
|
|
5
|
+
- Follow local style so the repository reads as one cohesive codebase.
|
|
6
|
+
- Group and order related declarations logically throughout the repository, not just in Ruby code. Use consistent names,
|
|
7
|
+
placement, and spacing to make relationships clear.
|
|
8
|
+
- Use blank lines to separate logical steps and final return values, without fragmenting closely related code.
|
|
9
|
+
- Separate standard-library, third-party, and project requires with blank lines, keeping `require_relative` separate.
|
|
10
|
+
Respect load-order dependencies and order semantically rather than alphabetically.
|
|
11
|
+
- Prefer `atomic-ruby`'s CAS-based queues and synchronisation in server internals. Use mutex-backed alternatives only
|
|
12
|
+
when their semantics justify them, and avoid busy-spinning across blocking operations.
|
|
13
|
+
|
|
14
|
+
## Documentation and commits
|
|
15
|
+
|
|
16
|
+
- Keep comments focused on what and why. Explain implementation details only when the intent is otherwise unclear.
|
|
17
|
+
- Normally match commit subjects to changelog entries. Preserve separate entries for distinct changes worth calling out.
|
|
18
|
+
- Use inline code formatting for code references in commit subjects and changelog entries where applicable.
|
|
19
|
+
|
|
20
|
+
## Tests
|
|
21
|
+
|
|
22
|
+
- Load `test_helper` first in test files and group test-support requires together, respecting boot dependencies.
|
|
23
|
+
- Rely on assertions' default failure messages rather than adding custom messages.
|
|
24
|
+
- Follow suite conventions for descriptions, setup, and assertions. Use Minitest's `parallelize_me!` when tests can
|
|
25
|
+
safely run independently.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.26.0] - 2026-11-10
|
|
4
|
+
|
|
5
|
+
- Add in-process Rack serving
|
|
6
|
+
|
|
7
|
+
## [0.25.0] - 2026-11-08
|
|
8
|
+
|
|
9
|
+
- Use `io-event` for reactor I/O readiness
|
|
10
|
+
|
|
3
11
|
## [0.24.0] - 2026-11-08
|
|
4
12
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
- Prefer
|
|
8
|
-
- Reject empty
|
|
9
|
-
- Preserve request
|
|
10
|
-
- Trim leading tabs
|
|
11
|
-
- Honour HTTP/1 Connection token lists
|
|
12
|
-
-
|
|
13
|
-
- Close HTTP/2 response bodies when writing fails
|
|
14
|
-
- Bound HTTP/1 chunk size lines and trailers
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- Add plugins
|
|
13
|
+
- Enforce request body limits on HTTP/2 streams
|
|
14
|
+
- Notify systemd only after every current worker boots
|
|
15
|
+
- Prefer enumerable Rack bodies over callable streaming
|
|
16
|
+
- Reject empty request lengths and unsupported transfer codings
|
|
17
|
+
- Preserve `Version` request headers in the Rack environment
|
|
18
|
+
- Trim optional leading tabs from HTTP/1 header values
|
|
19
|
+
- Honour HTTP/1 `Connection` token lists
|
|
20
|
+
- Complete worker boot hooks before starting request handling
|
|
21
|
+
- Close HTTP/2 response bodies when writing fails
|
|
22
|
+
- Bound HTTP/1 chunk size lines and trailers
|
|
23
|
+
- Validate HTTP/1 chunk payload delimiters
|
|
24
|
+
- Preserve request header identity during Rack normalization
|
|
25
|
+
- Add plugins
|
|
18
26
|
|
|
19
27
|
## [0.23.0] - 2026-10-18
|
|
20
28
|
|
|
@@ -28,13 +36,13 @@
|
|
|
28
36
|
- Apply HTTP/2 initial window changes to responses waiting for flow control
|
|
29
37
|
- Default `SERVER_NAME` for HTTP/2 requests without a host name
|
|
30
38
|
- Join repeated HTTP/2 request headers instead of keeping only the last
|
|
31
|
-
- Ignore HTTP/2 DATA frames for streams that already closed
|
|
39
|
+
- Ignore HTTP/2 `DATA` frames for streams that already closed
|
|
32
40
|
- Credit each HTTP/2 stream window for the data that stream receives
|
|
33
|
-
- Strip padding from HTTP/2 DATA and HEADERS frames
|
|
41
|
+
- Strip padding from HTTP/2 `DATA` and `HEADERS` frames
|
|
34
42
|
- Encode and decode long HTTP/2 header strings without overflowing the stack
|
|
35
43
|
- Limit HTTP/2 request header lists to 112 KiB
|
|
36
44
|
- Reject HTTP/2 frames larger than the maximum frame size
|
|
37
|
-
- Send GOAWAY when an HTTP/2 frame fails to decode
|
|
45
|
+
- Send `GOAWAY` when an HTTP/2 frame fails to decode
|
|
38
46
|
- Keep pipelined HTTP/1.1 requests out of the previous request's body
|
|
39
47
|
- Process chunked requests on kept-alive HTTP/1.1 connections without waiting for more data
|
|
40
48
|
- Validate request framing on kept-alive HTTP/1.1 connections
|
|
@@ -93,12 +101,12 @@
|
|
|
93
101
|
- Close connected control socket clients during shutdown
|
|
94
102
|
- Skip TCP corking when `writev(2)` emits a response in one call
|
|
95
103
|
- Return idle HTTP/1.1 keep-alive connections to the reactor without waiting
|
|
96
|
-
- Increase
|
|
97
|
-
-
|
|
98
|
-
- Pass
|
|
99
|
-
- Add
|
|
104
|
+
- Increase HTTP/1.1 keep-alive request limit
|
|
105
|
+
- Expose cluster stats over a control socket
|
|
106
|
+
- Pass worker indexes to lifecycle hooks
|
|
107
|
+
- Add worker and thread environment variables
|
|
100
108
|
- Make worker CPU affinity opt-in
|
|
101
|
-
-
|
|
109
|
+
- Clean request-local state between requests
|
|
102
110
|
|
|
103
111
|
## [0.19.0] - 2026-08-29
|
|
104
112
|
|
|
@@ -169,7 +177,7 @@
|
|
|
169
177
|
## [0.10.0] - 2026-07-07
|
|
170
178
|
|
|
171
179
|
- Memoize the server port string for the Rack env
|
|
172
|
-
- Parse the Host header without regex
|
|
180
|
+
- Parse the `Host` header without regex
|
|
173
181
|
- Apply `TCP_NODELAY` on the listener rather than each accepted socket
|
|
174
182
|
- Buffer chunked body writes up to 512KB before flushing
|
|
175
183
|
- Add `QUERY_STRING` to the Rack env template
|
|
@@ -184,7 +192,7 @@
|
|
|
184
192
|
- Batch response header and body writes into a single `writev(2)` syscall
|
|
185
193
|
- Close the binder as the last step of graceful shutdown
|
|
186
194
|
- Lower the backpressure floor for tighter load balancing on small pools
|
|
187
|
-
- Add load-aware `SO_REUSEPORT` routing
|
|
195
|
+
- Add load-aware `SO_REUSEPORT` routing via an attached BPF program
|
|
188
196
|
- Reuse per-thread read buffers across HTTP/1.1 requests
|
|
189
197
|
- Preserve the first `--bind` when it equals the default
|
|
190
198
|
|
|
@@ -231,7 +239,7 @@
|
|
|
231
239
|
|
|
232
240
|
- Load `raptor.rb` or `config/raptor.rb` by default when no config path is supplied
|
|
233
241
|
- Honour the peer's HTTP/2 flow-control windows when sending `DATA` frames
|
|
234
|
-
- Assemble HEADERS across `CONTINUATION` frames
|
|
242
|
+
- Assemble `HEADERS` across `CONTINUATION` frames
|
|
235
243
|
- Validate HTTP/2 stream IDs and emit `GOAWAY` on protocol errors
|
|
236
244
|
- Offload TLS handshakes to the thread pool to keep the server thread responsive
|
|
237
245
|
- Exit eager keep-alive loops on cluster shutdown
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Raptor
|
|
2
2
|
|
|
3
3
|
Raptor is a high-performance, preloading, pre-forking, multi-threaded Ruby 4+ web server implementing Rack 3.2+, using
|
|
4
|
-
|
|
4
|
+
io-event for non-blocking I/O and Ractors for parallel HTTP/1.1 and HTTP/2 parsing via native C extensions, which also
|
|
5
5
|
implement HPACK compression.
|
|
6
6
|
|
|
7
7
|
> [!NOTE]
|
|
@@ -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.26.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
|
|
@@ -76,6 +76,30 @@ Also works with `rackup` and `rails server`:
|
|
|
76
76
|
Rails apps using `SOLID_QUEUE_IN_PUMA` run jobs through `config/puma.rb`, which Raptor does not load. Run `bin/jobs` as
|
|
77
77
|
a separately supervised process or container instead.
|
|
78
78
|
|
|
79
|
+
### Rails system tests
|
|
80
|
+
|
|
81
|
+
Use Raptor as Capybara's server without forking, so Rails can share transactional test data with browser requests.
|
|
82
|
+
Add this to your system test setup, alongside your browser driver configuration:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
require "rackup"
|
|
86
|
+
require "rackup/handler/raptor"
|
|
87
|
+
|
|
88
|
+
Capybara.register_server :raptor do |app, port, host|
|
|
89
|
+
Rackup::Handler::Raptor.run(app, Host: host, Port: port, Workers: 0) do |server|
|
|
90
|
+
at_exit { server.shutdown }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
Capybara.server = :raptor
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The exit hook shuts the server down before Ruby tears down its background threads.
|
|
97
|
+
|
|
98
|
+
For other in-process uses, `require "raptor/embedded"` and call `Raptor::Embedded.new(app, host: "127.0.0.1", port: 0)`.
|
|
99
|
+
`start` returns when serving is ready, `port` reports the bound port, `shutdown` drains requests and releases resources,
|
|
100
|
+
and `join` waits for termination. Call `shutdown` before process exit. The embedded server leaves signal handlers and
|
|
101
|
+
process-wide configuration to its caller. Cluster plugins and process lifecycle hooks do not run in this mode.
|
|
102
|
+
|
|
79
103
|
## Configuration
|
|
80
104
|
|
|
81
105
|
Raptor reads options from a Ruby config file, environment variables, and command line flags, in increasing order of
|
|
@@ -250,7 +274,7 @@ Set `control_url` to a Unix socket URL such as `unix:///tmp/raptor-control.sock`
|
|
|
250
274
|
|
|
251
275
|
## (Micro) Benchmarks
|
|
252
276
|
|
|
253
|
-
Raptor 0.
|
|
277
|
+
Raptor 0.26.0 vs Puma 8.0.2 vs Falcon 0.57.0. Each case has 3 fresh-process samples, interleaved in seeded shuffled
|
|
254
278
|
rounds. Each sample warms up for 3s and measures for 15s. Metrics are independent medians. ± is (max − min) / 2 /
|
|
255
279
|
median, not a confidence interval. Failure counts below each table sum all samples, so a failed run cannot disappear in
|
|
256
280
|
a median. Throughput counts successful ordinary requests only.
|
|
@@ -278,92 +302,92 @@ accounting for shared pages instead of counting them fully in every worker.
|
|
|
278
302
|
|
|
279
303
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
280
304
|
| ------ | -------------- | --------------- | ------------ | --------------- | ------ | ------------- | ---------- | ------- |
|
|
281
|
-
| Raptor | Fixed (3) | - | 3.
|
|
282
|
-
| Raptor | Scaling (3–16) | - | 8.
|
|
283
|
-
| Puma | Fixed (3) | Fixed (3) | 1.
|
|
284
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 8.
|
|
285
|
-
| Falcon | Fibers | Fixed (3) | 10.
|
|
286
|
-
| Falcon | Fibers | Scaling (3–16) | 10.
|
|
305
|
+
| Raptor | Fixed (3) | - | 3.39k ±0.6% | - | 46.17 | - | 2.434 | 337.1 |
|
|
306
|
+
| Raptor | Scaling (3–16) | - | 8.18k ±1.3% | - | 22.19 | - | 0.467 | 325.6 |
|
|
307
|
+
| Puma | Fixed (3) | Fixed (3) | 1.52k ±1.1% | 55.2% lower | 117.49 | 154.5% higher | 0.996 | 272.0 |
|
|
308
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 8.14k ±1.3% | 0.4% lower | 22.37 | 0.8% higher | 0.473 | 318.8 |
|
|
309
|
+
| Falcon | Fibers | Fixed (3) | 10.99k ±1.2% | 224.1% higher | 15.60 | 66.2% lower | 0.440 | 447.2 |
|
|
310
|
+
| Falcon | Fibers | Scaling (3–16) | 10.99k ±1.2% | 34.3% higher | 15.60 | 29.7% lower | 0.440 | 447.2 |
|
|
287
311
|
|
|
288
312
|
### IO over HTTP/1.1 keep-alive
|
|
289
313
|
|
|
290
314
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
291
315
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
292
|
-
| Raptor | Fixed (3) | - | 3.
|
|
293
|
-
| Raptor | Scaling (3–16) | - | 7.
|
|
294
|
-
| Raptor | Fixed (16) | - | 7.
|
|
295
|
-
| Puma | Fixed (3) | Fixed (3) | 1.
|
|
296
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 7.
|
|
297
|
-
| Puma | Fixed (16) | Fixed (16) | 7.
|
|
298
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
299
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
300
|
-
| Falcon | Fibers | Fixed (16) | 7.
|
|
316
|
+
| Raptor | Fixed (3) | - | 3.37k ±1.8% | - | 37.11 | - | 2.460 | 353.2 |
|
|
317
|
+
| Raptor | Scaling (3–16) | - | 7.39k ±1.7% | - | 24.53 | - | 0.470 | 344.9 |
|
|
318
|
+
| Raptor | Fixed (16) | - | 7.49k ±1.6% | - | 24.24 | - | 0.455 | 341.6 |
|
|
319
|
+
| Puma | Fixed (3) | Fixed (3) | 1.48k ±0.2% | 56.2% lower | 106.41 | 186.8% higher | 0.979 | 282.1 |
|
|
320
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 7.58k ±1.1% | 2.5% higher | 24.00 | 2.2% lower | 0.435 | 310.5 |
|
|
321
|
+
| Puma | Fixed (16) | Fixed (16) | 7.59k ±0.9% | 1.4% higher | 23.97 | 1.1% lower | 0.437 | 316.5 |
|
|
322
|
+
| Falcon | Fibers | Fixed (3) | 7.31k ±1.5% | 116.9% higher | 24.86 | 33.0% lower | 0.380 | 336.6 |
|
|
323
|
+
| Falcon | Fibers | Scaling (3–16) | 7.31k ±1.5% | 1.1% lower | 24.86 | 1.4% higher | 0.380 | 336.6 |
|
|
324
|
+
| Falcon | Fibers | Fixed (16) | 7.31k ±1.5% | 2.3% lower | 24.86 | 2.6% higher | 0.380 | 336.6 |
|
|
301
325
|
|
|
302
326
|
### IO over HTTP/2
|
|
303
327
|
|
|
304
|
-
| Server | Mode | Raptor baseline | req/s
|
|
305
|
-
| ------ | -------------- | --------------- |
|
|
306
|
-
| Raptor | Fixed (3) | - | 1.
|
|
307
|
-
| Raptor | Scaling (3–16) | - |
|
|
308
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
309
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
328
|
+
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
329
|
+
| ------ | -------------- | --------------- | ------------ | --------------- | ------ | ------------- | ---------- | ------- |
|
|
330
|
+
| Raptor | Fixed (3) | - | 1.61k ±4.7% | - | 120.85 | - | 0.815 | 399.9 |
|
|
331
|
+
| Raptor | Scaling (3–16) | - | 6.80k ±22.8% | - | 40.32 | - | 0.580 | 912.4 |
|
|
332
|
+
| Falcon | Fibers | Fixed (3) | 7.12k ±3.5% | 342.8% higher | 26.17 | 78.3% lower | 0.432 | 539.7 |
|
|
333
|
+
| Falcon | Fibers | Scaling (3–16) | 7.12k ±3.5% | 4.7% higher | 26.17 | 35.1% lower | 0.432 | 539.7 |
|
|
310
334
|
|
|
311
335
|
### CPU over HTTP/1.1
|
|
312
336
|
|
|
313
337
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
314
338
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
315
|
-
| Raptor | Fixed (3) | - | 3.
|
|
316
|
-
| Raptor | Scaling (3–16) | - | 3.
|
|
317
|
-
| Puma | Fixed (3) | Fixed (3) |
|
|
318
|
-
| Puma | Scaling (3–16) | Scaling (3–16) |
|
|
319
|
-
| Falcon | Fibers | Fixed (3) | 3.
|
|
320
|
-
| Falcon | Fibers | Scaling (3–16) | 3.
|
|
339
|
+
| Raptor | Fixed (3) | - | 3.43k ±1.1% | - | 67.46 | - | 2.507 | 509.4 |
|
|
340
|
+
| Raptor | Scaling (3–16) | - | 3.26k ±2.5% | - | 70.51 | - | 2.572 | 499.6 |
|
|
341
|
+
| Puma | Fixed (3) | Fixed (3) | 3.21k ±3.5% | 6.3% lower | 56.00 | 17.0% lower | 2.454 | 441.3 |
|
|
342
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 3.15k ±2.3% | 3.4% lower | 49.26 | 30.1% lower | 2.186 | 431.0 |
|
|
343
|
+
| Falcon | Fibers | Fixed (3) | 3.36k ±4.2% | 2.1% lower | 48.20 | 28.5% lower | 2.555 | 435.8 |
|
|
344
|
+
| Falcon | Fibers | Scaling (3–16) | 3.36k ±4.2% | 3.0% higher | 48.20 | 31.6% lower | 2.555 | 435.8 |
|
|
321
345
|
|
|
322
346
|
### CPU over HTTP/1.1 keep-alive
|
|
323
347
|
|
|
324
348
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
325
349
|
| ------ | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
326
|
-
| Raptor | Fixed (3) | - |
|
|
327
|
-
| Raptor | Scaling (3–16) | - | 2.
|
|
328
|
-
| Puma | Fixed (3) | Fixed (3) | 3.
|
|
329
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 2.
|
|
330
|
-
| Falcon | Fibers | Fixed (3) | 3.
|
|
331
|
-
| Falcon | Fibers | Scaling (3–16) | 3.
|
|
350
|
+
| Raptor | Fixed (3) | - | 3.10k ±5.2% | - | 54.76 | - | 2.560 | 484.4 |
|
|
351
|
+
| Raptor | Scaling (3–16) | - | 2.97k ±1.2% | - | 49.64 | - | 2.553 | 505.3 |
|
|
352
|
+
| Puma | Fixed (3) | Fixed (3) | 3.52k ±6.1% | 13.7% higher | 51.14 | 6.6% lower | 2.299 | 384.4 |
|
|
353
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 2.90k ±4.2% | 2.3% lower | 65.73 | 32.4% higher | 2.283 | 472.5 |
|
|
354
|
+
| Falcon | Fibers | Fixed (3) | 3.17k ±1.9% | 2.4% higher | 69.58 | 27.1% higher | 2.766 | 536.5 |
|
|
355
|
+
| Falcon | Fibers | Scaling (3–16) | 3.17k ±1.9% | 6.7% higher | 69.58 | 40.2% higher | 2.766 | 536.5 |
|
|
332
356
|
|
|
333
357
|
### CPU over HTTP/2
|
|
334
358
|
|
|
335
359
|
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
336
360
|
| ------ | -------------- | --------------- | ------------ | --------------- | ------ | ------------- | ---------- | ------- |
|
|
337
|
-
| Raptor | Fixed (3) | - | 3.
|
|
338
|
-
| Raptor | Scaling (3–16) | - | 3.
|
|
339
|
-
| Falcon | Fibers | Fixed (3) |
|
|
340
|
-
| Falcon | Fibers | Scaling (3–16) |
|
|
361
|
+
| Raptor | Fixed (3) | - | 3.26k ±2.1% | - | 71.84 | - | 2.329 | 573.4 |
|
|
362
|
+
| Raptor | Scaling (3–16) | - | 3.25k ±2.6% | - | 69.37 | - | 2.383 | 590.6 |
|
|
363
|
+
| Falcon | Fibers | Fixed (3) | 2.02k ±11.2% | 37.9% lower | 176.57 | 145.8% higher | 2.405 | 559.2 |
|
|
364
|
+
| Falcon | Fibers | Scaling (3–16) | 2.02k ±11.2% | 37.9% lower | 176.57 | 154.5% higher | 2.405 | 559.2 |
|
|
341
365
|
|
|
342
366
|
### 64 KiB body over HTTP/1.1 keep-alive
|
|
343
367
|
|
|
344
|
-
| Server | Mode | Raptor baseline | req/s
|
|
345
|
-
| ------ | -------------- | --------------- |
|
|
346
|
-
| Raptor | Scaling (3–16) | - |
|
|
347
|
-
| Puma | Scaling (3–16) | Scaling (3–16) |
|
|
348
|
-
| Falcon | Fibers | Scaling (3–16) |
|
|
368
|
+
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
369
|
+
| ------ | -------------- | --------------- | ------------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
370
|
+
| Raptor | Scaling (3–16) | - | 94.58k ±2.0% | - | 3.77 | - | 0.052 | 507.6 |
|
|
371
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 88.40k ±2.1% | 6.5% lower | 3.66 | 2.9% lower | 0.054 | 1237.4 |
|
|
372
|
+
| Falcon | Fibers | Scaling (3–16) | 103.33k ±5.0% | 9.2% higher | 3.89 | 3.2% higher | 0.051 | 344.7 |
|
|
349
373
|
|
|
350
374
|
### IO + SSE (native) over HTTP/1.1 keep-alive
|
|
351
375
|
|
|
352
|
-
| Server | Mode | Raptor baseline | req/s
|
|
353
|
-
| ------------- | -------------- | --------------- |
|
|
354
|
-
| Raptor | Fixed (3) | - | 3.
|
|
355
|
-
| Raptor | Scaling (3–16) | - | 7.
|
|
356
|
-
| Puma | Fixed (3) | Fixed (3) | 0.
|
|
357
|
-
| Puma | Scaling (3–16) | Scaling (3–16) | 7.
|
|
358
|
-
| Falcon | Fibers | Fixed (3) | 7.
|
|
359
|
-
| Falcon | Fibers | Scaling (3–16) | 7.
|
|
360
|
-
| Raptor (enum) | Fixed (3) | - | 0.
|
|
376
|
+
| Server | Mode | Raptor baseline | req/s | vs Raptor req/s | p95 ms | vs Raptor p95 | CPU ms/req | PSS MiB |
|
|
377
|
+
| ------------- | -------------- | --------------- | ----------- | --------------- | ------ | ------------- | ---------- | ------- |
|
|
378
|
+
| Raptor | Fixed (3) | - | 3.24k ±1.6% | - | 44.69 | - | 2.396 | 355.6 |
|
|
379
|
+
| Raptor | Scaling (3–16) | - | 7.39k ±0.8% | - | 24.47 | - | 0.468 | 346.4 |
|
|
380
|
+
| Puma | Fixed (3) | Fixed (3) | 0.35k ±4.5% | 89.3% lower | 316.86 | 609.1% higher | 1.764 | 243.6 |
|
|
381
|
+
| Puma | Scaling (3–16) | Scaling (3–16) | 7.38k ±4.3% | 0.2% lower | 24.61 | 0.6% higher | 0.440 | 311.3 |
|
|
382
|
+
| Falcon | Fibers | Fixed (3) | 7.32k ±1.0% | 125.8% higher | 24.83 | 44.4% lower | 0.383 | 354.4 |
|
|
383
|
+
| Falcon | Fibers | Scaling (3–16) | 7.32k ±1.0% | 1.0% lower | 24.83 | 1.5% higher | 0.383 | 354.4 |
|
|
384
|
+
| Raptor (enum) | Fixed (3) | - | 0.97k ±4.8% | - | 56.38 | - | 7.624 | 324.4 |
|
|
361
385
|
|
|
362
|
-
Puma, Fixed (3) had
|
|
386
|
+
Puma, Fixed (3) had 68 timeouts, 1 stalled stream across all samples.
|
|
363
387
|
|
|
364
|
-
Raptor (enum), Fixed (3) had
|
|
388
|
+
Raptor (enum), Fixed (3) had 21 timeouts, 2 stalled streams across all samples.
|
|
365
389
|
|
|
366
|
-
> ruby 4.1.0dev (2026-
|
|
390
|
+
> ruby 4.1.0dev (2026-11-10T00:02:04Z HEAD 7a07e456e4) +YJIT +PRISM [aarch64-linux]
|
|
367
391
|
> 10 workers, 120 HTTP/1.1 client connections, 40 HTTP/2 connections × 3 streams.
|
|
368
392
|
|
|
369
393
|
Run [bin/benchmark](bin/benchmark) to reproduce.
|
data/Rakefile
CHANGED
|
@@ -20,6 +20,13 @@ Rake::ExtensionTask.new("raptor_native", GEMSPEC) do |ext|
|
|
|
20
20
|
ext.lib_dir = "lib/raptor"
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
namespace :test do
|
|
24
|
+
desc "Run Rails system tests"
|
|
25
|
+
task rails: :compile do
|
|
26
|
+
ruby "-Itest", "test/system/rails.rb"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
23
30
|
namespace :bpf do
|
|
24
31
|
task :compile do
|
|
25
32
|
if RUBY_PLATFORM.include?("linux")
|
|
@@ -41,6 +48,6 @@ namespace :rbs do
|
|
|
41
48
|
end
|
|
42
49
|
end
|
|
43
50
|
|
|
44
|
-
task default:
|
|
45
|
-
task build:
|
|
46
|
-
task ci:
|
|
51
|
+
task default: [:clobber, :compile, :"bpf:compile", :"rbs:generate", :test]
|
|
52
|
+
task build: [:clobber, :compile, :"rbs:generate"]
|
|
53
|
+
task ci: [:clobber, :compile, :test]
|
data/docs/raptor-vs-puma.md
CHANGED
|
@@ -16,7 +16,7 @@ This guide compares their architecture and relates it to the [current README ben
|
|
|
16
16
|
| Protocol processing | App threads and reactor share the worker's GVL | Eager parsing inline, separate protocol Ractors on the pipeline path |
|
|
17
17
|
| Pool coordination | Mutex and condition variables | Lock-free work queue with idle-thread parking |
|
|
18
18
|
| Pool growth | Adds threads when queued work exceeds waiting capacity | Adds threads when queued work and blocking time justify growth |
|
|
19
|
-
| Idle and partial connections | `nio4r` reactor | `
|
|
19
|
+
| Idle and partial connections | `nio4r` reactor | `io-event` reactor |
|
|
20
20
|
| Cluster dispatch | Shared listeners with load-sensitive accept delay | Shared listeners, plus load-aware BPF dispatch on supported Linux TCP bindings |
|
|
21
21
|
| HTTP/2 | Not implemented | Native parsing, HPACK and stream flow control |
|
|
22
22
|
| Worker monitoring | Pipe messages | Shared-memory stats |
|
|
@@ -129,7 +129,7 @@ flowchart TB
|
|
|
129
129
|
subgraph Worker["Raptor worker process, one of N"]
|
|
130
130
|
SRV["Server thread<br/>IO.select + accept_nonblock<br/>polls for pool capacity"]
|
|
131
131
|
|
|
132
|
-
RCT["Reactor thread<br/>
|
|
132
|
+
RCT["Reactor thread<br/>io-event selector<br/>read_nonblock 64KB<br/>Red-Black-Tree timeouts"]
|
|
133
133
|
|
|
134
134
|
subgraph RP1["HTTP/1.1 RactorPool"]
|
|
135
135
|
RW1["Pipeline Ractor 1<br/>HTTP/1.x parser<br/>chunked decode"]
|
|
@@ -211,6 +211,8 @@ Puma's cluster accept delay backs off according to worker load. On supported Lin
|
|
|
211
211
|
|
|
212
212
|
Both reactors keep idle and partial connections off application threads. Puma tracks deadlines in a sorted array. Raptor uses a red-black tree for logarithmic insertion and removal of individual deadlines. Updating a connection's deadline does not require scanning the other connections.
|
|
213
213
|
|
|
214
|
+
Raptor uses `io-event` readiness fibers only on its reactor thread. It does not install a Fiber scheduler or change how the application runs.
|
|
215
|
+
|
|
214
216
|
### Responses and streaming
|
|
215
217
|
|
|
216
218
|
Ordinary HTTP/1.1 response writing runs on app threads in both servers. Raptor can gather headers and array-body buffers with `writev` and reuse per-thread response buffers. Puma uses its own buffered writes and Linux TCP corking. Both support file bodies and enumerable streaming bodies.
|
data/docs/talk.md
CHANGED
|
@@ -640,7 +640,7 @@ with no hard maximum. You don't have to choose one fixed thread count.</big>
|
|
|
640
640
|
|
|
641
641
|
Slow clients, half-sent requests, and idle keep-alive connections aren't ready to be read yet. The reactor watches all of them at once.
|
|
642
642
|
|
|
643
|
-
<big>
|
|
643
|
+
<big>It uses [`io-event`](https://github.com/socketry/io-event), backed by `epoll` on Linux
|
|
644
644
|
and `kqueue` on macOS.</big>
|
|
645
645
|
|
|
646
646
|
<big>The HTTP/1.1 defaults are 30 seconds for the first bytes,
|
|
@@ -12,24 +12,31 @@ module Rackup
|
|
|
12
12
|
Port: 9292
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
|
-
#
|
|
15
|
+
# Serves the given Rack application using the handler options.
|
|
16
16
|
#
|
|
17
17
|
# @param app [#call] the Rack application to serve
|
|
18
18
|
# @param options [Hash] handler options provided by Rackup or the host
|
|
19
|
-
# @yield before the
|
|
20
|
-
# @yieldparam
|
|
19
|
+
# @yield before the server starts running
|
|
20
|
+
# @yieldparam server [::Raptor::Cluster, ::Raptor::Embedded] the server instance
|
|
21
21
|
# @return [void]
|
|
22
22
|
#
|
|
23
|
-
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
|
|
23
|
+
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster | ::Raptor::Embedded) -> void } -> void
|
|
24
24
|
def self.run(app, **options)
|
|
25
25
|
require_relative "../../raptor/cli"
|
|
26
26
|
require_relative "../../raptor/cluster"
|
|
27
|
+
require_relative "../../raptor/embedded"
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
configuration = build_options(app, options)
|
|
30
|
+
server = if configuration[:workers].zero?
|
|
31
|
+
configuration.delete(:app)
|
|
32
|
+
::Raptor::Embedded.new(app, **configuration)
|
|
33
|
+
else
|
|
34
|
+
::Raptor::Cluster.new(configuration)
|
|
35
|
+
end
|
|
29
36
|
|
|
30
|
-
yield
|
|
37
|
+
yield server if block_given?
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
server.run
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
# Returns the handler-specific options surfaced by `rackup --help`.
|
|
@@ -41,24 +48,24 @@ module Rackup
|
|
|
41
48
|
{
|
|
42
49
|
"Host=HOST" => "Hostname to listen on (default: #{DEFAULT_OPTIONS[:Host]})",
|
|
43
50
|
"Port=PORT" => "Port to listen on (default: #{DEFAULT_OPTIONS[:Port]})",
|
|
44
|
-
"Workers=NUM" => "Number of worker processes (default: available processor count)",
|
|
51
|
+
"Workers=NUM" => "Number of worker processes, or 0 for in-process serving (default: available processor count)",
|
|
45
52
|
"Threads=NUM" => "Number of threads per worker (default: 3)",
|
|
46
53
|
"MaxThreads=NUM" => "Maximum threads per worker (`unlimited` for no limit; default: unlimited)",
|
|
47
54
|
"Config=PATH" => "Load additional configuration from PATH"
|
|
48
55
|
}
|
|
49
56
|
end
|
|
50
57
|
|
|
51
|
-
# Builds
|
|
58
|
+
# Builds Raptor server configuration from Rack handler options.
|
|
52
59
|
#
|
|
53
60
|
# Options not explicitly supplied by the user (per the `:user_supplied_options`
|
|
54
61
|
# key) are treated as host-provided defaults.
|
|
55
62
|
#
|
|
56
63
|
# @param app [#call] the Rack application to serve
|
|
57
64
|
# @param options [Hash] handler options provided by Rackup or the host
|
|
58
|
-
# @return [Hash{Symbol => untyped}]
|
|
65
|
+
# @return [Hash{Symbol => untyped}] server configuration
|
|
59
66
|
#
|
|
60
67
|
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
|
|
61
|
-
def self.
|
|
68
|
+
def self.build_options(app, options)
|
|
62
69
|
defaults = DEFAULT_OPTIONS.dup
|
|
63
70
|
|
|
64
71
|
if (user_supplied_options = options.delete(:user_supplied_options))
|
|
@@ -116,7 +123,7 @@ module Rackup
|
|
|
116
123
|
result[:on_error] = config[:on_error] if config.key?(:on_error)
|
|
117
124
|
config.merge(result)
|
|
118
125
|
end
|
|
119
|
-
private_class_method :
|
|
126
|
+
private_class_method :build_options
|
|
120
127
|
end
|
|
121
128
|
|
|
122
129
|
register :raptor, Raptor
|