Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions lib/thin/controllers/cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Thin
# An exception class to handle the event that server didn't start on time
class RestartTimeout < RuntimeError; end

module Controllers
# Control a set of servers.
# * Generate start and stop commands and run them.
Expand All @@ -12,18 +12,18 @@ module Controllers
class Cluster < Controller
# Cluster only options that should not be passed in the command sent
# to the indiviual servers.
CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
CLUSTER_OPTIONS = [:servers, :only, :onebyone]

# Maximum wait time for the server to be restarted
DEFAULT_WAIT_TIME = 30 # seconds

# Create a new cluster of servers launched using +options+.
def initialize(options)
super
# Cluster can only contain daemonized servers
@options.merge!(:daemonize => true)
end

def first_port; @options[:port] end
def address; @options[:address] end
def socket; @options[:socket] end
Expand All @@ -33,35 +33,35 @@ def size; @options[:servers] end
def only; @options[:only] end
def onebyone; @options[:onebyone] end
def wait; @options[:wait] end

def swiftiply?
@options.has_key?(:swiftiply)
end

# Start the servers
def start
with_each_server { |n| start_server n }
end

# Start a single server
def start_server(number)
log_info "Starting server on #{server_id(number)} ... "

run :start, number
end

# Stop the servers
def stop
with_each_server { |n| stop_server n }
end

# Stop a single server
def stop_server(number)
log_info "Stopping server on #{server_id(number)} ... "

run :stop, number
end

# Stop and start the servers.
def restart
unless onebyone
Expand All @@ -70,15 +70,15 @@ def restart
sleep 0.1 # Let's breath a bit shall we ?
start
else
with_each_server do |n|
with_each_server do |n|
stop_server(n)
sleep 0.1 # Let's breath a bit shall we ?
start_server(n)
wait_until_server_started(n)
end
end
end

def test_socket(number)
if socket
UNIXSocket.new(socket_for(number))
Expand All @@ -88,12 +88,12 @@ def test_socket(number)
rescue
nil
end

# Make sure the server is running before moving on to the next one.
def wait_until_server_started(number)
log_info "Waiting for server to start ..."
STDOUT.flush # Need this to make sure user got the message

tries = 0
loop do
if test_socket = test_socket(number)
Expand All @@ -109,7 +109,7 @@ def wait_until_server_started(number)
end
end
end

def server_id(number)
if socket
socket_for(number)
Expand All @@ -119,23 +119,23 @@ def server_id(number)
[address, number].join(':')
end
end

def log_file_for(number)
include_server_number log_file, number
end

def pid_file_for(number)
include_server_number pid_file, number
end

def socket_for(number)
include_server_number socket, number
end

def pid_for(number)
File.read(pid_file_for(number)).chomp.to_i
end

private
# Send the command to the +thin+ script
def run(cmd, number)
Expand All @@ -150,7 +150,7 @@ def run(cmd, number)
end
Command.run(cmd, cmd_options)
end

def with_each_server
if only
if first_port && only < 80
Expand All @@ -166,7 +166,7 @@ def with_each_server
size.times { |n| yield first_port + n }
end
end

# Add the server port or number in the filename
# so each instance get its own file
def include_server_number(path, number)
Expand Down
44 changes: 22 additions & 22 deletions lib/thin/controllers/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,45 @@
module Thin
# Error raised that will abort the process and print not backtrace.
class RunnerError < RuntimeError; end

# Raised when a mandatory option is missing to run a command.
class OptionRequired < RunnerError
def initialize(option)
super("#{option} option required")
end
end

# Raised when an option is not valid.
class InvalidOption < RunnerError; end

# Build and control Thin servers.
# Hey Controller pattern is not only for web apps yo!
module Controllers
module Controllers
# Controls one Thin server.
# Allow to start, stop, restart and configure a single thin server.
class Controller
include Logging

# Command line options passed to the thin script
attr_accessor :options

def initialize(options)
@options = options

if @options[:socket]
@options.delete(:address)
@options.delete(:port)
end
end

def start
# Constantize backend class
@options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend]

server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket
@options[:port], # Port ignored on UNIX socket
@options)

# Set options
server.pid_file = @options[:pid]
server.log_file = @options[:log]
Expand All @@ -63,7 +63,7 @@ def start

# +config+ must be called before changing privileges since it might require superuser power.
server.config

server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]

# If a Rack config file is specified we eval it inside a Rack::Builder block to create
Expand All @@ -86,27 +86,27 @@ def start

server.start
end

def stop
raise OptionRequired, :pid unless @options[:pid]

tail_log(@options[:log]) do
if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60))
if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:wait] || 60))
wait_for_file :deletion, @options[:pid]
end
end
end

def restart
raise OptionRequired, :pid unless @options[:pid]

tail_log(@options[:log]) do
if Server.restart(@options[:pid])
wait_for_file :creation, @options[:pid]
end
end
end

def config
config_file = @options.delete(:config) || raise(OptionRequired, :config)

Expand All @@ -116,19 +116,19 @@ def config
File.open(config_file, 'w') { |f| f << @options.to_yaml }
log_info "Wrote configuration to #{config_file}"
end

protected
# Wait for a pid file to either be created or deleted.
def wait_for_file(state, file)
Timeout.timeout(@options[:timeout] || 30) do
Timeout.timeout(@options[:wait] || 30) do
case state
when :creation then sleep 0.1 until File.exist?(file)
when :deletion then sleep 0.1 while File.exist?(file)
end
end
end
# Tail the log file of server +number+ during the execution of the block.

# Tail the log file of server +number+ during the execution of the block.
def tail_log(log_file)
if log_file
tail_thread = tail(log_file)
Expand All @@ -138,7 +138,7 @@ def tail_log(log_file)
yield
end
end

# Acts like GNU tail command. Taken from Rails.
def tail(file)
cursor = File.exist?(file) ? File.size(file) : 0
Expand Down Expand Up @@ -171,7 +171,7 @@ def load_adapter
rescue Rack::AdapterNotFound => e
raise InvalidOption, e.message
end

def load_rackup_config
ENV['RACK_ENV'] = @options[:environment]
case @options[:rackup]
Expand Down
Loading