Class: Navy::Admiral

Inherits:
Rank
  • Object
show all
Defined in:
lib/navy/admiral.rb

Defined Under Namespace

Classes: Orders, Speak

Constant Summary collapse

CAPTAINS =

This hash maps PIDs to Captains

{}
RESPAWNS =
{}
SELF_PIPE =
[]
SIG_QUEUE =

signal queue used for self-piping

[]
QUEUE_SIGS =

list of signals we care about and trap in admiral.

[ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
START_CTX =
{
  :argv => ARGV.map { |arg| arg.dup },
  0 => $0.dup,
}

Instance Attribute Summary collapse

Attributes inherited from Rank

#after_fork, #after_stop, #before_fork, #before_stop, #current_stderr, #current_stdout, #heartbeat, #logger, #orders, #orig_stderr, #orig_stdout, #patience, #pid, #post_fork, #preload, #reexec_pid, #respawn_limit, #respawn_limit_seconds, #stderr_path, #stdout_path, #timeout

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Admiral

Returns a new instance of Admiral.



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/navy/admiral.rb', line 35

def initialize(options = {})
  self.orig_stderr = $stderr.dup
  self.orig_stdout = $stdout.dup
  self.reexec_pid = 0

  @options                = options.dup
  @ready_pipe             = @options.delete(:ready_pipe)
  @options[:use_defaults] = true
  self.orders             = Navy::Admiral::Orders.new(self.class, @options)

  orders.give!(self, except: [ :stderr_path, :stdout_path ])
end

Instance Attribute Details

#admiral_pidObject

Returns the value of attribute admiral_pid.



32
33
34
# File 'lib/navy/admiral.rb', line 32

def admiral_pid
  @admiral_pid
end

#before_execObject

callbacks ##



30
31
32
# File 'lib/navy/admiral.rb', line 30

def before_exec
  @before_exec
end

#captainsObject

Returns the value of attribute captains.



32
33
34
# File 'lib/navy/admiral.rb', line 32

def captains
  @captains
end

#optionsObject (readonly)

Returns the value of attribute options.



33
34
35
# File 'lib/navy/admiral.rb', line 33

def options
  @options
end

Instance Method Details

#joinObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/navy/admiral.rb', line 68

def join
  respawn = true
  last_check = Time.now

  proc_name 'admiral'
  logger.info "admiral process ready"
  if @ready_pipe
    @ready_pipe.syswrite($$.to_s)
    @ready_pipe = @ready_pipe.close rescue nil
  end
  begin
    reap_all_captains
    case SIG_QUEUE.shift
    when nil
      # avoid murdering workers after our master process (or the
      # machine) comes out of suspend/hibernation
      heartbeat.call(self) if heartbeat
      if (last_check + @timeout) >= (last_check = Time.now)
        sleep_time = murder_lazy_captains
        logger.debug("would normally murder lazy captains") if $DEBUG
      else
        sleep_time = @timeout/2.0 + 1
        logger.debug("waiting #{sleep_time}s after suspend/hibernation")
      end
      maintain_captain_count if respawn
      admiral_sleep(sleep_time)
    when :QUIT # graceful shutdown
      break
    when :TERM, :INT # immediate shutdown
      stop(false)
      break
    when :USR1 # rotate logs
      logger.info "admiral reopening logs..."
      Navy::Util.reopen_logs
      logger.info "admiral done reopening logs"
      kill_each_captain(:USR1)
    when :USR2 # exec binary, stay alive in case something went wrong
      reexec
    when :WINCH
      # if Unicorn::Configurator::RACKUP[:daemonized]
      #   respawn = false
      #   logger.info "gracefully stopping all workers"
      #   kill_each_worker(:QUIT)
      #   self.worker_processes = 0
      # else
        logger.info "SIGWINCH ignored because we're not daemonized"
      # end
    when :TTIN
      respawn = true
      kill_each_captain(:TTIN)
    when :TTOU
      kill_each_captain(:TTOU)
    when :HUP
      respawn = true
      # if config.config_file
        # load_config!
      # else # exec binary and exit if there's no config file
        logger.info "config_file not present, reexecuting binary"
        reexec
      # end
    end
  rescue => e
    Navy.log_error(logger, "admiral loop error", e)
  end while true
  stop # gracefully shutdown all captains on our way out
  logger.info "admiral complete"
end

#startObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/navy/admiral.rb', line 48

def start
  orders.give!(self, only: [ :stderr_path, :stdout_path ])
  init_self_pipe!
  QUEUE_SIGS.each do |sig|
    trap(sig) do
      logger.debug "admiral received #{sig}" if $DEBUG
      SIG_QUEUE << sig
      awaken_admiral
    end
  end
  trap(:CHLD) { awaken_admiral }

  logger.info "admiral starting"

  self.admiral_pid = $$
  preload.call(self) if preload
  spawn_missing_captains
  self
end

#stop(graceful = true) ⇒ Object

Terminates all captains, but does not exit admiral process



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/navy/admiral.rb', line 137

def stop(graceful = true)
  before_stop.call(self, graceful) if before_stop
  limit = Time.now + patience
  until CAPTAINS.empty? || (n = Time.now) > limit
    kill_each_captain(graceful ? :QUIT : :TERM)
    sleep(0.1)
    reap_all_captains
  end
  if n and n > limit
    logger.debug "admiral patience exceeded by #{n - limit} seconds (limit #{patience} seconds)" if $DEBUG
  end
  kill_each_captain(:KILL)
  after_stop.call(self, graceful) if after_stop
end