raptor 0.8.0 → 0.10.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 +90 -32
- data/.dockerignore +5 -0
- data/CHANGELOG.md +22 -0
- data/Dockerfile +28 -0
- data/README.md +17 -9
- data/Rakefile +19 -1
- data/docs/raptor-vs-puma.md +557 -0
- data/ext/raptor_bpf/reuseport_select.bpf.c +63 -0
- data/ext/raptor_http/extconf.rb +1 -1
- data/ext/raptor_http/raptor_http.c +87 -6
- data/ext/raptor_native/extconf.rb +7 -0
- data/ext/raptor_native/raptor_native.c +99 -0
- data/lib/raptor/binder.rb +19 -4
- data/lib/raptor/cli.rb +1 -1
- data/lib/raptor/cluster.rb +27 -2
- data/lib/raptor/http.rb +41 -0
- data/lib/raptor/http1.rb +115 -56
- data/lib/raptor/reuseport_bpf.rb +109 -0
- data/lib/raptor/server.rb +40 -12
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +10 -0
- data/sig/generated/raptor/cluster.rbs +4 -2
- data/sig/generated/raptor/http.rbs +13 -0
- data/sig/generated/raptor/http1.rbs +42 -13
- data/sig/generated/raptor/reuseport_bpf.rbs +56 -0
- data/sig/generated/raptor/server.rbs +20 -6
- metadata +24 -1
|
@@ -36,6 +36,19 @@ module Raptor
|
|
|
36
36
|
# @rbs (TCPSocket socket, String string, ?timeout: Integer) -> void
|
|
37
37
|
def self.socket_write: (TCPSocket socket, String string, ?timeout: Integer) -> void
|
|
38
38
|
|
|
39
|
+
# Writes `strings` in full via a single `writev(2)` syscall when possible,
|
|
40
|
+
# falling back to per-string writes on partial results. Bounded by
|
|
41
|
+
# `timeout` so a slow client can't pin the writing thread.
|
|
42
|
+
#
|
|
43
|
+
# @param socket [TCPSocket] the socket to write to
|
|
44
|
+
# @param strings [Array<String>] the buffers to write in order
|
|
45
|
+
# @param timeout [Integer] seconds to wait for the socket to become writable on each retry
|
|
46
|
+
# @return [void]
|
|
47
|
+
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
48
|
+
#
|
|
49
|
+
# @rbs (TCPSocket socket, Array[String] strings, ?timeout: Integer) -> void
|
|
50
|
+
def self.socket_writev: (TCPSocket socket, Array[String] strings, ?timeout: Integer) -> void
|
|
51
|
+
|
|
39
52
|
# Writes a Common Log Format entry to `io`. Write failures are silently
|
|
40
53
|
# ignored.
|
|
41
54
|
#
|
|
@@ -8,12 +8,16 @@ module Raptor
|
|
|
8
8
|
class Http1
|
|
9
9
|
BODY_BUFFER_THRESHOLD: untyped
|
|
10
10
|
|
|
11
|
+
CHUNKED_WRITE_THRESHOLD: untyped
|
|
12
|
+
|
|
11
13
|
FILE_CHUNK_SIZE: untyped
|
|
12
14
|
|
|
13
15
|
MAX_CHUNK_OVERHEAD: untyped
|
|
14
16
|
|
|
15
17
|
READ_BUFFER_SIZE: untyped
|
|
16
18
|
|
|
19
|
+
RESPONSE_BUFFER_CAPACITY: untyped
|
|
20
|
+
|
|
17
21
|
KEEPALIVE_READ_TIMEOUT: ::Float
|
|
18
22
|
|
|
19
23
|
MAX_KEEPALIVE_REQUESTS: ::Integer
|
|
@@ -86,23 +90,27 @@ module Raptor
|
|
|
86
90
|
# @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
|
|
87
91
|
def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
|
|
88
92
|
|
|
89
|
-
@
|
|
93
|
+
@app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
|
|
90
94
|
|
|
91
|
-
@
|
|
95
|
+
@server_port: Integer
|
|
92
96
|
|
|
93
|
-
@
|
|
97
|
+
@server_port_string: String
|
|
94
98
|
|
|
95
|
-
@
|
|
99
|
+
@write_timeout: Integer
|
|
100
|
+
|
|
101
|
+
@max_body_size: Integer?
|
|
96
102
|
|
|
97
103
|
@body_spool_threshold: Integer?
|
|
98
104
|
|
|
99
|
-
@
|
|
105
|
+
@max_keepalive_requests: Integer
|
|
100
106
|
|
|
101
|
-
@
|
|
107
|
+
@access_log_io: IO?
|
|
102
108
|
|
|
103
|
-
@
|
|
109
|
+
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
104
110
|
|
|
105
|
-
@
|
|
111
|
+
@running: AtomicBoolean
|
|
112
|
+
|
|
113
|
+
@env_template: Hash[String, untyped]
|
|
106
114
|
|
|
107
115
|
# Creates a new Http1 handler.
|
|
108
116
|
#
|
|
@@ -132,6 +140,17 @@ module Raptor
|
|
|
132
140
|
# @rbs (TCPSocket socket, String string) -> void
|
|
133
141
|
def socket_write: (TCPSocket socket, String string) -> void
|
|
134
142
|
|
|
143
|
+
# Instance-level wrapper around {Http.socket_writev} that applies the
|
|
144
|
+
# configured `write_timeout`.
|
|
145
|
+
#
|
|
146
|
+
# @param socket [TCPSocket] the socket to write to
|
|
147
|
+
# @param strings [Array<String>] the buffers to write in order
|
|
148
|
+
# @return [void]
|
|
149
|
+
# @raise [Http::WriteError] if the socket is not writable within the timeout or raises IOError
|
|
150
|
+
#
|
|
151
|
+
# @rbs (TCPSocket socket, Array[String] strings) -> void
|
|
152
|
+
def socket_writev: (TCPSocket socket, Array[String] strings) -> void
|
|
153
|
+
|
|
135
154
|
# Signals eager keep-alive loops to stop processing further requests on
|
|
136
155
|
# their connections. In-flight requests complete normally.
|
|
137
156
|
#
|
|
@@ -406,7 +425,8 @@ module Raptor
|
|
|
406
425
|
# @rbs (Hash[String, String | Array[String]] headers, Integer status) -> void
|
|
407
426
|
def validate_headers: (Hash[String, String | Array[String]] headers, Integer status) -> void
|
|
408
427
|
|
|
409
|
-
#
|
|
428
|
+
# Returns the HTTP status line for `status`. Callers must not retain the
|
|
429
|
+
# returned string across further calls on the same thread.
|
|
410
430
|
#
|
|
411
431
|
# @param http_version [String] "HTTP/1.1" or "HTTP/1.0"
|
|
412
432
|
# @param status [Integer] HTTP status code
|
|
@@ -499,10 +519,7 @@ module Raptor
|
|
|
499
519
|
# @rbs (TCPSocket socket, String response, Array[String] body_array, bool use_chunked) -> void
|
|
500
520
|
def write_array_body: (TCPSocket socket, String response, Array[String] body_array, bool use_chunked) -> void
|
|
501
521
|
|
|
502
|
-
# Writes a single-element array body
|
|
503
|
-
#
|
|
504
|
-
# Small bodies are concatenated with the headers into one write to reduce
|
|
505
|
-
# system call overhead.
|
|
522
|
+
# Writes a single-element array body to the socket.
|
|
506
523
|
#
|
|
507
524
|
# @param socket [TCPSocket] the client socket
|
|
508
525
|
# @param response [String] headers already serialized, to be written before the body
|
|
@@ -565,6 +582,18 @@ module Raptor
|
|
|
565
582
|
# @rbs (Hash[String, String | Array[String]] headers) -> String
|
|
566
583
|
def format_headers: (Hash[String, String | Array[String]] headers) -> String
|
|
567
584
|
|
|
585
|
+
# Appends one or more `name: value` header lines to `result`. Newline-
|
|
586
|
+
# separated values are emitted as separate lines; empty values and values
|
|
587
|
+
# with illegal characters are skipped silently.
|
|
588
|
+
#
|
|
589
|
+
# @param result [String] the buffer to append to
|
|
590
|
+
# @param name [String] the header name
|
|
591
|
+
# @param value [Object] the header value (any object responding to `to_s`)
|
|
592
|
+
# @return [void]
|
|
593
|
+
#
|
|
594
|
+
# @rbs (String result, String name, untyped value) -> void
|
|
595
|
+
def append_header_value: (String result, String name, untyped value) -> void
|
|
596
|
+
|
|
568
597
|
# Calls all rack.response_finished callbacks registered in the environment.
|
|
569
598
|
#
|
|
570
599
|
# Callbacks are called in reverse registration order. Individual callback
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Generated from lib/raptor/reuseport_bpf.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Raptor
|
|
4
|
+
# Routes incoming connections across worker processes to the worker with
|
|
5
|
+
# the lowest reactor backlog.
|
|
6
|
+
#
|
|
7
|
+
# Auto-enabled on Linux when the `libbpf-ruby` gem is installed and the
|
|
8
|
+
# BPF object file has been compiled. Falls back silently to standard
|
|
9
|
+
# `SO_REUSEPORT` when either is missing. Raises when the kernel refuses
|
|
10
|
+
# the BPF program. Wraps `tcp://` bindings only; non-tcp bindings are
|
|
11
|
+
# left to the binder's shared listeners.
|
|
12
|
+
module ReuseportBPF
|
|
13
|
+
LIBBPF_LOADED: untyped
|
|
14
|
+
|
|
15
|
+
BPF_OBJECT_PATH: untyped
|
|
16
|
+
|
|
17
|
+
# Whether the preconditions for BPF-directed reuseport are met on this
|
|
18
|
+
# platform. Does not imply that the kernel will accept the program.
|
|
19
|
+
#
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
#
|
|
22
|
+
# @rbs () -> bool
|
|
23
|
+
def self.supported?: () -> bool
|
|
24
|
+
|
|
25
|
+
# Prepares BPF-directed routing for `worker_count` workers.
|
|
26
|
+
#
|
|
27
|
+
# Returns true when BPF-directed dispatch is active. Returns false
|
|
28
|
+
# when the platform is unsupported. Raises when the kernel refuses
|
|
29
|
+
# the program.
|
|
30
|
+
#
|
|
31
|
+
# @param worker_count [Integer] number of worker processes that will bind sockets
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
#
|
|
34
|
+
# @rbs (Integer worker_count) -> bool
|
|
35
|
+
def self.setup: (Integer worker_count) -> bool
|
|
36
|
+
|
|
37
|
+
# Returns tcp listening sockets bound and registered for `worker_index`.
|
|
38
|
+
# Skips non-tcp bind URIs.
|
|
39
|
+
#
|
|
40
|
+
# @param bind_uris [Array<String>] the configured bind URIs
|
|
41
|
+
# @param worker_index [Integer] slot index for this worker in the socks map
|
|
42
|
+
# @param socket_backlog [Integer] kernel listen() queue depth
|
|
43
|
+
# @return [Array<TCPServer>] the tcp listening sockets created for this worker
|
|
44
|
+
#
|
|
45
|
+
# @rbs (Array[String] bind_uris, Integer worker_index, Integer socket_backlog) -> Array[TCPServer]
|
|
46
|
+
def self.create_worker_listeners: (Array[String] bind_uris, Integer worker_index, Integer socket_backlog) -> Array[TCPServer]
|
|
47
|
+
|
|
48
|
+
# Publishes this worker's current backlog to the BPF map.
|
|
49
|
+
#
|
|
50
|
+
# @param backlog [Integer] the worker's current reactor backlog
|
|
51
|
+
# @return [void]
|
|
52
|
+
#
|
|
53
|
+
# @rbs (Integer backlog) -> void
|
|
54
|
+
def self.update_load: (Integer backlog) -> void
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -17,7 +17,7 @@ module Raptor
|
|
|
17
17
|
# reactor = Reactor.new(ractor_pool, thread_pool, connection_options: {}, http1_options: {})
|
|
18
18
|
# http1 = Http1.new(app, 3000)
|
|
19
19
|
# http2 = Http2.new(app, 3000)
|
|
20
|
-
# server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 })
|
|
20
|
+
# server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 }, listeners: binder.listeners)
|
|
21
21
|
# server.run
|
|
22
22
|
# # ... later
|
|
23
23
|
# server.shutdown
|
|
@@ -36,6 +36,8 @@ module Raptor
|
|
|
36
36
|
|
|
37
37
|
@running: AtomicBoolean
|
|
38
38
|
|
|
39
|
+
@bpf_active: bool
|
|
40
|
+
|
|
39
41
|
@drain_accept_queue: bool
|
|
40
42
|
|
|
41
43
|
@first_data_timeout: Integer
|
|
@@ -48,6 +50,8 @@ module Raptor
|
|
|
48
50
|
|
|
49
51
|
@reactor: Reactor
|
|
50
52
|
|
|
53
|
+
@listeners: Array[TCPServer | UNIXServer | Binder::SslListener]
|
|
54
|
+
|
|
51
55
|
@binder: Binder
|
|
52
56
|
|
|
53
57
|
# Creates a new Server instance.
|
|
@@ -58,18 +62,20 @@ module Raptor
|
|
|
58
62
|
# @param http1 [Http1] the HTTP/1.1 handler
|
|
59
63
|
# @param http2 [Http2] the HTTP/2 handler (provides the initial SETTINGS frame)
|
|
60
64
|
# @param connection_options [Hash] per-connection timeout configuration, used to bound TLS handshakes
|
|
65
|
+
# @param listeners [Array] the per-worker listeners this server accepts on
|
|
61
66
|
# @param drain_accept_queue [Boolean] whether to drain the kernel accept queue on shutdown
|
|
67
|
+
# @param worker_index [Integer, nil] the slot index for BPF load reporting
|
|
62
68
|
# @return [void]
|
|
63
69
|
#
|
|
64
|
-
# @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], ?drain_accept_queue: bool) -> void
|
|
65
|
-
def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], ?drain_accept_queue: bool) -> void
|
|
70
|
+
# @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], listeners: Array[untyped], ?drain_accept_queue: bool, ?worker_index: Integer?) -> void
|
|
71
|
+
def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], listeners: Array[untyped], ?drain_accept_queue: bool, ?worker_index: Integer?) -> void
|
|
66
72
|
|
|
67
73
|
# Starts the server's main accept loop in a new thread.
|
|
68
74
|
#
|
|
69
75
|
# The accept loop polls listening sockets for ready connections and accepts
|
|
70
|
-
# them when
|
|
71
|
-
#
|
|
72
|
-
# worker
|
|
76
|
+
# them when the reactor backlog is under the backpressure threshold. On
|
|
77
|
+
# Linux with BPF-directed dispatch active, a companion thread publishes
|
|
78
|
+
# this worker's backlog to the BPF map.
|
|
73
79
|
#
|
|
74
80
|
# @return [Thread] the thread running the accept loop
|
|
75
81
|
#
|
|
@@ -89,6 +95,14 @@ module Raptor
|
|
|
89
95
|
|
|
90
96
|
private
|
|
91
97
|
|
|
98
|
+
# Starts a background thread that publishes this worker's reactor
|
|
99
|
+
# backlog to the BPF map for load-aware dispatch.
|
|
100
|
+
#
|
|
101
|
+
# @return [void]
|
|
102
|
+
#
|
|
103
|
+
# @rbs () -> void
|
|
104
|
+
def spawn_load_reporter: () -> void
|
|
105
|
+
|
|
92
106
|
# Dispatches every connection already in the kernel accept queue for each
|
|
93
107
|
# listener until all are drained.
|
|
94
108
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raptor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Young
|
|
@@ -93,6 +93,20 @@ dependencies:
|
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: libbpf-ruby
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
96
110
|
email:
|
|
97
111
|
- djry1999@gmail.com
|
|
98
112
|
executables:
|
|
@@ -100,22 +114,29 @@ executables:
|
|
|
100
114
|
extensions:
|
|
101
115
|
- ext/raptor_http/extconf.rb
|
|
102
116
|
- ext/raptor_http2/extconf.rb
|
|
117
|
+
- ext/raptor_native/extconf.rb
|
|
103
118
|
extra_rdoc_files: []
|
|
104
119
|
files:
|
|
105
120
|
- ".buildkite/pipeline.yml"
|
|
121
|
+
- ".dockerignore"
|
|
106
122
|
- ".mise.toml"
|
|
107
123
|
- Brewfile
|
|
108
124
|
- CHANGELOG.md
|
|
109
125
|
- CODE_OF_CONDUCT.md
|
|
126
|
+
- Dockerfile
|
|
110
127
|
- LICENSE.txt
|
|
111
128
|
- README.md
|
|
112
129
|
- Rakefile
|
|
130
|
+
- docs/raptor-vs-puma.md
|
|
113
131
|
- exe/raptor
|
|
132
|
+
- ext/raptor_bpf/reuseport_select.bpf.c
|
|
114
133
|
- ext/raptor_http/extconf.rb
|
|
115
134
|
- ext/raptor_http/raptor_http.c
|
|
116
135
|
- ext/raptor_http2/extconf.rb
|
|
117
136
|
- ext/raptor_http2/huffman_table.h
|
|
118
137
|
- ext/raptor_http2/raptor_http2.c
|
|
138
|
+
- ext/raptor_native/extconf.rb
|
|
139
|
+
- ext/raptor_native/raptor_native.c
|
|
119
140
|
- lib/rackup/handler/raptor.rb
|
|
120
141
|
- lib/raptor.rb
|
|
121
142
|
- lib/raptor/binder.rb
|
|
@@ -126,6 +147,7 @@ files:
|
|
|
126
147
|
- lib/raptor/http2.rb
|
|
127
148
|
- lib/raptor/log.rb
|
|
128
149
|
- lib/raptor/reactor.rb
|
|
150
|
+
- lib/raptor/reuseport_bpf.rb
|
|
129
151
|
- lib/raptor/server.rb
|
|
130
152
|
- lib/raptor/stats.rb
|
|
131
153
|
- lib/raptor/systemd.rb
|
|
@@ -140,6 +162,7 @@ files:
|
|
|
140
162
|
- sig/generated/raptor/http2.rbs
|
|
141
163
|
- sig/generated/raptor/log.rbs
|
|
142
164
|
- sig/generated/raptor/reactor.rbs
|
|
165
|
+
- sig/generated/raptor/reuseport_bpf.rbs
|
|
143
166
|
- sig/generated/raptor/server.rbs
|
|
144
167
|
- sig/generated/raptor/stats.rbs
|
|
145
168
|
- sig/generated/raptor/systemd.rbs
|