Skip to content

Instantly share code, notes, and snippets.

@x2es
Created February 23, 2011 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save x2es/840590 to your computer and use it in GitHub Desktop.
Save x2es/840590 to your computer and use it in GitHub Desktop.
Controls server load by 1min load average bounds using SIGSTOP and SIGCONT signals
#!/usr/bin/env ruby
# -- 8< --
# Original source: https://gist.github.com/840590
# Author: x@ES (KEIvanov@gmail.com)
#
# This is draft.
#
# For load test you can run in therminal
# $ perl -e '$|=1;do{$l==$r or print "."; $l=$r}until(($r=time-$^T)>5000)'
# and catch this by pid in la_control
# -- 8< --
class ProcessLASupervisor
CHECK_INTERVAL = 5 # sec
def initialize pid, la_suspend_bound, la_resume_bound
@pid = pid
# TODO: check pid for exisitng
@la_suspend_bound = la_suspend_bound
@la_resume_bound = la_resume_bound
check_bounds
# TODO: check state automatically
@suspended = false
end
def start_control
begin
while true
log_stat
if suspended?
self.resume! if less_resume_bound?
else
self.suspend! if over_suspend_bound?
end
StatGrabber.clear_state
sleep CHECK_INTERVAL
end
ensure
self.resume!
end
end
def suspend!
unless suspended?
log " !suspend PID##{@pid}"
`kill -s SIGSTOP #{@pid}`
@suspended = true
end
end
def resume!
if suspended?
log " !resume PID##{@pid}"
`kill -s SIGCONT #{@pid}`
@suspended = false
end
end
private
def check_bounds
if @la_resume_bound - @la_suspend_bound > 0 # @la_resume_bound > @la_suspend_bound
raise ArgumentError, '<suspend LA bound> should be more than <resume LA bound>'
end
end
def log message
puts message
end
def log_stat
puts " PID##{@pid} [ sLA:#{@la_suspend_bound} < eLA:#{StatGrabber.effective_la} < rLA:#{@la_resume_bound} ]"
unless @suppress_stat_legend
@suppress_stat_legend = true
puts ' > eLA - effective LA (LA - Zombies)'
puts ' > sLA - suspend LA bound'
puts ' > rLA - resume LA bound'
puts ''
end
end
def suspended?
@suspended
end
def over_suspend_bound?
(StatGrabber.effective_la - @la_suspend_bound >= 0) # effective_la >= @la_suspend_bound
end
def less_resume_bound?
(@la_resume_bound - StatGrabber.effective_la >= 0) # effective_la <= @la_resume_bound
end
end
class StatGrabber
SH_ZOMBIE_FINDER = 'ps ax | grep Zsl'
def self.effective_la
self.min1_la - self.zombies_count
end
# la for 1 minute
def self.min1_la
if defined? @@min1_la
return @@min1_la unless @@min1_la.nil?
end
@@min1_la = `cat /proc/loadavg`.split(' ').first.to_f
end
def self.zombies_count
if defined? @@zombies_count
return @@zombies_count unless @@zombies_count.nil?
end
ps_ax_grep = `#{SH_ZOMBIE_FINDER}`.split("\n")
ps_ax_grep.delete_if { |e| !(e =~/#{SH_ZOMBIE_FINDER}/).nil? }
@@zombies_count = ps_ax_grep.count
end
def self.clear_state
@@min1_la = nil
@@zombies_count = nil
end
end
def parse_args
unless ARGV.count == 3
print_usage
exit 1
end
res = []
while arg = ARGV.shift; res << arg; end
cast_params res
end
def print_usage
puts ''
puts ' Usage: ./la_control.rb <suspend LA bound> <resume LA bound> <PID>'
puts ''
puts ' WARNINGs:'
puts ' > PID does not checked for existence yet'
puts ' > considered that PID is not suspended before running la_control (not yet checked for process status)'
puts ''
puts " Current server effective LA: #{StatGrabber.effective_la} (zombies count - LA)"
puts ''
end
def cast_params params_arr
la_suspend_bound, la_resume_bound, pid = params_arr
la_suspend_bound = la_suspend_bound.to_f
la_resume_bound = la_resume_bound.to_f
pid = pid.to_i
[la_suspend_bound, la_resume_bound, pid]
end
def main
la_suspend_bound, la_resume_bound, pid = parse_args
puts " LA under control! [PID: #{pid}; LA suspend bound: #{la_suspend_bound}; LA resume bound: #{la_resume_bound}]"
plas = ProcessLASupervisor.new pid, la_suspend_bound, la_resume_bound
plas.start_control
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment