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.
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 configuration file and returns the hash it evaluates to.
74
- #
75
- # The file is evaluated at the top level so constants like `Raptor::*` resolve
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 and parses command-line arguments.
110
- #
111
- # Parses the provided command-line arguments and configures the server
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
- # Scans argv for a `-c`/`--config` flag and returns the configured path.
184
- #
185
- # The pre-scan runs before the main OptionParser pass so the config file
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
@@ -18,39 +18,15 @@ require_relative "stats"
18
18
  require_relative "systemd"
19
19
 
20
20
  module Raptor
21
- # Multi-process web server cluster with advanced concurrency architecture.
22
- #
23
- # Cluster manages multiple worker processes, each running a complete server
24
- # stack including a ractor pool for HTTP parsing, a thread pool for
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
- # Convenience method to create and run a cluster with the given options.
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 specified configuration.
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
- # Starts the multi-process cluster and manages worker processes.
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, :requests, :backlog, :started_at, :last_checkin, and :booted
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
- # The activation is skipped (with a warning) when the FD count doesn't
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.http_parser_worker
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 { thread_pool.shutdown }
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
  #