Class: Navy::Captain

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

Defined Under Namespace

Classes: Orders, Speak

Constant Summary collapse

OFFICERS =

This hash maps PIDs to Officers

{}
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 ]

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(admiral, label, config, options = {}) ⇒ Captain

Returns a new instance of Captain.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/navy/captain.rb', line 18

def initialize(admiral, label, config, options = {})
  self.orig_stderr = $stderr.dup
  self.orig_stdout = $stdout.dup

  @options                = options.dup
  @options[:use_defaults] = true
  @options[:config_file]  = config
  self.orders             = Navy::Captain::Orders.new(self.class, @options)
  @options.merge!(orders.set)

  @admiral, @label = admiral, label

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

Instance Attribute Details

#admiralObject (readonly)

Returns the value of attribute admiral.



16
17
18
# File 'lib/navy/captain.rb', line 16

def admiral
  @admiral
end

#captain_pidObject

Returns the value of attribute captain_pid.



15
16
17
# File 'lib/navy/captain.rb', line 15

def captain_pid
  @captain_pid
end

#labelObject

Returns the value of attribute label.



15
16
17
# File 'lib/navy/captain.rb', line 15

def label
  @label
end

#officer_countObject

Returns the value of attribute officer_count.



15
16
17
# File 'lib/navy/captain.rb', line 15

def officer_count
  @officer_count
end

#officer_jobObject

Returns the value of attribute officer_job.



15
16
17
# File 'lib/navy/captain.rb', line 15

def officer_job
  @officer_job
end

#optionsObject (readonly)

Returns the value of attribute options.



16
17
18
# File 'lib/navy/captain.rb', line 16

def options
  @options
end

Instance Method Details

#==(other_label) ⇒ Object



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

def ==(other_label)
  @label == other_label
end

#joinObject



58
59
60
61
62
63
64
65
66
67
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
# File 'lib/navy/captain.rb', line 58

def join
  respawn = true
  last_check = Time.now

  proc_name "captain[#{label}]"
  logger.info "captain[#{label}] process ready"

  begin
    reap_all_officers
    case SIG_QUEUE.shift
    when nil
      # logger.info "captain[#{label}] heartbeat"
      # avoid murdering workers after our master process (or the
      # machine) comes out of suspend/hibernation
      if (last_check + @timeout) >= (last_check = Time.now)
        heartbeat.call(self) if heartbeat
        sleep_time = murder_lazy_officers
        logger.debug("would normally murder lazy officers") if $DEBUG
      else
        sleep_time = @timeout/2.0 + 1
        logger.debug("waiting #{sleep_time}s after suspend/hibernation")
      end
      maintain_officer_count if respawn
      captain_sleep(sleep_time)
    when :QUIT # graceful shutdown
      break
    when :TERM, :INT # immediate shutdown
      stop(false)
      break
    when :USR1 # rotate logs
      logger.info "captain[#{label}] reopening logs..."
      Navy::Util.reopen_logs
      logger.info "captaion[#{label}] done reopening logs"
      kill_each_officer(:USR1)
    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
      self.officer_count += 1
    when :TTOU
      self.officer_count -= 1 if self.officer_count > 0
    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, "captain[#{label}] loop error", e)
  end while true
  stop # gracefully shutdown all captains on our way out
  logger.info "captain[#{label}] complete"
end

#startObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/navy/captain.rb', line 37

def start
  init_self_pipe!
  QUEUE_SIGS.each do |sig|
    trap(sig) do
      if $DEBUG
        logger.debug "captain[#{label}] received #{sig}"
      end
      SIG_QUEUE << sig
      awaken_captain
    end
  end
  trap(:CHLD) { awaken_captain }

  logger.info "captain[#{label}] starting"

  self.captain_pid = $$
  preload.call(self) if preload
  spawn_missing_officers
  self
end

#stop(graceful = true) ⇒ Object

Terminates all captains, but does not exit admiral process



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/navy/captain.rb', line 123

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