Created
February 23, 2011 15:54
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# -- 8< -- | |
# Original source: https://gist.github.com/840590 | |
# Author: x@ES ([email protected]) | |
# | |
# 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