fix initialization issues with unicorn

amend unicorn script to demonize sidekiq
create a sidekiq demon that unicorn consumes
correct bug in exec_sql with empty params
This commit is contained in:
Sam 2013-10-10 13:33:52 +11:00
parent 15de4ac890
commit c4bab8915c
4 changed files with 190 additions and 37 deletions

View File

@ -11,20 +11,20 @@ Thread.new do
old_time = File.ctime(file).to_i if File.exists? file
wait_seconds = 4
return if $PROGRAM_NAME !~ /thin/
if $PROGRAM_NAME =~ /thin/
while true
time = File.ctime(file).to_i if File.exists? file
while true
time = File.ctime(file).to_i if File.exists? file
if old_time != time
Rails.logger.info "attempting to reload #{$$} #{$PROGRAM_NAME} in #{wait_seconds} seconds"
$shutdown = true
sleep wait_seconds
Rails.logger.info "restarting #{$$}"
Process.kill("HUP", $$)
break
end
if old_time != time
Rails.logger.info "attempting to reload #{$$} #{$PROGRAM_NAME} in #{wait_seconds} seconds"
$shutdown = true
sleep wait_seconds
Rails.logger.info "restarting #{$$}"
Process.kill("HUP", $$)
return
sleep 1
end
sleep 1
end
end

View File

@ -3,11 +3,12 @@
discourse_path = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../")
# tune down if not enough ram
worker_processes 2
worker_processes 3
working_directory discourse_path
listen 8080, :tcp_nopush => true
# listen "#{discourse_path}/tmp/sockets/unicorn.sock"
listen 3000
# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30
@ -34,42 +35,26 @@ check_client_connection false
initialized = false
before_fork do |server, worker|
unless initialized
# load up the yaml for the localization bits, in master process
I18n.t(:posts)
# get rid of rubbish so we don't share it
GC.start
require 'demon/sidekiq'
Demon::Sidekiq.start(1)
end
ActiveRecord::Base.connection.disconnect!
$redis.client.disconnect
#TODO
# at this point we want to fork out sidekiq, it will let us reuse the shared memory
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
# twice as many worker_processes as you have configured.
#
# # This allows a new master process to incrementally
# # phase out the old master process with SIGTTOU to avoid a
# # thundering herd (especially in the "preload_app false" case)
# # when doing a transparent upgrade. The last worker spawned
# # will then kill off the old master process with a SIGQUIT.
# old_pid = "#{server.config[:pid]}.oldbin"
# if old_pid != server.pid
# begin
# sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
# Process.kill(sig, File.read(old_pid).to_i)
# rescue Errno::ENOENT, Errno::ESRCH
# end
# end
#
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
sleep 1
end
after_fork do |server, worker|

164
lib/demon/sidekiq.rb Normal file
View File

@ -0,0 +1,164 @@
module Demon; end
# intelligent fork based demonizer for sidekiq
class Demon::Base
def self.start(count)
@demons ||= {}
count.times do |i|
(@demons["#{prefix}_#{i}"] ||= new(i)).start
end
end
def self.stop
@demons.values.each do |demon|
demon.stop
end
end
def initialize(index)
@index = index
@pid = nil
@parent_pid = Process.pid
@monitor = nil
end
def pid_file
"#{Rails.root}/tmp/pids/#{self.class.prefix}_#{@index}.pid"
end
def stop
if @monitor
@monitor.kill
@monitor.join
@monitor = nil
end
if @pid
Process.kill("SIGHUP",@pid)
@pid = nil
end
end
def start
if existing = already_running?
# should not happen ... so kill violently
Process.kill("SIGTERM",existing)
end
return if @pid
if @pid = fork
write_pid_file
monitor_child
return
end
monitor_parent
establish_app
after_fork
end
def already_running?
if File.exists? pid_file
pid = File.read(pid_file).to_i
if alive?(pid)
return pid
end
end
nil
end
private
def monitor_child
@monitor ||= Thread.new do
while true
sleep 5
unless alive?(@pid)
STDERR.puts "#{@pid} died, restarting sidekiq"
@pid = nil
start
end
end
end
end
def write_pid_file
FileUtils.mkdir_p(Rails.root + "tmp/pids")
File.open(pid_file,'w') do |f|
f.write(@pid)
end
end
def delete_pid_file
File.delete(pid_file)
end
def monitor_parent
Thread.new do
while true
exit unless alive?(@parent_pid)
sleep 1
end
end
end
def alive?(pid)
begin
Process.getpgid(pid)
true
rescue Errno::ESRCH
false
end
end
def establish_app
ActiveRecord::Base.connection_handler.clear_active_connections!
ActiveRecord::Base.establish_connection
$redis.client.reconnect
Rails.cache.reconnect
MessageBus.after_fork
Signal.trap("HUP") do
begin
delete_pid_file
ensure
exit
end
end
# keep stuff simple for now
$stdout.reopen("/dev/null", "w")
# $stderr.reopen("/dev/null", "w")
end
def after_fork
end
end
class Demon::Sidekiq < Demon::Base
def self.prefix
"sidekiq"
end
private
def after_fork
require 'sidekiq/cli'
begin
cli = Sidekiq::CLI.instance
cli.parse([])
cli.run
rescue => e
STDERR.puts e.message
STDERR.puts e.backtrace.join("\n")
exit 1
end
end
end

View File

@ -61,7 +61,11 @@ class SqlBuilder
if @klass
@klass.find_by_sql(ActiveRecord::Base.send(:sanitize_sql_array, [sql, @args]))
else
ActiveRecord::Base.exec_sql(sql,@args)
if @args == {}
ActiveRecord::Base.exec_sql(sql)
else
ActiveRecord::Base.exec_sql(sql,@args)
end
end
end