unicorn-worker-killer 0.3.6 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 472f5a54002c4f7d424ef88e613bce3b8d0b6a12
4
- data.tar.gz: 50cf272d41b6cd9bbbd9d3e2218fe189c2aac5d6
2
+ SHA256:
3
+ metadata.gz: 3b19012954bb3a259a52418a03a945bd4e1d0fbe682109a5c6a074f98daeeed2
4
+ data.tar.gz: d4882e751bae724d4d24a9194a3b76fe519d45427be55b52685a55309a82bca7
5
5
  SHA512:
6
- metadata.gz: 7a5a124e1b518ebb417abc8a14f6a93202a816973a6b69073b050072193c082d071a6f6ca418b04b15b211f80327e643cccf3534452a33286df916a2aa6050bd
7
- data.tar.gz: 65bb35218c266c2264d90d71c641ea872eba204a6420b98173256f67454cb301ad9170099e54e4c340c66adc2e53f54fa74d98f2bc77f334339ffb57cc59fafa
6
+ metadata.gz: 642bf83fc8c7ee86a2d7bfa36910418627b53701e4c53a6b61d2f2df5ce12563ff689f80189a647343677bab1bd05765bb6a05d65fcdd50a1fe9078ade066fe1
7
+ data.tar.gz: 36100a92f0d917fff98a7ff73bdc64bb0b4adb16b17389d7109807f65a78842d64b4dec0fd370b9571b92780002c94d91e8a2e368ac5d6b958f063bca0b15d34
data/AUTHORS CHANGED
@@ -1,2 +1,3 @@
1
1
  Kazuki Ohta <kazuki.ohta _at_ gmail.com>
2
2
  Sadayuki FURUHASHI <frsyuki _at_ gmail.com>
3
+ Jonathan Clem <jonathan _at_ jclem.net>
data/ChangeLog CHANGED
@@ -1,3 +1,22 @@
1
+ Release 0.4.5 - 2021/04/13
2
+ * update for Unicorn 6
3
+
4
+ Release 0.4.4 - 2015/11/13
5
+ * update for Unicorn 5
6
+
7
+ Release 0.4.3 - 2013/12/23
8
+ * fix for occasional 'invalid argument' error
9
+
10
+ Release 0.4.2 - 2013/09/23
11
+ * `verbose` options provides verbose logging
12
+
13
+ Release 0.4.1 - 2013/03/12
14
+ * Kill attempts happen once per request, not in a loop
15
+
16
+ Release 0.4.0 - 2013/03/12
17
+ * Send signals from inside a Thread (required for QUIT signal)
18
+ * Fix broken if/else in kill_self
19
+
1
20
  Release 0.3.6 - 2013/03/09
2
21
  * do not require configuration
3
22
  * fix missing randomize() in MaxRequests killer
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # unicorn-worker-killer
2
2
 
3
- [Unicorn](http://unicorn.bogomips.org/) is widely used HTTP-server for Rack applications. One thing we thought Unicorn misssed, is killing the Unicorn workers based on the number of requests and consumed memories.
3
+ [Unicorn](http://unicorn.bogomips.org/) is widely used HTTP-server for Rack applications. One thing we thought Unicorn missed, is killing the Unicorn workers based on the number of requests and consumed memories.
4
4
 
5
5
  `unicorn-worker-killer` gem provides automatic restart of Unicorn workers based on 1) max number of requests, and 2) process memory size (RSS), without affecting any requests. This will greatly improves site's stability by avoiding unexpected memory exhaustion at the application nodes.
6
6
 
@@ -12,7 +12,7 @@ No external process like `god` is required. Just install one gem: `unicorn-worke
12
12
 
13
13
  # Usage
14
14
 
15
- Add these lines to your `config.ru`.
15
+ Add these lines to your `config.ru`. (These lines should be added above the `require ::File.expand_path('../config/environment', __FILE__)` line.
16
16
 
17
17
  # Unicorn self-process killer
18
18
  require 'unicorn/worker_killer'
@@ -25,19 +25,25 @@ Add these lines to your `config.ru`.
25
25
 
26
26
  This gem provides two modules.
27
27
 
28
- ### Unicorn::WorkerKiller::MaxRequests(max_requests_min=3072, max_requests_max=4096)
28
+ ### `Unicorn::WorkerKiller::MaxRequests(max_requests_min=3072, max_requests_max=4096, verbose=false)`
29
29
 
30
30
  This module automatically restarts the Unicorn workers, based on the number of requests which worker processed.
31
31
 
32
32
  `max_requests_min` and `max_requests_max` specify the min and max of maximum requests per worker. The actual limit is decided by rand() between `max_requests_min` and `max_requests_max` per worker, to prevent all workers to be dead at the same time. Once the number exceeds the limit, that worker is automatically restarted.
33
33
 
34
- ### Unicorn::WorkerKiller::Oom(memory_limit_min=(1024**3), memory_limit_max=(2*(1024**3)), check_cycle = 16)
34
+ If `verbose` is set to true, then after every request, your log will show the requests left before restart. This logging is done at the `info` level.
35
+
36
+ ### `Unicorn::WorkerKiller::Oom(memory_limit_min=(1024\*\*3), memory_limit_max=(2\*(1024\*\*3)), check_cycle = 16, verbose = false)`
35
37
 
36
38
  This module automatically restarts the Unicorn workers, based on its memory size.
37
39
 
38
- `memory_limit_min` and `memory_limit_max` specify the min and max of maximum memory per worker. The actual limit is decided by rand() between `memory_limit_min` and `memory_limit_max` per worker, to prevent all workers to be dead at the same time. Once the memory size exceeds `memory_size`, that worker is automatically restarted.
40
+ `memory_limit_min` and `memory_limit_max` specify the min and max of maximum memory in bytes per worker. The actual limit is decided by rand() between `memory_limit_min` and `memory_limit_max` per worker, to prevent all workers to be dead at the same time. Once the memory size exceeds `memory_size`, that worker is automatically restarted.
39
41
 
40
42
  The memory size check is done in every `check_cycle` requests.
41
43
 
42
- # TODO
43
- - Get RSS (Resident Set Size) without forking the child process at Mac OS and Windows
44
+ If `verbose` is set to true, then every memory size check will be shown in your logs. This logging is done at the `info` level.
45
+
46
+ # Special Thanks
47
+
48
+ - [@hotchpotch](http://github.com/hotchpotch/) for the [original idea](https://gist.github.com/hotchpotch/1258681)
49
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.6
1
+ 0.4.5
@@ -1,32 +1,30 @@
1
- require 'unicorn/configuration'
1
+ require 'unicorn'
2
+ require 'unicorn/worker_killer/configuration'
3
+ require 'get_process_mem'
2
4
 
3
5
  module Unicorn::WorkerKiller
4
6
  class << self
5
7
  attr_accessor :configuration
6
8
  end
7
9
 
8
- # Self-destruction by sending the signals to myself. The process sometimes
9
- # doesn't terminate by SIGQUIT, so this tries to send SIGTERM and SIGKILL
10
- # if it doesn't finish immediately.
10
+ # Kill the current process by telling it to send signals to itself. If
11
+ # the process isn't killed after `configuration.max_quit` QUIT signals,
12
+ # send TERM signals until `configuration.max_term`. Finally, send a KILL
13
+ # signal. A single signal is sent per request.
11
14
  # @see http://unicorn.bogomips.org/SIGNALS.html
12
15
  def self.kill_self(logger, start_time)
13
- alive_sec = (Time.now - start_time).to_i
14
-
15
- i = 0
16
- while true
17
- i += 1
18
- sig = :QUIT
19
- if i > configuration.max_quit
20
- sig = :TERM
21
- elsif i > configuration.max_term
22
- sig = :KILL
23
- end
16
+ alive_sec = (Time.now - start_time).round
17
+ worker_pid = Process.pid
24
18
 
25
- logger.warn "#{self} send SIG#{sig} (pid: #{Process.pid}) alive: #{alive_sec} sec (trial #{i})"
26
- Process.kill sig, Process.pid
19
+ @@kill_attempts ||= 0
20
+ @@kill_attempts += 1
27
21
 
28
- sleep configuration.sleep_interval
29
- end
22
+ sig = :QUIT
23
+ sig = :TERM if @@kill_attempts > configuration.max_quit
24
+ sig = :KILL if @@kill_attempts > configuration.max_term
25
+
26
+ logger.warn "#{self} send SIG#{sig} (pid: #{worker_pid}) alive: #{alive_sec} sec (trial #{@@kill_attempts})"
27
+ Process.kill sig, worker_pid
30
28
  end
31
29
 
32
30
  module Oom
@@ -35,19 +33,20 @@ module Unicorn::WorkerKiller
35
33
  # affect the request.
36
34
  #
37
35
  # @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
38
- def self.new(app, memory_limit_min = (1024**3), memory_limit_max = (2*(1024**3)), check_cycle = 16)
36
+ def self.new(app, memory_limit_min = (1024**3), memory_limit_max = (2*(1024**3)), check_cycle = 16, verbose = false)
39
37
  ObjectSpace.each_object(Unicorn::HttpServer) do |s|
40
38
  s.extend(self)
41
39
  s.instance_variable_set(:@_worker_memory_limit_min, memory_limit_min)
42
40
  s.instance_variable_set(:@_worker_memory_limit_max, memory_limit_max)
43
41
  s.instance_variable_set(:@_worker_check_cycle, check_cycle)
44
42
  s.instance_variable_set(:@_worker_check_count, 0)
43
+ s.instance_variable_set(:@_verbose, verbose)
45
44
  end
46
45
  app # pretend to be Rack middleware since it was in the past
47
46
  end
48
47
 
49
48
  def randomize(integer)
50
- RUBY_VERSION > "1.9" ? Random.rand(integer) : rand(integer)
49
+ RUBY_VERSION > "1.9" ? Random.rand(integer.abs) : rand(integer)
51
50
  end
52
51
 
53
52
  def process_client(client)
@@ -58,7 +57,8 @@ module Unicorn::WorkerKiller
58
57
  @_worker_memory_limit ||= @_worker_memory_limit_min + randomize(@_worker_memory_limit_max - @_worker_memory_limit_min + 1)
59
58
  @_worker_check_count += 1
60
59
  if @_worker_check_count % @_worker_check_cycle == 0
61
- rss = _worker_rss()
60
+ rss = GetProcessMem.new.bytes
61
+ logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
62
62
  if rss > @_worker_memory_limit
63
63
  logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
64
64
  Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start)
@@ -66,38 +66,6 @@ module Unicorn::WorkerKiller
66
66
  @_worker_check_count = 0
67
67
  end
68
68
  end
69
-
70
- private
71
-
72
- def _worker_rss
73
- proc_status = "/proc/#{Process.pid}/status"
74
- if File.exists? proc_status
75
- open(proc_status).each_line { |l|
76
- if l.include? 'VmRSS'
77
- ls = l.split
78
- if ls.length == 3
79
- value = ls[1].to_i
80
- unit = ls[2]
81
- case unit.downcase
82
- when 'kb'
83
- return value*(1024**1)
84
- when 'mb'
85
- return value*(1024**2)
86
- when 'gb'
87
- return value*(1024**3)
88
- end
89
- end
90
- end
91
- }
92
- end
93
-
94
- # Forking the child process sometimes fails under low memory condition.
95
- # It would be ideal for not forking the process to get RSS. For Linux,
96
- # this module reads '/proc/<pid>/status' to get RSS, but not for other
97
- # environments (e.g. MacOS and Windows).
98
- kb = `ps -o rss= -p #{Process.pid}`.to_i
99
- return kb * 1024
100
- end
101
69
  end
102
70
 
103
71
  module MaxRequests
@@ -106,17 +74,19 @@ module Unicorn::WorkerKiller
106
74
  # affect the request.
107
75
  #
108
76
  # @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
109
- def self.new(app, max_requests_min = 3072, max_requests_max = 4096)
77
+ def self.new(app, max_requests_min = 3072, max_requests_max = 4096, verbose = false)
110
78
  ObjectSpace.each_object(Unicorn::HttpServer) do |s|
111
79
  s.extend(self)
112
80
  s.instance_variable_set(:@_worker_max_requests_min, max_requests_min)
113
81
  s.instance_variable_set(:@_worker_max_requests_max, max_requests_max)
82
+ s.instance_variable_set(:@_verbose, verbose)
114
83
  end
84
+
115
85
  app # pretend to be Rack middleware since it was in the past
116
86
  end
117
87
 
118
88
  def randomize(integer)
119
- RUBY_VERSION > "1.9" ? Random.rand(integer) : rand(integer)
89
+ RUBY_VERSION > "1.9" ? Random.rand(integer.abs) : rand(integer)
120
90
  end
121
91
 
122
92
  def process_client(client)
@@ -126,6 +96,8 @@ module Unicorn::WorkerKiller
126
96
  @_worker_process_start ||= Time.now
127
97
  @_worker_cur_requests ||= @_worker_max_requests_min + randomize(@_worker_max_requests_max - @_worker_max_requests_min + 1)
128
98
  @_worker_max_requests ||= @_worker_cur_requests
99
+ logger.info "#{self}: worker (pid: #{Process.pid}) has #{@_worker_cur_requests} left before being killed" if @_verbose
100
+
129
101
  if (@_worker_cur_requests -= 1) <= 0
130
102
  logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@_worker_max_requests})"
131
103
  Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start)
@@ -7,14 +7,16 @@ Gem::Specification.new do |gem|
7
7
  gem.homepage = "https://github.com/kzk/unicorn-worker-killer"
8
8
  gem.summary = gem.description
9
9
  gem.version = File.read("VERSION").strip
10
- gem.authors = ["Kazuki Ohta", "Sadayuki Furuhashi"]
11
- gem.email = ["kazuki.ohta@gmail.com", "frsyuki@gmail.com"]
10
+ gem.authors = ["Kazuki Ohta", "Sadayuki Furuhashi", "Jonathan Clem"]
11
+ gem.email = ["kazuki.ohta@gmail.com", "frsyuki@gmail.com", "jonathan@jclem.net"]
12
12
  gem.has_rdoc = false
13
13
  #gem.platform = Gem::Platform::RUBY
14
14
  gem.files = `git ls-files`.split("\n")
15
15
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
16
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ gem.licenses = ["GPLv2+", "Ruby 1.8"]
17
18
  gem.require_paths = ['lib']
18
- gem.add_dependency "unicorn", "~> 4"
19
+ gem.add_dependency "unicorn", [">= 4", "< 7"]
20
+ gem.add_dependency "get_process_mem", "~> 0"
19
21
  gem.add_development_dependency "rake", ">= 0.9.2"
20
22
  end
metadata CHANGED
@@ -1,48 +1,70 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unicorn-worker-killer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuki Ohta
8
8
  - Sadayuki Furuhashi
9
+ - Jonathan Clem
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-03-09 00:00:00.000000000 Z
13
+ date: 2021-04-14 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: unicorn
16
17
  requirement: !ruby/object:Gem::Requirement
17
18
  requirements:
18
- - - ~>
19
+ - - ">="
19
20
  - !ruby/object:Gem::Version
20
21
  version: '4'
22
+ - - "<"
23
+ - !ruby/object:Gem::Version
24
+ version: '7'
21
25
  type: :runtime
22
26
  prerelease: false
23
27
  version_requirements: !ruby/object:Gem::Requirement
24
28
  requirements:
25
- - - ~>
29
+ - - ">="
26
30
  - !ruby/object:Gem::Version
27
31
  version: '4'
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '7'
35
+ - !ruby/object:Gem::Dependency
36
+ name: get_process_mem
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
28
49
  - !ruby/object:Gem::Dependency
29
50
  name: rake
30
51
  requirement: !ruby/object:Gem::Requirement
31
52
  requirements:
32
- - - '>='
53
+ - - ">="
33
54
  - !ruby/object:Gem::Version
34
55
  version: 0.9.2
35
56
  type: :development
36
57
  prerelease: false
37
58
  version_requirements: !ruby/object:Gem::Requirement
38
59
  requirements:
39
- - - '>='
60
+ - - ">="
40
61
  - !ruby/object:Gem::Version
41
62
  version: 0.9.2
42
63
  description: Kill unicorn workers by memory and request counts
43
64
  email:
44
65
  - kazuki.ohta@gmail.com
45
66
  - frsyuki@gmail.com
67
+ - jonathan@jclem.net
46
68
  executables: []
47
69
  extensions: []
48
70
  extra_rdoc_files: []
@@ -54,11 +76,13 @@ files:
54
76
  - README.md
55
77
  - Rakefile
56
78
  - VERSION
57
- - lib/unicorn/configuration.rb
58
79
  - lib/unicorn/worker_killer.rb
80
+ - lib/unicorn/worker_killer/configuration.rb
59
81
  - unicorn-worker-killer.gemspec
60
82
  homepage: https://github.com/kzk/unicorn-worker-killer
61
- licenses: []
83
+ licenses:
84
+ - GPLv2+
85
+ - Ruby 1.8
62
86
  metadata: {}
63
87
  post_install_message:
64
88
  rdoc_options: []
@@ -66,17 +90,16 @@ require_paths:
66
90
  - lib
67
91
  required_ruby_version: !ruby/object:Gem::Requirement
68
92
  requirements:
69
- - - '>='
93
+ - - ">="
70
94
  - !ruby/object:Gem::Version
71
95
  version: '0'
72
96
  required_rubygems_version: !ruby/object:Gem::Requirement
73
97
  requirements:
74
- - - '>='
98
+ - - ">="
75
99
  - !ruby/object:Gem::Version
76
100
  version: '0'
77
101
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.0.0
102
+ rubygems_version: 3.0.8
80
103
  signing_key:
81
104
  specification_version: 4
82
105
  summary: Kill unicorn workers by memory and request counts