raptor 0.12.0 → 0.13.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 +4 -0
- data/README.md +10 -10
- data/docs/raptor-vs-puma.md +29 -29
- data/ext/raptor_bpf/reuseport_select.bpf.c +8 -4
- data/lib/raptor/binder.rb +37 -87
- data/lib/raptor/cli.rb +9 -25
- data/lib/raptor/cluster.rb +17 -53
- data/lib/raptor/http.rb +18 -0
- data/lib/raptor/http1.rb +244 -233
- data/lib/raptor/http2.rb +42 -63
- data/lib/raptor/reactor.rb +21 -52
- data/lib/raptor/reuseport_bpf.rb +14 -5
- data/lib/raptor/server.rb +10 -36
- data/lib/raptor/stats.rb +3 -8
- data/lib/raptor/systemd.rb +1 -1
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +34 -80
- data/sig/generated/raptor/cli.rbs +9 -25
- data/sig/generated/raptor/cluster.rbs +10 -51
- data/sig/generated/raptor/http.rbs +10 -0
- data/sig/generated/raptor/http1.rbs +129 -113
- data/sig/generated/raptor/http2.rbs +31 -48
- data/sig/generated/raptor/reactor.rbs +21 -52
- data/sig/generated/raptor/reuseport_bpf.rbs +11 -2
- data/sig/generated/raptor/server.rbs +9 -35
- data/sig/generated/raptor/stats.rbs +3 -8
- metadata +1 -1
data/lib/raptor/cli.rb
CHANGED
|
@@ -70,11 +70,9 @@ module Raptor
|
|
|
70
70
|
|
|
71
71
|
DEFAULT_CONFIG_PATHS = ["raptor.rb", "config/raptor.rb"].freeze
|
|
72
72
|
|
|
73
|
-
# Loads a
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# the same as in a regular Ruby script. The final expression must be a Hash
|
|
77
|
-
# of cluster options (the same keys accepted by {Raptor::Cluster#initialize}).
|
|
73
|
+
# Loads a Ruby config file and returns the options hash it evaluates
|
|
74
|
+
# to. Evaluated at the top level so `Raptor::*` constants resolve the
|
|
75
|
+
# same as in a regular script.
|
|
78
76
|
#
|
|
79
77
|
# @param path [String] path to a Ruby file that evaluates to a Hash
|
|
80
78
|
# @return [Hash{Symbol => untyped}] cluster options
|
|
@@ -91,9 +89,6 @@ module Raptor
|
|
|
91
89
|
# Returns the first existing path in {DEFAULT_CONFIG_PATHS} resolved
|
|
92
90
|
# against `root`, or nil if none exist.
|
|
93
91
|
#
|
|
94
|
-
# Used to pick up a project-local config file when no `-c`/`--config`
|
|
95
|
-
# flag was supplied.
|
|
96
|
-
#
|
|
97
92
|
# @param root [String] directory to resolve the default paths against
|
|
98
93
|
# @return [String, nil] the config path, or nil if no default file exists
|
|
99
94
|
#
|
|
@@ -106,22 +101,14 @@ module Raptor
|
|
|
106
101
|
# @rbs @options: Hash[Symbol, untyped]
|
|
107
102
|
# @rbs @parser: OptionParser
|
|
108
103
|
|
|
109
|
-
# Creates a new CLI instance
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
# options accordingly. A rackup file can be provided as the first
|
|
113
|
-
# positional argument (defaults to config.ru).
|
|
104
|
+
# Creates a new CLI instance from `argv`. A rackup file may be given
|
|
105
|
+
# as the first positional argument; every other value is parsed as a
|
|
106
|
+
# flag.
|
|
114
107
|
#
|
|
115
108
|
# @param argv [Array<String>] command-line arguments to parse
|
|
116
109
|
# @return [void]
|
|
117
110
|
# @raise [OptionParser::ParseError] if invalid options are provided
|
|
118
111
|
#
|
|
119
|
-
# @example With rackup file
|
|
120
|
-
# cli = CLI.new(["my_app.ru", "-w", "4"])
|
|
121
|
-
#
|
|
122
|
-
# @example With options only
|
|
123
|
-
# cli = CLI.new(["-t", "8", "-r", "2"])
|
|
124
|
-
#
|
|
125
112
|
# @rbs (Array[String] argv) -> void
|
|
126
113
|
def initialize(argv)
|
|
127
114
|
@options = DEFAULT_OPTIONS.dup
|
|
@@ -180,12 +167,9 @@ module Raptor
|
|
|
180
167
|
end
|
|
181
168
|
end
|
|
182
169
|
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# can be applied as a base layer that CLI args then override. All four
|
|
187
|
-
# OptionParser-accepted forms (`-c PATH`, `-cPATH`, `--config PATH`,
|
|
188
|
-
# `--config=PATH`) are recognized.
|
|
170
|
+
# Returns the path from a `-c`/`--config` flag in `argv`, recognising
|
|
171
|
+
# all four OptionParser-accepted forms (`-c PATH`, `-cPATH`,
|
|
172
|
+
# `--config PATH`, `--config=PATH`).
|
|
189
173
|
#
|
|
190
174
|
# @param argv [Array<String>] command-line arguments to scan
|
|
191
175
|
# @return [String, nil] the config path, or nil if no flag was supplied
|
data/lib/raptor/cluster.rb
CHANGED
|
@@ -18,39 +18,15 @@ require_relative "stats"
|
|
|
18
18
|
require_relative "systemd"
|
|
19
19
|
|
|
20
20
|
module Raptor
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# application processing, plus dedicated reactor and server threads. It
|
|
26
|
-
# handles process forking, signal management, graceful shutdown, and
|
|
27
|
-
# automatic worker restart when a worker process unexpectedly exits.
|
|
28
|
-
#
|
|
29
|
-
# The architecture provides horizontal scaling through processes while
|
|
30
|
-
# maintaining efficient I/O and CPU utilization within each process
|
|
31
|
-
# through the combination of ractor-based parsing and thread pools on
|
|
32
|
-
# top of NIO reactors.
|
|
33
|
-
#
|
|
34
|
-
# Flow per worker process:
|
|
35
|
-
# 1. Server continuously accepts connections but skips acceptance when backlog is high
|
|
36
|
-
# 2. Reactor manages I/O multiplexing and provides backlog metrics for load control
|
|
37
|
-
# 3. Ractor pool handles CPU-intensive HTTP parsing in parallel
|
|
38
|
-
# 4. Thread pool processes Rack applications and handles response writing
|
|
39
|
-
# 5. Natural load balancing occurs through backpressure-based acceptance control
|
|
40
|
-
#
|
|
41
|
-
# @example Basic usage
|
|
42
|
-
# options = {
|
|
43
|
-
# workers: 4, ractors: 2, threads: 8,
|
|
44
|
-
# binds: ["tcp://0.0.0.0:3000"],
|
|
45
|
-
# rackup: "config.ru",
|
|
46
|
-
# connection: { first_data_timeout: 30, chunk_data_timeout: 10 }
|
|
47
|
-
# }
|
|
48
|
-
# Cluster.run(options)
|
|
21
|
+
# Forks and supervises worker processes. Handles graceful shutdown on
|
|
22
|
+
# `INT` and `TERM`, phased restart on `USR1`, hot restart on `USR2`,
|
|
23
|
+
# and (on Linux) refork on `URG`. Restarts workers that exit
|
|
24
|
+
# unexpectedly or stop checking in.
|
|
49
25
|
#
|
|
50
26
|
class Cluster
|
|
51
27
|
INHERITED_FDS_ENV = "RAPTOR_INHERITED_FDS"
|
|
52
28
|
|
|
53
|
-
#
|
|
29
|
+
# Creates and runs a cluster with the given options.
|
|
54
30
|
#
|
|
55
31
|
# @param options [Hash] cluster configuration options
|
|
56
32
|
# @return [void]
|
|
@@ -108,11 +84,7 @@ module Raptor
|
|
|
108
84
|
# @rbs @resp_w: IO?
|
|
109
85
|
# @rbs @bpf_active: bool
|
|
110
86
|
|
|
111
|
-
# Creates a new Cluster with the
|
|
112
|
-
#
|
|
113
|
-
# Initializes the cluster with worker, ractor, and thread counts,
|
|
114
|
-
# sets up network binding, loads the Rack application, and prepares
|
|
115
|
-
# for multi-process operation.
|
|
87
|
+
# Creates a new Cluster with the given options.
|
|
116
88
|
#
|
|
117
89
|
# @param options [Hash] cluster configuration options
|
|
118
90
|
# @option options [Array<String>] :binds array of bind URIs
|
|
@@ -207,20 +179,7 @@ module Raptor
|
|
|
207
179
|
@seed_vacated_index = nil
|
|
208
180
|
end
|
|
209
181
|
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
# Forks the configured number of worker processes and monitors them,
|
|
213
|
-
# restarting any that exit unexpectedly or stop checking in. Handles
|
|
214
|
-
# graceful shutdown via INT or TERM signals, phased restart via USR1,
|
|
215
|
-
# and hot restart via USR2.
|
|
216
|
-
#
|
|
217
|
-
# Each worker process includes:
|
|
218
|
-
# - 1 server thread (continuously accepts connections with backpressure control)
|
|
219
|
-
# - 1 reactor thread (I/O multiplexing, timeout handling, backlog monitoring)
|
|
220
|
-
# - N pipeline ractors (parallel HTTP parsing)
|
|
221
|
-
# - 1 pipeline collector thread (coordinates parsing results)
|
|
222
|
-
# - M worker threads (Rack application processing and response writing)
|
|
223
|
-
# - 1 stats thread (writes per-worker metrics to shared memory every second)
|
|
182
|
+
# Runs the cluster until a graceful shutdown is signalled.
|
|
224
183
|
#
|
|
225
184
|
# @return [void]
|
|
226
185
|
#
|
|
@@ -288,7 +247,8 @@ module Raptor
|
|
|
288
247
|
# Returns stats for all worker processes.
|
|
289
248
|
#
|
|
290
249
|
# @return [Array<Hash>] array of per-worker stat hashes, each containing
|
|
291
|
-
# :pid, :
|
|
250
|
+
# :pid, :index, :phase, :requests, :backlog, :busy_threads,
|
|
251
|
+
# :thread_capacity, :started_at, :last_checkin, and :booted
|
|
292
252
|
#
|
|
293
253
|
# @rbs () -> Array[Hash[Symbol, untyped]]
|
|
294
254
|
def stats
|
|
@@ -299,8 +259,7 @@ module Raptor
|
|
|
299
259
|
|
|
300
260
|
# Returns the inherited-FDs hash for a systemd socket-activation handoff,
|
|
301
261
|
# pairing each bind URI with the FD systemd passed at the same index.
|
|
302
|
-
#
|
|
303
|
-
# match the number of bind URIs.
|
|
262
|
+
# Returns an empty hash when the FD count doesn't match the bind count.
|
|
304
263
|
#
|
|
305
264
|
# @param bind_uris [Array<String>] the configured bind URIs
|
|
306
265
|
# @param filenos [Array<Integer>] file descriptors passed by systemd
|
|
@@ -779,7 +738,7 @@ module Raptor
|
|
|
779
738
|
)
|
|
780
739
|
ractor_pool = RactorPool.new(
|
|
781
740
|
size: @ractor_count,
|
|
782
|
-
worker: http1.
|
|
741
|
+
worker: http1.parser_worker
|
|
783
742
|
) do |parsed_result|
|
|
784
743
|
begin
|
|
785
744
|
if parsed_result[:protocol] == :http2
|
|
@@ -802,6 +761,7 @@ module Raptor
|
|
|
802
761
|
|
|
803
762
|
worker_listeners = if @bpf_active
|
|
804
763
|
bpf_listeners = ReuseportBPF.create_worker_listeners(@binder.bind_uris, index, @binder.socket_backlog)
|
|
764
|
+
ReuseportBPF.enable_load_reporting(index)
|
|
805
765
|
non_tcp_listeners = @binder.listeners.reject { |listener| listener.is_a?(TCPServer) }
|
|
806
766
|
bpf_listeners + non_tcp_listeners
|
|
807
767
|
else
|
|
@@ -917,7 +877,11 @@ module Raptor
|
|
|
917
877
|
#
|
|
918
878
|
# @rbs (AtomicThreadPool thread_pool) -> void
|
|
919
879
|
def drain_thread_pool(thread_pool)
|
|
920
|
-
drain = Thread.new
|
|
880
|
+
drain = Thread.new do
|
|
881
|
+
Thread.current.name = "Thread Pool Drain"
|
|
882
|
+
|
|
883
|
+
thread_pool.shutdown
|
|
884
|
+
end
|
|
921
885
|
return if drain.join(@worker_drain_timeout)
|
|
922
886
|
|
|
923
887
|
Log.warn "Force-killing in-flight app threads after #{@worker_drain_timeout}s drain timeout"
|
data/lib/raptor/http.rb
CHANGED
|
@@ -93,6 +93,24 @@ module Raptor
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
# Returns a Ractor-safe proc that routes each pipeline task to the
|
|
97
|
+
# HTTP/1.x or HTTP/2 handler based on the state hash's `:protocol` key.
|
|
98
|
+
#
|
|
99
|
+
# @param env_template [Hash] the Rack env template to seed each HTTP/1.x request with
|
|
100
|
+
# @param max_body_size [Integer, nil] byte limit for HTTP/1.x request bodies, or nil for no limit
|
|
101
|
+
# @return [Proc]
|
|
102
|
+
#
|
|
103
|
+
# @rbs (Hash[String, untyped] env_template, Integer? max_body_size) -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
|
|
104
|
+
def self.parser_worker(env_template, max_body_size)
|
|
105
|
+
proc do |data|
|
|
106
|
+
if data[:protocol] == :http2
|
|
107
|
+
Raptor::Http2.process_frames(data)
|
|
108
|
+
else
|
|
109
|
+
Raptor::Http1.parse(data, env_template, max_body_size)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
96
114
|
# Writes a Common Log Format entry to `io`. Write failures are silently
|
|
97
115
|
# ignored.
|
|
98
116
|
#
|