Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>
Add unicorn config file
config/unicorn.conf.rb | 178 +++++++++++++++++++++++++++++++++++++++++++
diff --git a/config/unicorn.conf.rb b/config/unicorn.conf.rb new file mode 100644 index 0000000000000000000000000000000000000000..0788c2bf5b6da4ee7048be39b2b2a5f1df9b05b2 --- /dev/null +++ b/config/unicorn.conf.rb @@ -0,0 +1,178 @@ +require_relative 'dotenv' + +RailsRoot = File.expand_path "#{File.dirname __FILE__}/.." +PidsDir = "#{RailsRoot}/tmp/pids" +OldPidFile = "#{PidsDir}/unicorn.pid.oldbin" + +ListenAddress = "127.0.0.1" +ListenPort = 50000 +UnixListen = "run/unicorn.sock" +Backlog = 2048 + +Workers = 5 +Timeout = 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{ + Thread.new{ sleep 30.minutes; Process.kill :SIGTERM, Process.pid } + require_relative 'dotenv' + worker = Delayed::Worker.new + worker.name_prefix = 'unicorn worker 0' + worker.start + }, + }, + 1 => { + :name => 'feed-updater', + :run => proc{ + Thread.new{ sleep 30.minutes; Process.kill :SIGTERM, Process.pid } + 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 +