raptor 0.11.0 → 0.12.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/.buildkite/pipeline.yml +100 -0
- data/CHANGELOG.md +7 -0
- data/README.md +47 -31
- data/docs/raptor-vs-puma.md +61 -43
- data/ext/raptor_bpf/reuseport_select.bpf.c +3 -2
- data/ext/raptor_native/raptor_native.c +16 -2
- data/lib/raptor/cli.rb +9 -0
- data/lib/raptor/cluster.rb +368 -29
- data/lib/raptor/reuseport_bpf.rb +13 -0
- data/lib/raptor/server.rb +14 -6
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/cluster.rbs +173 -34
- data/sig/generated/raptor/reuseport_bpf.rbs +9 -0
- data/sig/generated/raptor/server.rbs +10 -4
- metadata +1 -1
|
@@ -40,57 +40,85 @@ module Raptor
|
|
|
40
40
|
# @rbs (Hash[Symbol, untyped] options) -> void
|
|
41
41
|
def self.run: (Hash[Symbol, untyped] options) -> void
|
|
42
42
|
|
|
43
|
-
@
|
|
43
|
+
@bpf_active: bool
|
|
44
44
|
|
|
45
|
-
@
|
|
45
|
+
@server_port: Integer
|
|
46
46
|
|
|
47
|
-
@
|
|
47
|
+
@binder: Binder
|
|
48
48
|
|
|
49
|
-
@
|
|
49
|
+
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
50
50
|
|
|
51
|
-
@
|
|
51
|
+
@launch_argv: Array[String]?
|
|
52
52
|
|
|
53
|
-
@
|
|
53
|
+
@launch_command: String?
|
|
54
54
|
|
|
55
|
-
@
|
|
55
|
+
@access_log_io: IO?
|
|
56
56
|
|
|
57
|
-
@
|
|
57
|
+
@access_log_file: String?
|
|
58
58
|
|
|
59
59
|
@stderr_file: String?
|
|
60
60
|
|
|
61
|
-
@
|
|
61
|
+
@stdout_file: String?
|
|
62
62
|
|
|
63
|
-
@
|
|
63
|
+
@pid_file: String?
|
|
64
64
|
|
|
65
|
-
@
|
|
65
|
+
@stats_file: String?
|
|
66
66
|
|
|
67
|
-
@
|
|
67
|
+
@before_refork: Array[Proc]
|
|
68
68
|
|
|
69
|
-
@
|
|
69
|
+
@before_worker_shutdown: Array[Proc]
|
|
70
70
|
|
|
71
|
-
@
|
|
71
|
+
@before_worker_boot: Array[Proc]
|
|
72
72
|
|
|
73
|
-
@
|
|
73
|
+
@before_fork: Array[Proc]
|
|
74
74
|
|
|
75
|
-
@
|
|
75
|
+
@worker_shutdown_timeout: Integer
|
|
76
76
|
|
|
77
|
-
@
|
|
77
|
+
@worker_drain_timeout: Integer
|
|
78
78
|
|
|
79
|
-
@
|
|
79
|
+
@worker_timeout: Integer
|
|
80
80
|
|
|
81
|
-
@
|
|
81
|
+
@worker_boot_timeout: Integer
|
|
82
82
|
|
|
83
|
-
@
|
|
83
|
+
@resp_w: IO?
|
|
84
84
|
|
|
85
|
-
@
|
|
85
|
+
@resp_r: IO?
|
|
86
86
|
|
|
87
|
-
@
|
|
87
|
+
@fork_w: IO?
|
|
88
88
|
|
|
89
|
-
@
|
|
89
|
+
@fork_r: IO?
|
|
90
|
+
|
|
91
|
+
@seed_vacated_index: Integer?
|
|
92
|
+
|
|
93
|
+
@seed_ready: bool
|
|
94
|
+
|
|
95
|
+
@seed_pid: Integer?
|
|
96
|
+
|
|
97
|
+
@refork_threshold_idx: Integer
|
|
98
|
+
|
|
99
|
+
@refork_requested: bool
|
|
100
|
+
|
|
101
|
+
@refork_thresholds: Array[Integer]
|
|
90
102
|
|
|
91
103
|
@hot_restart_requested: bool
|
|
92
104
|
|
|
93
|
-
@
|
|
105
|
+
@phased_restarting: bool
|
|
106
|
+
|
|
107
|
+
@phased_restart_requested: bool
|
|
108
|
+
|
|
109
|
+
@phase: Integer
|
|
110
|
+
|
|
111
|
+
@stats: Stats
|
|
112
|
+
|
|
113
|
+
@timed_out: Set[Integer]
|
|
114
|
+
|
|
115
|
+
@workers: Hash[Integer, Integer]
|
|
116
|
+
|
|
117
|
+
@shutdown: bool
|
|
118
|
+
|
|
119
|
+
@app: untyped
|
|
120
|
+
|
|
121
|
+
@http2_options: Hash[Symbol, untyped]
|
|
94
122
|
|
|
95
123
|
@http1_options: Hash[Symbol, untyped]
|
|
96
124
|
|
|
@@ -130,6 +158,11 @@ module Raptor
|
|
|
130
158
|
# @option options [Integer] :worker_timeout seconds to wait for a booted worker to check in before killing it
|
|
131
159
|
# @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
|
|
132
160
|
# @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
|
|
161
|
+
# @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)
|
|
162
|
+
# @option options [Array<Proc>] :before_fork procs called in the master before every worker fork
|
|
163
|
+
# @option options [Array<Proc>] :before_worker_boot procs called in each worker before it begins serving
|
|
164
|
+
# @option options [Array<Proc>] :before_worker_shutdown procs called in each worker before its graceful shutdown
|
|
165
|
+
# @option options [Array<Proc>] :before_refork procs called in a worker before it transitions to a seed
|
|
133
166
|
# @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
|
|
134
167
|
# @option options [String, nil] :pid_file path to write the master PID to, or nil to disable
|
|
135
168
|
# @option options [String, nil] :stdout_file path to redirect stdout to, reopened on SIGHUP, or nil to disable
|
|
@@ -185,8 +218,18 @@ module Raptor
|
|
|
185
218
|
# @rbs (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
|
|
186
219
|
def pair_systemd_fds: (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
|
|
187
220
|
|
|
188
|
-
#
|
|
189
|
-
#
|
|
221
|
+
# Normalises the `refork_after` option into a sorted array of positive
|
|
222
|
+
# thresholds. Accepts nil, 0, an Integer, or an Array<Integer>; anything
|
|
223
|
+
# else falls back to an empty array (feature disabled).
|
|
224
|
+
#
|
|
225
|
+
# @param value [Integer, Array<Integer>, nil] the raw option value
|
|
226
|
+
# @return [Array<Integer>]
|
|
227
|
+
#
|
|
228
|
+
# @rbs (untyped value) -> Array[Integer]
|
|
229
|
+
def normalize_refork_thresholds: (untyped value) -> Array[Integer]
|
|
230
|
+
|
|
231
|
+
# Forks a new worker process and registers it at the given index,
|
|
232
|
+
# forking from the seed when one is active and off the master otherwise.
|
|
190
233
|
#
|
|
191
234
|
# @param index [Integer] slot index for this worker in the stats region
|
|
192
235
|
# @return [void]
|
|
@@ -194,16 +237,87 @@ module Raptor
|
|
|
194
237
|
# @rbs (Integer index) -> void
|
|
195
238
|
def spawn_worker: (Integer index) -> void
|
|
196
239
|
|
|
240
|
+
# Asks the seed to fork a new worker at the given index, returning the
|
|
241
|
+
# child pid, or nil when the seed doesn't respond in time.
|
|
242
|
+
#
|
|
243
|
+
# @param index [Integer] slot index for the new worker
|
|
244
|
+
# @return [Integer, nil] the forked worker's pid, or nil on failure
|
|
245
|
+
#
|
|
246
|
+
# @rbs (Integer index) -> Integer?
|
|
247
|
+
def spawn_worker_via_seed: (Integer index) -> Integer?
|
|
248
|
+
|
|
249
|
+
# Checks whether a process with the given pid is currently alive.
|
|
250
|
+
#
|
|
251
|
+
# @param pid [Integer] the pid to probe
|
|
252
|
+
# @return [Boolean] true if the process exists
|
|
253
|
+
#
|
|
254
|
+
# @rbs (Integer pid) -> bool
|
|
255
|
+
def pid_alive?: (Integer pid) -> bool
|
|
256
|
+
|
|
197
257
|
# Reaps any worker processes that have exited, respawning each one
|
|
198
258
|
# unless the cluster is shutting down.
|
|
199
259
|
#
|
|
200
|
-
# @return [Symbol] :no_children when
|
|
260
|
+
# @return [Symbol] :no_children when nothing is left to supervise, otherwise :reaped
|
|
201
261
|
#
|
|
202
262
|
# @rbs () -> Symbol
|
|
203
263
|
def reap_workers: () -> Symbol
|
|
204
264
|
|
|
205
|
-
#
|
|
206
|
-
#
|
|
265
|
+
# Records a reaped pid, respawning its worker slot unless the cluster
|
|
266
|
+
# is shutting down. Clears the seed reference when the seed exits.
|
|
267
|
+
#
|
|
268
|
+
# @param pid [Integer] the reaped pid
|
|
269
|
+
# @param status [Process::Status] the exit status
|
|
270
|
+
# @return [void]
|
|
271
|
+
#
|
|
272
|
+
# @rbs (Integer pid, Process::Status status) -> void
|
|
273
|
+
def reap_pid: (Integer pid, Process::Status status) -> void
|
|
274
|
+
|
|
275
|
+
# Promotes the most-experienced worker to a seed and starts a phased
|
|
276
|
+
# refork when the next `refork_after` threshold is crossed or a manual
|
|
277
|
+
# refork was requested via `SIGURG`.
|
|
278
|
+
#
|
|
279
|
+
# @return [void]
|
|
280
|
+
#
|
|
281
|
+
# @rbs () -> void
|
|
282
|
+
def check_refork_trigger: () -> void
|
|
283
|
+
|
|
284
|
+
# Picks the most-experienced booted worker in the current phase,
|
|
285
|
+
# returning its slot index and its request count. Returns nil when
|
|
286
|
+
# no worker qualifies.
|
|
287
|
+
#
|
|
288
|
+
# @return [Array<Integer>, nil]
|
|
289
|
+
#
|
|
290
|
+
# @rbs () -> Array[Integer]?
|
|
291
|
+
def pick_refork_candidate: () -> Array[Integer]?
|
|
292
|
+
|
|
293
|
+
# Retires the current seed and promotes the given worker into its place,
|
|
294
|
+
# queueing a phased refork for the remaining workers.
|
|
295
|
+
#
|
|
296
|
+
# @param index [Integer] slot index of the worker to promote
|
|
297
|
+
# @return [void]
|
|
298
|
+
#
|
|
299
|
+
# @rbs (Integer index) -> void
|
|
300
|
+
def promote_worker_to_seed: (Integer index) -> void
|
|
301
|
+
|
|
302
|
+
# Terminates the currently-active seed process, if any, and waits for
|
|
303
|
+
# it to exit. Its seed-forked workers stay attached to the master and
|
|
304
|
+
# keep serving.
|
|
305
|
+
#
|
|
306
|
+
# @return [void]
|
|
307
|
+
#
|
|
308
|
+
# @rbs () -> void
|
|
309
|
+
def retire_current_seed: () -> void
|
|
310
|
+
|
|
311
|
+
# Records the seed's readiness when its ready marker has arrived.
|
|
312
|
+
# Non-blocking.
|
|
313
|
+
#
|
|
314
|
+
# @return [void]
|
|
315
|
+
#
|
|
316
|
+
# @rbs () -> void
|
|
317
|
+
def poll_seed_ready: () -> void
|
|
318
|
+
|
|
319
|
+
# Stops every worker (and the seed if one is active), escalating
|
|
320
|
+
# from TERM to KILL if any fail to exit within `worker_shutdown_timeout`.
|
|
207
321
|
#
|
|
208
322
|
# @return [void]
|
|
209
323
|
#
|
|
@@ -229,6 +343,23 @@ module Raptor
|
|
|
229
343
|
# @rbs () -> void
|
|
230
344
|
def perform_phased_restart: () -> void
|
|
231
345
|
|
|
346
|
+
# Blocks until the freshly-promoted seed has signalled readiness,
|
|
347
|
+
# or times out and clears the seed reference. No-op when no seed is
|
|
348
|
+
# being promoted.
|
|
349
|
+
#
|
|
350
|
+
# @return [void]
|
|
351
|
+
#
|
|
352
|
+
# @rbs () -> void
|
|
353
|
+
def wait_for_seed_ready: () -> void
|
|
354
|
+
|
|
355
|
+
# Spawns a replacement worker for the slot the seed vacated when
|
|
356
|
+
# it was promoted, returning the slot index it filled.
|
|
357
|
+
#
|
|
358
|
+
# @return [Integer, nil] the filled slot index, or nil if none was vacated
|
|
359
|
+
#
|
|
360
|
+
# @rbs () -> Integer?
|
|
361
|
+
def fill_vacated_seed_slot: () -> Integer?
|
|
362
|
+
|
|
232
363
|
# Re-execs the master process with a fresh boot of the same Raptor
|
|
233
364
|
# invocation, handing the new master its listening sockets so accepted
|
|
234
365
|
# connections continue to be served across the swap.
|
|
@@ -238,11 +369,10 @@ module Raptor
|
|
|
238
369
|
# @rbs () -> void
|
|
239
370
|
def perform_hot_restart: () -> void
|
|
240
371
|
|
|
241
|
-
# Runs
|
|
242
|
-
#
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
# critical component fails.
|
|
372
|
+
# Runs a worker process's full server stack until a shutdown signal is
|
|
373
|
+
# received or a critical component fails. On `SIGURG` the worker drains
|
|
374
|
+
# and transitions into a seed loop that forks replacement workers on
|
|
375
|
+
# master's request.
|
|
246
376
|
#
|
|
247
377
|
# @param index [Integer] slot index for this worker in the stats region
|
|
248
378
|
# @param phase [Integer] the cluster phase this worker was forked at
|
|
@@ -251,6 +381,15 @@ module Raptor
|
|
|
251
381
|
# @rbs (Integer index, Integer phase) -> void
|
|
252
382
|
def run_worker: (Integer index, Integer phase) -> void
|
|
253
383
|
|
|
384
|
+
# Runs the seed's fork loop, forking a replacement worker for each
|
|
385
|
+
# slot index the master asks for.
|
|
386
|
+
#
|
|
387
|
+
# @param index [Integer] the seed's original slot index
|
|
388
|
+
# @return [void]
|
|
389
|
+
#
|
|
390
|
+
# @rbs (Integer index) -> void
|
|
391
|
+
def run_seed_loop: (Integer index) -> void
|
|
392
|
+
|
|
254
393
|
# Shuts down the worker's application thread pool, force-killing the
|
|
255
394
|
# underlying threads if in-flight requests have not finished within
|
|
256
395
|
# `worker_drain_timeout` seconds.
|
|
@@ -52,5 +52,14 @@ module Raptor
|
|
|
52
52
|
#
|
|
53
53
|
# @rbs (Integer backlog) -> void
|
|
54
54
|
def self.update_load: (Integer backlog) -> void
|
|
55
|
+
|
|
56
|
+
# Marks a worker's slot as unavailable by parking its load at the
|
|
57
|
+
# sentinel maximum so the BPF program's least-loaded pick skips it.
|
|
58
|
+
#
|
|
59
|
+
# @param worker_index [Integer] slot index for the worker to mark
|
|
60
|
+
# @return [void]
|
|
61
|
+
#
|
|
62
|
+
# @rbs (Integer worker_index) -> void
|
|
63
|
+
def self.mark_unavailable: (Integer worker_index) -> void
|
|
55
64
|
end
|
|
56
65
|
end
|
|
@@ -82,11 +82,17 @@ module Raptor
|
|
|
82
82
|
# @rbs () -> Thread
|
|
83
83
|
def run: () -> Thread
|
|
84
84
|
|
|
85
|
-
#
|
|
85
|
+
# Stops the accept loop and, when `drain_accept_queue` is enabled,
|
|
86
|
+
# dispatches every connection already in the kernel accept queue. Leaves
|
|
87
|
+
# the listening sockets open so they can be handed to replacement workers.
|
|
86
88
|
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
89
|
+
# @return [void]
|
|
90
|
+
#
|
|
91
|
+
# @rbs () -> void
|
|
92
|
+
def stop_accepting: () -> void
|
|
93
|
+
|
|
94
|
+
# Gracefully shuts down the server, stopping the accept loop and closing
|
|
95
|
+
# all listening sockets.
|
|
90
96
|
#
|
|
91
97
|
# @return [void]
|
|
92
98
|
#
|