puma_worker_killer 0.0.5 → 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
- SHA1:
3
- metadata.gz: b5655d10238db0d4a493057719a99621386373a7
4
- data.tar.gz: 853a84a27a47ea134bc5812fa7a6cd4f0e2de231
2
+ SHA256:
3
+ metadata.gz: 656a49cbbcbc36e3735d20ade22027cc209213f029e51faa7f0f08c7dbcffb70
4
+ data.tar.gz: 5a328323d77f9ede2a44ab0c37577b6277a682c73ce1ad0f1bf167efb720a62d
5
5
  SHA512:
6
- metadata.gz: 8e43e8ea674c6afafa5e693ffbcf12cb07785ee1176e3b33e30f00faa536fe277903782138172a285b4e44dc264e033358b6693308d84b3dca6bc6159130b687
7
- data.tar.gz: 7f2575bbde18a5250049b7af098902bed20baf08ac5e7bcbb86443b3b2d3adb6c17a3a5acaa14cbd0c8ec7380da8ef3492fa347132e7b33fb197893f9a030168
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,14 +1,22 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.0
3
+ - 2.3.8
4
+ - 2.4.5
5
+ - 2.5.3
6
+ - 2.6.0
6
7
  - ruby-head
7
- - jruby-19mode
8
- - rbx-19mode
8
+ - rbx
9
+ before_install:
10
+ - gem install bundler -v 1.12.5
9
11
 
10
12
  matrix:
11
13
  allow_failures:
12
14
  - rvm: ruby-head
13
- - rvm: rbx-19mode
14
- - rvm: jruby-19mode
15
+ - rvm: rbx
16
+
17
+ install:
18
+ - bundle install
19
+
20
+ script:
21
+ - bundle exec rubocop
22
+ - bundle exec rake test
@@ -1,3 +1,28 @@
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
+
10
+ ## 0.1.1
11
+
12
+ - Allow PWK to be used with Puma 4 (#72)
13
+
14
+ ## 0.1.0
15
+
16
+ - Emit extra data via `pre_term` callback before puma worker killer terminates a worker #49.
17
+
18
+ ## 0.0.7
19
+
20
+ - Logging is configurable #41
21
+
22
+ ## 0.0.6
23
+
24
+ - Log PID of worker insead of inspecting the worker #33
25
+
1
26
  ## 0.0.5
2
27
 
3
28
  - Support for Puma 3.x
@@ -9,3 +34,4 @@
9
34
  ## 0.0.3
10
35
 
11
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
@@ -1,7 +1,24 @@
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
 
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.
5
22
 
6
23
  ## What
7
24
 
@@ -9,7 +26,7 @@ If you have a memory leak in your code, finding and plugging it can be a hercule
9
26
 
10
27
  Puma worker killer can only function if you have enabled cluster mode or hybrid mode (threads + worker cluster). If you are only using threads (and not workers) then puma worker killer cannot help keep your memory in control.
11
28
 
12
- BTW restarting your processes to controll memory is like putting a bandaid on a gunshot wound, try figuring out the reason you're seeing so much memory bloat [derailed benchmarks](https://github.com/schneems/derailed_benchmarks) can help.
29
+ BTW restarting your processes to control memory is like putting a bandaid on a gunshot wound, try figuring out the reason you're seeing so much memory bloat [derailed benchmarks](https://github.com/schneems/derailed_benchmarks) can help.
13
30
 
14
31
 
15
32
  ## Install
@@ -22,21 +39,69 @@ gem 'puma_worker_killer'
22
39
 
23
40
  Then run `$ bundle install`
24
41
 
25
- <!--
26
- ## Use
42
+ ## Turn on Rolling Restarts - Heroku Mode
27
43
 
28
- > 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!
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:
29
45
 
30
- -->
46
+ ```ruby
47
+ # config/puma.rb
48
+
49
+ before_fork do
50
+ require 'puma_worker_killer'
31
51
 
32
- Somewhere in your main process run this code:
52
+ PumaWorkerKiller.enable_rolling_restart # Default is every 6 hours
53
+ end
54
+
55
+ ```
56
+
57
+ or you can pass in the restart frequency:
33
58
 
34
59
  ```ruby
35
- # config/initializers/puma_worker_killer.rb
36
- PumaWorkerKiller.start
60
+ PumaWorkerKiller.enable_rolling_restart(12 * 3600) # 12 hours in seconds
61
+ ```
62
+
63
+ Make sure if you do this to not accidentally call `PumaWorkerKiller.start` as well.
64
+
65
+ ## Enable Worker Killing
66
+
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`:
68
+
69
+ ```ruby
70
+ # config/puma.rb
71
+
72
+ before_fork do
73
+ require 'puma_worker_killer'
74
+
75
+ PumaWorkerKiller.start
76
+ end
37
77
  ```
38
78
 
39
- 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.
79
+ 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.
80
+
81
+ ## Troubleshooting
82
+
83
+ When you boot your program locally you should see debug output:
84
+
85
+ ```
86
+ [77773] Puma starting in cluster mode...
87
+ [77773] * Version 3.1.0 (ruby 2.3.1-p112), codename: El Niño Winter Wonderland
88
+ [77773] * Min threads: 0, max threads: 16
89
+ [77773] * Environment: development
90
+ [77773] * Process workers: 2
91
+ [77773] * Phased restart available
92
+ [77773] * Listening on tcp://0.0.0.0:9292
93
+ [77773] Use Ctrl-C to stop
94
+ [77773] PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.
95
+ ```
96
+
97
+ If you don't see any `PumaWorkerKiller` output, make sure that you are running with multiple workers. PWK only functions if you have workers enabled, you should see something like this when Puma boots:
98
+
99
+ ```
100
+ [77773] * Process workers: 2
101
+ ```
102
+
103
+ If you've configured PWK's frequency try reducing it to a very low value
104
+
40
105
 
41
106
  ## Configure
42
107
 
@@ -47,21 +112,63 @@ PumaWorkerKiller.config do |config|
47
112
  config.ram = 1024 # mb
48
113
  config.frequency = 5 # seconds
49
114
  config.percent_usage = 0.98
50
- config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
115
+ config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
116
+ config.reaper_status_logs = true # setting this to false will not log lines like:
117
+ # PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.
118
+
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" }
51
121
  end
52
122
  PumaWorkerKiller.start
53
123
  ```
54
124
 
55
- ## Attention
56
- If you start puma as a daemon, to add puma worker killer config into puma config file, rather than into initializers:
57
- Sample like this: (in puma.rb file)
125
+ ### pre_term
126
+
127
+ `config.pre_term` will be called just prior to worker termination with the worker that is about to be terminated. This may be useful to use in keeping track of metrics, time of day workers are restarted, etc.
128
+
129
+ By default Puma Worker Killer will emit a log when a worker is being killed
130
+
131
+ ```
132
+ PumaWorkerKiller: Out of memory. 5 workers consuming total: 500 mb out of max: 450 mb. Sending TERM to pid 23 consuming 53 mb.
133
+ ```
134
+
135
+ or
136
+
137
+ ```
138
+ PumaWorkerKiller: Rolling Restart. 5 workers consuming total: 650mb mb. Sending TERM to pid 34.
139
+ ```
140
+
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.
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
+
154
+ ### on_calculation
155
+
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.
157
+
158
+ This callback lambda is given a single value for the amount of memory used.
159
+
160
+ ## Attention
161
+
162
+ If you start puma as a daemon, to add puma worker killer config into puma config file, rather than into initializers:
163
+ Sample like this: (in `config/puma.rb` file):
164
+
58
165
  ```ruby
59
166
  before_fork do
60
167
  PumaWorkerKiller.config do |config|
61
168
  config.ram = 1024 # mb
62
169
  config.frequency = 5 # seconds
63
170
  config.percent_usage = 0.98
64
- config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
171
+ config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
65
172
  end
66
173
  PumaWorkerKiller.start
67
174
  end
@@ -88,32 +195,23 @@ PumaWorkerKiller.frequency = 20 # seconds
88
195
  You may want to periodically restart all of your workers rather than simply killing your largest. To do that set:
89
196
 
90
197
  ```ruby
91
- PumaWorkerKiller.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
198
+ PumaWorkerKiller.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds, or 12.hours if using Rails
92
199
  ```
93
200
 
94
- By default PumaWorkerKiller will perform a rolling restart of all your worker processes every 12 hours. To disable, set to `false`.
201
+ By default PumaWorkerKiller will perform a rolling restart of all your worker processes every 6 hours. To disable, set to `false`.
95
202
 
96
- ## Only turn on Rolling Restarts
97
-
98
- If you're running on a platform like [Heroku where it is difficult to measure RAM from inside of a container accurately](https://github.com/schneems/get_process_mem/issues/7), you may want to disable the "worker killer" functionality and only use the rolling restart. You can do that by running:
99
-
100
- ```ruby
101
- PumaWorkerKiller.enable_rolling_restart
102
- ```
103
-
104
- or you can pass in the restart frequency
203
+ You may want to hide the following log lines: `PumaWorkerKiller: Consuming 54.34765625 mb with master and 2 workers.`. To do that set:
105
204
 
106
205
  ```ruby
107
- PumaWorkerKiller.enable_rolling_restart(12 * 3600) # 12 hours in seconds
206
+ PumaWorkerKiller.reaper_status_logs = false
108
207
  ```
109
208
 
110
- Make sure if you do this to not accidentally call `PumaWorkerKiller.start` as well.
209
+ Note: It is `true` by default.
111
210
 
112
211
  ## License
113
212
 
114
213
  MIT
115
214
 
116
-
117
215
  ## Feedback
118
216
 
119
217
  Open up an issue or ping me on twitter [@schneems](http://twitter.com/schneems).
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,20 +1,28 @@
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
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
- self.rolling_restart_frequency = 6 * 3600
14
+ self.rolling_restart_frequency = 6 * 3600 # 6 hours in seconds
15
+ self.reaper_status_logs = true
16
+ self.pre_term = nil
17
+ self.rolling_pre_term = nil
18
+ self.on_calculation = nil
11
19
 
12
20
  def config
13
21
  yield self
14
22
  end
15
23
 
16
- def reaper(ram = self.ram, percent = self.percent_usage)
17
- Reaper.new(ram * percent_usage)
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)
25
+ Reaper.new(ram * percent_usage, nil, reaper_status_logs, pre_term, on_calculation)
18
26
  end
19
27
 
20
28
  def start(frequency = self.frequency, reaper = self.reaper)
@@ -22,9 +30,9 @@ module PumaWorkerKiller
22
30
  enable_rolling_restart(rolling_restart_frequency) if rolling_restart_frequency
23
31
  end
24
32
 
25
- def enable_rolling_restart(frequency = self.rolling_restart_frequency)
26
- frequency = frequency + rand(0..10.0) # so all workers don't restart at the exact same time across multiple machines
27
- 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
28
36
  end
29
37
  end
30
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)
@@ -11,11 +13,10 @@ module PumaWorkerKiller
11
13
 
12
14
  Thread.new do
13
15
  while @running
14
- @reaper.reap
15
16
  sleep @timeout
17
+ @reaper.reap
16
18
  end
17
19
  end
18
20
  end
19
-
20
21
  end
21
22
  end
@@ -1,21 +1,28 @@
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
13
14
  end
14
15
 
16
+ def term_worker(worker)
17
+ worker.term
18
+ end
19
+
15
20
  def term_largest_worker
16
21
  largest_worker.term
17
- # Process.wait(largest_worker.pid)
18
- # rescue Errno::ECHILD
22
+ end
23
+
24
+ def workers_stopped?
25
+ !running?
19
26
  end
20
27
 
21
28
  def running?
@@ -23,7 +30,7 @@ module PumaWorkerKiller
23
30
  end
24
31
 
25
32
  def smallest_worker
26
- smallest, _ = workers.to_a.first
33
+ smallest, = workers.to_a.first
27
34
  smallest
28
35
  end
29
36
 
@@ -33,7 +40,7 @@ module PumaWorkerKiller
33
40
  end
34
41
 
35
42
  def largest_worker
36
- largest_worker, _ = workers.to_a.last
43
+ largest_worker, = workers.to_a.last
37
44
  largest_worker
38
45
  end
39
46
 
@@ -45,10 +52,10 @@ module PumaWorkerKiller
45
52
  # Will refresh @workers
46
53
  def get_total(workers = set_workers)
47
54
  master_memory = GetProcessMem.new(Process.pid).mb
48
- worker_memory = workers.map {|_, mem| mem }.inject(&:+) || 0
55
+ worker_memory = workers.values.inject(:+) || 0
49
56
  worker_memory + master_memory
50
57
  end
51
- alias :get_total_memory :get_total
58
+ alias get_total_memory get_total
52
59
 
53
60
  def workers
54
61
  @workers || set_workers
@@ -64,14 +71,14 @@ module PumaWorkerKiller
64
71
  # sorted by memory ascending (smallest first, largest last)
65
72
  def set_workers
66
73
  workers = {}
67
- @master.instance_variable_get("@workers").each do |worker|
74
+ @master.instance_variable_get('@workers').each do |worker|
68
75
  workers[worker] = GetProcessMem.new(worker.pid).mb
69
76
  end
70
77
  if workers.any?
71
- @workers = Hash[ workers.sort_by {|_, mem| mem } ]
78
+ @workers = Hash[workers.sort_by { |_, mem| mem }]
72
79
  else
73
80
  {}
74
81
  end
75
82
  end
76
83
  end
77
- end
84
+ end
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PumaWorkerKiller
2
4
  class Reaper
3
- def initialize(max_ram, master = nil)
5
+ def initialize(max_ram, master = nil, reaper_status_logs = true, pre_term = nil, on_calculation = nil)
4
6
  @cluster = PumaWorkerKiller::PumaMemory.new(master)
5
7
  @max_ram = max_ram
8
+ @reaper_status_logs = reaper_status_logs
9
+ @pre_term = pre_term
10
+ @on_calculation = on_calculation
6
11
  end
7
12
 
8
13
  # used for tes
@@ -11,12 +16,27 @@ module PumaWorkerKiller
11
16
  end
12
17
 
13
18
  def reap
14
- return false unless @cluster.running?
15
- if (total = get_total_memory) > @max_ram
16
- @cluster.master.log "PumaWorkerKiller: Out of memory. #{@cluster.workers.count} workers consuming total: #{total} mb out of max: #{@max_ram} mb. Sending TERM to #{@cluster.largest_worker.inspect} consuming #{@cluster.largest_worker_memory} mb."
17
- @cluster.term_largest_worker
18
- else
19
- @cluster.master.log "PumaWorkerKiller: Consuming #{total} mb with master and #{@cluster.workers.count} workers"
19
+ return false if @cluster.workers_stopped?
20
+
21
+ total = get_total_memory
22
+ @on_calculation&.call(total)
23
+
24
+ if total > @max_ram
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."
26
+
27
+ # Fetch the largest_worker so that both `@pre_term` and `term_worker` are called with the same worker
28
+ # Avoids a race condition where:
29
+ # Worker A consume 100 mb memory
30
+ # Worker B consume 99 mb memory
31
+ # pre_term gets called with Worker A
32
+ # A new request comes in, Worker B takes it, and consumes 101 mb memory
33
+ # term_largest_worker (previously here) gets called and terms Worker B (thus not passing the about-to-be-terminated worker to `@pre_term`)
34
+ largest_worker = @cluster.largest_worker
35
+ @pre_term&.call(largest_worker)
36
+ @cluster.term_worker(largest_worker)
37
+
38
+ elsif @reaper_status_logs
39
+ @cluster.master.log "PumaWorkerKiller: Consuming #{total} mb with master and #{@cluster.workers.count} workers."
20
40
  end
21
41
  end
22
42
  end
@@ -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 out of max: #{@max_ram} mb. Sending TERM to #{worker.inspect}"
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.0.5"
4
+ VERSION = '0.2.0'
3
5
  end
@@ -1,27 +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", "< 4"
22
- gem.add_dependency "get_process_mem", "~> 0.2"
23
- gem.add_development_dependency "rack", "~> 1.6"
24
- gem.add_development_dependency "rake", "~> 10.1"
25
- gem.add_development_dependency "test-unit", ">= 0"
20
+ gem.require_paths = ['lib']
26
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'
27
28
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
4
+
5
+ PumaWorkerKiller.start
6
+
7
+ @memory = []
8
+ 10_000.times.each do
9
+ @memory << SecureRandom.hex
10
+ end
11
+
12
+ run HelloWorldApp
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('../fixture_helper.rb', __dir__)
4
+
5
+ before_fork do
6
+ require 'puma_worker_killer'
7
+ PumaWorkerKiller.start
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
4
+
5
+ PumaWorkerKiller.start
6
+
7
+ run HelloWorldApp
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
1
5
  require 'rack'
2
6
  require 'rack/server'
3
7
 
@@ -7,21 +11,17 @@ PumaWorkerKiller.config do |config|
7
11
  config.ram = Integer(ENV['PUMA_RAM']) if ENV['PUMA_RAM']
8
12
  config.frequency = Integer(ENV['PUMA_FREQUENCY']) if ENV['PUMA_FREQUENCY']
9
13
  end
10
- PumaWorkerKiller.start
11
-
12
14
 
13
15
  puts "Frequency: #{PumaWorkerKiller.frequency}" if ENV['PUMA_FREQUENCY']
14
16
 
15
17
  class HelloWorld
16
- def response
18
+ def response(_env)
17
19
  [200, {}, ['Hello World']]
18
20
  end
19
21
  end
20
22
 
21
23
  class HelloWorldApp
22
24
  def self.call(env)
23
- HelloWorld.new.response
25
+ HelloWorld.new.response(env)
24
26
  end
25
27
  end
26
-
27
- run HelloWorldApp
@@ -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.on_calculation = ->(usage) { puts("Current memory footprint: #{usage} mb") }
7
+ end
8
+ PumaWorkerKiller.start
9
+
10
+ run HelloWorldApp
@@ -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.pre_term = ->(worker) { puts("About to terminate worker: #{worker.inspect}") }
7
+ end
8
+ PumaWorkerKiller.start
9
+
10
+ run HelloWorldApp
@@ -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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('fixture_helper.rb', __dir__)
4
+
5
+ PumaWorkerKiller.enable_rolling_restart(1) # 1 second
6
+
7
+ run HelloWorldApp
@@ -1,77 +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
- app_path = fixture_path.join("app.ru")
7
7
  port = 0 # http://stackoverflow.com/questions/200484/how-do-you-find-a-free-tcp-server-port-using-ruby
8
- puma_log = Pathname.new "#{ SecureRandom.hex }-puma.log"
9
- pid = Process.spawn("PUMA_FREQUENCY=1 bundle exec puma #{ app_path } -t 1:1 -w 5 --preload --debug -p #{ port } > #{puma_log}")
10
- sleep 5
11
- assert_match "PumaWorkerKiller:", puma_log.read
12
- ensure
13
- puma_log.delete
14
- Process.kill('TERM', pid) if pid
15
- end
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 } }
16
10
 
17
- def test_worker_reaped
18
- ram = 1 #mb
19
- cluster = FakeCluster.new
20
- reaper = PumaWorkerKiller::Reaper.new(ram, cluster)
21
- worker_count = 10
22
- worker_count.times { cluster.add_worker }
11
+ WaitForIt.new(command, options) do |spawn|
12
+ assert_contains(spawn, 'PumaWorkerKiller')
13
+ end
14
+ end
23
15
 
24
- assert_equal worker_count, cluster.workers.count
25
- refute cluster.workers.detect {|w| w.is_term? }
16
+ def test_without_preload
17
+ port = 0 # http://stackoverflow.com/questions/200484/how-do-you-find-a-free-tcp-server-port-using-ruby
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 } }
26
20
 
27
- reaper.reap
28
- assert_equal 1, cluster.workers.select {|w| w.is_term? }.count
21
+ WaitForIt.new(command, options) do |spawn|
22
+ assert_contains(spawn, 'PumaWorkerKiller')
23
+ end
24
+ end
29
25
 
30
- reaper.reap
31
- assert_equal 2, cluster.workers.select {|w| w.is_term? }.count
26
+ def test_kills_large_app
27
+ file = fixture_path.join('big.ru')
28
+ port = 0
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 } }
32
31
 
33
- reaper.reap
34
- assert_equal 3, cluster.workers.select {|w| w.is_term? }.count
35
- ensure
36
- cluster.workers.map(&:term)
32
+ WaitForIt.new(command, options) do |spawn|
33
+ assert_contains(spawn, 'Out of memory')
34
+ end
37
35
  end
38
36
 
39
- def test_kills_memory_leak
40
- ram = rand(75..100) #mb
41
- cluster = FakeCluster.new
42
- reaper = PumaWorkerKiller::Reaper.new(ram, cluster)
43
- while reaper.get_total_memory < (ram * 0.80)
44
- cluster.add_worker
45
- sleep 0.01
37
+ def test_pre_term
38
+ file = fixture_path.join('pre_term.ru')
39
+ port = 0
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 } }
42
+
43
+ WaitForIt.new(command, options) do |spawn|
44
+ assert_contains(spawn, 'Out of memory')
45
+ assert_contains(spawn, 'About to terminate worker:') # defined in pre_term.ru
46
46
  end
47
+ end
47
48
 
48
- reaper.reap
49
- assert_equal 0, cluster.workers.select {|w| w.is_term? }.count
49
+ def test_on_calculation
50
+ file = fixture_path.join('on_calculation.ru')
51
+ port = 0
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 } }
50
54
 
51
- until reaper.get_total_memory > ram
52
- cluster.add_worker
53
- sleep 0.01
55
+ WaitForIt.new(command, options) do |spawn|
56
+ assert_contains(spawn, 'Out of memory')
57
+ assert_contains(spawn, 'Current memory footprint:') # defined in on_calculate.ru
54
58
  end
55
-
56
- reaper.reap
57
- assert_equal 1, cluster.workers.select {|w| w.is_term? }.count
58
- ensure
59
- cluster.workers.map(&:term)
60
59
  end
61
60
 
61
+ def assert_contains(spawn, string)
62
+ assert spawn.wait(string), "Expected logs to contain '#{string}' but it did not, contents: #{spawn.log.read}"
63
+ end
62
64
 
63
65
  def test_rolling_restart
64
- ram = rand(75..100) #mb
65
- cluster = FakeCluster.new
66
- cluster.add_worker
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
67
76
 
68
- worker = cluster.workers.first
69
- reaper = PumaWorkerKiller::RollingRestart.new(cluster)
70
- reaper.reap(1)
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
91
+
92
+ def test_rolling_pre_term
93
+ file = fixture_path.join('rolling_pre_term.ru')
94
+ port = 0
95
+ command = "bundle exec puma #{file} -t 1:1 -w 2 --preload --debug -p #{port}"
96
+ puts command.inspect
97
+ options = { wait_for: 'booted', timeout: 15, env: {} }
71
98
 
72
- assert_equal 1, cluster.workers.select {|w| w.is_term? }.count
73
- ensure
74
- cluster.workers.map(&:term)
99
+ WaitForIt.new(command, options) do |spawn|
100
+ assert_contains(spawn, 'Rolling Restart')
101
+ assert_contains(spawn, 'About to terminate (rolling) worker:') # defined in rolling_pre_term.ru
102
+ end
75
103
  end
76
104
  end
77
-
@@ -1,71 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Bundler.require
2
4
 
3
5
  require 'puma_worker_killer'
4
6
  require 'test/unit'
7
+ require 'wait_for_it'
5
8
 
6
9
  def fixture_path
7
- Pathname.new(File.expand_path("../fixtures", __FILE__))
8
- end
9
-
10
- # Mock object stand in for Puma::Cluster
11
- class FakeCluster
12
- def initialize
13
- @workers = []
14
- end
15
-
16
- def wakeup!
17
- end
18
-
19
- class Worker
20
- attr_accessor :pid
21
-
22
- def initialize(pid)
23
- @pid = pid
24
- end
25
-
26
- def memory_leak
27
- while true
28
-
29
- end
30
- end
31
-
32
- # not public interface, used for testing
33
- def is_term?
34
- @first_term_sent
35
- end
36
-
37
- def term
38
- begin
39
- if @first_term_sent && (Time.new - @first_term_sent) > 30
40
- @signal = "KILL"
41
- else
42
- @first_term_sent ||= Time.new
43
- end
44
-
45
- Process.kill "TERM", @pid
46
- Process.wait(@pid)
47
- rescue Errno::ESRCH, Errno::ECHILD
48
- end
49
- end
50
- end
51
-
52
- def log(msg)
53
- puts msg
54
- end
55
-
56
- def do_work
57
- while true
58
- sleep 1
59
- end
60
- end
61
-
62
- # not a public interface, added to make testing easier
63
- def workers
64
- @workers
65
- end
66
-
67
- def add_worker
68
- pid = fork { do_work }
69
- @workers << Worker.new(pid)
70
- end
10
+ Pathname.new(File.expand_path('fixtures', __dir__))
71
11
  end
metadata CHANGED
@@ -1,49 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma_worker_killer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
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: 2016-03-02 00:00:00.000000000 Z
11
+ date: 2020-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: puma
14
+ name: get_process_mem
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '2.7'
20
- - - "<"
17
+ - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: '4'
19
+ version: '0.2'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: '2.7'
30
- - - "<"
24
+ - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: '4'
26
+ version: '0.2'
33
27
  - !ruby/object:Gem::Dependency
34
- name: get_process_mem
28
+ name: puma
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
31
+ - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: '0.2'
33
+ version: '2.7'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '5'
40
37
  type: :runtime
41
38
  prerelease: false
42
39
  version_requirements: !ruby/object:Gem::Requirement
43
40
  requirements:
44
- - - "~>"
41
+ - - ">="
45
42
  - !ruby/object:Gem::Version
46
- version: '0.2'
43
+ version: '2.7'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '5'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rack
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +86,20 @@ dependencies:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: wait_for_it
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.1'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.1'
89
103
  description: " Kills pumas, the code kind "
90
104
  email:
91
105
  - richard.schneeman+rubygems@gmail.com
@@ -93,7 +107,10 @@ executables: []
93
107
  extensions: []
94
108
  extra_rdoc_files: []
95
109
  files:
110
+ - ".github/workflows/check_changelog.yml"
96
111
  - ".gitignore"
112
+ - ".rubocop.yml"
113
+ - ".rubocop_todo.yml"
97
114
  - ".travis.yml"
98
115
  - CHANGELOG.md
99
116
  - Gemfile
@@ -106,14 +123,21 @@ files:
106
123
  - lib/puma_worker_killer/rolling_restart.rb
107
124
  - lib/puma_worker_killer/version.rb
108
125
  - puma_worker_killer.gemspec
109
- - test/fixtures/app.ru
126
+ - test/fixtures/big.ru
127
+ - test/fixtures/config/puma_worker_killer_start.rb
128
+ - test/fixtures/default.ru
129
+ - test/fixtures/fixture_helper.rb
130
+ - test/fixtures/on_calculation.ru
131
+ - test/fixtures/pre_term.ru
132
+ - test/fixtures/rolling_pre_term.ru
133
+ - test/fixtures/rolling_restart.ru
110
134
  - test/puma_worker_killer_test.rb
111
135
  - test/test_helper.rb
112
136
  homepage: https://github.com/schneems/puma_worker_killer
113
137
  licenses:
114
138
  - MIT
115
139
  metadata: {}
116
- post_install_message:
140
+ post_install_message:
117
141
  rdoc_options: []
118
142
  require_paths:
119
143
  - lib
@@ -128,13 +152,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
152
  - !ruby/object:Gem::Version
129
153
  version: '0'
130
154
  requirements: []
131
- rubyforge_project:
132
- rubygems_version: 2.5.1
133
- signing_key:
155
+ rubygems_version: 3.1.2
156
+ signing_key:
134
157
  specification_version: 4
135
158
  summary: If you have a memory leak in your web code puma_worker_killer can keep it
136
159
  in check.
137
160
  test_files:
138
- - test/fixtures/app.ru
161
+ - test/fixtures/big.ru
162
+ - test/fixtures/config/puma_worker_killer_start.rb
163
+ - test/fixtures/default.ru
164
+ - test/fixtures/fixture_helper.rb
165
+ - test/fixtures/on_calculation.ru
166
+ - test/fixtures/pre_term.ru
167
+ - test/fixtures/rolling_pre_term.ru
168
+ - test/fixtures/rolling_restart.ru
139
169
  - test/puma_worker_killer_test.rb
140
170
  - test/test_helper.rb