worker_killer 1.0.5.213977 → 1.1.0.214159
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -23
- data/lib/worker_killer/count_limiter.rb +9 -3
- data/lib/worker_killer/delayed_job_plugin.rb +2 -2
- data/lib/worker_killer/killer/passenger.rb +1 -1
- data/lib/worker_killer/killer/puma.rb +10 -42
- data/lib/worker_killer/killer.rb +2 -2
- data/lib/worker_killer/memory_limiter.rb +11 -4
- data/lib/worker_killer/puma/plugin/worker_killer.rb +5 -96
- data/lib/worker_killer/puma_plugin.rb +53 -31
- data/lib/worker_killer/version.rb +1 -1
- data/spec/count_limiter_spec.rb +20 -11
- data/spec/killer/puma_spec.rb +7 -5
- data/spec/memory_limiter_spec.rb +15 -2
- data/spec/puma_plugin_spec.rb +34 -0
- data/spec/spec_helper.rb +1 -0
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a765c4a2926183623220942f0919ad8f7d83302b7e03140fa8e2509e5639788f
|
4
|
+
data.tar.gz: e06fc24c3067f25d03ee1f43ead253330c0a7734e139d8f3678ec42cc2d59e0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a7a69c2c344967413ce45923a07e97bd265ccb6c84a14e6eb18377f3a1c8c791d32360fbc81aa1d0ff8c3e1af0da2d5120d7e003fa9a601383c7a954052de1d
|
7
|
+
data.tar.gz: 1da895a3b057fc9b896fa2a0a5d3cf670f1eaafa2b190ccaf5ee3c31080a48ecae93d795d2d46d130700fd106cf3a1a54cef6e2f0adc2b8d462938253fb623b5
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ Features:
|
|
17
17
|
|
18
18
|
- generic middleware implementation
|
19
19
|
- Phusion Passenger support(through `passenger-config detach-process <PID>`)
|
20
|
-
- Puma
|
20
|
+
- Puma support through native plugin
|
21
21
|
- DelayedJob support
|
22
22
|
- custom reaction hook
|
23
23
|
|
@@ -82,33 +82,26 @@ Add these lines to your `puma.rb` AND `application.rb`.
|
|
82
82
|
```ruby
|
83
83
|
# puma.rb
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
require 'active_support/notifications'
|
88
|
-
|
89
|
-
::ActiveSupport::Notifications.subscribe 'initialize.custom.rails' do |*eargs|
|
90
|
-
event = ActiveSupport::Notifications::Event.new(*eargs)
|
91
|
-
config = event.payload[:config]
|
92
|
-
|
93
|
-
killer = ::WorkerKiller::Killer::Puma.new
|
94
|
-
config.middleware.insert_before(
|
95
|
-
::Rack::Runtime,
|
96
|
-
::WorkerKiller::Middleware::RequestsLimiter, killer: killer, min: 3072, max: 4096
|
97
|
-
)
|
98
|
-
|
99
|
-
config.middleware.insert_before(
|
100
|
-
::Rack::Runtime,
|
101
|
-
::WorkerKiller::Middleware::OOMLimiter, killer: killer, min: nil, max: 0.5, verbose: true, check_cycle: 16
|
102
|
-
)
|
103
|
-
end
|
104
|
-
end
|
85
|
+
require 'worker_killer/puma/plugin/worker_killer'
|
86
|
+
plugin('worker_killer')
|
105
87
|
|
106
88
|
# application.rb
|
89
|
+
if defined?(::Puma::Client)
|
90
|
+
killer = ::WorkerKiller::PumaPlugin.instance.killer
|
91
|
+
|
92
|
+
config.middleware.insert_before(
|
93
|
+
Rack::Runtime,
|
94
|
+
WorkerKiller::Middleware::OOMLimiter, killer: killer, min: nil, max: 0.5, verbose: false, check_cycle: 16
|
95
|
+
)
|
107
96
|
|
108
|
-
|
97
|
+
config.middleware.insert_before(
|
98
|
+
Rack::Runtime,
|
99
|
+
WorkerKiller::Middleware::RequestsLimiter, killer: killer, min: 3000, max: 4000, verbose: false
|
100
|
+
)
|
101
|
+
end
|
109
102
|
```
|
110
103
|
|
111
|
-
This gem provides two modules: WorkerKiller::CountLimiter and WorkerKiller::MemoryLimiter, some Rack integration and
|
104
|
+
This gem provides two modules: WorkerKiller::CountLimiter and WorkerKiller::MemoryLimiter, some Rack integration, DelayedJob plugin and Puma plugin.
|
112
105
|
|
113
106
|
### WorkerKiller::Middleware::RequestsLimiter and WorkerKiller::DelayedJobPlugin::JobsLimiter
|
114
107
|
|
@@ -128,6 +121,29 @@ The memory size check is done in every `check_cycle` requests.
|
|
128
121
|
|
129
122
|
If `verbose` is set to true, then every memory size check will be shown in your logs. This logging is done at the `info` level.
|
130
123
|
|
124
|
+
### WorkerKiller::PumaPlugin
|
125
|
+
|
126
|
+
Works in Puma `Cluster Mode`. Work correctly with `phased-restart` and `fork_workers`, but has maximum effectivity (memory usage) in simple `Cluster Mode` with **preload_app!**.
|
127
|
+
|
128
|
+
Example production config:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
port ENV.fetch('PUMA_PORT', 3000).to_i
|
132
|
+
|
133
|
+
environment ENV.fetch('RAILS_ENV', 'development')
|
134
|
+
|
135
|
+
pidfile ENV.fetch('PUMA_PIDFILE', 'tmp/pids/server.pid')
|
136
|
+
|
137
|
+
threads ENV.fetch('PUMA_THREADS', 4).to_i
|
138
|
+
|
139
|
+
workers ENV.fetch('PUMA_WORKERS', 3).to_i
|
140
|
+
|
141
|
+
require 'worker_killer/puma/plugin/worker_killer'
|
142
|
+
plugin('worker_killer')
|
143
|
+
|
144
|
+
preload_app!
|
145
|
+
```
|
146
|
+
|
131
147
|
# Special Thanks
|
132
148
|
|
133
149
|
- [@hotchpotch](http://github.com/hotchpotch/) for the [original idea](https://gist.github.com/hotchpotch/1258681)
|
@@ -6,13 +6,19 @@ module WorkerKiller
|
|
6
6
|
def initialize(min: 3072, max: 4096, verbose: false)
|
7
7
|
@min = min
|
8
8
|
@max = max
|
9
|
-
@limit =
|
10
|
-
@left =
|
9
|
+
@limit = nil
|
10
|
+
@left = nil
|
11
11
|
@verbose = verbose
|
12
12
|
end
|
13
13
|
|
14
14
|
def check
|
15
|
-
|
15
|
+
# initialize limits on first check
|
16
|
+
if @limit.nil?
|
17
|
+
@limit = min + WorkerKiller.randomize(max - min + 1)
|
18
|
+
@left = @limit
|
19
|
+
end
|
20
|
+
|
21
|
+
return nil if @limit < 1
|
16
22
|
|
17
23
|
@started_at ||= Time.now
|
18
24
|
|
@@ -23,10 +23,10 @@ module WorkerKiller
|
|
23
23
|
|
24
24
|
def configure_lifecycle(lifecycle)
|
25
25
|
# Count condition after every job
|
26
|
-
lifecycle.after(:perform) do |
|
26
|
+
lifecycle.after(:perform) do |_worker, *_args|
|
27
27
|
@time_to_burn ||= limiter.check
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Stop execution only after whole loop completed
|
31
31
|
lifecycle.after(:loop) do |worker, *_args|
|
32
32
|
@time_to_burn ||= limiter.check
|
@@ -4,55 +4,23 @@ module ::WorkerKiller
|
|
4
4
|
module Killer
|
5
5
|
class Puma < ::WorkerKiller::Killer::Base
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :ipc_path, :worker_num
|
8
8
|
|
9
|
-
def initialize(
|
9
|
+
def initialize(ipc_path:, worker_num: nil, **kwargs)
|
10
10
|
super(**kwargs)
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@num = num
|
11
|
+
@ipc_path = ipc_path
|
12
|
+
@worker_num = worker_num
|
14
13
|
end
|
15
14
|
|
16
|
-
def do_kill(sig, pid, alive_sec, **
|
17
|
-
if @
|
18
|
-
do_phased_kill(sig, pid, alive_sec, **params)
|
19
|
-
elsif @type == :plugin
|
20
|
-
do_plugin_kill(sig, pid, alive_sec, **params)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def do_phased_kill(sig, pid, alive_sec, **_params)
|
25
|
-
cmd = 'pumactl phased-restart'
|
26
|
-
|
27
|
-
if sig == :KILL
|
28
|
-
logger.error "#{self} force to kill self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
29
|
-
Process.kill sig, pid
|
30
|
-
return
|
31
|
-
end
|
32
|
-
|
33
|
-
return if @already_detached
|
15
|
+
def do_kill(sig, pid, alive_sec, **_params)
|
16
|
+
return if @already_sended == sig
|
34
17
|
|
35
|
-
logger.warn "#{self}
|
36
|
-
@already_detached = true
|
37
|
-
|
38
|
-
Thread.new(cmd) do |command|
|
39
|
-
unless Kernel.system(command)
|
40
|
-
logger.warn "#{self} run #{command.inspect} failed: #{$?.inspect}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def do_plugin_kill(sig, pid, alive_sec, **_params)
|
46
|
-
if sig == :KILL
|
47
|
-
logger.error "#{self} force to kill self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
48
|
-
Process.kill sig, pid
|
49
|
-
return
|
50
|
-
end
|
18
|
+
logger.warn "#{self} send #{worker_num} to Puma Plugin (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
51
19
|
|
52
|
-
|
20
|
+
@already_sended = sig
|
53
21
|
|
54
|
-
Socket.unix(
|
55
|
-
sock.puts
|
22
|
+
Socket.unix(ipc_path) do |sock|
|
23
|
+
sock.puts Integer(worker_num).to_s
|
56
24
|
end
|
57
25
|
end
|
58
26
|
|
data/lib/worker_killer/killer.rb
CHANGED
@@ -2,7 +2,7 @@ module WorkerKiller
|
|
2
2
|
module Killer
|
3
3
|
class Base
|
4
4
|
|
5
|
-
attr_accessor :config, :kill_attempts
|
5
|
+
attr_accessor :config, :kill_attempts
|
6
6
|
|
7
7
|
def initialize(logger: nil, **_kwargs)
|
8
8
|
@logger = logger
|
@@ -27,7 +27,7 @@ module WorkerKiller
|
|
27
27
|
raise 'Not Implemented'
|
28
28
|
end
|
29
29
|
# :nocov:
|
30
|
-
|
30
|
+
|
31
31
|
def logger
|
32
32
|
@logger || WorkerKiller.configuration.logger
|
33
33
|
end
|
@@ -10,8 +10,6 @@ module WorkerKiller
|
|
10
10
|
# set static memory limits
|
11
11
|
@min = min
|
12
12
|
@max = max
|
13
|
-
@limit = @min + WorkerKiller.randomize(@max - @min + 1)
|
14
|
-
@limit_mb = (@limit / 1024 / 1024).round(1)
|
15
13
|
else
|
16
14
|
# prepare for relative memory limits
|
17
15
|
@max_percent = max
|
@@ -26,11 +24,19 @@ module WorkerKiller
|
|
26
24
|
@started_at ||= Time.now
|
27
25
|
@check_count += 1
|
28
26
|
|
27
|
+
|
29
28
|
return nil if (@check_count % @check_cycle) != 0
|
30
29
|
|
31
30
|
rss = GetProcessMem.new.bytes
|
31
|
+
|
32
32
|
# initialize relative memory limits on first check
|
33
|
-
|
33
|
+
if @limit.nil?
|
34
|
+
if min.nil?
|
35
|
+
set_limits(rss, rss + rss * @max_percent)
|
36
|
+
else
|
37
|
+
set_limits(min, min + WorkerKiller.randomize(max - min + 1))
|
38
|
+
end
|
39
|
+
end
|
34
40
|
|
35
41
|
do_check(rss)
|
36
42
|
end
|
@@ -52,7 +58,8 @@ module WorkerKiller
|
|
52
58
|
|
53
59
|
def set_limits(min, max)
|
54
60
|
@min = min
|
55
|
-
@limit =
|
61
|
+
@limit = max
|
62
|
+
@max ||= max
|
56
63
|
@limit_mb = (@limit / 1024 / 1024).round(1)
|
57
64
|
end
|
58
65
|
|
@@ -1,104 +1,13 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'active_support/notifications'
|
4
|
-
|
5
|
-
require 'worker_killer/killer'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
module WorkerKiller
|
11
|
-
module Puma
|
12
|
-
module Plugin
|
13
|
-
# Puma requires such plugin name and path :(
|
14
|
-
class WorkerKiller
|
15
|
-
|
16
|
-
def initialize(launcher, path)
|
17
|
-
@runner = launcher.instance_variable_get('@runner')
|
18
|
-
|
19
|
-
@thread = Thread.new do
|
20
|
-
Socket.unix_server_loop(path) do |sock, _client_addrinfo|
|
21
|
-
if (line = sock.gets)
|
22
|
-
worker_num = Integer(line.strip)
|
23
|
-
if (worker = find_worker(worker_num))
|
24
|
-
log "Killing worker #{worker_num}"
|
25
|
-
worker.term!
|
26
|
-
end
|
27
|
-
end
|
28
|
-
rescue StandardError => e
|
29
|
-
log("Exception: #{e.inspect}")
|
30
|
-
ensure
|
31
|
-
sock.close
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def find_worker(worker_num)
|
37
|
-
worker = @runner.worker_at(worker_num)
|
38
|
-
unless worker
|
39
|
-
log "Unknown worker index: #{worker_num.inspect}. Skipping."
|
40
|
-
return nil
|
41
|
-
end
|
42
|
-
|
43
|
-
unless worker.booted?
|
44
|
-
log "Worker #{worker_num.inspect} is not booted yet. Skipping."
|
45
|
-
return nil
|
46
|
-
end
|
47
|
-
|
48
|
-
if worker.term?
|
49
|
-
log "Worker #{worker_num.inspect} already terminating. Skipping."
|
50
|
-
return nil
|
51
|
-
end
|
52
|
-
|
53
|
-
worker
|
54
|
-
end
|
55
|
-
|
56
|
-
def log(msg)
|
57
|
-
warn("#{self.class}: #{msg}")
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
1
|
+
require 'worker_killer'
|
2
|
+
require 'worker_killer/puma_plugin'
|
64
3
|
|
65
4
|
Puma::Plugin.create do
|
66
|
-
|
67
|
-
|
68
|
-
def config(c)
|
69
|
-
c.on_worker_boot do |num|
|
70
|
-
|
71
|
-
if @killer
|
72
|
-
puts "ON OTHER WORKER BOOT: #{num}"
|
73
|
-
@killer.num = num
|
74
|
-
else
|
75
|
-
puts "ON MAIN WORKER BOOT: #{num}"
|
76
|
-
::ActiveSupport::Notifications.subscribe 'worker_killer.initialize' do |*eargs|
|
77
|
-
@killer = ::WorkerKiller::Killer::Puma.new(num: num, path: path, type: :plugin)
|
78
|
-
puts "ON WORKER BOOT INIT:#{num}"
|
79
|
-
event = ActiveSupport::Notifications::Event.new(*eargs)
|
80
|
-
config = event.payload[:config]
|
81
|
-
|
82
|
-
@register_killer&.call(self, config, @killer)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
5
|
+
def config(cfg)
|
6
|
+
::WorkerKiller::PumaPlugin.instance.config(cfg)
|
86
7
|
end
|
87
8
|
|
88
9
|
def start(launcher)
|
89
|
-
|
90
|
-
|
91
|
-
launcher.events.on_booted do
|
92
|
-
@controller ||= WorkerKiller::Puma::Plugin::WorkerKiller.new(launcher, path)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def configure(&block)
|
97
|
-
self.instance_eval(&block)
|
98
|
-
end
|
99
|
-
|
100
|
-
def register_killer(&block)
|
101
|
-
@register_killer = block
|
10
|
+
::WorkerKiller::PumaPlugin.instance.start(launcher)
|
102
11
|
end
|
103
12
|
end
|
104
13
|
|
@@ -1,56 +1,78 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
require 'worker_killer/memory_limiter'
|
2
4
|
require 'worker_killer/count_limiter'
|
3
|
-
require 'puma/plugin'
|
4
5
|
|
5
|
-
Puma::Plugin.create do
|
6
6
|
|
7
7
|
module WorkerKiller
|
8
8
|
class PumaPlugin
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
def initialize(klass:, killer:, reaction: nil, **opts)
|
13
|
-
@killer = killer
|
10
|
+
include Singleton
|
14
11
|
|
15
|
-
|
16
|
-
k.kill(l.started_at, dj: dj)
|
17
|
-
end
|
12
|
+
attr_accessor :ipc_path, :killer, :thread
|
18
13
|
|
19
|
-
|
20
|
-
@
|
14
|
+
def initialize
|
15
|
+
@ipc_path = File.join('tmp', "puma_worker_killer_#{Process.pid}.socket")
|
16
|
+
@killer = ::WorkerKiller::Killer::Puma.new(worker_num: nil, ipc_path: ipc_path)
|
17
|
+
log "Initializing IPC: #{@ipc_path}"
|
21
18
|
end
|
22
19
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def configure_lifecycle(lifecycle)
|
28
|
-
# Count condition after every job
|
29
|
-
lifecycle.after(:perform) do |worker, *_args|
|
30
|
-
@time_to_burn ||= limiter.check
|
31
|
-
end
|
32
|
-
|
33
|
-
# Stop execution only after whole loop completed
|
34
|
-
lifecycle.after(:loop) do |worker, *_args|
|
35
|
-
@time_to_burn ||= limiter.check
|
36
|
-
reaction.call(limiter, killer, worker) if @time_to_burn
|
20
|
+
def config(puma)
|
21
|
+
puma.on_worker_boot do |num|
|
22
|
+
log "Set worker_num: #{num}"
|
23
|
+
@killer.worker_num = num
|
37
24
|
end
|
38
25
|
end
|
39
26
|
|
40
|
-
|
27
|
+
def start(launcher)
|
28
|
+
@runner = launcher.instance_variable_get('@runner')
|
41
29
|
|
42
|
-
|
43
|
-
|
30
|
+
launcher.events.on_booted do
|
31
|
+
@thread ||= start_ipc_listener
|
44
32
|
end
|
33
|
+
end
|
45
34
|
|
35
|
+
def start_ipc_listener
|
36
|
+
log 'Start IPC listener'
|
37
|
+
Thread.new do
|
38
|
+
Socket.unix_server_loop(ipc_path) do |sock, *args|
|
39
|
+
if (line = sock.gets)
|
40
|
+
worker_num = Integer(line.strip)
|
41
|
+
if (worker = find_worker(worker_num))
|
42
|
+
log "Killing worker #{worker_num}"
|
43
|
+
worker.term!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue StandardError => e
|
47
|
+
log("Exception: #{e.inspect}")
|
48
|
+
ensure
|
49
|
+
sock.close
|
50
|
+
end
|
51
|
+
end
|
46
52
|
end
|
47
53
|
|
48
|
-
|
54
|
+
def find_worker(worker_num)
|
55
|
+
worker = @runner.worker_at(worker_num)
|
56
|
+
unless worker
|
57
|
+
log "Unknown worker index: #{worker_num.inspect}. Skipping."
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
|
61
|
+
unless worker.booted?
|
62
|
+
log "Worker #{worker_num.inspect} is not booted yet. Skipping."
|
63
|
+
return nil
|
64
|
+
end
|
49
65
|
|
50
|
-
|
51
|
-
|
66
|
+
if worker.term?
|
67
|
+
log "Worker #{worker_num.inspect} already terminating. Skipping."
|
68
|
+
return nil
|
52
69
|
end
|
53
70
|
|
71
|
+
worker
|
72
|
+
end
|
73
|
+
|
74
|
+
def log(msg)
|
75
|
+
warn("#{self.class}[#{Process.pid}]: #{msg}")
|
54
76
|
end
|
55
77
|
|
56
78
|
end
|
data/spec/count_limiter_spec.rb
CHANGED
@@ -4,21 +4,30 @@ RSpec.describe WorkerKiller::CountLimiter do
|
|
4
4
|
let(:max){ min + rand(100) }
|
5
5
|
let(:options){ { min: min, max: max, verbose: true } }
|
6
6
|
|
7
|
-
it { is_expected.to have_attributes(min: min, max: max, limit:
|
7
|
+
it { is_expected.to have_attributes(min: min, max: max, limit: nil, left: nil) }
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
context 'initialize limits after first check' do
|
10
|
+
before { subject.check }
|
11
|
+
|
12
|
+
it {
|
13
|
+
is_expected.to have_attributes(min: min, max: max,
|
14
|
+
limit: a_value_between(min, max), left: subject.limit - 1)
|
15
|
+
}
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
it 'expect not to react while less than limit' do
|
18
|
+
(subject.limit - 2).times do
|
19
|
+
expect(subject.check).to be_falsey
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
|
-
expect
|
21
|
-
|
23
|
+
it 'expect call reaction when check succeded' do
|
24
|
+
(subject.limit - 2).times do
|
25
|
+
expect(subject.check).to be_falsey
|
26
|
+
end
|
27
|
+
|
28
|
+
expect(subject.check).to be_truthy
|
29
|
+
expect(subject.check).to be_truthy
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
24
33
|
|
data/spec/killer/puma_spec.rb
CHANGED
@@ -6,7 +6,9 @@ RSpec.describe WorkerKiller::Killer::Puma do
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
let(:
|
9
|
+
let(:ipc_path) { '/tmp/test_ipx.sock' }
|
10
|
+
let(:killer){ described_class.new(ipc_path: ipc_path, worker_num: 99) }
|
11
|
+
let(:buffer) { StringIO.new }
|
10
12
|
|
11
13
|
describe '#kill' do
|
12
14
|
around do |example|
|
@@ -18,11 +20,11 @@ RSpec.describe WorkerKiller::Killer::Puma do
|
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'expect right signal order' do
|
21
|
-
expect(
|
22
|
-
expect(Process).
|
23
|
+
expect(Socket).to receive(:unix).with(ipc_path).and_yield(buffer).exactly(3).times
|
24
|
+
expect(Process).not_to receive(:kill)
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
killer.kill(Time.now)
|
27
|
+
expect(buffer.string.strip).to eq(99.to_s)
|
26
28
|
|
27
29
|
1.times { killer.kill(Time.now) } # 1 QUIT
|
28
30
|
2.times { killer.kill(Time.now) } # 1 TERM
|
data/spec/memory_limiter_spec.rb
CHANGED
@@ -22,7 +22,7 @@ RSpec.describe WorkerKiller::MemoryLimiter do
|
|
22
22
|
let(:min){ rand(50..100) * mb }
|
23
23
|
let(:max){ min + rand(100) * mb }
|
24
24
|
|
25
|
-
it { is_expected.to have_attributes(min: min, max: max, limit:
|
25
|
+
it { is_expected.to have_attributes(min: min, max: max, limit: nil) }
|
26
26
|
|
27
27
|
it 'expect to skip check while less than cycle count' do
|
28
28
|
expect(GetProcessMem).not_to receive(:new)
|
@@ -30,6 +30,15 @@ RSpec.describe WorkerKiller::MemoryLimiter do
|
|
30
30
|
skip_cycles(subject, check_cycle)
|
31
31
|
end
|
32
32
|
|
33
|
+
it 'expect to initialize limits after cycle count' do
|
34
|
+
expect(memory).to receive(:bytes).and_return(min)
|
35
|
+
is_expected.to have_attributes(min: min, max: max, limit: nil)
|
36
|
+
|
37
|
+
skip_cycles(subject, check_cycle)
|
38
|
+
subject.check
|
39
|
+
is_expected.to have_attributes(min: min, max: max, limit: a_value_between(min, max))
|
40
|
+
end
|
41
|
+
|
33
42
|
it 'expect to skip check after cycle count reached' do
|
34
43
|
expect(memory).to receive(:bytes).and_return(min - 1)
|
35
44
|
|
@@ -38,8 +47,12 @@ RSpec.describe WorkerKiller::MemoryLimiter do
|
|
38
47
|
end
|
39
48
|
|
40
49
|
it 'expect call reaction when check succeded' do
|
41
|
-
expect(memory).to receive(:bytes).and_return(
|
50
|
+
expect(memory).to receive(:bytes).and_return(min)
|
51
|
+
|
52
|
+
skip_cycles(subject, check_cycle)
|
53
|
+
subject.check
|
42
54
|
|
55
|
+
expect(memory).to receive(:bytes).and_return(subject.limit + 1)
|
43
56
|
skip_cycles(subject, check_cycle)
|
44
57
|
expect(subject.check).to be_truthy
|
45
58
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
RSpec.describe WorkerKiller::PumaPlugin do
|
2
|
+
subject(:plugin){ described_class.instance }
|
3
|
+
let(:puma) { double }
|
4
|
+
let(:runner){ double }
|
5
|
+
let(:events) { double }
|
6
|
+
let(:launcher){ double('@runner' => runner, 'events' => events) }
|
7
|
+
let(:worker){ double('booted?' => true, 'term?' => false) }
|
8
|
+
|
9
|
+
it {
|
10
|
+
is_expected.to have_attributes(ipc_path: /puma_worker_.*socket/,
|
11
|
+
killer: ::WorkerKiller::Killer::Puma)
|
12
|
+
}
|
13
|
+
|
14
|
+
context 'Puma initialization' do
|
15
|
+
it do
|
16
|
+
expect(puma).to receive(:on_worker_boot)
|
17
|
+
plugin.config(puma)
|
18
|
+
end
|
19
|
+
|
20
|
+
it do
|
21
|
+
launcher.instance_variable_set('@runner', runner)
|
22
|
+
|
23
|
+
expect(Socket).to receive(:unix_server_loop).with(/puma_worker_.*socket/).and_yield(StringIO.new('99'))
|
24
|
+
expect(events).to receive(:on_booted).and_yield
|
25
|
+
expect(runner).to receive(:worker_at).with(99).and_return(worker)
|
26
|
+
expect(plugin).to receive(:find_worker).with(99).and_call_original
|
27
|
+
expect(worker).to receive(:term!)
|
28
|
+
|
29
|
+
plugin.start(launcher)
|
30
|
+
plugin.thread.join
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -30,6 +30,7 @@ SimpleCov.start
|
|
30
30
|
|
31
31
|
require 'worker_killer'
|
32
32
|
require 'worker_killer/delayed_job_plugin'
|
33
|
+
require 'worker_killer/puma_plugin'
|
33
34
|
|
34
35
|
$root = File.join(File.dirname(__dir__), 'spec')
|
35
36
|
Dir[File.join(__dir__, 'support', '**', '*.rb')].sort.each {|f| require f }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: worker_killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.1.0.214159
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samoilenko Yuri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: get_process_mem
|
@@ -148,6 +148,7 @@ files:
|
|
148
148
|
- spec/killer_spec.rb
|
149
149
|
- spec/memory_limiter_spec.rb
|
150
150
|
- spec/middleware_spec.rb
|
151
|
+
- spec/puma_plugin_spec.rb
|
151
152
|
- spec/spec_helper.rb
|
152
153
|
- spec/support/logger.rb
|
153
154
|
- spec/worker_killer_spec.rb
|
@@ -175,16 +176,17 @@ signing_key:
|
|
175
176
|
specification_version: 4
|
176
177
|
summary: Kill any workers by memory and request counts or take custom reaction
|
177
178
|
test_files:
|
178
|
-
- spec/
|
179
|
+
- spec/killer/signal_spec.rb
|
180
|
+
- spec/killer/puma_spec.rb
|
181
|
+
- spec/killer/delayed_job_spec.rb
|
182
|
+
- spec/killer/passenger_spec.rb
|
183
|
+
- spec/puma_plugin_spec.rb
|
179
184
|
- spec/killer_spec.rb
|
185
|
+
- spec/count_limiter_spec.rb
|
186
|
+
- spec/worker_killer_spec.rb
|
187
|
+
- spec/support/logger.rb
|
188
|
+
- spec/middleware_spec.rb
|
180
189
|
- spec/spec_helper.rb
|
181
190
|
- spec/delayed_job_plugin/oom_limiter_spec.rb
|
182
191
|
- spec/delayed_job_plugin/jobs_limiter_spec.rb
|
183
|
-
- spec/killer/passenger_spec.rb
|
184
|
-
- spec/killer/puma_spec.rb
|
185
|
-
- spec/killer/signal_spec.rb
|
186
|
-
- spec/killer/delayed_job_spec.rb
|
187
192
|
- spec/memory_limiter_spec.rb
|
188
|
-
- spec/support/logger.rb
|
189
|
-
- spec/worker_killer_spec.rb
|
190
|
-
- spec/count_limiter_spec.rb
|