Class: Procodile::Supervisor

Inherits:
Object
  • Object
show all
Defined in:
lib/procodile/supervisor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, run_options = {}) ⇒ Supervisor

Returns a new instance of Supervisor.



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/procodile/supervisor.rb', line 13

def initialize(config, run_options = {})
  @config = config
  @run_options = run_options
  @processes = {}
  @readers = {}
  @signal_handler = SignalHandler.new('TERM', 'USR1', 'USR2', 'INT', 'HUP')
  @signal_handler.register('TERM') { stop_supervisor }
  @signal_handler.register('INT') { stop(:stop_supervisor => true) }
  @signal_handler.register('USR1') { restart }
  @signal_handler.register('USR2') { status }
  @signal_handler.register('HUP') { reload_config }
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



7
8
9
# File 'lib/procodile/supervisor.rb', line 7

def config
  @config
end

#processesObject (readonly)

Returns the value of attribute processes.



8
9
10
# File 'lib/procodile/supervisor.rb', line 8

def processes
  @processes
end

#started_atObject (readonly)

Returns the value of attribute started_at.



9
10
11
# File 'lib/procodile/supervisor.rb', line 9

def started_at
  @started_at
end

#tagObject (readonly)

Returns the value of attribute tag.



10
11
12
# File 'lib/procodile/supervisor.rb', line 10

def tag
  @tag
end

#tcp_proxyObject (readonly)

Returns the value of attribute tcp_proxy.



11
12
13
# File 'lib/procodile/supervisor.rb', line 11

def tcp_proxy
  @tcp_proxy
end

Instance Method Details

#add_instance(instance, io = nil) ⇒ Object



174
175
176
177
178
179
180
# File 'lib/procodile/supervisor.rb', line 174

def add_instance(instance, io = nil)
  add_reader(instance, io) if io
  @processes[instance.process] ||= []
  unless @processes[instance.process].include?(instance)
    @processes[instance.process] << instance
  end
end

#add_reader(instance, io) ⇒ Object



169
170
171
172
# File 'lib/procodile/supervisor.rb', line 169

def add_reader(instance, io)
  @readers[io] = instance
  @signal_handler.notice
end

#allow_respawning?Boolean

Returns:

  • (Boolean)


26
27
28
# File 'lib/procodile/supervisor.rb', line 26

def allow_respawning?
  @run_options[:respawn] != false
end

#check_concurrency(options = {}) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/procodile/supervisor.rb', line 144

def check_concurrency(options = {})
  Procodile.log nil, "system", "Checking process concurrency"
  reload_config unless options[:reload] == false
  result = check_instance_quantities
  if result[:started].empty? && result[:stopped].empty?
    Procodile.log nil, "system", "Process concurrency looks good"
  else
    unless result[:started].empty?
      Procodile.log nil, "system", "Concurrency check started #{result[:started].map(&:description).join(', ')}"
    end

    unless result[:stopped].empty?
      Procodile.log nil, "system", "Concurrency check stopped #{result[:stopped].map(&:description).join(', ')}"
    end
  end
  result
end

#reload_configObject



139
140
141
142
# File 'lib/procodile/supervisor.rb', line 139

def reload_config
  Procodile.log nil, "system", "Reloading configuration"
  @config.reload
end

#remove_instance(instance) ⇒ Object



182
183
184
185
186
187
# File 'lib/procodile/supervisor.rb', line 182

def remove_instance(instance)
  if @processes[instance.process]
    @processes[instance.process].delete(instance)
    @readers.delete(instance)
  end
end

#restart(options = {}) ⇒ Object



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
# File 'lib/procodile/supervisor.rb', line 84

def restart(options = {})
  @tag = options[:tag]
  reload_config
  Array.new.tap do |instances_restarted|
    if options[:processes].nil?
      Procodile.log nil, "system", "Restarting all #{@config.app_name} processes"
      instances = @processes.values.flatten
    else
      instances = process_names_to_instances(options[:processes])
      Procodile.log nil, "system", "Restarting #{instances.size} process(es)"
    end

    # Stop any processes that are no longer wanted at this point
    instances_restarted.push(*check_instance_quantities(:stopped, options[:processes])[:stopped].map { |i| [i, nil]})

    instances.each do |instance|
      next if instance.stopping?
      new_instance = instance.restart
      instances_restarted << [instance, new_instance]
    end

    # Start any processes that are needed at this point
    instances_restarted.push(*check_instance_quantities(:started, options[:processes])[:started].map { |i| [nil, i]})
  end
end

#start(&after_start) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/procodile/supervisor.rb', line 30

def start(&after_start)
  Procodile.log nil, "system", "Procodile supervisor started with PID #{::Process.pid}"
  if @run_options[:respawn] == false
    Procodile.log nil, "system", "Automatic respawning is disabled"
  end
  ControlServer.start(self)
  if @run_options[:proxy]
    Procodile.log nil, "system", "Proxy is enabled"
    @tcp_proxy = TCPProxy.start(self)
  end
  watch_for_output
  @started_at = Time.now
  after_start.call(self) if block_given?
  loop { supervise; sleep 3 }
end

#start_processes(types = nil, options = {}) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/procodile/supervisor.rb', line 46

def start_processes(types = nil, options = {})
  @tag = options[:tag]
  reload_config
  Array.new.tap do |instances_started|
    @config.processes.each do |name, process|
      next if types && !types.include?(name.to_s)                   # Not a process we want
      next if @processes[process] && !@processes[process].empty?    # Process type already running
      instances = process.generate_instances(self).each(&:start)
      instances_started.push(*instances)
    end
  end
end

#stop(options = {}) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/procodile/supervisor.rb', line 59

def stop(options = {})
  if options[:stop_supervisor]
    @run_options[:stop_when_none] = true
  end
  reload_config
  Array.new.tap do |instances_stopped|
    if options[:processes].nil?
      Procodile.log nil, "system", "Stopping all #{@config.app_name} processes"
      @processes.each do |_, instances|
        instances.each do |instance|
          instance.stop
          instances_stopped << instance
        end
      end
    else
      instances = process_names_to_instances(options[:processes])
      Procodile.log nil, "system", "Stopping #{instances.size} process(es)"
      instances.each do |instance|
        instance.stop
        instances_stopped << instance
      end
    end
  end
end

#stop_supervisorObject



110
111
112
113
114
# File 'lib/procodile/supervisor.rb', line 110

def stop_supervisor
  Procodile.log nil, 'system', "Stopping Procodile supervisor"
  FileUtils.rm_f(File.join(@config.pid_root, 'procodile.pid'))
  ::Process.exit 0
end

#superviseObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/procodile/supervisor.rb', line 116

def supervise
  # Tell instances that have been stopped that they have been stopped
  remove_stopped_instances

  # Remove removed processes
  remove_removed_processes

  # Check all instances that we manage and let them do their things.
  @processes.each do |_, instances|
    instances.each do |instance|
      instance.check
    end
  end

  if @run_options[:stop_when_none]
    # If the processes go away, we can stop the supervisor now
    if @processes.all? { |_,instances| instances.size == 0 }
      Procodile.log nil, "system", "All processes have stopped"
      stop_supervisor
    end
  end
end

#to_hashObject



162
163
164
165
166
167
# File 'lib/procodile/supervisor.rb', line 162

def to_hash
  {
    :started_at => @started_at ? @started_at.to_i : nil,
    :pid => ::Process.pid
  }
end