Class: Ziltoid::Process

Inherits:
Object
  • Object
show all
Defined in:
lib/ziltoid/process.rb

Constant Summary collapse

WAIT_TIME_BEFORE_CHECK =
1.0
ALLOWED_STATES =
["started", "stopped", "restarted", "above_cpu_limit", "above_ram_limit"]
PREDOMINANT_STATES =
["started", "stopped", "restarted"]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ Process

Returns a new instance of Process.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/ziltoid/process.rb', line 9

def initialize(name, options = {})
  self.name = name
  self.ram_limit = options[:limit] ? options[:limit][:ram] : nil
  self.cpu_limit = options[:limit] ? options[:limit][:cpu] : nil
  self.pid_file = options[:pid_file] || "~/.ziltoid/#{name}.pid"

  if options[:commands]
    self.start_command = options[:commands][:start] || nil
    self.stop_command = options[:commands][:stop] || nil
    self.restart_command = options[:commands][:restart] || nil
  end

  if options[:grace_times]
    self.start_grace_time = options[:grace_times][:start] || 0
    self.stop_grace_time = options[:grace_times][:stop] || 0
    self.restart_grace_time = options[:grace_times][:restart] || 0
    self.ram_grace_time = options[:grace_times][:ram] || 0
    self.cpu_grace_time = options[:grace_times][:cpu] || 0
  end
end

Instance Attribute Details

#cpu_grace_timeObject

Returns the value of attribute cpu_grace_time.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def cpu_grace_time
  @cpu_grace_time
end

#cpu_limitObject

Returns the value of attribute cpu_limit.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def cpu_limit
  @cpu_limit
end

#nameObject

Returns the value of attribute name.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def name
  @name
end

#pid_fileObject

Returns the value of attribute pid_file.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def pid_file
  @pid_file
end

#ram_grace_timeObject

Returns the value of attribute ram_grace_time.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def ram_grace_time
  @ram_grace_time
end

#ram_limitObject

Returns the value of attribute ram_limit.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def ram_limit
  @ram_limit
end

#restart_commandObject

Returns the value of attribute restart_command.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def restart_command
  @restart_command
end

#restart_grace_timeObject

Returns the value of attribute restart_grace_time.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def restart_grace_time
  @restart_grace_time
end

#start_commandObject

Returns the value of attribute start_command.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def start_command
  @start_command
end

#start_grace_timeObject

Returns the value of attribute start_grace_time.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def start_grace_time
  @start_grace_time
end

#stop_commandObject

Returns the value of attribute stop_command.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def stop_command
  @stop_command
end

#stop_grace_timeObject

Returns the value of attribute stop_grace_time.



3
4
5
# File 'lib/ziltoid/process.rb', line 3

def stop_grace_time
  @stop_grace_time
end

Instance Method Details

#above_cpu_limit?(include_children = true) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
# File 'lib/ziltoid/process.rb', line 51

def above_cpu_limit?(include_children = true)
  Ziltoid::System.cpu_usage(self.pid, include_children) > self.cpu_limit.to_f
end

#above_ram_limit?(include_children = true) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/ziltoid/process.rb', line 55

def above_ram_limit?(include_children = true)
  Ziltoid::System.ram_usage(self.pid, include_children) > self.ram_limit.to_i * 1024
end

#alive?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/ziltoid/process.rb', line 37

def alive?
  Ziltoid::System.pid_alive?(self.pid)
end

#dead?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/ziltoid/process.rb', line 41

def dead?
  !alive?
end

#pending_grace_time?Boolean

Returns:

  • (Boolean)


87
88
89
90
# File 'lib/ziltoid/process.rb', line 87

def pending_grace_time?
  current_state = self.state
  PREDOMINANT_STATES.include?(current_state) && self.updated_at.to_i > Time.now.to_i - self.send("#{current_state.gsub(/p?ed/, '')}_grace_time").to_i
end

#pidObject



30
31
32
33
34
35
# File 'lib/ziltoid/process.rb', line 30

def pid
  if self.pid_file && File.exist?(self.pid_file)
    str = File.read(pid_file)
    str.to_i if str.size > 0
  end
end

#processable?(target_state) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ziltoid/process.rb', line 69

def processable?(target_state)
  current_state = self.state
  # started, stopped and restarted are 'predominant' current states,
  # we never proceed unless the corresponding grace time is over
  Watcher.log("Current state : #{current_state} - updated_at : #{self.updated_at.to_i} - target_state : #{target_state}")
  return false if pending_grace_time?
  return true if PREDOMINANT_STATES.include?(target_state)

  # above_cpu_limit and above_ram_limit grace times are different,
  # they represent a time a process has to be in that state to actually be processed (restarted most likely)
  case target_state
  when "above_cpu_limit"
    current_state == target_state && self.updated_at.to_i < Time.now.to_i - self.cpu_grace_time.to_i
  when "above_ram_limit"
    current_state == target_state && self.updated_at.to_i < Time.now.to_i - self.ram_grace_time.to_i
  end
end

#remove_pid_fileObject



45
46
47
48
49
# File 'lib/ziltoid/process.rb', line 45

def remove_pid_file
  if self.pid_file && File.exist?(self.pid_file)
    File.delete(self.pid_file)
  end
end

#restart!Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ziltoid/process.rb', line 166

def restart!
  return unless processable?("restarted")

  Watcher.log("Ziltoid is restarting process #{self.name}", Logger::WARN)
  alive = self.alive?

  if alive && self.restart_command
    update_state("restarted")
    return system("#{self.restart_command}")
  end

  stop! if alive
  return start!
end

#start!Object



126
127
128
129
130
131
132
133
134
# File 'lib/ziltoid/process.rb', line 126

def start!
  return if Ziltoid::System.pid_alive?(self.pid)
  return unless processable?("started")

  Watcher.log("Ziltoid is starting process #{self.name}", Logger::WARN)
  remove_pid_file
  system(self.start_command)
  update_state("started")
end

#stateObject



59
60
61
62
# File 'lib/ziltoid/process.rb', line 59

def state
  state_hash = Ziltoid::Watcher.read_state[self.name]
  state_hash["state"] if state_hash
end

#stop!Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/ziltoid/process.rb', line 136

def stop!
  return unless processable?("stopped")

  Watcher.log("Ziltoid is stoping process #{self.name}", Logger::WARN)
  memoized_pid = self.pid

  if dead?
    remove_pid_file
  else

    Thread.new do
      system(self.stop_command)
      sleep(WAIT_TIME_BEFORE_CHECK)
      if alive?
        system("kill #{memoized_pid}")
        sleep(WAIT_TIME_BEFORE_CHECK)
        if alive?
          system("kill -9 #{memoized_pid}")
          sleep(WAIT_TIME_BEFORE_CHECK)
        end
      end
      if dead?
        remove_pid_file
        update_state("stopped")
      end
    end.join

  end
end

#update_state(state) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ziltoid/process.rb', line 92

def update_state(state)
  process_states = Ziltoid::Watcher.read_state
  return nil unless ALLOWED_STATES.include?(state)
  memoized_process_state = process_states[self.name]

  process_states[self.name] = {
    "state" => state,
    "updated_at" => memoized_process_state && memoized_process_state["state"] == state ? memoized_process_state["updated_at"].to_i : Time.now.to_i
  }
  Ziltoid::Watcher.write_state(process_states)
end

#updated_atObject



64
65
66
67
# File 'lib/ziltoid/process.rb', line 64

def updated_at
  state_hash = Ziltoid::Watcher.read_state[self.name]
  state_hash["updated_at"] if state_hash
end

#watch!Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ziltoid/process.rb', line 104

def watch!
  Watcher.log("Ziltoid is watching process #{self.name}")
  if !alive?
    Watcher.log("Process #{self.name} is dead", Logger::WARN)
    return start!
  end
  if above_cpu_limit?
    update_state("above_cpu_limit") unless pending_grace_time?
    if processable?("above_cpu_limit")
      Watcher.log("Process #{self.name} is above CPU limit (#{self.cpu_limit.to_f})", Logger::WARN)
      return restart!
    end
  end
  if above_ram_limit?
    update_state("above_ram_limit") unless pending_grace_time?
    if processable?("above_ram_limit")
      Watcher.log("Process #{self.name} is above RAM limit (#{self.ram_limit.to_f})", Logger::WARN)
      return restart!
    end
  end
end