zombie_passenger_killer 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -2
- data/Gemfile.lock +6 -6
- data/Rakefile +15 -12
- data/Readme.md +3 -3
- data/bin/zombie_passenger_killer +2 -6
- data/lib/zombie_passenger_killer/reaper.rb +75 -0
- data/lib/zombie_passenger_killer/version.rb +3 -0
- data/lib/zombie_passenger_killer.rb +3 -70
- data/spec/zombie_passenger_killer_spec.rb +1 -1
- data/zombie_passenger_killer.gemspec +7 -36
- metadata +10 -9
- data/VERSION +0 -1
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
zombie_passenger_killer (0.2.0)
|
5
|
+
|
1
6
|
GEM
|
2
7
|
remote: http://rubygems.org/
|
3
8
|
specs:
|
4
9
|
diff-lcs (1.1.3)
|
5
|
-
git (1.2.5)
|
6
|
-
jeweler (1.6.4)
|
7
|
-
bundler (~> 1.0)
|
8
|
-
git (>= 1.2.5)
|
9
|
-
rake
|
10
10
|
rake (0.9.2)
|
11
11
|
rspec (2.6.0)
|
12
12
|
rspec-core (~> 2.6.0)
|
@@ -21,6 +21,6 @@ PLATFORMS
|
|
21
21
|
ruby
|
22
22
|
|
23
23
|
DEPENDENCIES
|
24
|
-
jeweler
|
25
24
|
rake
|
26
25
|
rspec (~> 2)
|
26
|
+
zombie_passenger_killer!
|
data/Rakefile
CHANGED
@@ -1,18 +1,21 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
1
3
|
task :default do
|
2
4
|
sh "rspec spec/"
|
3
5
|
end
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
rule /^version:bump:.*/ do |t|
|
8
|
+
sh "git status | grep 'nothing to commit'" # ensure we are not dirty
|
9
|
+
index = ['major', 'minor','patch'].index(t.name.split(':').last)
|
10
|
+
file = 'lib/zombie_passenger_killer/version.rb'
|
11
|
+
|
12
|
+
version_file = File.read(file)
|
13
|
+
old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
|
14
|
+
version_parts[index] = version_parts[index].to_i + 1
|
15
|
+
version_parts[2] = 0 if index < 2 # remove patch for minor
|
16
|
+
version_parts[1] = 0 if index < 1 # remove minor for major
|
17
|
+
new_version = version_parts * '.'
|
18
|
+
File.open(file,'w'){|f| f.write(version_file.sub(old_version, new_version)) }
|
14
19
|
|
15
|
-
|
16
|
-
rescue LoadError
|
17
|
-
puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
|
20
|
+
sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
|
18
21
|
end
|
data/Readme.md
CHANGED
@@ -43,14 +43,14 @@ Usage
|
|
43
43
|
|
44
44
|
### God script
|
45
45
|
|
46
|
-
TODO
|
47
|
-
|
46
|
+
# TODO
|
48
47
|
|
49
48
|
Author
|
50
49
|
======
|
51
50
|
|
52
51
|
###Contributors
|
53
|
-
- [
|
52
|
+
- [Roman Heinrich](https://github.com/mindreframer)
|
53
|
+
- [Kevin Mullin](https://github.com/kmullin)
|
54
54
|
|
55
55
|
[Michael Grosser](http://grosser.it)<br/>
|
56
56
|
michael@grosser.it<br/>
|
data/bin/zombie_passenger_killer
CHANGED
@@ -1,14 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
2
|
require 'optparse'
|
4
3
|
|
5
4
|
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
6
5
|
require 'zombie_passenger_killer'
|
7
6
|
|
8
|
-
unless system("which timeout > /dev/null")
|
9
|
-
warn "Please install timeout commandline tool e.g. via apt-get install timeout / apt-get install coreutils"
|
10
|
-
end
|
11
|
-
|
12
7
|
options = {}
|
13
8
|
OptionParser.new do |opts|
|
14
9
|
opts.banner = <<BANNER
|
@@ -32,8 +27,9 @@ end.parse!
|
|
32
27
|
$stdout.sync = true
|
33
28
|
puts "Started at #{Time.now}"
|
34
29
|
|
35
|
-
killer = ZombiePassengerKiller.new(options)
|
30
|
+
killer = ZombiePassengerKiller::Reaper.new(options)
|
36
31
|
|
32
|
+
$0 = File.basename(__FILE__)
|
37
33
|
loop do
|
38
34
|
killer.hunt_zombies
|
39
35
|
sleep options[:interval] || 10
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module ZombiePassengerKiller
|
2
|
+
class Reaper
|
3
|
+
|
4
|
+
attr_accessor :out # overwriteable for tests
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@history = {}
|
8
|
+
@history_entries = options[:history] || 5
|
9
|
+
@max_high_cpu = options[:max]
|
10
|
+
@high_cpu = options[:cpu] || 70
|
11
|
+
@grace_time = options[:grace] || 5
|
12
|
+
@pattern = options[:pattern] || ' Rack: '
|
13
|
+
@strace_time = 5
|
14
|
+
@out = STDOUT
|
15
|
+
end
|
16
|
+
|
17
|
+
def store_current_cpu(processes)
|
18
|
+
keys_to_remove = @history.keys - processes.map{|x| x[:pid] }
|
19
|
+
keys_to_remove.each{|k| !@history.delete k }
|
20
|
+
|
21
|
+
processes.each do |process|
|
22
|
+
@history[process[:pid]] ||= []
|
23
|
+
@history[process[:pid]] << process[:cpu]
|
24
|
+
@history[process[:pid]] = @history[process[:pid]].last(@history_entries)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_strace(pid, time)
|
29
|
+
Process.getpgid(pid) rescue return 'No such process'
|
30
|
+
`( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
|
31
|
+
end
|
32
|
+
|
33
|
+
def hunt_zombies
|
34
|
+
active_pids_in_passenger_status = passenger_pids
|
35
|
+
active_processes_in_processlist = process_status
|
36
|
+
zombies = active_processes_in_processlist.map{|x| x[:pid] } - active_pids_in_passenger_status rescue Array.new
|
37
|
+
|
38
|
+
# kill processes with high CPU if user wants it
|
39
|
+
high_load = if @max_high_cpu
|
40
|
+
store_current_cpu active_processes_in_processlist
|
41
|
+
active_pids_in_passenger_status.select do |pid|
|
42
|
+
@history[pid].count{|x| x > @high_cpu } >= @max_high_cpu
|
43
|
+
end
|
44
|
+
else
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
(high_load + zombies).each do |pid|
|
49
|
+
kill_zombie pid
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# return array of pids reported from passenger-status command, nil if passenger-status doesn't run
|
54
|
+
def passenger_pids
|
55
|
+
pids = %x(passenger-status|grep PID).split("\n").map { |x| x.strip.match(/PID: \d*/).to_s.split[1].to_i }
|
56
|
+
pids if $?.exitstatus.zero?
|
57
|
+
end
|
58
|
+
|
59
|
+
def process_status
|
60
|
+
%x(ps -eo pid,pcpu,args|grep -v grep|grep '#{@pattern}').split("\n").map do |line|
|
61
|
+
values = line.strip.split[0..1]
|
62
|
+
{:pid => values.first.to_i, :cpu => values.last.to_f}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def kill_zombie(pid)
|
67
|
+
@out.puts "Killing passenger process #{pid}"
|
68
|
+
@out.puts get_strace(pid, @strace_time)
|
69
|
+
Process.kill('TERM', pid) rescue nil
|
70
|
+
sleep @grace_time # wait for it to die
|
71
|
+
Process.kill('KILL', pid) rescue nil
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -1,71 +1,4 @@
|
|
1
|
-
|
2
|
-
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(options)
|
7
|
-
@history = {}
|
8
|
-
@history_entries = options[:history] || 5
|
9
|
-
@max_high_cpu = options[:max]
|
10
|
-
@high_cpu = options[:cpu] || 70
|
11
|
-
@grace_time = options[:grace] || 5
|
12
|
-
@pattern = options[:pattern] || ' Rack: '
|
13
|
-
@strace_time = 5
|
14
|
-
@out = STDOUT
|
15
|
-
end
|
16
|
-
|
17
|
-
def store_current_cpu(processes)
|
18
|
-
keys_to_remove = @history.keys - processes.map{|x| x[:pid] }
|
19
|
-
keys_to_remove.each{|k| !@history.delete k }
|
20
|
-
|
21
|
-
processes.each do |process|
|
22
|
-
@history[process[:pid]] ||= []
|
23
|
-
@history[process[:pid]] << process[:cpu]
|
24
|
-
@history[process[:pid]] = @history[process[:pid]].last(@history_entries)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def get_strace(pid, time)
|
29
|
-
Process.getpgid(pid) rescue return 'No such process'
|
30
|
-
`( strace -p #{pid} 2>&1 ) & sleep #{time} ; kill $! 2>&1`
|
31
|
-
end
|
32
|
-
|
33
|
-
def hunt_zombies
|
34
|
-
active_pids_in_passenger_status = passenger_pids
|
35
|
-
active_processes_in_processlist = process_status
|
36
|
-
zombies = active_processes_in_processlist.map{|x| x[:pid] } - active_pids_in_passenger_status
|
37
|
-
|
38
|
-
# kill processes with high CPU if user wants it
|
39
|
-
high_load = if @max_high_cpu
|
40
|
-
store_current_cpu active_processes_in_processlist
|
41
|
-
active_pids_in_passenger_status.select do |pid|
|
42
|
-
@history[pid].count{|x| x > @high_cpu } >= @max_high_cpu
|
43
|
-
end
|
44
|
-
else
|
45
|
-
[]
|
46
|
-
end
|
47
|
-
|
48
|
-
(high_load + zombies).each do |pid|
|
49
|
-
kill_zombie pid
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def passenger_pids
|
54
|
-
%x(passenger-status|grep PID).split("\n").map{|x| x.strip.match(/PID: \d*/).to_s.split[1]}.map(&:to_i)
|
55
|
-
end
|
56
|
-
|
57
|
-
def process_status
|
58
|
-
%x(ps -eo pid,pcpu,args|grep -v grep|grep '#{@pattern}').split("\n").map do |line|
|
59
|
-
values = line.strip.split[0..1]
|
60
|
-
{:pid => values.first.to_i, :cpu => values.last.to_f}
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def kill_zombie(pid)
|
65
|
-
@out.puts "Killing passenger process #{pid}"
|
66
|
-
@out.puts get_strace(pid, @strace_time)
|
67
|
-
Process.kill('TERM', pid) rescue nil
|
68
|
-
sleep @grace_time
|
69
|
-
Process.kill('KILL', pid) rescue nil
|
70
|
-
end
|
71
|
-
end
|
3
|
+
require 'zombie_passenger_killer/version'
|
4
|
+
require 'zombie_passenger_killer/reaper'
|
@@ -1,41 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'zombie_passenger_killer/version'
|
5
3
|
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.
|
8
|
-
s.version = "0.1.3"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
|
+
Gem::Specification.new "zombie_passenger_killer", ZombiePassengerKiller::VERSION do |s|
|
5
|
+
s.summary = "Guaranteed zombie passengers death"
|
11
6
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date = "2012-01-05"
|
13
7
|
s.email = "michael@grosser.it"
|
14
|
-
s.executables = ["zombie_passenger_killer"]
|
15
|
-
s.files = [
|
16
|
-
"Gemfile",
|
17
|
-
"Gemfile.lock",
|
18
|
-
"Rakefile",
|
19
|
-
"Readme.md",
|
20
|
-
"VERSION",
|
21
|
-
"bin/zombie_passenger_killer",
|
22
|
-
"lib/zombie_passenger_killer.rb",
|
23
|
-
"spec/spec_helper.rb",
|
24
|
-
"spec/zombie_passenger_killer_spec.rb",
|
25
|
-
"zombie_passenger_killer.gemspec"
|
26
|
-
]
|
27
8
|
s.homepage = "http://github.com/grosser/zombie_passenger_killer"
|
28
|
-
s.
|
29
|
-
s.
|
30
|
-
s.
|
31
|
-
|
32
|
-
if s.respond_to? :specification_version then
|
33
|
-
s.specification_version = 3
|
34
|
-
|
35
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
36
|
-
else
|
37
|
-
end
|
38
|
-
else
|
39
|
-
end
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.executables = ["zombie_passenger_killer"]
|
11
|
+
s.license = "MIT"
|
40
12
|
end
|
41
|
-
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zombie_passenger_killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Michael Grosser
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-02-09 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description:
|
@@ -31,15 +31,16 @@ files:
|
|
31
31
|
- Gemfile.lock
|
32
32
|
- Rakefile
|
33
33
|
- Readme.md
|
34
|
-
- VERSION
|
35
34
|
- bin/zombie_passenger_killer
|
36
35
|
- lib/zombie_passenger_killer.rb
|
36
|
+
- lib/zombie_passenger_killer/reaper.rb
|
37
|
+
- lib/zombie_passenger_killer/version.rb
|
37
38
|
- spec/spec_helper.rb
|
38
39
|
- spec/zombie_passenger_killer_spec.rb
|
39
40
|
- zombie_passenger_killer.gemspec
|
40
41
|
homepage: http://github.com/grosser/zombie_passenger_killer
|
41
|
-
licenses:
|
42
|
-
|
42
|
+
licenses:
|
43
|
+
- MIT
|
43
44
|
post_install_message:
|
44
45
|
rdoc_options: []
|
45
46
|
|
@@ -66,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
67
|
requirements: []
|
67
68
|
|
68
69
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.8.
|
70
|
+
rubygems_version: 1.8.15
|
70
71
|
signing_key:
|
71
72
|
specification_version: 3
|
72
73
|
summary: Guaranteed zombie passengers death
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.3
|