raptor 0.25.0 → 0.27.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.
@@ -0,0 +1,225 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ require "atomic-ruby/atom"
5
+ require "atomic-ruby/atomic_condition_variable"
6
+
7
+ require_relative "cli"
8
+ require_relative "worker"
9
+
10
+ module Raptor
11
+ # Serves an existing Rack application in the caller's process without
12
+ # installing signal handlers or changing process-wide configuration.
13
+ #
14
+ class Embedded
15
+ # @rbs @app: untyped
16
+ # @rbs @options: Hash[Symbol, untyped]
17
+ # @rbs @binds: Array[String]
18
+ # @rbs @log: bool
19
+ # @rbs @lifecycle_owner: Atom
20
+ # @rbs @lifecycle_available: AtomicConditionVariable
21
+ # @rbs @started: bool
22
+ # @rbs @stopped: bool
23
+ # @rbs @binder: Binder?
24
+ # @rbs @worker: Worker?
25
+
26
+ attr_reader :port #: Integer
27
+ attr_reader :addresses #: Array[String]
28
+
29
+ # Creates an in-process server. A port of zero selects an available port.
30
+ # Thread and protocol options follow the regular server configuration.
31
+ # Set log to report startup information.
32
+ #
33
+ # @rbs (untyped app, ?host: String, ?port: Integer, ?log: bool, **untyped options) -> void
34
+ def initialize(app, host: "127.0.0.1", port: 0, log: false, **options)
35
+ @app = app
36
+ @port = port
37
+ @log = log
38
+ @addresses = []
39
+ @options = CLI::DEFAULT_OPTIONS.merge(options)
40
+ CLI::NESTED_OPTION_KEYS.each do |key|
41
+ @options[key] = CLI::DEFAULT_OPTIONS[key].merge(options[key] || {})
42
+ end
43
+ @options[:max_threads] = CLI.parse_max_threads(@options[:max_threads], threads: @options[:threads])
44
+ @binds = options[:binds] || ["tcp://#{host}:#{port}"]
45
+ @lifecycle_owner = Atom.new(nil)
46
+ @lifecycle_available = AtomicConditionVariable.new
47
+ @started = false
48
+ @stopped = false
49
+ end
50
+
51
+ # Starts serving and returns once the request-handling stack is ready.
52
+ # Startup errors are raised in the calling thread.
53
+ #
54
+ # @rbs () -> Embedded
55
+ def start
56
+ with_lifecycle do
57
+ raise ThreadError, "server has already been started or stopped" if @started || @stopped
58
+
59
+ @started = true
60
+ begin
61
+ @binder = Binder.new(@binds, socket_backlog: @options[:socket_backlog])
62
+ @port = @binder.server_port
63
+ @addresses = @binder.addresses
64
+ start_worker
65
+ rescue
66
+ @stopped = true
67
+ @binder&.close
68
+ raise
69
+ end
70
+ log_initialization if @log
71
+ end
72
+
73
+ self
74
+ end
75
+
76
+ # Starts serving and waits until shutdown.
77
+ #
78
+ # @rbs () -> void
79
+ def run
80
+ start
81
+ join
82
+ end
83
+
84
+ # Drains serving work while retaining the bound listeners for resume.
85
+ # Connections may queue, but requests are not served while paused.
86
+ # Existing keep-alive connections close. Repeated pauses have no effect.
87
+ #
88
+ # Pause before master forks and resume in the parent. Close the inherited
89
+ # instance before other worker boot hooks, and shut down when the plugin stops.
90
+ #
91
+ # @example From a plugin's start callback
92
+ # @server = Raptor::Embedded.new(app, port: 9293).start
93
+ # cluster.options[:before_fork] << proc { @server.pause }
94
+ # cluster.options[:after_fork] << proc { @server.resume }
95
+ # cluster.options[:before_worker_boot].unshift(proc { @server.shutdown })
96
+ #
97
+ # @rbs () -> Embedded
98
+ def pause
99
+ with_lifecycle do
100
+ raise ThreadError, "server is not running" unless @started && !@stopped
101
+
102
+ begin
103
+ @worker&.shutdown(close_listeners: false)
104
+ @worker = nil
105
+ rescue
106
+ @stopped = true
107
+ @binder&.close
108
+ raise
109
+ end
110
+ end
111
+
112
+ self
113
+ end
114
+
115
+ # Resumes serving on the retained listeners. Has no effect while running.
116
+ # Shutdown remains terminal. Startup failures release the listeners and propagate.
117
+ #
118
+ # @rbs () -> Embedded
119
+ def resume
120
+ with_lifecycle do
121
+ raise ThreadError, "server has not been started or has been stopped" unless @started && !@stopped
122
+
123
+ begin
124
+ start_worker unless @worker
125
+ rescue
126
+ @stopped = true
127
+ @binder&.close
128
+ raise
129
+ end
130
+ end
131
+
132
+ self
133
+ end
134
+
135
+ # Drains requests and closes the server's sockets, threads, and Ractors.
136
+ # Calling shutdown more than once has no effect.
137
+ #
138
+ # @rbs () -> void
139
+ def shutdown
140
+ with_lifecycle do
141
+ return if @stopped
142
+
143
+ @stopped = true
144
+ begin
145
+ @worker&.shutdown
146
+ ensure
147
+ @binder&.close
148
+ end
149
+ end
150
+ end
151
+
152
+ # Waits across pauses until termination and propagates serving failures.
153
+ #
154
+ # @rbs () -> void
155
+ def join
156
+ started = with_lifecycle { @started }
157
+ raise ThreadError, "server has not been started" unless started
158
+
159
+ loop do
160
+ worker, stopped = with_lifecycle { [@worker, @stopped] }
161
+ break if stopped
162
+
163
+ if worker
164
+ worker.join
165
+ break if with_lifecycle { @worker.equal?(worker) }
166
+ else
167
+ @lifecycle_available.wait { @worker || @stopped }
168
+ end
169
+ end
170
+ ensure
171
+ shutdown if started
172
+ end
173
+
174
+ private
175
+
176
+ # @rbs () -> void
177
+ def start_worker
178
+ @worker = Worker.new(
179
+ @app,
180
+ listeners: @binder.listeners,
181
+ port: @port,
182
+ options: @options,
183
+ http1_ractors: @options[:http1][:ractors] || Cluster.default_http1_ractor_count(1),
184
+ http2_ractors: @options[:http2][:ractors] || Cluster.default_http2_ractor_count(1)
185
+ )
186
+ @worker.start
187
+ end
188
+
189
+ # @rbs () -> void
190
+ def log_initialization
191
+ environment = @options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
192
+ thread_count = @options[:threads]
193
+ max_threads = @options[:max_threads]
194
+ thread_limit = if max_threads == Float::INFINITY
195
+ " (scaling, no limit)"
196
+ elsif max_threads > thread_count
197
+ " (scaling up to #{max_threads})"
198
+ else
199
+ ""
200
+ end
201
+
202
+ Log.info "Server initializing:"
203
+ Log.info "├─ Version: #{VERSION}"
204
+ Log.info "├─ Ruby Version: #{RUBY_DESCRIPTION}"
205
+ Log.info "├─ Environment: #{environment}"
206
+ Log.info "├─ PID: #{Process.pid}"
207
+ Log.info "├─ #{thread_count} worker thread#{"s" if thread_count > 1}#{thread_limit}"
208
+ Log.info "└─ Listening on #{@addresses.join(", ")}"
209
+ end
210
+
211
+ # @rbs () { () -> untyped } -> untyped
212
+ def with_lifecycle
213
+ thread = Thread.current
214
+ @lifecycle_available.wait do
215
+ @lifecycle_owner.swap { |owner| owner || thread }.equal?(thread)
216
+ end
217
+ yield
218
+ ensure
219
+ if @lifecycle_owner.value.equal?(thread)
220
+ @lifecycle_owner.swap { nil }
221
+ @lifecycle_available.broadcast
222
+ end
223
+ end
224
+ end
225
+ end
data/lib/raptor/plugin.rb CHANGED
@@ -53,6 +53,7 @@ module Raptor
53
53
 
54
54
  # Starts in the master before workers fork. Override to open resources
55
55
  # and append callbacks to the cluster's lifecycle hook arrays.
56
+ # See {Cluster#initialize} for hooks and {Embedded#pause} for fork-safe Rack listeners.
56
57
  #
57
58
  # @rbs (Cluster cluster) -> void
58
59
  def start(cluster)
@@ -263,12 +263,21 @@ module Raptor
263
263
  #
264
264
  # @rbs () -> Thread
265
265
  def run
266
- Thread.new do
266
+ ready = AtomicConditionVariable.new
267
+ startup_error = nil
268
+ thread = Thread.new do
267
269
  Thread.current.name = "Reactor"
268
270
  @thread = Thread.current
269
- selector = IO::Event::Selector.new(Fiber.current)
270
- @wakeup = Fiber.new { loop { selector.transfer } }
271
- @selector = selector
271
+ begin
272
+ selector = IO::Event::Selector.new(Fiber.current)
273
+ @wakeup = Fiber.new { loop { selector.transfer } }
274
+ @selector = selector
275
+ rescue => error
276
+ startup_error = error
277
+ ready.signal
278
+ next
279
+ end
280
+ ready.signal
272
281
 
273
282
  until stopped?
274
283
  begin
@@ -310,6 +319,14 @@ module Raptor
310
319
  @thread = nil
311
320
  @selector.close
312
321
  end
322
+
323
+ ready.wait { @selector || startup_error }
324
+ if startup_error
325
+ thread.join
326
+ raise startup_error
327
+ end
328
+
329
+ thread
313
330
  end
314
331
 
315
332
  # Adds a new client connection to the reactor.
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Raptor
5
- VERSION = "0.25.0"
5
+ VERSION = "0.27.0"
6
6
  end
@@ -0,0 +1,200 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ require "atomic-ruby/atomic_thread_pool"
5
+ require "ractor-pool"
6
+
7
+ require_relative "http1"
8
+ require_relative "http2"
9
+ require_relative "reactor"
10
+ require_relative "server"
11
+
12
+ module Raptor
13
+ # Runs the request-handling stack shared by forked and embedded servers.
14
+ #
15
+ class Worker
16
+ # @rbs @app: untyped
17
+ # @rbs @listeners: Array[TCPServer | UNIXServer | Binder::SslListener | Binder::H2cListener]
18
+ # @rbs @port: Integer
19
+ # @rbs @options: Hash[Symbol, untyped]
20
+ # @rbs @http1_ractors: Integer
21
+ # @rbs @http2_ractors: Integer
22
+ # @rbs @access_log_io: IO?
23
+ # @rbs @worker_index: Integer?
24
+ # @rbs @stopped: bool
25
+ # @rbs @http1: Http1?
26
+ # @rbs @http2: Http2?
27
+ # @rbs @http1_ractor_pool: RactorPool?
28
+ # @rbs @http2_ractor_pool: RactorPool?
29
+ # @rbs @reactor_thread: Thread?
30
+ # @rbs @server: Server?
31
+ # @rbs @server_thread: Thread?
32
+
33
+ attr_reader :requests #: Integer
34
+ attr_reader :reactor #: Reactor?
35
+ attr_reader :thread_pool #: AtomicThreadPool?
36
+
37
+ # Creates a serving stack using the supplied listeners and configuration.
38
+ #
39
+ # @rbs (untyped app, listeners: Array[TCPServer | UNIXServer | Binder::SslListener | Binder::H2cListener], port: Integer, options: Hash[Symbol, untyped], http1_ractors: Integer, http2_ractors: Integer, ?access_log_io: IO?, ?worker_index: Integer?) -> void
40
+ def initialize(app, listeners:, port:, options:, http1_ractors:, http2_ractors:, access_log_io: nil, worker_index: nil)
41
+ @app = app
42
+ @listeners = listeners
43
+ @port = port
44
+ @options = options
45
+ @http1_ractors = http1_ractors
46
+ @http2_ractors = http2_ractors
47
+ @access_log_io = access_log_io
48
+ @worker_index = worker_index
49
+ @requests = 0
50
+ @stopped = false
51
+ end
52
+
53
+ # Starts the application pool, protocol parsers, reactor, and acceptor.
54
+ #
55
+ # @rbs () -> void
56
+ def start
57
+ counting_app = ->(env) {
58
+ @requests += 1
59
+ @app.call(env)
60
+ }
61
+ @thread_pool = AtomicThreadPool.new(size: @options[:threads], max_size: @options[:max_threads])
62
+ @http1 = Http1.new(
63
+ counting_app,
64
+ @port,
65
+ connection_options: @options[:connection],
66
+ http1_options: @options[:http1],
67
+ access_log_io: @access_log_io,
68
+ clean_thread_locals: @options[:clean_thread_locals],
69
+ clean_fiber_locals: @options[:clean_fiber_locals],
70
+ on_error: @options[:on_error]
71
+ )
72
+ @http2 = Http2.new(
73
+ counting_app,
74
+ @port,
75
+ connection_options: @options[:connection],
76
+ http2_options: @options[:http2],
77
+ access_log_io: @access_log_io,
78
+ clean_thread_locals: @options[:clean_thread_locals],
79
+ clean_fiber_locals: @options[:clean_fiber_locals],
80
+ on_error: @options[:on_error]
81
+ )
82
+ @http1_ractor_pool = RactorPool.new(
83
+ size: @http1_ractors,
84
+ worker: @http1.parser_worker,
85
+ name: "HTTP/1.1"
86
+ ) do |parsed_result|
87
+ begin
88
+ @http1.handle_parsed_request(parsed_result, @reactor, @thread_pool)
89
+ rescue => error
90
+ Log.rescued_error(error)
91
+ end
92
+ end
93
+
94
+ if @listeners.any? { |listener| listener.is_a?(Binder::SslListener) || listener.is_a?(Binder::H2cListener) }
95
+ @http2_ractor_pool = RactorPool.new(
96
+ size: @http2_ractors,
97
+ worker: @http2.parser_worker,
98
+ name: "HTTP/2"
99
+ ) do |parsed_result|
100
+ begin
101
+ @http2.handle_parsed_request(parsed_result, @reactor, @thread_pool)
102
+ rescue => error
103
+ Log.rescued_error(error)
104
+ end
105
+ end
106
+ end
107
+
108
+ @reactor = Reactor.new(
109
+ @http1,
110
+ @http1_ractor_pool,
111
+ @http2_ractor_pool,
112
+ @thread_pool,
113
+ connection_options: @options[:connection],
114
+ http1_options: @options[:http1],
115
+ http2_options: @options[:http2]
116
+ )
117
+ @reactor_thread = @reactor.run
118
+ @server = Server.new(
119
+ @reactor,
120
+ @thread_pool,
121
+ @http1,
122
+ @http2,
123
+ connection_options: @options[:connection],
124
+ drain_accept_queue: @options[:drain_accept_queue],
125
+ listeners: @listeners,
126
+ worker_index: @worker_index
127
+ )
128
+ @server_thread = @server.run
129
+ rescue
130
+ shutdown
131
+ raise
132
+ end
133
+
134
+ # Returns whether both serving threads are still running.
135
+ #
136
+ # @rbs () -> bool
137
+ def running?
138
+ !!(@server_thread&.alive? && @reactor_thread&.alive?)
139
+ end
140
+
141
+ # Waits until serving stops, propagating background thread failures.
142
+ #
143
+ # @rbs () -> void
144
+ def join
145
+ @server_thread.join(0.1) while running?
146
+ @server_thread.value if @server_thread && !@server_thread.alive?
147
+ @reactor_thread.value if @reactor_thread && !@reactor_thread.alive?
148
+ end
149
+
150
+ # Drains requests and releases the serving threads and parser Ractors.
151
+ # A seed keeps its listeners open for the cluster to manage.
152
+ #
153
+ # @rbs (?close_listeners: bool) -> void
154
+ def shutdown(close_listeners: true)
155
+ return if @stopped
156
+
157
+ @stopped = true
158
+ begin
159
+ if close_listeners
160
+ @server&.shutdown
161
+ else
162
+ @server&.stop_accepting
163
+ end
164
+ @server_thread&.join
165
+ ensure
166
+ @http1&.shutdown
167
+ if @reactor_thread&.alive?
168
+ @http2&.shutdown(@reactor)
169
+ @reactor.drain_detached_bodies(@options[:worker_drain_timeout])
170
+ end
171
+ drain_thread_pool if @thread_pool
172
+ @reactor&.shutdown
173
+ begin
174
+ @reactor_thread&.join
175
+ ensure
176
+ @http1_ractor_pool&.shutdown
177
+ @http2_ractor_pool&.shutdown
178
+ end
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ # Waits for app work to finish within the configured drain timeout.
185
+ #
186
+ # @rbs () -> void
187
+ def drain_thread_pool
188
+ drain_thread = Thread.new do
189
+ Thread.current.name = "Thread Pool Drain"
190
+
191
+ @thread_pool.shutdown
192
+ end
193
+ return if drain_thread.join(@options[:worker_drain_timeout])
194
+
195
+ Log.warn "Force-killing in-flight app threads after #{@options[:worker_drain_timeout]}s drain timeout"
196
+ @thread_pool.instance_variable_get(:@threads).each(&:kill)
197
+ drain_thread.join
198
+ end
199
+ end
200
+ end
@@ -7,16 +7,16 @@ module Rackup
7
7
  module Raptor
8
8
  DEFAULT_OPTIONS: untyped
9
9
 
10
- # Boots a Raptor cluster serving the given Rack application.
10
+ # Serves the given Rack application using the handler options.
11
11
  #
12
12
  # @param app [#call] the Rack application to serve
13
13
  # @param options [Hash] handler options provided by Rackup or the host
14
- # @yield before the cluster starts running
15
- # @yieldparam cluster [::Raptor::Cluster] the cluster instance
14
+ # @yield before the server starts running
15
+ # @yieldparam server [::Raptor::Cluster, ::Raptor::Embedded] the server instance
16
16
  # @return [void]
17
17
  #
18
- # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
19
- def self.run: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
18
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster | ::Raptor::Embedded) -> void } -> void
19
+ def self.run: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, **untyped options) { (::Raptor::Cluster | ::Raptor::Embedded) -> void } -> void
20
20
 
21
21
  # Returns the handler-specific options surfaced by `rackup --help`.
22
22
  #
@@ -25,17 +25,17 @@ module Rackup
25
25
  # @rbs () -> Hash[String, String]
26
26
  def self.valid_options: () -> Hash[String, String]
27
27
 
28
- # Builds a Raptor cluster options hash from Rack handler options.
28
+ # Builds Raptor server configuration from Rack handler options.
29
29
  #
30
30
  # Options not explicitly supplied by the user (per the `:user_supplied_options`
31
31
  # key) are treated as host-provided defaults.
32
32
  #
33
33
  # @param app [#call] the Rack application to serve
34
34
  # @param options [Hash] handler options provided by Rackup or the host
35
- # @return [Hash{Symbol => untyped}] cluster configuration
35
+ # @return [Hash{Symbol => untyped}] server configuration
36
36
  #
37
37
  # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
38
- def self.build_cluster_options: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
38
+ def self.build_options: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
39
39
  end
40
40
  end
41
41
  end
@@ -81,6 +81,11 @@ module Raptor
81
81
 
82
82
  private
83
83
 
84
+ # Serves in the CLI process until interrupted or terminated.
85
+ #
86
+ # @rbs () -> void
87
+ def run_embedded: () -> void
88
+
84
89
  # Reads and prints the stats file.
85
90
  #
86
91
  # @return [void]
@@ -44,10 +44,6 @@ module Raptor
44
44
  # @rbs (Integer worker_count, ?cores: Integer) -> Integer
45
45
  def self.default_http2_ractor_count: (Integer worker_count, ?cores: Integer) -> Integer
46
46
 
47
- @stats_file: String?
48
-
49
- @control_server: ControlServer?
50
-
51
47
  @pid_file: String?
52
48
 
53
49
  @stdout_file: String?
@@ -62,34 +58,36 @@ module Raptor
62
58
 
63
59
  @launch_argv: Array[String]?
64
60
 
65
- @on_error: (^(Hash[String, untyped]?, Exception) -> void)?
66
-
67
61
  @binder: Binder
68
62
 
63
+ @server_port: Integer
64
+
65
+ @app: untyped
66
+
69
67
  @plugins: Array[Plugin]
70
68
 
69
+ @control_server: ControlServer?
70
+
71
+ @stats_file: String?
72
+
71
73
  @before_refork: Array[Proc]
72
74
 
75
+ @after_worker_exit: Array[Proc]
76
+
77
+ @after_worker_drain: Array[Proc]
78
+
73
79
  @before_worker_shutdown: Array[Proc]
74
80
 
75
81
  @before_worker_boot: Array[Proc]
76
82
 
83
+ @after_fork: Array[Proc]
84
+
77
85
  @before_fork: Array[Proc]
78
86
 
79
87
  @worker_shutdown_timeout: Integer
80
88
 
81
89
  @worker_drain_timeout: Integer
82
90
 
83
- @worker_timeout: Integer
84
-
85
- @worker_boot_timeout: Integer
86
-
87
- @http2_options: Hash[Symbol, untyped]
88
-
89
- @http1_options: Hash[Symbol, untyped]
90
-
91
- @connection_options: Hash[Symbol, untyped]
92
-
93
91
  @bpf_active: bool
94
92
 
95
93
  @resp_w: IO?
@@ -126,20 +124,18 @@ module Raptor
126
124
 
127
125
  @timed_out: Set[Integer]
128
126
 
127
+ @worker_indices: Hash[Integer, Integer]
128
+
129
129
  @workers: Hash[Integer, Integer]
130
130
 
131
131
  @shutdown: bool
132
132
 
133
- @app: untyped
133
+ @worker_timeout: Integer
134
134
 
135
- @server_port: Integer
135
+ @worker_boot_timeout: Integer
136
136
 
137
137
  @environment: String
138
138
 
139
- @clean_fiber_locals: bool
140
-
141
- @clean_thread_locals: bool
142
-
143
139
  @cpu_affinity: bool
144
140
 
145
141
  @max_thread_count: (Integer | Float)?
@@ -152,8 +148,6 @@ module Raptor
152
148
 
153
149
  @worker_count: Integer
154
150
 
155
- @drain_accept_queue: bool
156
-
157
151
  # Returns the cluster configuration available to plugins.
158
152
  #
159
153
  # @return [Hash{Symbol => untyped}]
@@ -183,9 +177,12 @@ module Raptor
183
177
  # @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
184
178
  # @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
185
179
  # @option options [Integer, Array<Integer>, nil] :refork_after request-count threshold(s) at which a warm worker is promoted to a fork source for phased refork; nil or 0 disables. Requires `PR_SET_CHILD_SUBREAPER` (Linux)
186
- # @option options [Array<Proc>] :before_fork procs called in the master before every worker fork
180
+ # @option options [Array<Proc>] :before_fork procs called in the master before each direct worker fork, not seed forks
181
+ # @option options [Array<Proc>] :after_fork procs called in the master after each direct fork attempt, including failures
187
182
  # @option options [Array<Proc>] :before_worker_boot procs called with the worker index before it begins serving
188
183
  # @option options [Array<Proc>] :before_worker_shutdown procs called with the worker index before its graceful shutdown
184
+ # @option options [Array<Proc>] :after_worker_drain procs called with the worker index after draining, before exit or seed promotion
185
+ # @option options [Array<Proc>] :after_worker_exit procs called in the master after reaping, with the worker index, pid, and Process::Status
189
186
  # @option options [Array<Proc>] :before_refork procs called in a worker before it transitions to a seed
190
187
  # @option options [Array<String, Symbol>] :plugins plugins to load and start in order
191
188
  # @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
@@ -420,16 +417,6 @@ module Raptor
420
417
  # @rbs (Integer index) -> void
421
418
  def run_seed_loop: (Integer index) -> void
422
419
 
423
- # Shuts down the worker's application thread pool, force-killing the
424
- # underlying threads if in-flight requests have not finished within
425
- # `worker_drain_timeout` seconds.
426
- #
427
- # @param thread_pool [AtomicThreadPool] the worker's thread pool
428
- # @return [void]
429
- #
430
- # @rbs (AtomicThreadPool thread_pool) -> void
431
- def drain_thread_pool: (AtomicThreadPool thread_pool) -> void
432
-
433
420
  # Returns a human-readable description of how a process exited.
434
421
  #
435
422
  # @param status [Process::Status] the exit status of the process