Skip to content

Instantly share code, notes, and snippets.

@brauliobo
Last active November 21, 2020 19:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brauliobo/11298486 to your computer and use it in GitHub Desktop.
Save brauliobo/11298486 to your computer and use it in GitHub Desktop.
Unicorn configuration with master warm up and other daemons as workers support. (See lastest and full version at https://github.com/coletivoEITA/noosfero-cookbook/blob/master/templates/default/unicorn.conf.rb.erb)
RailsRoot = File.expand_path "#{File.dirname __FILE__}/.."
PidsDir = "#{RailsRoot}/tmp/pids"
OldPidFile = "#{PidsDir}/unicorn.pid.oldbin"
ListenAddress = "127.0.0.1"
ListenPort = 50000
UnixListen = "tmp/unicorn.sock"
Backlog = 2048
Workers = 4
Timeout = 20 * 60
WarmUp = true
WarmUpTime = 1
# caution use non cacheable urls
WarmUpUrl = ['/admin/plugins', '/', '/profile/content']
CurrentPrio = Process.getpriority Process::PRIO_PROCESS, 0
# put "* - nice 0" on /etc/security/limits.conf to enable
WarmUpRenice = `bash -c 'ulimit -e'`.to_i-20 >= CurrentPrio rescue false
WarmUpRenicePrio = 19
begin
require 'unicorn/worker_killer'
WorkerKiller = true
rescue LoadError
WorkerKiller = nil
end
WorkerKillByRequests = 500..600
WorkerKillByMemory = 208..256
WorkerListen = true
WorkerPidFile = true
WorkerDaemons = {
0 => {
:name => 'delayed_job',
:run => proc{
Delayed::Worker.before_fork
Delayed::Command.new([]).run 'delayed_job'
},
},
1 => {
:name => 'feed-updater',
:run => proc{
FeedUpdater.new.start
},
},
}
working_directory RailsRoot
worker_processes (if Workers < WorkerDaemons.count+1 then WorkerDaemons.count+1 else Workers end)
timeout Timeout
stderr_path "#{RailsRoot}/log/unicorn.stderr.log"
stdout_path "#{RailsRoot}/log/unicorn.stdout.log"
pid "#{PidsDir}/unicorn.pid"
listen "#{RailsRoot}/#{UnixListen}", :backlog => Backlog
listen "#{ListenAddress}:#{ListenPort}", :tcp_nopush => true
# combine Ruby 2 or REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
preload_app true
GC.copy_on_write_friendly = true if GC.respond_to? :copy_on_write_friendly=
# this is loaded on first run or restart conditions (URS2, HUP). Unset on first before_fork call
master_run = true
before_fork do |server, worker|
if master_run
if WarmUp
Process.setpriority Process::PRIO_PROCESS, 0, WarmUpRenicePrio if WarmUpRenice
require 'rack/test'
client = Rack::MockRequest.new server.app
Array(WarmUpUrl).each do |url|
client.get url
end
Process.setpriority Process::PRIO_PROCESS, 0, CurrentPrio if WarmUpRenice
end
# Disconnect since the database connection will not carry over
ActiveRecord::Base.connection.disconnect! if defined? ActiveRecord::Base
if File.exists? OldPidFile
Thread.new do
# wait a little for the new master
sleep WarmUpTime
# a .oldbin file exists if unicorn was gracefully restarted with a USR2 signal
# we should terminate the old process now that we're up and running
old_pid = File.read(OldPidFile).to_i
begin
Process.kill "QUIT", old_pid
File.delete OldPidFile
rescue Errno::ENOENT, Errno::ESRCH
# someone else did our job for us
end
end
end
master_run = false
end
end
after_fork do |server, worker|
daemon = WorkerDaemons[worker.nr]
# Start up the database connection again in the worker
ActiveRecord::Base.establish_connection if defined? ActiveRecord::Base
# reset memcache connection (if using memcache-client)
Rails.cache.instance_variable_get(:@data).reset if Rails.cache.class.to_s == 'ActiveSupport::Cache::MemCacheStore'
if WorkerKiller
Unicorn::WorkerKiller::MaxRequests.new nil, WorkerKillByRequests.begin, WorkerKillByRequests.end if WorkerKillByRequests
Unicorn::WorkerKiller::Oom.new nil, WorkerKillByMemory.begin * (1024**2), WorkerKillByMemory.end * (1024**2) if WorkerKillByMemory
end unless daemon
# say to the kernel to kill very big workers first than other processes
# Not very secure
#File.open("/proc/#{Process.pid}/oom_adj", "w"){ |f| f << '12' }
if WorkerListen
# per-process listener ports for debugging/admin/migrations
server.listen "#{ListenAddress}:#{ListenPort + worker.nr}", :tries => -1, :delay => 5
end unless daemon
if WorkerPidFile
child_pid_file = server.config[:pid].sub '.pid', ".#{worker.nr}.pid"
system "echo #{Process.pid} > #{child_pid_file}"
end
end
# daemons support
require 'active_support/all'
class Unicorn::HttpServer
def worker_loop_with_daemons worker
daemon = WorkerDaemons[worker.nr]
if daemon
ctx = START_CTX.dup #save for later use with proc_name
init_worker_process worker
START_CTX.merge! ctx
name = "#{worker.nr}:#{daemon[:name]}"
proc_name "worker[#{name}]"
@logger.info "worker=#{name} ready as a daemon"
daemon[:run].call
else
worker_loop_without_daemons worker
end
end
alias_method_chain :worker_loop, :daemons
def awaken_master_with_daemons
if SIG_QUEUE.include? :QUIT
WORKERS.each do |pid, worker|
daemon = WorkerDaemons[worker.nr]
next unless daemon
Process.kill 'TERM', pid
end
end
awaken_master_without_daemons
end
alias_method_chain :awaken_master, :daemons
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment