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.
@@ -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
- @running: AtomicBoolean
93
+ @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
90
94
 
91
- @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
95
+ @server_port: Integer
92
96
 
93
- @access_log_io: IO?
97
+ @server_port_string: String
94
98
 
95
- @max_keepalive_requests: Integer
99
+ @write_timeout: Integer
100
+
101
+ @max_body_size: Integer?
96
102
 
97
103
  @body_spool_threshold: Integer?
98
104
 
99
- @max_body_size: Integer?
105
+ @max_keepalive_requests: Integer
100
106
 
101
- @write_timeout: Integer
107
+ @access_log_io: IO?
102
108
 
103
- @server_port: Integer
109
+ @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
104
110
 
105
- @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
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
- # Builds the HTTP status line string.
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, optionally buffering it with the headers.
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 system capacity allows. It checks reactor backlog before accepting
71
- # to prevent overload. This provides natural load balancing across multiple
72
- # 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.
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.8.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