puma_worker_killer 0.1.1 → 0.2.0

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
2
  SHA256:
3
- metadata.gz: e591941aca312d730ef3105775b2d452d8ee331c6731a02ee0914613c3fb3929
4
- data.tar.gz: 7ac7144c3768f6d0d3ace259e1ff5369d2d45a9f466bba46d89f999550f870b0
3
+ metadata.gz: 656a49cbbcbc36e3735d20ade22027cc209213f029e51faa7f0f08c7dbcffb70
4
+ data.tar.gz: 5a328323d77f9ede2a44ab0c37577b6277a682c73ce1ad0f1bf167efb720a62d
5
5
  SHA512:
6
- metadata.gz: 437a491f123bc729f862fac31f77a1ad51db03b5833930025bc401cd216ee60c0756c92cee153d10962df50b3cf3c68316252d438ccad23859d013bc0a1d6d3f
7
- data.tar.gz: 9c70eddaa070856429951f3dd6bcc46db08afb32425cd97c92e65ecfcae4265fcb887dc00b4816717aa8f43c3c31916aaf5a7dbbac82c531e3414a5626372fca
6
+ metadata.gz: f77c25f16200fc313b7d230f852fcd2543f1f2ff1f40863c757c9288cbbc520c6b44f9ba3c51fef26693e1d233b66519100d0443b3020402f55ebee397babd09
7
+ data.tar.gz: df97c81b1e0d93a8ace45cd1b24a9e6de4fb2f94a85b181650a163f458aae754a6571b5b34721b6b718649059305c2637f0766597b0560f9412a57c56e91281a
@@ -0,0 +1,12 @@
1
+ name: Check Changelog
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v1
10
+ - name: Check that CHANGELOG is touched
11
+ run: |
12
+ cat $GITHUB_EVENT_PATH | jq .pull_request.title | grep -i '\[\(\(changelog skip\)\|\(ci skip\)\)\]' || git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md
@@ -0,0 +1,22 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Naming/AccessorMethodName:
4
+ Enabled: false
5
+
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ Style/HashSyntax:
10
+ Enabled: false
11
+
12
+ Style/ModuleFunction:
13
+ Enabled: false
14
+
15
+ Style/StringLiterals:
16
+ EnforcedStyle: single_quotes
17
+
18
+ Style/StringLiteralsInInterpolation:
19
+ EnforcedStyle: double_quotes
20
+
21
+ Gemspec/RequiredRubyVersion:
22
+ Enabled: false
@@ -0,0 +1,24 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2020-04-03 13:12:53 +0200 using RuboCop version 0.81.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: IgnoredMethods.
11
+ Metrics/AbcSize:
12
+ Max: 21
13
+
14
+ # Offense count: 1
15
+ # Configuration parameters: CountComments, ExcludedMethods.
16
+ Metrics/MethodLength:
17
+ Max: 11
18
+
19
+ # Offense count: 32
20
+ # Cop supports --auto-correct.
21
+ # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
22
+ # URISchemes: http, https
23
+ Layout/LineLength:
24
+ Max: 252
@@ -1,12 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.10
6
- - 2.2.9
7
- - 2.3.6
8
- - 2.4.3
9
- - 2.5.0
3
+ - 2.3.8
4
+ - 2.4.5
5
+ - 2.5.3
6
+ - 2.6.0
10
7
  - ruby-head
11
8
  - rbx
12
9
  before_install:
@@ -16,3 +13,10 @@ matrix:
16
13
  allow_failures:
17
14
  - rvm: ruby-head
18
15
  - rvm: rbx
16
+
17
+ install:
18
+ - bundle install
19
+
20
+ script:
21
+ - bundle exec rubocop
22
+ - bundle exec rake test
@@ -1,3 +1,12 @@
1
+ ## Master
2
+
3
+ ## 0.2.0
4
+
5
+ - Simplify workers memory calculation in PumaMemory‘s `get_total` method #81
6
+ - Add rubocop in gemspec and CI, with offenses corrected and unnecessary cops disabled.
7
+ - Add `pre_term`-like `rolling_pre_term` config for terminations caused by rolling restart (#86)
8
+ - Fix compatibility with ruby version 2.3.X (#87)
9
+
1
10
  ## 0.1.1
2
11
 
3
12
  - Allow PWK to be used with Puma 4 (#72)
@@ -25,3 +34,4 @@
25
34
  ## 0.0.3
26
35
 
27
36
  - Fix memory metrics in on linux
37
+
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  gemspec
6
+
7
+ gem 'rubocop', require: false
data/README.md CHANGED
@@ -3,6 +3,22 @@
3
3
  [![Build Status](https://travis-ci.org/schneems/puma_worker_killer.png?branch=master)](https://travis-ci.org/schneems/puma_worker_killer)
4
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)
5
5
 
6
+ ## !!!!!!!!!!!!!!!! STOP !!!!!!!!!!!!!!!!
7
+
8
+ Before you use this gem, know that it is dangerous. If you have a memory issue, you need to fix the issue. The original idea behind this gem is that it would act as a temporary band-aid to buy you time to allow you to fix your issues. If you turn this on and don't fix the underlying memory problems, then things will only get worse over time.
9
+
10
+ This gem can also make your performance WORSE. When a worker is killed, and comes back it takes CPU cycles and time. If you are frequently restarting your workers then you're killing your performance.
11
+
12
+ Here are some places to start improving your understanding of memory behvior in Ruby:
13
+
14
+ - [Complete Guide to Rails Performance (Book)](https://www.railsspeed.com)
15
+ - [How Ruby uses Memory](https://www.sitepoint.com/ruby-uses-memory/)
16
+ - [Ruby Memory Use (Heroku Devcenter article I maintain)](https://devcenter.heroku.com/articles/ruby-memory-use)
17
+ - [Jumping off the Ruby Memory Cliff](https://www.schneems.com/2017/04/12/jumping-off-the-memory-cliff/)
18
+ - [How Ruby uses memory (Talk)](https://www.schneems.com/2015/05/11/how-ruby-uses-memory.html) (you can skip the first story in the video, the rest are about memory)
19
+ - [Debugging a memory leak on Heroku](https://blog.codeship.com/debugging-a-memory-leak-on-heroku/)
20
+
21
+ If you still need this gem, proceed with caution.
6
22
 
7
23
  ## What
8
24
 
@@ -23,14 +39,7 @@ gem 'puma_worker_killer'
23
39
 
24
40
  Then run `$ bundle install`
25
41
 
26
- <!--
27
- ## Use
28
-
29
- > If you like `puma_worker_killer` consider using [puma_auto_tune instead](https://github.com/schneems/puma_auto_tune). It handles memory leaks and tunes your workers too!
30
-
31
- -->
32
-
33
- ## Turn on Rolling Restarts
42
+ ## Turn on Rolling Restarts - Heroku Mode
34
43
 
35
44
  A rolling restart will kill each of your workers on a rolling basis. You set the frequency which it conducts the restart. This is a simple way to keep memory down as Ruby web programs generally increase memory usage over time. If you're using Heroku [it is difficult to measure RAM from inside of a container accurately](https://github.com/schneems/get_process_mem/issues/7), so it is recommended to use this feature or use a [log-drain-based worker killer](https://github.com/arches/whacamole). You can enable roling restarts by running:
36
45
 
@@ -55,7 +64,7 @@ Make sure if you do this to not accidentally call `PumaWorkerKiller.start` as we
55
64
 
56
65
  ## Enable Worker Killing
57
66
 
58
- If you're not running on a containerized platform you can try to detect the amount of memory you're using and only kill Puma workers when you're over that limit. It may allow you to go for longer periods of time without killing a worker however it is more error prone than rolling restarts. To enable measurement based worker killing put this in your `config/puma.rb`:
67
+ If you're not running on a containerized platform (like Heroku or Docker) you can try to detect the amount of memory you're using and only kill Puma workers when you're over that limit. It may allow you to go for longer periods of time without killing a worker however it is more error prone than rolling restarts. To enable measurement based worker killing put this in your `config/puma.rb`:
59
68
 
60
69
  ```ruby
61
70
  # config/puma.rb
@@ -108,6 +117,7 @@ PumaWorkerKiller.config do |config|
108
117
  # PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.
109
118
 
110
119
  config.pre_term = -> (worker) { puts "Worker #{worker.inspect} being killed" }
120
+ config.rolling_pre_term = -> (worker) { puts "Worker #{worker.inspect} being killed by rolling restart" }
111
121
  end
112
122
  PumaWorkerKiller.start
113
123
  ```
@@ -130,6 +140,17 @@ PumaWorkerKiller: Rolling Restart. 5 workers consuming total: 650mb mb. Sending
130
140
 
131
141
  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.
132
142
 
143
+ ### rolling_pre_term
144
+
145
+ `config.rolling_pre_term` will be called just prior to worker termination by rolling restart when rolling restart is enabled.
146
+
147
+ It is similar to `config.pre_term`.
148
+
149
+ Difference:
150
+
151
+ - `config.pre_term` is triggered only by terminations related with exceeding RAM
152
+ - `config.rolling_pre_term` is triggered only by terminations caused by enabled rolling restart
153
+
133
154
  ### on_calculation
134
155
 
135
156
  `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.
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
 
@@ -7,7 +7,7 @@ require 'rake/testtask'
7
7
 
8
8
  task :default => [:test]
9
9
 
10
- test_task = Rake::TestTask.new(:test) do |t|
10
+ Rake::TestTask.new(:test) do |t|
11
11
  t.libs << 'lib'
12
12
  t.libs << 'test'
13
13
  t.pattern = 'test/**/*_test.rb'
@@ -1,22 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'get_process_mem'
2
4
 
3
5
  module PumaWorkerKiller
4
6
  extend self
5
7
 
6
- attr_accessor :ram, :frequency, :percent_usage, :rolling_restart_frequency, :reaper_status_logs, :pre_term, :on_calculation
8
+ attr_accessor :ram, :frequency, :percent_usage, :rolling_restart_frequency,
9
+ :reaper_status_logs, :pre_term, :rolling_pre_term, :on_calculation
10
+
7
11
  self.ram = 512 # mb
8
12
  self.frequency = 10 # seconds
9
13
  self.percent_usage = 0.99 # percent of RAM to use
10
14
  self.rolling_restart_frequency = 6 * 3600 # 6 hours in seconds
11
15
  self.reaper_status_logs = true
12
16
  self.pre_term = nil
17
+ self.rolling_pre_term = nil
13
18
  self.on_calculation = nil
14
19
 
15
20
  def config
16
21
  yield self
17
22
  end
18
23
 
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)
24
+ def reaper(ram = self.ram, percent_usage = self.percent_usage, reaper_status_logs = self.reaper_status_logs, pre_term = self.pre_term, on_calculation = self.on_calculation)
20
25
  Reaper.new(ram * percent_usage, nil, reaper_status_logs, pre_term, on_calculation)
21
26
  end
22
27
 
@@ -25,9 +30,9 @@ module PumaWorkerKiller
25
30
  enable_rolling_restart(rolling_restart_frequency) if rolling_restart_frequency
26
31
  end
27
32
 
28
- def enable_rolling_restart(frequency = self.rolling_restart_frequency)
29
- frequency = frequency + rand(0..10.0) # so all workers don't restart at the exact same time across multiple machines
30
- AutoReap.new(frequency, RollingRestart.new).start
33
+ def enable_rolling_restart(frequency = rolling_restart_frequency)
34
+ frequency += rand(0..10.0) # so all workers don't restart at the exact same time across multiple machines
35
+ AutoReap.new(frequency, RollingRestart.new(nil, rolling_pre_term)).start
31
36
  end
32
37
  end
33
38
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
4
  class AutoReap
3
5
  def initialize(timeout, reaper = Reaper.new)
@@ -16,6 +18,5 @@ module PumaWorkerKiller
16
18
  end
17
19
  end
18
20
  end
19
-
20
21
  end
21
22
  end
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
4
  class PumaMemory
3
5
  def initialize(master = nil)
4
6
  @master = master || get_master
7
+ @workers = nil
5
8
  end
6
9
 
7
- def master
8
- @master
9
- end
10
+ attr_reader :master
10
11
 
11
12
  def size
12
13
  workers.size
@@ -18,8 +19,6 @@ module PumaWorkerKiller
18
19
 
19
20
  def term_largest_worker
20
21
  largest_worker.term
21
- # Process.wait(largest_worker.pid)
22
- # rescue Errno::ECHILD
23
22
  end
24
23
 
25
24
  def workers_stopped?
@@ -31,7 +30,7 @@ module PumaWorkerKiller
31
30
  end
32
31
 
33
32
  def smallest_worker
34
- smallest, _ = workers.to_a.first
33
+ smallest, = workers.to_a.first
35
34
  smallest
36
35
  end
37
36
 
@@ -41,7 +40,7 @@ module PumaWorkerKiller
41
40
  end
42
41
 
43
42
  def largest_worker
44
- largest_worker, _ = workers.to_a.last
43
+ largest_worker, = workers.to_a.last
45
44
  largest_worker
46
45
  end
47
46
 
@@ -53,10 +52,10 @@ module PumaWorkerKiller
53
52
  # Will refresh @workers
54
53
  def get_total(workers = set_workers)
55
54
  master_memory = GetProcessMem.new(Process.pid).mb
56
- worker_memory = workers.map {|_, mem| mem }.inject(&:+) || 0
55
+ worker_memory = workers.values.inject(:+) || 0
57
56
  worker_memory + master_memory
58
57
  end
59
- alias :get_total_memory :get_total
58
+ alias get_total_memory get_total
60
59
 
61
60
  def workers
62
61
  @workers || set_workers
@@ -72,11 +71,11 @@ module PumaWorkerKiller
72
71
  # sorted by memory ascending (smallest first, largest last)
73
72
  def set_workers
74
73
  workers = {}
75
- @master.instance_variable_get("@workers").each do |worker|
74
+ @master.instance_variable_get('@workers').each do |worker|
76
75
  workers[worker] = GetProcessMem.new(worker.pid).mb
77
76
  end
78
77
  if workers.any?
79
- @workers = Hash[ workers.sort_by {|_, mem| mem } ]
78
+ @workers = Hash[workers.sort_by { |_, mem| mem }]
80
79
  else
81
80
  {}
82
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
4
  class Reaper
3
5
  def initialize(max_ram, master = nil, reaper_status_logs = true, pre_term = nil, on_calculation = nil)
@@ -15,8 +17,9 @@ module PumaWorkerKiller
15
17
 
16
18
  def reap
17
19
  return false if @cluster.workers_stopped?
20
+
18
21
  total = get_total_memory
19
- @on_calculation.call(total) unless @on_calculation.nil?
22
+ @on_calculation&.call(total)
20
23
 
21
24
  if total > @max_ram
22
25
  @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."
@@ -29,7 +32,7 @@ module PumaWorkerKiller
29
32
  # A new request comes in, Worker B takes it, and consumes 101 mb memory
30
33
  # term_largest_worker (previously here) gets called and terms Worker B (thus not passing the about-to-be-terminated worker to `@pre_term`)
31
34
  largest_worker = @cluster.largest_worker
32
- @pre_term.call(largest_worker) unless @pre_term.nil?
35
+ @pre_term&.call(largest_worker)
33
36
  @cluster.term_worker(largest_worker)
34
37
 
35
38
  elsif @reaper_status_logs
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
4
  class RollingRestart
3
- def initialize(master = nil)
5
+ def initialize(master = nil, rolling_pre_term = nil)
4
6
  @cluster = PumaWorkerKiller::PumaMemory.new(master)
7
+ @rolling_pre_term = rolling_pre_term
5
8
  end
6
9
 
7
10
  # used for tes
@@ -9,12 +12,17 @@ module PumaWorkerKiller
9
12
  @cluster.get_total_memory
10
13
  end
11
14
 
12
- def reap(wait_between_worker_kill = 60) # seconds
15
+ def reap(seconds_between_worker_kill = 60)
16
+ # this will implicitly call set_workers
17
+ total_memory = get_total_memory
13
18
  return false unless @cluster.running?
14
- @cluster.workers.each do |worker, ram|
15
- @cluster.master.log "PumaWorkerKiller: Rolling Restart. #{@cluster.workers.count} workers consuming total: #{ get_total_memory } mb. Sending TERM to pid #{worker.pid}."
19
+
20
+ @cluster.workers.each do |worker, _ram|
21
+ @cluster.master.log "PumaWorkerKiller: Rolling Restart. #{@cluster.workers.count} workers consuming total: #{total_memory} mb. Sending TERM to pid #{worker.pid}."
22
+ @rolling_pre_term&.call(worker)
23
+
16
24
  worker.term
17
- sleep wait_between_worker_kill
25
+ sleep seconds_between_worker_kill
18
26
  end
19
27
  end
20
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
- VERSION = "0.1.1"
4
+ VERSION = '0.2.0'
3
5
  end
@@ -1,28 +1,28 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'puma_worker_killer/version'
5
6
 
6
7
  Gem::Specification.new do |gem|
7
- gem.name = "puma_worker_killer"
8
+ gem.name = 'puma_worker_killer'
8
9
  gem.version = PumaWorkerKiller::VERSION
9
- gem.authors = ["Richard Schneeman"]
10
- gem.email = ["richard.schneeman+rubygems@gmail.com"]
11
- gem.description = %q{ Kills pumas, the code kind }
12
- gem.summary = %q{ If you have a memory leak in your web code puma_worker_killer can keep it in check. }
13
- gem.homepage = "https://github.com/schneems/puma_worker_killer"
14
- gem.license = "MIT"
10
+ gem.authors = ['Richard Schneeman']
11
+ gem.email = ['richard.schneeman+rubygems@gmail.com']
12
+ gem.description = ' Kills pumas, the code kind '
13
+ gem.summary = ' If you have a memory leak in your web code puma_worker_killer can keep it in check. '
14
+ gem.homepage = 'https://github.com/schneems/puma_worker_killer'
15
+ gem.license = 'MIT'
15
16
 
16
- gem.files = `git ls-files`.split($/)
17
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
- gem.require_paths = ["lib"]
20
-
21
- gem.add_dependency "puma", ">= 2.7", "< 5"
22
- gem.add_dependency "get_process_mem", "~> 0.2"
23
- gem.add_development_dependency "rack", "~> 1.6"
24
- gem.add_development_dependency "wait_for_it", "~> 0.1"
25
- gem.add_development_dependency "rake", "~> 10.1"
26
- gem.add_development_dependency "test-unit", ">= 0"
20
+ gem.require_paths = ['lib']
27
21
 
22
+ gem.add_dependency 'get_process_mem', '~> 0.2'
23
+ gem.add_dependency 'puma', '>= 2.7', '< 5'
24
+ gem.add_development_dependency 'rack', '~> 1.6'
25
+ gem.add_development_dependency 'rake', '~> 10.1'
26
+ gem.add_development_dependency 'test-unit', '>= 0'
27
+ gem.add_development_dependency 'wait_for_it', '~> 0.1'
28
28
  end
@@ -1,4 +1,6 @@
1
- load File.expand_path("../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
2
4
 
3
5
  PumaWorkerKiller.start
4
6
 
@@ -1,4 +1,6 @@
1
- load File.expand_path("../../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('../fixture_helper.rb', __dir__)
2
4
 
3
5
  before_fork do
4
6
  require 'puma_worker_killer'
@@ -1,4 +1,6 @@
1
- load File.expand_path("../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
2
4
 
3
5
  PumaWorkerKiller.start
4
6
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'securerandom'
2
4
 
3
5
  require 'rack'
@@ -10,10 +12,10 @@ PumaWorkerKiller.config do |config|
10
12
  config.frequency = Integer(ENV['PUMA_FREQUENCY']) if ENV['PUMA_FREQUENCY']
11
13
  end
12
14
 
13
- puts "Frequency: #{ PumaWorkerKiller.frequency }" if ENV['PUMA_FREQUENCY']
15
+ puts "Frequency: #{PumaWorkerKiller.frequency}" if ENV['PUMA_FREQUENCY']
14
16
 
15
17
  class HelloWorld
16
- def response(env)
18
+ def response(_env)
17
19
  [200, {}, ['Hello World']]
18
20
  end
19
21
  end
@@ -1,7 +1,9 @@
1
- load File.expand_path("../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
2
4
 
3
5
  PumaWorkerKiller.config do |config|
4
- config.on_calculation = lambda { |usage| puts("Current memory footprint: #{usage} mb") }
6
+ config.on_calculation = ->(usage) { puts("Current memory footprint: #{usage} mb") }
5
7
  end
6
8
  PumaWorkerKiller.start
7
9
 
@@ -1,7 +1,9 @@
1
- load File.expand_path("../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
2
4
 
3
5
  PumaWorkerKiller.config do |config|
4
- config.pre_term = lambda { |worker| puts("About to terminate worker: #{worker.inspect}") }
6
+ config.pre_term = ->(worker) { puts("About to terminate worker: #{worker.inspect}") }
5
7
  end
6
8
  PumaWorkerKiller.start
7
9
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
4
+
5
+ PumaWorkerKiller.config do |config|
6
+ config.rolling_pre_term = ->(worker) { puts("About to terminate (rolling) worker: #{worker.pid}") }
7
+ end
8
+ PumaWorkerKiller.enable_rolling_restart(1) # 1 second
9
+
10
+ run HelloWorldApp
@@ -1,4 +1,6 @@
1
- load File.expand_path("../fixture_helper.rb", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
2
4
 
3
5
  PumaWorkerKiller.enable_rolling_restart(1) # 1 second
4
6
 
@@ -1,76 +1,104 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class PumaWorkerKillerTest < Test::Unit::TestCase
4
-
5
6
  def test_starts
6
7
  port = 0 # http://stackoverflow.com/questions/200484/how-do-you-find-a-free-tcp-server-port-using-ruby
7
- command = "bundle exec puma #{ fixture_path.join("default.ru") } -t 1:1 -w 2 --preload --debug -p #{ port }"
8
- options = { wait_for: "booted", timeout: 5, env: { "PUMA_FREQUENCY" => 1 } }
8
+ command = "bundle exec puma #{fixture_path.join("default.ru")} -t 1:1 -w 2 --preload --debug -p #{port}"
9
+ options = { wait_for: 'booted', timeout: 5, env: { 'PUMA_FREQUENCY' => 1 } }
9
10
 
10
11
  WaitForIt.new(command, options) do |spawn|
11
- assert_contains(spawn, "PumaWorkerKiller")
12
+ assert_contains(spawn, 'PumaWorkerKiller')
12
13
  end
13
14
  end
14
15
 
15
16
  def test_without_preload
16
17
  port = 0 # http://stackoverflow.com/questions/200484/how-do-you-find-a-free-tcp-server-port-using-ruby
17
- command = "bundle exec puma #{ fixture_path.join("default.ru") } -t 1:1 -w 2 --debug -p #{ port } -C #{ fixture_path.join("config/puma_worker_killer_start.rb") }"
18
- options = { wait_for: "booted", timeout: 10, env: { "PUMA_FREQUENCY" => 1 } }
18
+ command = "bundle exec puma #{fixture_path.join("default.ru")} -t 1:1 -w 2 --debug -p #{port} -C #{fixture_path.join("config/puma_worker_killer_start.rb")}"
19
+ options = { wait_for: 'booted', timeout: 10, env: { 'PUMA_FREQUENCY' => 1 } }
19
20
 
20
21
  WaitForIt.new(command, options) do |spawn|
21
- assert_contains(spawn, "PumaWorkerKiller")
22
+ assert_contains(spawn, 'PumaWorkerKiller')
22
23
  end
23
24
  end
24
25
 
25
26
  def test_kills_large_app
26
- file = fixture_path.join("big.ru")
27
+ file = fixture_path.join('big.ru')
27
28
  port = 0
28
- command = "bundle exec puma #{ file } -t 1:1 -w 2 --preload --debug -p #{ port }"
29
- options = { wait_for: "booted", timeout: 5, env: { "PUMA_FREQUENCY" => 1, 'PUMA_RAM' => 1} }
29
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
30
+ options = { wait_for: 'booted', timeout: 5, env: { 'PUMA_FREQUENCY' => 1, 'PUMA_RAM' => 1 } }
30
31
 
31
32
  WaitForIt.new(command, options) do |spawn|
32
- assert_contains(spawn, "Out of memory")
33
+ assert_contains(spawn, 'Out of memory')
33
34
  end
34
35
  end
35
36
 
36
37
  def test_pre_term
37
- file = fixture_path.join("pre_term.ru")
38
+ file = fixture_path.join('pre_term.ru')
38
39
  port = 0
39
- command = "bundle exec puma #{ file } -t 1:1 -w 2 --preload --debug -p #{ port }"
40
- options = { wait_for: "booted", timeout: 5, env: { "PUMA_FREQUENCY" => 1, 'PUMA_RAM' => 1} }
40
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
41
+ options = { wait_for: 'booted', timeout: 5, env: { 'PUMA_FREQUENCY' => 1, 'PUMA_RAM' => 1 } }
41
42
 
42
43
  WaitForIt.new(command, options) do |spawn|
43
- assert_contains(spawn, "Out of memory")
44
- assert_contains(spawn, "About to terminate worker:") # defined in pre_term.ru
44
+ assert_contains(spawn, 'Out of memory')
45
+ assert_contains(spawn, 'About to terminate worker:') # defined in pre_term.ru
45
46
  end
46
47
  end
47
48
 
48
49
  def test_on_calculation
49
- file = fixture_path.join("on_calculation.ru")
50
+ file = fixture_path.join('on_calculation.ru')
50
51
  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} }
52
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
53
+ options = { wait_for: 'booted', timeout: 5, env: { 'PUMA_FREQUENCY' => 1, 'PUMA_RAM' => 1 } }
53
54
 
54
55
  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
56
+ assert_contains(spawn, 'Out of memory')
57
+ assert_contains(spawn, 'Current memory footprint:') # defined in on_calculate.ru
57
58
  end
58
59
  end
59
60
 
60
61
  def assert_contains(spawn, string)
61
- assert spawn.wait(string), "Expected logs to contain '#{string}' but it did not, contents: #{ spawn.log.read }"
62
+ assert spawn.wait(string), "Expected logs to contain '#{string}' but it did not, contents: #{spawn.log.read}"
62
63
  end
63
64
 
64
65
  def test_rolling_restart
66
+ file = fixture_path.join('rolling_restart.ru')
67
+ port = 0
68
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
69
+ puts command.inspect
70
+ options = { wait_for: 'booted', timeout: 15, env: {} }
71
+
72
+ WaitForIt.new(command, options) do |spawn|
73
+ assert_contains(spawn, 'Rolling Restart')
74
+ end
75
+ end
76
+
77
+ def test_rolling_restart_worker_kill_check
78
+ file = fixture_path.join('rolling_restart.ru')
79
+ port = 0
80
+ command = "bundle exec puma #{file} -t 1:1 -w 1 --preload --debug -p #{port}"
81
+ puts command.inspect
82
+ options = { wait_for: 'booted', timeout: 120, env: {} }
83
+
84
+ WaitForIt.new(command, options) do |spawn|
85
+ # at least 2 matches for TERM (so we set a timeout value longer - 120sec)
86
+ spawn.wait!(/TERM.*TERM/m)
87
+ term_ids = spawn.log.read.scan(/TERM to pid (\d*)/)
88
+ assert term_ids.sort == term_ids.uniq.sort
89
+ end
90
+ end
65
91
 
66
- file = fixture_path.join("rolling_restart.ru")
92
+ def test_rolling_pre_term
93
+ file = fixture_path.join('rolling_pre_term.ru')
67
94
  port = 0
68
- command = "bundle exec puma #{ file } -t 1:1 -w 2 --preload --debug -p #{ port }"
95
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
69
96
  puts command.inspect
70
- options = { wait_for: "booted", timeout: 15, env: { } }
97
+ options = { wait_for: 'booted', timeout: 15, env: {} }
71
98
 
72
99
  WaitForIt.new(command, options) do |spawn|
73
- assert_contains(spawn, "Rolling Restart")
100
+ assert_contains(spawn, 'Rolling Restart')
101
+ assert_contains(spawn, 'About to terminate (rolling) worker:') # defined in rolling_pre_term.ru
74
102
  end
75
103
  end
76
104
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Bundler.require
2
4
 
3
5
  require 'puma_worker_killer'
@@ -5,5 +7,5 @@ require 'test/unit'
5
7
  require 'wait_for_it'
6
8
 
7
9
  def fixture_path
8
- Pathname.new(File.expand_path("../fixtures", __FILE__))
10
+ Pathname.new(File.expand_path('fixtures', __dir__))
9
11
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma_worker_killer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-26 00:00:00.000000000 Z
11
+ date: 2020-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: get_process_mem
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: puma
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -30,20 +44,6 @@ dependencies:
30
44
  - - "<"
31
45
  - !ruby/object:Gem::Version
32
46
  version: '5'
33
- - !ruby/object:Gem::Dependency
34
- name: get_process_mem
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0.2'
40
- type: :runtime
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '0.2'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rack
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -59,47 +59,47 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: '1.6'
61
61
  - !ruby/object:Gem::Dependency
62
- name: wait_for_it
62
+ name: rake
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0.1'
67
+ version: '10.1'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0.1'
74
+ version: '10.1'
75
75
  - !ruby/object:Gem::Dependency
76
- name: rake
76
+ name: test-unit
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '10.1'
81
+ version: '0'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - "~>"
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '10.1'
88
+ version: '0'
89
89
  - !ruby/object:Gem::Dependency
90
- name: test-unit
90
+ name: wait_for_it
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ">="
93
+ - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '0'
95
+ version: '0.1'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - ">="
100
+ - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '0'
102
+ version: '0.1'
103
103
  description: " Kills pumas, the code kind "
104
104
  email:
105
105
  - richard.schneeman+rubygems@gmail.com
@@ -107,7 +107,10 @@ executables: []
107
107
  extensions: []
108
108
  extra_rdoc_files: []
109
109
  files:
110
+ - ".github/workflows/check_changelog.yml"
110
111
  - ".gitignore"
112
+ - ".rubocop.yml"
113
+ - ".rubocop_todo.yml"
111
114
  - ".travis.yml"
112
115
  - CHANGELOG.md
113
116
  - Gemfile
@@ -126,6 +129,7 @@ files:
126
129
  - test/fixtures/fixture_helper.rb
127
130
  - test/fixtures/on_calculation.ru
128
131
  - test/fixtures/pre_term.ru
132
+ - test/fixtures/rolling_pre_term.ru
129
133
  - test/fixtures/rolling_restart.ru
130
134
  - test/puma_worker_killer_test.rb
131
135
  - test/test_helper.rb
@@ -133,7 +137,7 @@ homepage: https://github.com/schneems/puma_worker_killer
133
137
  licenses:
134
138
  - MIT
135
139
  metadata: {}
136
- post_install_message:
140
+ post_install_message:
137
141
  rdoc_options: []
138
142
  require_paths:
139
143
  - lib
@@ -148,8 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
152
  - !ruby/object:Gem::Version
149
153
  version: '0'
150
154
  requirements: []
151
- rubygems_version: 3.0.3
152
- signing_key:
155
+ rubygems_version: 3.1.2
156
+ signing_key:
153
157
  specification_version: 4
154
158
  summary: If you have a memory leak in your web code puma_worker_killer can keep it
155
159
  in check.
@@ -160,6 +164,7 @@ test_files:
160
164
  - test/fixtures/fixture_helper.rb
161
165
  - test/fixtures/on_calculation.ru
162
166
  - test/fixtures/pre_term.ru
167
+ - test/fixtures/rolling_pre_term.ru
163
168
  - test/fixtures/rolling_restart.ru
164
169
  - test/puma_worker_killer_test.rb
165
170
  - test/test_helper.rb