Class: Raptor::Cluster

Inherits:
Object
  • Object
show all
Defined in:
lib/raptor/cluster.rb

Overview

Multi-process web server cluster with advanced concurrency architecture.

Cluster manages multiple worker processes, each running a complete server stack including a ractor pool for HTTP parsing, a thread pool for application processing, plus dedicated reactor and server threads. It handles process forking, signal management, graceful shutdown, and automatic worker restart when a worker process unexpectedly exits.

The architecture provides horizontal scaling through processes while maintaining efficient I/O and CPU utilization within each process through the combination of ractor-based parsing and thread pools on top of NIO reactors.

Flow per worker process:

  1. Server continuously accepts connections but skips acceptance when backlog is high

  2. Reactor manages I/O multiplexing and provides backlog metrics for load control

  3. Ractor pool handles CPU-intensive HTTP parsing in parallel

  4. Thread pool processes Rack applications and handles response writing

  5. Natural load balancing occurs through backpressure-based acceptance control

Examples:

Basic usage

options = {
  workers: 4, ractors: 2, threads: 8,
  binds: ["tcp://0.0.0.0:3000"],
  rackup: "config.ru",
  client: { first_data_timeout: 30, chunk_data_timeout: 10 }
}
Cluster.run(options)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ void

Creates a new Cluster with the specified configuration.

Initializes the cluster with worker, ractor, and thread counts, sets up network binding, loads the Rack application, and prepares for multi-process operation.

Parameters:

  • options (Hash)

    cluster configuration options

Options Hash (options):

  • :binds (Array<String>)

    array of bind URIs

  • :workers (Integer)

    number of worker processes

  • :ractors (Integer)

    number of ractors per worker process

  • :threads (Integer)

    number of threads per worker process

  • :app (#call)

    pre-built Rack application

  • :rackup (String)

    path to Rack configuration file

  • :client (Hash)

    client configuration

  • :worker_timeout (Integer)

    seconds to wait for a booted worker to check in before killing it

  • :worker_boot_timeout (Integer)

    seconds to wait for a worker to finish booting before killing it

  • :worker_shutdown_timeout (Integer)

    seconds to wait for graceful worker exit before force-killing

  • :stats_file (String, nil)

    path to write per-worker stats JSON, or nil to disable

  • :pid_file (String, nil)

    path to write the master PID to, or nil to disable

  • :on_error (#call)

    callback invoked with (env, exception) when the Rack app raises



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/raptor/cluster.rb', line 103

def initialize(options)
  @worker_count = options[:workers]
  @ractor_count = options[:ractors]
  @thread_count = options[:threads]
  @client_options = options[:client]
  @worker_timeout = options[:worker_timeout]
  @worker_boot_timeout = options[:worker_boot_timeout]
  @worker_shutdown_timeout = options[:worker_shutdown_timeout]
  @stats_file = options[:stats_file]
  @pid_file = options[:pid_file]
  @on_error = options[:on_error]

  @binder = Binder.new(options[:binds])
  @server_port = @binder.server_port
  @app = options[:app] || Rack::Builder.parse_file(options[:rackup])
  log_initialization

  @shutdown = false
  @workers = {}
  @timed_out = Set.new
  @stats = Stats.new(@worker_count)
  @phase = 0
  @phased_restart_requested = false
  @phased_restarting = false
end

Class Method Details

.run(options) ⇒ void

This method returns an undefined value.

Convenience method to create and run a cluster with the given options.

Parameters:

  • options (Hash)

    cluster configuration options



55
56
57
# File 'lib/raptor/cluster.rb', line 55

def self.run(options)
  new(options).run
end

Instance Method Details

#runvoid

This method returns an undefined value.

Starts the multi-process cluster and manages worker processes.

Forks the configured number of worker processes and monitors them, restarting any that exit unexpectedly or stop checking in. Handles graceful shutdown via INT or TERM signals, stats logging via USR1, and phased restart via USR2.

Each worker process includes:

  • 1 server thread (continuously accepts connections with backpressure control)

  • 1 reactor thread (I/O multiplexing, timeout handling, backlog monitoring)

  • N pipeline ractors (parallel HTTP parsing)

  • 1 pipeline collector thread (coordinates parsing results)

  • M worker threads (Rack application processing and response writing)

  • 1 stats thread (writes per-worker metrics to shared memory every second)



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/raptor/cluster.rb', line 147

def run
  trap("INT") { shutdown }
  trap("TERM") { shutdown }
  trap("USR1") { log_stats }
  trap("USR2") { @phased_restart_requested = true }

  File.open(@pid_file, File::CREAT | File::EXCL | File::WRONLY) { |file| file.write(Process.pid.to_s) } if @pid_file

  @worker_count.times { |index| spawn_worker(index) }

  stats_file_thread = if @stats_file
    Thread.new do
      Thread.current.name = "Raptor Stats File"

      write_stats_file_loop
    end
  end

  until @shutdown
    break if reap_workers == :no_children

    perform_phased_restart if @phased_restart_requested && !@phased_restarting
    timeout_hung_workers

    sleep 0.1
  end

  stop_workers
  stats_file_thread&.join
  File.delete(@stats_file) rescue nil if @stats_file
  File.delete(@pid_file) rescue nil if @pid_file
  @stats.unmap
end

#statsArray<Hash>

Returns stats for all worker processes.

Returns:

  • (Array<Hash>)

    array of per-worker stat hashes, each containing :pid, :requests, :backlog, :started_at, :last_checkin, and :booted



187
188
189
# File 'lib/raptor/cluster.rb', line 187

def stats
  @stats.all
end