module UnicornWrangler

Read RSS based on the OS we are in. When in linux, we can read the proc status file and parse out the RSS which is much faster than forking+execing ps.

Constants

STATS_NAMESPACE
VERSION

Attributes

handlers[R]
request_time[R]
requests[R]
sending_myself_term[RW]

Public Class Methods

kill_worker() click to toggle source
# File lib/unicorn_wrangler.rb, line 60
def kill_worker
  self.sending_myself_term = true # no need to clean up since we are dead after
  Process.kill(:TERM, Process.pid)
end
perform_hook(name) click to toggle source
# File lib/unicorn_wrangler.rb, line 77
def perform_hook(name)
  if hook = @hooks[name]
    hook.call
  end
end
perform_request() { || ... } click to toggle source

called from the unicorn server after each request

# File lib/unicorn_wrangler.rb, line 66
def perform_request
  returned = nil
  @requests ||= 0
  @requests += 1
  @request_time ||= 0
  @request_time += Benchmark.realtime { returned = yield }
  returned
ensure
  @handlers.each { |handler| handler.call(@requests, @request_time) }
end
setup( kill_after_requests: 10000, gc_after_request_time: 10, kill_on_too_much_memory: {}, map_term_to_quit: false, logger:, stats: nil ) click to toggle source

called from unicorn config (usually config/unicorn.rb) high level interface to keep setup consistent / simple set values to false to disable

# File lib/unicorn_wrangler.rb, line 18
def setup(
  kill_after_requests: 10000,
  gc_after_request_time: 10,
  kill_on_too_much_memory: {},
  map_term_to_quit: false,
  logger:,
  stats: nil # provide a statsd client with your apps namespace to collect stats
)
  logger.info "Sending stats to under #{stats.namespace}.#{STATS_NAMESPACE}" if stats
  @handlers = []
  @handlers << RequestKiller.new(logger, stats, kill_after_requests) if kill_after_requests
  @handlers << OutOfMemoryKiller.new(logger, stats, kill_on_too_much_memory) if kill_on_too_much_memory
  @handlers << OutOfBandGC.new(logger, stats, gc_after_request_time) if gc_after_request_time

  @hooks = {}
  if map_term_to_quit
    # - on heroku & kubernetes all processes get TERM, so we need to trap in master and worker
    # - trapping has to be done in the before_fork since unicorn sets up it's own traps on start
    # - we cannot write to logger inside of a trap, so need to spawn a new Thread
    # - manual test: add a slow route + rails s + curl + pkill -TERM -f 'unicorn master' - request finished?
    @hooks[:before_fork] = -> do
      Signal.trap :TERM do
        Thread.new { logger.info 'master intercepting TERM and sending myself QUIT instead' }
        Process.kill :QUIT, Process.pid
      end
    end

    @hooks[:after_fork] = ->(*) do
      # Signal.trap returns the trap that unicorn set, which is an exit!(0) and calls that when sending myself term
      previous_trap = Signal.trap :TERM do
        if sending_myself_term
          previous_trap.call
        else
          Thread.new { logger.info 'worker intercepting TERM and doing nothing. Wait for master to send QUIT' }
        end
      end
    end
  end

  Unicorn::HttpServer.prepend UnicornExtension
end