puma_worker_killer 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -3
- data/CHANGELOG.md +4 -0
- data/README.md +12 -5
- data/lib/puma_worker_killer.rb +5 -4
- data/lib/puma_worker_killer/reaper.rb +7 -3
- data/lib/puma_worker_killer/version.rb +1 -1
- data/puma_worker_killer.gemspec +1 -1
- data/test/fixtures/on_calculation.ru +8 -0
- data/test/puma_worker_killer_test.rb +12 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e591941aca312d730ef3105775b2d452d8ee331c6731a02ee0914613c3fb3929
|
4
|
+
data.tar.gz: 7ac7144c3768f6d0d3ace259e1ff5369d2d45a9f466bba46d89f999550f870b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 437a491f123bc729f862fac31f77a1ad51db03b5833930025bc401cd216ee60c0756c92cee153d10962df50b3cf3c68316252d438ccad23859d013bc0a1d6d3f
|
7
|
+
data.tar.gz: 9c70eddaa070856429951f3dd6bcc46db08afb32425cd97c92e65ecfcae4265fcb887dc00b4816717aa8f43c3c31916aaf5a7dbbac82c531e3414a5626372fca
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Puma Worker Killer
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/schneems/puma_worker_killer.png?branch=master)](https://travis-ci.org/schneems/puma_worker_killer)
|
4
|
+
[![Help Contribute to Open Source](https://www.codetriage.com/schneems/puma_worker_killer/badges/users.svg)](https://www.codetriage.com/schneems/puma_worker_killer)
|
4
5
|
|
5
6
|
|
6
7
|
## What
|
@@ -66,7 +67,7 @@ before_fork do
|
|
66
67
|
end
|
67
68
|
```
|
68
69
|
|
69
|
-
That's it. Now on a regular basis the size of all Puma and all of it's forked processes will be evaluated and if they're over the RAM threshold will be killed. Don't worry Puma will notice a process is missing
|
70
|
+
That's it. Now on a regular basis the size of all Puma and all of it's forked processes will be evaluated and if they're over the RAM threshold will be killed. Don't worry Puma will notice a process is missing and spawn a fresh copy with a much smaller RAM footprint ASAP.
|
70
71
|
|
71
72
|
## Troubleshooting
|
72
73
|
|
@@ -102,7 +103,7 @@ PumaWorkerKiller.config do |config|
|
|
102
103
|
config.ram = 1024 # mb
|
103
104
|
config.frequency = 5 # seconds
|
104
105
|
config.percent_usage = 0.98
|
105
|
-
config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
|
106
|
+
config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
|
106
107
|
config.reaper_status_logs = true # setting this to false will not log lines like:
|
107
108
|
# PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.
|
108
109
|
|
@@ -129,6 +130,12 @@ PumaWorkerKiller: Rolling Restart. 5 workers consuming total: 650mb mb. Sending
|
|
129
130
|
|
130
131
|
However you may want to collect more data, such as sending an event to an error collection service like rollbar or airbrake. The `pre_term` lambda gets called before any worker is killed by PWK for any reason.
|
131
132
|
|
133
|
+
### on_calculation
|
134
|
+
|
135
|
+
`config.on_calculation` will be called every time Puma Worker Killer calculates memory usage (`config.frequency`). This may be useful for monitoring your total puma application memory usage, which can be contrasted with other application monitoring solutions.
|
136
|
+
|
137
|
+
This callback lambda is given a single value for the amount of memory used.
|
138
|
+
|
132
139
|
## Attention
|
133
140
|
|
134
141
|
If you start puma as a daemon, to add puma worker killer config into puma config file, rather than into initializers:
|
@@ -140,7 +147,7 @@ before_fork do
|
|
140
147
|
config.ram = 1024 # mb
|
141
148
|
config.frequency = 5 # seconds
|
142
149
|
config.percent_usage = 0.98
|
143
|
-
config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
|
150
|
+
config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
|
144
151
|
end
|
145
152
|
PumaWorkerKiller.start
|
146
153
|
end
|
@@ -167,10 +174,10 @@ PumaWorkerKiller.frequency = 20 # seconds
|
|
167
174
|
You may want to periodically restart all of your workers rather than simply killing your largest. To do that set:
|
168
175
|
|
169
176
|
```ruby
|
170
|
-
PumaWorkerKiller.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
|
177
|
+
PumaWorkerKiller.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
|
171
178
|
```
|
172
179
|
|
173
|
-
By default PumaWorkerKiller will perform a rolling restart of all your worker processes every
|
180
|
+
By default PumaWorkerKiller will perform a rolling restart of all your worker processes every 6 hours. To disable, set to `false`.
|
174
181
|
|
175
182
|
You may want to hide the following log lines: `PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.`. To do that set:
|
176
183
|
|
data/lib/puma_worker_killer.rb
CHANGED
@@ -3,20 +3,21 @@ require 'get_process_mem'
|
|
3
3
|
module PumaWorkerKiller
|
4
4
|
extend self
|
5
5
|
|
6
|
-
attr_accessor :ram, :frequency, :percent_usage, :rolling_restart_frequency, :reaper_status_logs, :pre_term
|
6
|
+
attr_accessor :ram, :frequency, :percent_usage, :rolling_restart_frequency, :reaper_status_logs, :pre_term, :on_calculation
|
7
7
|
self.ram = 512 # mb
|
8
8
|
self.frequency = 10 # seconds
|
9
9
|
self.percent_usage = 0.99 # percent of RAM to use
|
10
10
|
self.rolling_restart_frequency = 6 * 3600 # 6 hours in seconds
|
11
11
|
self.reaper_status_logs = true
|
12
|
-
self.pre_term =
|
12
|
+
self.pre_term = nil
|
13
|
+
self.on_calculation = nil
|
13
14
|
|
14
15
|
def config
|
15
16
|
yield self
|
16
17
|
end
|
17
18
|
|
18
|
-
def reaper(ram = self.ram, percent = self.percent_usage, reaper_status_logs = self.reaper_status_logs, pre_term = self.pre_term)
|
19
|
-
Reaper.new(ram * percent_usage, nil, reaper_status_logs, pre_term)
|
19
|
+
def reaper(ram = self.ram, percent = self.percent_usage, reaper_status_logs = self.reaper_status_logs, pre_term = self.pre_term, on_calculation = self.on_calculation)
|
20
|
+
Reaper.new(ram * percent_usage, nil, reaper_status_logs, pre_term, on_calculation)
|
20
21
|
end
|
21
22
|
|
22
23
|
def start(frequency = self.frequency, reaper = self.reaper)
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module PumaWorkerKiller
|
2
2
|
class Reaper
|
3
|
-
def initialize(max_ram, master = nil, reaper_status_logs = true, pre_term)
|
3
|
+
def initialize(max_ram, master = nil, reaper_status_logs = true, pre_term = nil, on_calculation = nil)
|
4
4
|
@cluster = PumaWorkerKiller::PumaMemory.new(master)
|
5
5
|
@max_ram = max_ram
|
6
6
|
@reaper_status_logs = reaper_status_logs
|
7
7
|
@pre_term = pre_term
|
8
|
+
@on_calculation = on_calculation
|
8
9
|
end
|
9
10
|
|
10
11
|
# used for tes
|
@@ -14,7 +15,10 @@ module PumaWorkerKiller
|
|
14
15
|
|
15
16
|
def reap
|
16
17
|
return false if @cluster.workers_stopped?
|
17
|
-
|
18
|
+
total = get_total_memory
|
19
|
+
@on_calculation.call(total) unless @on_calculation.nil?
|
20
|
+
|
21
|
+
if total > @max_ram
|
18
22
|
@cluster.master.log "PumaWorkerKiller: Out of memory. #{@cluster.workers.count} workers consuming total: #{total} mb out of max: #{@max_ram} mb. Sending TERM to pid #{@cluster.largest_worker.pid} consuming #{@cluster.largest_worker_memory} mb."
|
19
23
|
|
20
24
|
# Fetch the largest_worker so that both `@pre_term` and `term_worker` are called with the same worker
|
@@ -25,7 +29,7 @@ module PumaWorkerKiller
|
|
25
29
|
# A new request comes in, Worker B takes it, and consumes 101 mb memory
|
26
30
|
# term_largest_worker (previously here) gets called and terms Worker B (thus not passing the about-to-be-terminated worker to `@pre_term`)
|
27
31
|
largest_worker = @cluster.largest_worker
|
28
|
-
@pre_term.call(largest_worker)
|
32
|
+
@pre_term.call(largest_worker) unless @pre_term.nil?
|
29
33
|
@cluster.term_worker(largest_worker)
|
30
34
|
|
31
35
|
elsif @reaper_status_logs
|
data/puma_worker_killer.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_dependency "puma", ">= 2.7", "<
|
21
|
+
gem.add_dependency "puma", ">= 2.7", "< 5"
|
22
22
|
gem.add_dependency "get_process_mem", "~> 0.2"
|
23
23
|
gem.add_development_dependency "rack", "~> 1.6"
|
24
24
|
gem.add_development_dependency "wait_for_it", "~> 0.1"
|
@@ -45,6 +45,18 @@ class PumaWorkerKillerTest < Test::Unit::TestCase
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
def test_on_calculation
|
49
|
+
file = fixture_path.join("on_calculation.ru")
|
50
|
+
port = 0
|
51
|
+
command = "bundle exec puma #{ file } -t 1:1 -w 2 --preload --debug -p #{ port }"
|
52
|
+
options = { wait_for: "booted", timeout: 5, env: { "PUMA_FREQUENCY" => 1, 'PUMA_RAM' => 1} }
|
53
|
+
|
54
|
+
WaitForIt.new(command, options) do |spawn|
|
55
|
+
assert_contains(spawn, "Out of memory")
|
56
|
+
assert_contains(spawn, "Current memory footprint:") # defined in on_calculate.ru
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
48
60
|
def assert_contains(spawn, string)
|
49
61
|
assert spawn.wait(string), "Expected logs to contain '#{string}' but it did not, contents: #{ spawn.log.read }"
|
50
62
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma_worker_killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Schneeman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: puma
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '2.7'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '5'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '2.7'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '5'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: get_process_mem
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,6 +124,7 @@ files:
|
|
124
124
|
- test/fixtures/config/puma_worker_killer_start.rb
|
125
125
|
- test/fixtures/default.ru
|
126
126
|
- test/fixtures/fixture_helper.rb
|
127
|
+
- test/fixtures/on_calculation.ru
|
127
128
|
- test/fixtures/pre_term.ru
|
128
129
|
- test/fixtures/rolling_restart.ru
|
129
130
|
- test/puma_worker_killer_test.rb
|
@@ -147,8 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
148
|
- !ruby/object:Gem::Version
|
148
149
|
version: '0'
|
149
150
|
requirements: []
|
150
|
-
|
151
|
-
rubygems_version: 2.6.11
|
151
|
+
rubygems_version: 3.0.3
|
152
152
|
signing_key:
|
153
153
|
specification_version: 4
|
154
154
|
summary: If you have a memory leak in your web code puma_worker_killer can keep it
|
@@ -158,6 +158,7 @@ test_files:
|
|
158
158
|
- test/fixtures/config/puma_worker_killer_start.rb
|
159
159
|
- test/fixtures/default.ru
|
160
160
|
- test/fixtures/fixture_helper.rb
|
161
|
+
- test/fixtures/on_calculation.ru
|
161
162
|
- test/fixtures/pre_term.ru
|
162
163
|
- test/fixtures/rolling_restart.ru
|
163
164
|
- test/puma_worker_killer_test.rb
|