raptor 0.7.0 → 0.9.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.
@@ -10,10 +10,12 @@ module Raptor
10
10
  # the server uses for backpressure control to prevent overload.
11
11
  #
12
12
  # @example
13
- # reactor = Reactor.new(ractor_pool, thread_pool, client_options: {
14
- # first_data_timeout: 30,
15
- # chunk_data_timeout: 10
16
- # })
13
+ # reactor = Reactor.new(
14
+ # ractor_pool,
15
+ # thread_pool,
16
+ # connection_options: { first_data_timeout: 30, chunk_data_timeout: 10 },
17
+ # http1_options: { persistent_data_timeout: 65 }
18
+ # )
17
19
  # reactor.run
18
20
  # reactor.add(id: client.object_id, socket: client)
19
21
  # # ... later
@@ -71,7 +73,11 @@ module Raptor
71
73
 
72
74
  @selector: NIO::Selector
73
75
 
74
- @client_options: Hash[Symbol, Integer]
76
+ @persistent_data_timeout: Integer
77
+
78
+ @chunk_data_timeout: Integer
79
+
80
+ @first_data_timeout: Integer
75
81
 
76
82
  @ractor_pool: untyped
77
83
 
@@ -81,14 +87,15 @@ module Raptor
81
87
  #
82
88
  # @param ractor_pool [RactorPool] ractor pool for HTTP parsing
83
89
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
84
- # @param client_options [Hash] timeout configuration options
85
- # @option client_options [Integer] :first_data_timeout timeout for initial data
86
- # @option client_options [Integer] :chunk_data_timeout timeout for subsequent chunks
87
- # @option client_options [Integer] :persistent_data_timeout timeout for keep-alive connections
90
+ # @param connection_options [Hash] per-connection timeout configuration
91
+ # @option connection_options [Integer] :first_data_timeout timeout for initial data
92
+ # @option connection_options [Integer] :chunk_data_timeout timeout for subsequent chunks
93
+ # @param http1_options [Hash] HTTP/1.1-specific configuration
94
+ # @option http1_options [Integer] :persistent_data_timeout timeout for keep-alive idle connections
88
95
  # @return [void]
89
96
  #
90
- # @rbs (untyped ractor_pool, untyped thread_pool, client_options: Hash[Symbol, Integer]) -> void
91
- def initialize: (untyped ractor_pool, untyped thread_pool, client_options: Hash[Symbol, Integer]) -> void
97
+ # @rbs (untyped ractor_pool, untyped thread_pool, connection_options: Hash[Symbol, untyped], http1_options: Hash[Symbol, untyped]) -> void
98
+ def initialize: (untyped ractor_pool, untyped thread_pool, connection_options: Hash[Symbol, untyped], http1_options: Hash[Symbol, untyped]) -> void
92
99
 
93
100
  # Starts the reactor's main event loop in a new thread.
94
101
  #
@@ -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
@@ -14,9 +14,10 @@ module Raptor
14
14
  #
15
15
  # @example
16
16
  # binder = Binder.new(["tcp://0.0.0.0:3000"])
17
- # reactor = Reactor.new(ractor_pool, thread_pool, client_options: {})
18
- # request = Request.new(app, 3000)
19
- # server = Server.new(binder, reactor, thread_pool, request, client_options: { first_data_timeout: 30 })
17
+ # reactor = Reactor.new(ractor_pool, thread_pool, connection_options: {}, http1_options: {})
18
+ # http1 = Http1.new(app, 3000)
19
+ # http2 = Http2.new(app, 3000)
20
+ # server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 }, listeners: binder.listeners)
20
21
  # server.run
21
22
  # # ... later
22
23
  # server.shutdown
@@ -35,14 +36,22 @@ module Raptor
35
36
 
36
37
  @running: AtomicBoolean
37
38
 
38
- @client_options: Hash[Symbol, untyped]
39
+ @bpf_active: bool
39
40
 
40
- @request: Request
41
+ @drain_accept_queue: bool
42
+
43
+ @first_data_timeout: Integer
44
+
45
+ @http2: Http2
46
+
47
+ @http1: Http1
41
48
 
42
49
  @thread_pool: AtomicThreadPool
43
50
 
44
51
  @reactor: Reactor
45
52
 
53
+ @listeners: Array[TCPServer | UNIXServer | Binder::SslListener]
54
+
46
55
  @binder: Binder
47
56
 
48
57
  # Creates a new Server instance.
@@ -50,19 +59,23 @@ module Raptor
50
59
  # @param binder [Binder] the binder managing listening sockets
51
60
  # @param reactor [Reactor] the reactor for handling client connections
52
61
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
53
- # @param request [Request] the HTTP/1.1 request handler
54
- # @param client_options [Hash] client timeout configuration, used to bound TLS handshakes
62
+ # @param http1 [Http1] the HTTP/1.1 handler
63
+ # @param http2 [Http2] the HTTP/2 handler (provides the initial SETTINGS frame)
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
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
55
68
  # @return [void]
56
69
  #
57
- # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request, client_options: Hash[Symbol, untyped]) -> void
58
- def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request, client_options: Hash[Symbol, untyped]) -> 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
59
72
 
60
73
  # Starts the server's main accept loop in a new thread.
61
74
  #
62
75
  # The accept loop polls listening sockets for ready connections and accepts
63
- # them when system capacity allows. It checks reactor backlog before accepting
64
- # to prevent overload. This provides natural load balancing across multiple
65
- # worker processes through backpressure control.
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.
66
79
  #
67
80
  # @return [Thread] the thread running the accept loop
68
81
  #
@@ -71,8 +84,9 @@ module Raptor
71
84
 
72
85
  # Gracefully shuts down the server.
73
86
  #
74
- # Stops accepting new connections and closes all listening sockets.
75
- # The server thread will exit after handling any in-flight accept operations.
87
+ # Stops accepting new connections and closes all listening sockets. When
88
+ # `drain_accept_queue` is enabled, dispatches every connection already in
89
+ # the kernel accept queue before closing the listeners.
76
90
  #
77
91
  # @return [void]
78
92
  #
@@ -81,6 +95,22 @@ module Raptor
81
95
 
82
96
  private
83
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
+
106
+ # Dispatches every connection already in the kernel accept queue for each
107
+ # listener until all are drained.
108
+ #
109
+ # @return [void]
110
+ #
111
+ # @rbs () -> void
112
+ def drain_accept_queue: () -> void
113
+
84
114
  # Accepts a connection from the given listener and dispatches it.
85
115
  #
86
116
  # For SSL listeners the TLS handshake is offloaded to the thread pool so
@@ -90,11 +120,10 @@ module Raptor
90
120
  # follow the HTTP/1.1 path.
91
121
  #
92
122
  # @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
93
- # @param reactor [Reactor] the reactor to dispatch connections to
94
- # @return [void]
123
+ # @return [Boolean] true if a connection was accepted, false if the listener had nothing to dispatch
95
124
  #
96
- # @rbs (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
97
- def accept_connection: (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
125
+ # @rbs (TCPServer | UNIXServer | Binder::SslListener listener) -> bool
126
+ def accept_connection: (TCPServer | UNIXServer | Binder::SslListener listener) -> bool
98
127
 
99
128
  # Performs the TLS handshake for an accepted SSL connection and dispatches
100
129
  # it through the HTTP/2 or HTTP/1.1 path. The handshake is bounded by
@@ -103,11 +132,10 @@ module Raptor
103
132
  # @param listener [Binder::SslListener] the SSL listener that accepted the connection
104
133
  # @param tcp_client [TCPSocket] the accepted TCP socket
105
134
  # @param remote_addr [String] the client's IP address
106
- # @param reactor [Reactor] the reactor to dispatch the connection to
107
135
  # @return [void]
108
136
  #
109
- # @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr, Reactor reactor) -> void
110
- def dispatch_ssl_connection: (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr, Reactor reactor) -> void
137
+ # @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr) -> void
138
+ def dispatch_ssl_connection: (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr) -> void
111
139
 
112
140
  # Drives a non-blocking SSL handshake to completion, bounded by the
113
141
  # configured first-data timeout. Returns true on success, false on
@@ -0,0 +1,42 @@
1
+ # Generated from lib/raptor/systemd.rb with RBS::Inline
2
+
3
+ module Raptor
4
+ # Integration with systemd's service notification protocol and
5
+ # socket-activation file descriptors.
6
+ module Systemd
7
+ LISTEN_FDS_START: ::Integer
8
+
9
+ LISTEN_FDNAMES_ENV: ::String
10
+
11
+ LISTEN_FDS_ENV: ::String
12
+
13
+ LISTEN_PID_ENV: ::String
14
+
15
+ NOTIFY_SOCKET_ENV: ::String
16
+
17
+ # Sends `message` to the systemd notification socket, returning true on
18
+ # success and false when the socket is unset or the send fails.
19
+ #
20
+ # @param message [String] notify protocol payload, e.g. "READY=1"
21
+ # @return [Boolean]
22
+ #
23
+ # @rbs (String message) -> bool
24
+ def self.notify: (String message) -> bool
25
+
26
+ # Returns the file descriptors passed in via socket activation, or an
27
+ # empty array when systemd has not exported any.
28
+ #
29
+ # @return [Array<Integer>]
30
+ #
31
+ # @rbs () -> Array[Integer]
32
+ def self.listen_fds: () -> Array[Integer]
33
+
34
+ # Clears the socket-activation environment variables so children don't
35
+ # act on stale values.
36
+ #
37
+ # @return [void]
38
+ #
39
+ # @rbs () -> void
40
+ def self.clear_listen_env: () -> void
41
+ end
42
+ end
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.7.0
4
+ version: 0.9.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,45 +114,58 @@ 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
122
143
  - lib/raptor/cli.rb
123
144
  - lib/raptor/cluster.rb
145
+ - lib/raptor/http.rb
146
+ - lib/raptor/http1.rb
124
147
  - lib/raptor/http2.rb
125
148
  - lib/raptor/log.rb
126
149
  - lib/raptor/reactor.rb
127
- - lib/raptor/request.rb
150
+ - lib/raptor/reuseport_bpf.rb
128
151
  - lib/raptor/server.rb
129
152
  - lib/raptor/stats.rb
153
+ - lib/raptor/systemd.rb
130
154
  - lib/raptor/version.rb
131
155
  - sig/generated/rackup/handler/raptor.rbs
132
156
  - sig/generated/raptor.rbs
133
157
  - sig/generated/raptor/binder.rbs
134
158
  - sig/generated/raptor/cli.rbs
135
159
  - sig/generated/raptor/cluster.rbs
160
+ - sig/generated/raptor/http.rbs
161
+ - sig/generated/raptor/http1.rbs
136
162
  - sig/generated/raptor/http2.rbs
137
163
  - sig/generated/raptor/log.rbs
138
164
  - sig/generated/raptor/reactor.rbs
139
- - sig/generated/raptor/request.rbs
165
+ - sig/generated/raptor/reuseport_bpf.rbs
140
166
  - sig/generated/raptor/server.rbs
141
167
  - sig/generated/raptor/stats.rbs
168
+ - sig/generated/raptor/systemd.rbs
142
169
  - sig/generated/raptor/version.rbs
143
170
  homepage: https://github.com/joshuay03/raptor
144
171
  licenses: