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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 83921642be90494f9eb9988caff6f2397d2ff761
4
- data.tar.gz: 0427e91feb9357ab8637739fac169edab633c8a1
2
+ SHA256:
3
+ metadata.gz: e591941aca312d730ef3105775b2d452d8ee331c6731a02ee0914613c3fb3929
4
+ data.tar.gz: 7ac7144c3768f6d0d3ace259e1ff5369d2d45a9f466bba46d89f999550f870b0
5
5
  SHA512:
6
- metadata.gz: f87c5688cb637837c82182cad0eb912a5bcdfde7aab9fc8bd892154d51cb5c78019ea85a9edcaceba2cbb103e8062e68d8c9e500b29073d32bf7b22fafad56cc
7
- data.tar.gz: e05e97981e2adf0e319b0ba92ff19f77e84a4f6c47906b7114fda2cdc18dce44e3476dd033f1995e076a073f25986fa74e342ea658f54b8dd7ba4a822af97998
6
+ metadata.gz: 437a491f123bc729f862fac31f77a1ad51db03b5833930025bc401cd216ee60c0756c92cee153d10962df50b3cf3c68316252d438ccad23859d013bc0a1d6d3f
7
+ data.tar.gz: 9c70eddaa070856429951f3dd6bcc46db08afb32425cd97c92e65ecfcae4265fcb887dc00b4816717aa8f43c3c31916aaf5a7dbbac82c531e3414a5626372fca
@@ -2,9 +2,11 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - 2.1.0
6
- - 2.2.4
7
- - 2.3.0
5
+ - 2.1.10
6
+ - 2.2.9
7
+ - 2.3.6
8
+ - 2.4.3
9
+ - 2.5.0
8
10
  - ruby-head
9
11
  - rbx
10
12
  before_install:
@@ -1,3 +1,7 @@
1
+ ## 0.1.1
2
+
3
+ - Allow PWK to be used with Puma 4 (#72)
4
+
1
5
  ## 0.1.0
2
6
 
3
7
  - Emit extra data via `pre_term` callback before puma worker killer terminates a worker #49.
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 a spawn a fresh copy with a much smaller RAM footprint ASAP.
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 12 hours. To disable, set to `false`.
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
 
@@ -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 = lambda { |_| } # nop
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
- if (total = get_total_memory) > @max_ram
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
@@ -1,3 +1,3 @@
1
1
  module PumaWorkerKiller
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -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", "< 4"
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"
@@ -0,0 +1,8 @@
1
+ load File.expand_path("../fixture_helper.rb", __FILE__)
2
+
3
+ PumaWorkerKiller.config do |config|
4
+ config.on_calculation = lambda { |usage| puts("Current memory footprint: #{usage} mb") }
5
+ end
6
+ PumaWorkerKiller.start
7
+
8
+ run HelloWorldApp
@@ -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.0
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: 2017-05-12 00:00:00.000000000 Z
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: '4'
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: '4'
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
- rubyforge_project:
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