raptor 0.22.3 → 0.24.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/Dockerfile +10 -2
  4. data/README.md +183 -61
  5. data/docs/assets/profile_picture.png +0 -0
  6. data/docs/assets/startup_output.svg +37 -0
  7. data/docs/raptor-vs-puma.md +104 -424
  8. data/docs/talk.md +1388 -0
  9. data/ext/raptor_bpf/reuseport_select.bpf.c +18 -9
  10. data/ext/raptor_http/extconf.rb +2 -0
  11. data/ext/raptor_http/raptor_http.c +165 -234
  12. data/ext/raptor_http2/raptor_http2.c +272 -223
  13. data/ext/raptor_native/raptor_native.c +38 -20
  14. data/lib/rackup/handler/raptor.rb +14 -12
  15. data/lib/raptor/binder.rb +12 -12
  16. data/lib/raptor/cli.rb +19 -18
  17. data/lib/raptor/cluster.rb +120 -74
  18. data/lib/raptor/control_server.rb +5 -2
  19. data/lib/raptor/detached_body.rb +6 -2
  20. data/lib/raptor/http.rb +7 -6
  21. data/lib/raptor/http1.rb +281 -234
  22. data/lib/raptor/http2.rb +356 -276
  23. data/lib/raptor/plugin.rb +98 -0
  24. data/lib/raptor/reactor.rb +138 -148
  25. data/lib/raptor/reuseport_bpf.rb +62 -38
  26. data/lib/raptor/server.rb +45 -34
  27. data/lib/raptor/stats.rb +15 -3
  28. data/lib/raptor/thread_locals.rb +4 -3
  29. data/lib/raptor/version.rb +1 -1
  30. data/sig/generated/rackup/handler/raptor.rbs +2 -1
  31. data/sig/generated/raptor/binder.rbs +9 -9
  32. data/sig/generated/raptor/cli.rbs +6 -6
  33. data/sig/generated/raptor/cluster.rbs +18 -5
  34. data/sig/generated/raptor/control_server.rbs +2 -1
  35. data/sig/generated/raptor/detached_body.rbs +5 -1
  36. data/sig/generated/raptor/http.rbs +3 -5
  37. data/sig/generated/raptor/http1.rbs +119 -102
  38. data/sig/generated/raptor/http2.rbs +204 -190
  39. data/sig/generated/raptor/plugin.rbs +52 -0
  40. data/sig/generated/raptor/reactor.rbs +107 -118
  41. data/sig/generated/raptor/reuseport_bpf.rbs +54 -17
  42. data/sig/generated/raptor/server.rbs +26 -26
  43. data/sig/generated/raptor/stats.rbs +3 -2
  44. data/sig/generated/raptor/thread_locals.rbs +4 -3
  45. metadata +6 -2
  46. data/docs/brisrails-talk.md +0 -2398
@@ -4,27 +4,26 @@
4
4
 
5
5
  #include "ruby.h"
6
6
  #include "ruby/io.h"
7
- #include <sys/uio.h>
7
+ #include "ruby/thread.h"
8
8
  #include <errno.h>
9
9
  #include <limits.h>
10
+ #include <sys/uio.h>
10
11
 
11
12
  #ifdef __linux__
12
13
  #include <sched.h>
13
14
  #include <sys/prctl.h>
14
- #include <unistd.h>
15
15
  #endif
16
16
 
17
17
  static VALUE eEAGAINWaitWritable;
18
18
 
19
- static VALUE raptor_native_writev_nonblock(VALUE self, VALUE io, VALUE strings) {
20
- (void)self;
19
+ static VALUE vector_io_writev_nonblock(VALUE self, VALUE io, VALUE strings) {
21
20
  Check_Type(strings, T_ARRAY);
22
21
  long len = RARRAY_LEN(strings);
23
22
  if (len == 0) return LONG2NUM(0);
24
23
  if (len > IOV_MAX) len = IOV_MAX;
25
24
 
26
25
  int fd = rb_io_descriptor(io);
27
- struct iovec *iov = alloca(len * sizeof(struct iovec));
26
+ struct iovec *iov = ALLOCA_N(struct iovec, len);
28
27
 
29
28
  for (long i = 0; i < len; i++) {
30
29
  VALUE str = RARRAY_AREF(strings, i);
@@ -43,8 +42,9 @@ static VALUE raptor_native_writev_nonblock(VALUE self, VALUE io, VALUE strings)
43
42
  return LONG2NUM((long)written);
44
43
  }
45
44
 
46
- static VALUE raptor_native_pin_to_cpu(VALUE self, VALUE cpu) {
47
- (void)self;
45
+ // Pins the calling thread to the Nth CPU of its current affinity mask, so
46
+ // worker indexes map onto the CPUs the process is actually allowed to use.
47
+ static VALUE cpu_pin(VALUE self, VALUE cpu) {
48
48
  #ifdef __linux__
49
49
  int worker_index = NUM2INT(cpu);
50
50
  cpu_set_t current;
@@ -55,7 +55,10 @@ static VALUE raptor_native_pin_to_cpu(VALUE self, VALUE cpu) {
55
55
  int seen = 0;
56
56
  for (int candidate = 0; candidate < CPU_SETSIZE; candidate++) {
57
57
  if (!CPU_ISSET(candidate, &current)) continue;
58
- if (seen == worker_index) { cpu_id = candidate; break; }
58
+ if (seen == worker_index) {
59
+ cpu_id = candidate;
60
+ break;
61
+ }
59
62
  seen++;
60
63
  }
61
64
  if (cpu_id < 0) return Qfalse;
@@ -66,13 +69,11 @@ static VALUE raptor_native_pin_to_cpu(VALUE self, VALUE cpu) {
66
69
  if (sched_setaffinity(0, sizeof(set), &set) < 0) rb_sys_fail("sched_setaffinity");
67
70
  return Qtrue;
68
71
  #else
69
- (void)cpu;
70
72
  return Qfalse;
71
73
  #endif
72
74
  }
73
75
 
74
- static VALUE raptor_native_cpu_count(VALUE self) {
75
- (void)self;
76
+ static VALUE cpu_count(VALUE self) {
76
77
  #ifdef __linux__
77
78
  cpu_set_t set;
78
79
  CPU_ZERO(&set);
@@ -83,8 +84,24 @@ static VALUE raptor_native_cpu_count(VALUE self) {
83
84
  #endif
84
85
  }
85
86
 
86
- static VALUE raptor_native_enable_subreaper(VALUE self) {
87
- (void)self;
87
+ #ifdef __linux__
88
+ static void *cpu_yield_without_gvl(void *unused) {
89
+ sched_yield();
90
+ return NULL;
91
+ }
92
+ #endif
93
+
94
+ // Thread.pass cannot yield to native threads that have not rejoined
95
+ // Ruby's ready queue.
96
+ static VALUE cpu_yield(VALUE self) {
97
+ rb_thread_schedule();
98
+ #ifdef __linux__
99
+ rb_thread_call_without_gvl(cpu_yield_without_gvl, NULL, NULL, NULL);
100
+ #endif
101
+ return Qnil;
102
+ }
103
+
104
+ static VALUE subreaper_enable(VALUE self) {
88
105
  #if defined(__linux__) && defined(PR_SET_CHILD_SUBREAPER)
89
106
  if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) < 0) rb_sys_fail("prctl");
90
107
  return Qtrue;
@@ -96,18 +113,19 @@ static VALUE raptor_native_enable_subreaper(VALUE self) {
96
113
  RUBY_FUNC_EXPORTED void Init_raptor_native(void) {
97
114
  rb_ext_ractor_safe(true);
98
115
 
116
+ rb_global_variable(&eEAGAINWaitWritable);
117
+ eEAGAINWaitWritable = rb_const_get(rb_cIO, rb_intern("EAGAINWaitWritable"));
118
+
99
119
  VALUE mRaptor = rb_define_module("Raptor");
100
120
 
101
121
  VALUE mVectorIO = rb_define_module_under(mRaptor, "VectorIO");
102
- rb_define_singleton_method(mVectorIO, "writev_nonblock", raptor_native_writev_nonblock, 2);
122
+ rb_define_singleton_method(mVectorIO, "writev_nonblock", vector_io_writev_nonblock, 2);
103
123
 
104
124
  VALUE mCPU = rb_define_module_under(mRaptor, "CPU");
105
- rb_define_singleton_method(mCPU, "pin", raptor_native_pin_to_cpu, 1);
106
- rb_define_singleton_method(mCPU, "count", raptor_native_cpu_count, 0);
125
+ rb_define_singleton_method(mCPU, "pin", cpu_pin, 1);
126
+ rb_define_singleton_method(mCPU, "count", cpu_count, 0);
127
+ rb_define_singleton_method(mCPU, "yield", cpu_yield, 0);
107
128
 
108
129
  VALUE mSubreaper = rb_define_module_under(mRaptor, "Subreaper");
109
- rb_define_singleton_method(mSubreaper, "enable", raptor_native_enable_subreaper, 0);
110
-
111
- eEAGAINWaitWritable = rb_const_get(rb_cIO, rb_intern("EAGAINWaitWritable"));
112
- rb_global_variable(&eEAGAINWaitWritable);
130
+ rb_define_singleton_method(mSubreaper, "enable", subreaper_enable, 0);
113
131
  }
@@ -1,8 +1,6 @@
1
1
  # rbs_inline: enabled
2
2
  # frozen_string_literal: true
3
3
 
4
- require "concurrent/utility/processor_counter"
5
-
6
4
  module Rackup
7
5
  module Handler
8
6
  # Rack handler for booting Raptor through Rackup, `rails server`, or any
@@ -18,7 +16,8 @@ module Rackup
18
16
  #
19
17
  # @param app [#call] the Rack application to serve
20
18
  # @param options [Hash] handler options provided by Rackup or the host
21
- # @yield [cluster] the cluster instance, before it starts running
19
+ # @yield before the cluster starts running
20
+ # @yieldparam cluster [::Raptor::Cluster] the cluster instance
22
21
  # @return [void]
23
22
  #
24
23
  # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
@@ -62,7 +61,7 @@ module Rackup
62
61
  def self.build_cluster_options(app, options)
63
62
  defaults = DEFAULT_OPTIONS.dup
64
63
 
65
- if user_supplied_options = options.delete(:user_supplied_options)
64
+ if (user_supplied_options = options.delete(:user_supplied_options))
66
65
  (options.keys - user_supplied_options).each do |key|
67
66
  defaults[key] = options.delete(key)
68
67
  end
@@ -71,7 +70,9 @@ module Rackup
71
70
  cli_defaults = ::Raptor::CLI::DEFAULT_OPTIONS
72
71
  config_path = options[:Config] || ::Raptor::CLI.default_config_path
73
72
  config = config_path ? ::Raptor::CLI.load_config_file(config_path) : {}
74
- threads = (options[:Threads] || ENV["RAPTOR_THREADS"] || config[:threads] || cli_defaults[:threads]).to_i
73
+ environment_workers = Integer(ENV["RAPTOR_WORKERS"], 10) if ENV["RAPTOR_WORKERS"]
74
+ environment_threads = Integer(ENV["RAPTOR_THREADS"], 10) if ENV["RAPTOR_THREADS"]
75
+ threads = (options[:Threads] || environment_threads || config[:threads] || cli_defaults[:threads]).to_i
75
76
  max_threads = options[:MaxThreads] || ENV["RAPTOR_MAX_THREADS"] || config.fetch(:max_threads, cli_defaults[:max_threads])
76
77
 
77
78
  result = {
@@ -81,13 +82,13 @@ module Rackup
81
82
  config[:binds] || ["tcp://#{defaults[:Host]}:#{defaults[:Port]}"]
82
83
  end,
83
84
  socket_backlog: (config[:socket_backlog] || cli_defaults[:socket_backlog]).to_i,
84
- drain_accept_queue: config.key?(:drain_accept_queue) ? config[:drain_accept_queue] : cli_defaults[:drain_accept_queue],
85
- workers: (options[:Workers] || ENV["RAPTOR_WORKERS"] || config[:workers] || Concurrent.available_processor_count).to_i,
85
+ drain_accept_queue: config.fetch(:drain_accept_queue, cli_defaults[:drain_accept_queue]),
86
+ workers: (options[:Workers] || environment_workers || config[:workers] || cli_defaults[:workers]).to_i,
86
87
  threads: threads,
87
88
  max_threads: ::Raptor::CLI.parse_max_threads(max_threads, threads: threads),
88
- cpu_affinity: config.key?(:cpu_affinity) ? config[:cpu_affinity] : cli_defaults[:cpu_affinity],
89
- clean_thread_locals: config.key?(:clean_thread_locals) ? config[:clean_thread_locals] : cli_defaults[:clean_thread_locals],
90
- clean_fiber_locals: config.key?(:clean_fiber_locals) ? config[:clean_fiber_locals] : cli_defaults[:clean_fiber_locals],
89
+ cpu_affinity: config.fetch(:cpu_affinity, cli_defaults[:cpu_affinity]),
90
+ clean_thread_locals: config.fetch(:clean_thread_locals, cli_defaults[:clean_thread_locals]),
91
+ clean_fiber_locals: config.fetch(:clean_fiber_locals, cli_defaults[:clean_fiber_locals]),
91
92
  app: app
92
93
  }
93
94
  result[:rackup] = config[:rackup] if config.key?(:rackup)
@@ -105,14 +106,15 @@ module Rackup
105
106
  result[:before_worker_boot] = config.fetch(:before_worker_boot, cli_defaults[:before_worker_boot])
106
107
  result[:before_worker_shutdown] = config.fetch(:before_worker_shutdown, cli_defaults[:before_worker_shutdown])
107
108
  result[:before_refork] = config.fetch(:before_refork, cli_defaults[:before_refork])
108
- result[:stats_file] = config.key?(:stats_file) ? config[:stats_file] : cli_defaults[:stats_file]
109
+ result[:plugins] = config.fetch(:plugins, cli_defaults[:plugins])
110
+ result[:stats_file] = config.fetch(:stats_file, cli_defaults[:stats_file])
109
111
  result[:control_url] = config[:control_url] if config.key?(:control_url)
110
112
  result[:pid_file] = config[:pid_file] if config.key?(:pid_file)
111
113
  result[:stdout_file] = config[:stdout_file] if config.key?(:stdout_file)
112
114
  result[:stderr_file] = config[:stderr_file] if config.key?(:stderr_file)
113
115
  result[:access_log_file] = config[:access_log_file] if config.key?(:access_log_file)
114
116
  result[:on_error] = config[:on_error] if config.key?(:on_error)
115
- result
117
+ config.merge(result)
116
118
  end
117
119
  private_class_method :build_cluster_options
118
120
  end
data/lib/raptor/binder.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # rbs_inline: enabled
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "openssl"
4
5
  require "socket"
5
6
  require "uri"
6
7
 
@@ -56,17 +57,17 @@ module Raptor
56
57
  # Returns the array of bind URIs.
57
58
  #
58
59
  # @return [Array<String>]
59
- attr_reader :bind_uris
60
+ attr_reader :bind_uris #: Array[String]
60
61
 
61
62
  # Returns the kernel `listen()` queue depth for TCP/SSL listeners.
62
63
  #
63
64
  # @return [Integer]
64
- attr_reader :socket_backlog
65
+ attr_reader :socket_backlog #: Integer
65
66
 
66
67
  # Returns the array of listening sockets.
67
68
  #
68
69
  # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
69
- attr_reader :listeners
70
+ attr_reader :listeners #: Array[TCPServer | UNIXServer | SslListener | H2cListener]
70
71
 
71
72
  # Creates a new Binder and binds each URI. `localhost` expands to both
72
73
  # IPv4 and IPv6 loopback; when `inherited_fds` supplies file descriptors
@@ -113,8 +114,8 @@ module Raptor
113
114
  end
114
115
  end
115
116
 
116
- # Returns the port of the first TCP or SSL listener, or 0 when none
117
- # is configured.
117
+ # Returns the port of the first TCP, SSL, or cleartext HTTP/2 listener,
118
+ # or 0 when none is configured.
118
119
  #
119
120
  # @return [Integer]
120
121
  #
@@ -185,7 +186,7 @@ module Raptor
185
186
 
186
187
  # Creates fresh listeners for the given URI.
187
188
  #
188
- # @param uri [URI] the parsed bind URI
189
+ # @param uri [URI::Generic] the parsed bind URI
189
190
  # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
190
191
  # @raise [UnknownBindSchemeError] if the URI scheme is not supported
191
192
  #
@@ -208,7 +209,7 @@ module Raptor
208
209
  # Reconstructs listeners for the given URI from inherited file
209
210
  # descriptors.
210
211
  #
211
- # @param uri [URI] the parsed bind URI the FDs were bound to
212
+ # @param uri [URI::Generic] the parsed bind URI the FDs were bound to
212
213
  # @param filenos [Array<Integer>] file descriptors to wrap
213
214
  # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
214
215
  # @raise [UnknownBindSchemeError] if the URI scheme is not supported
@@ -240,7 +241,7 @@ module Raptor
240
241
  # @rbs (String? host, Integer? port) -> Array[TCPServer]
241
242
  def create_tcp_listeners(host, port)
242
243
  if host == "localhost"
243
- return loopback_addresses.map { |address| create_tcp_listeners(address, port) }.flatten
244
+ return loopback_addresses.flat_map { |address| create_tcp_listeners(address, port) }
244
245
  end
245
246
 
246
247
  host = host[1..-2] if host&.start_with?("[")
@@ -250,6 +251,7 @@ module Raptor
250
251
  socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
251
252
  socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true) if Socket.const_defined?(:SO_REUSEPORT)
252
253
  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
254
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT, 1) if Socket.const_defined?(:TCP_DEFER_ACCEPT)
253
255
  socket.bind(addrinfo)
254
256
  socket.listen(@socket_backlog)
255
257
 
@@ -299,7 +301,7 @@ module Raptor
299
301
  #
300
302
  # @param host [String, nil] hostname or IP address to bind to
301
303
  # @param port [Integer, nil] port number to bind to
302
- # @param ssl_params [Hash<String, String>] SSL options (`cert` and `key` file paths)
304
+ # @param ssl_params [Hash{String => String}] SSL options (`cert` and `key` file paths)
303
305
  # @return [Array<SslListener>] array containing the created SSL listener(s)
304
306
  #
305
307
  # @rbs (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
@@ -312,13 +314,11 @@ module Raptor
312
314
  # Builds a frozen `OpenSSL::SSL::SSLContext` configured for HTTP/2 and
313
315
  # HTTP/1.1 ALPN negotiation.
314
316
  #
315
- # @param ssl_params [Hash<String, String>] SSL options ("cert" and "key" paths)
317
+ # @param ssl_params [Hash{String => String}] SSL options (`cert` and `key` file paths)
316
318
  # @return [OpenSSL::SSL::SSLContext]
317
319
  #
318
320
  # @rbs (Hash[String, String] ssl_params) -> OpenSSL::SSL::SSLContext
319
321
  def build_ssl_context(ssl_params)
320
- require "openssl"
321
-
322
322
  OpenSSL::SSL::SSLContext.new.tap do |ssl_context|
323
323
  ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_params["cert"]))
324
324
  ssl_context.key = OpenSSL::PKey.read(File.read(ssl_params["key"]))
data/lib/raptor/cli.rb CHANGED
@@ -7,6 +7,7 @@ require "optparse"
7
7
  require "concurrent/utility/processor_counter"
8
8
 
9
9
  require_relative "cluster"
10
+ require_relative "version"
10
11
 
11
12
  module Raptor
12
13
  # Command-line interface for the Raptor web server.
@@ -48,18 +49,18 @@ module Raptor
48
49
  write_timeout: 5,
49
50
  max_body_size: nil,
50
51
  body_spool_threshold: 1024 * 1024
51
- },
52
+ }.freeze,
52
53
  http1: {
53
54
  ractors: nil,
54
55
  persistent_data_timeout: 65,
55
56
  max_keepalive_requests: 1000
56
- },
57
+ }.freeze,
57
58
  http2: {
58
59
  ractors: nil,
59
60
  max_concurrent_streams: 100,
60
61
  keepalive_interval: 10,
61
62
  keepalive_timeout: 5
62
- },
63
+ }.freeze,
63
64
  worker_boot_timeout: 60,
64
65
  worker_timeout: 60,
65
66
  worker_drain_timeout: 25,
@@ -69,12 +70,13 @@ module Raptor
69
70
  before_worker_boot: [].freeze,
70
71
  before_worker_shutdown: [].freeze,
71
72
  before_refork: [].freeze,
73
+ plugins: [].freeze,
72
74
  stats_file: "tmp/raptor.json",
73
75
  control_url: nil,
74
76
  pid_file: nil,
75
77
  stdout_file: nil,
76
78
  stderr_file: nil,
77
- access_log_file: nil,
79
+ access_log_file: nil
78
80
  }.freeze
79
81
 
80
82
  DEFAULT_CONFIG_PATHS = ["raptor.rb", "config/raptor.rb"].freeze
@@ -131,17 +133,17 @@ module Raptor
131
133
  max_threads
132
134
  end
133
135
 
134
- # @rbs @command: Symbol
135
136
  # @rbs @options: Hash[Symbol, untyped]
137
+ # @rbs @command: Symbol
136
138
  # @rbs @parser: OptionParser
137
139
 
138
- # Creates a new CLI instance from `argv`. A rackup file may be given
139
- # as the first positional argument; every other value is parsed as a
140
- # flag.
140
+ # Creates a new CLI from `argv`. A rackup file may be given as the
141
+ # first positional argument; every other value is parsed as a flag.
141
142
  #
142
143
  # @param argv [Array<String>] command-line arguments to parse
143
144
  # @return [void]
144
145
  # @raise [OptionParser::ParseError] if invalid options are provided
146
+ # @raise [ArgumentError] if the config file or max threads value is invalid
145
147
  #
146
148
  # @rbs (Array[String] argv) -> void
147
149
  def initialize(argv)
@@ -203,7 +205,7 @@ module Raptor
203
205
  end
204
206
  end
205
207
 
206
- # Returns the path from a `-c`/`--config` flag in `argv`, recognising
208
+ # Returns the path from a `-c`/`--config` flag in `argv`, recognizing
207
209
  # all four OptionParser-accepted forms (`-c PATH`, `-cPATH`,
208
210
  # `--config PATH`, `--config=PATH`).
209
211
  #
@@ -258,6 +260,8 @@ module Raptor
258
260
  #
259
261
  # @rbs () -> OptionParser
260
262
  def create_parser
263
+ cli_binds = []
264
+
261
265
  OptionParser.new do |opts|
262
266
  opts.banner = "Usage: raptor [options] [rackup file]"
263
267
 
@@ -266,11 +270,8 @@ module Raptor
266
270
  end
267
271
 
268
272
  opts.on("-b", "--bind URI", String, "Bind address (default: tcp://0.0.0.0:9292)") do |bind|
269
- if @options[:binds].equal?(DEFAULT_OPTIONS[:binds])
270
- @options[:binds] = [bind]
271
- else
272
- @options[:binds] << bind
273
- end
273
+ cli_binds << bind
274
+ @options[:binds] = cli_binds
274
275
  end
275
276
 
276
277
  opts.on("--socket-backlog NUM", Integer, "Socket listen backlog (default: 1024)") do |num|
@@ -333,7 +334,7 @@ module Raptor
333
334
  @options[:connection][:body_spool_threshold] = bytes
334
335
  end
335
336
 
336
- opts.on("--http1-ractors NUM", Integer, "Number of HTTP/1.1 pipeline ractors per worker (default: `round(cores / workers)`, clamped to 1..3)") do |num|
337
+ opts.on("--http1-ractors NUM", Integer, "Number of HTTP/1.1 pipeline ractors per worker (default: `round(cores / workers)`, clamped to 1..#{Cluster::HTTP1_RACTOR_COUNT_CAP})") do |num|
337
338
  @options[:http1][:ractors] = num
338
339
  end
339
340
 
@@ -345,7 +346,7 @@ module Raptor
345
346
  @options[:http1][:max_keepalive_requests] = num
346
347
  end
347
348
 
348
- opts.on("--http2-ractors NUM", Integer, "Number of HTTP/2 pipeline ractors per worker (default: `round(cores / workers)`, clamped to 1..2)") do |num|
349
+ opts.on("--http2-ractors NUM", Integer, "Number of HTTP/2 pipeline ractors per worker (default: `round(cores / workers)`, clamped to 1..#{Cluster::HTTP2_RACTOR_COUNT_CAP})") do |num|
349
350
  @options[:http2][:ractors] = num
350
351
  end
351
352
 
@@ -377,7 +378,7 @@ module Raptor
377
378
  @options[:worker_shutdown_timeout] = timeout
378
379
  end
379
380
 
380
- opts.on("--refork-after NUM", Integer, "Refork workers from a warmed source after any worker crosses NUM requests; 0 disables (default: 1000)") do |num|
381
+ opts.on("--refork-after NUM", Integer, "Refork workers from a warmed source after any worker crosses NUM requests; 0 disables (default: 1000 on Linux, off elsewhere)") do |num|
381
382
  @options[:refork_after] = num
382
383
  end
383
384
 
@@ -411,7 +412,7 @@ module Raptor
411
412
  end
412
413
 
413
414
  opts.on("-v", "--version", "Show version") do
414
- puts Raptor::VERSION
415
+ puts VERSION
415
416
  exit
416
417
  end
417
418
  end