Module: UnicornWrangler

Defined in:
lib/unicorn_wrangler/rss_reader.rb,
lib/unicorn_wrangler.rb,
lib/unicorn_wrangler/version.rb

Overview

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.

Defined Under Namespace

Modules: UnicornExtension Classes: Killer, OutOfBandGC, OutOfMemoryKiller, RequestKiller, RssReader

Constant Summary collapse

STATS_NAMESPACE =
'unicorn'
VERSION =
"0.7.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.handlersObject (readonly)

Returns the value of attribute handlers.



12
13
14
# File 'lib/unicorn_wrangler.rb', line 12

def handlers
  @handlers
end

.request_timeObject (readonly)

Returns the value of attribute request_time.



12
13
14
# File 'lib/unicorn_wrangler.rb', line 12

def request_time
  @request_time
end

.requestsObject (readonly)

Returns the value of attribute requests.



12
13
14
# File 'lib/unicorn_wrangler.rb', line 12

def requests
  @requests
end

.sending_myself_termObject

Returns the value of attribute sending_myself_term.



13
14
15
# File 'lib/unicorn_wrangler.rb', line 13

def sending_myself_term
  @sending_myself_term
end

Class Method Details

.kill_workerObject



60
61
62
63
# 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) ⇒ Object



77
78
79
80
81
# File 'lib/unicorn_wrangler.rb', line 77

def perform_hook(name)
  if hook = @hooks[name]
    hook.call
  end
end

.perform_requestObject

called from the unicorn server after each request



66
67
68
69
70
71
72
73
74
75
# 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) ⇒ Object

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



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 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