worker_killer 1.0.5.213977 → 1.1.0.214146

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: 2ac9af870733f50ff006c5a31d9c3030342d76ff3208b8e4193a2407ad8f264c
4
- data.tar.gz: c222a1cada623f95f8668e61d15e4f32fa986fdf799572e1937365919bc651d4
3
+ metadata.gz: eabc2f6b314a0e9a0e81a078ac3954c5355e24017373fad26f4de4fbeea2f00f
4
+ data.tar.gz: deb1ebcf899be03574b4611aee0b3f8e08f4c6f57e5babddeaa2bc5d20f70ed7
5
5
  SHA512:
6
- metadata.gz: 88918e5de623c64691e96fbfc4f34b6eb19669e3f370be011eed996ef6e9a13aee61997b66adfae929e39cb685396557c3802d9a00dcb0ebef0d54e5cf1193fb
7
- data.tar.gz: bdb4e2fafb1da70554c3b73284538695bfd918b1b516a9a24f477b02056d721b69745f4ecfe6bfde044fb90815fa55f7178c08c33d9cfd283420754d0175ee69
6
+ metadata.gz: 80da7063d7ab1d3ce70baf42414e7dc6aea1310d85285cc10c95c9a09d980b0b8db98438985ffe422e82b20c4aab73ea05c717924dac47442c4aed1f257a1fae
7
+ data.tar.gz: 0e40fd946bb0a0f5e4cdb36e469738c195c555e4495e379f943a90fb336166f4f56e09a7a60ffafea782a597979db79d21123ded769c4412843d8f05397aa12d
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 phased-restart support(through `pumactl phased-restart`)
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
- on_worker_boot do |*args|
86
- require 'worker_killer/killer'
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
- ActiveSupport::Notifications.instrument 'initialize.custom.rails', config: config
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 DelayedJob plugin.
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 = @min + WorkerKiller.randomize(@max - @min + 1)
10
- @left = @limit
9
+ @limit = nil
10
+ @left = nil
11
11
  @verbose = verbose
12
12
  end
13
13
 
14
14
  def check
15
- return nil if @limit <= 1
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 |worker, *_args|
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
@@ -42,7 +42,7 @@ module WorkerKiller
42
42
  end
43
43
 
44
44
  def self.check_passenger_config! path
45
- if path = check_passenger_config(path)
45
+ if (path = check_passenger_config(path))
46
46
  path
47
47
  else
48
48
  raise "Can't find passenger config at #{path.inspect}"
@@ -4,55 +4,25 @@ module ::WorkerKiller
4
4
  module Killer
5
5
  class Puma < ::WorkerKiller::Killer::Base
6
6
 
7
- attr_accessor :type, :plugin_path, :num
7
+ attr_accessor :ipc_path, :worker_num
8
8
 
9
- def initialize(type: :phased, path: nil, num: nil, **kwargs)
9
+ def initialize(ipc_path:, worker_num: nil, **kwargs)
10
10
  super(**kwargs)
11
- @type = type
12
- @plugin_path = path
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, **params)
17
- if @type == :phased
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
34
-
35
- logger.warn "#{self} run #{cmd.inspect} (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
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)
15
+ def do_kill(sig, pid, alive_sec, **_params)
46
16
  if sig == :KILL
47
- logger.error "#{self} force to kill self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
17
+ logger.error "#{self} force to kill self[#{worker_num}] (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
48
18
  Process.kill sig, pid
49
19
  return
50
20
  end
51
21
 
52
- logger.warn "#{self} send #{num} to Puma Plugin (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
22
+ logger.warn "#{self} send #{worker_num} to Puma Plugin (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
53
23
 
54
- Socket.unix(plugin_path) do |sock|
55
- sock.puts num.to_s
24
+ Socket.unix(ipc_path) do |sock|
25
+ sock.puts Integer(worker_num).to_s
56
26
  end
57
27
  end
58
28
 
@@ -2,7 +2,7 @@ module WorkerKiller
2
2
  module Killer
3
3
  class Base
4
4
 
5
- attr_accessor :config, :kill_attempts, :logger
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
- set_limits(rss, rss + rss * @max_percent) if min.nil?
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 = @max = max
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 'socket'
2
- require 'puma/plugin'
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
- attr_reader :path
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
- @path = File.join('tmp', "puma_worker_killer_#{Process.pid}.socket")
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
- attr_reader :limiter, :killer, :reaction
11
-
12
- def initialize(klass:, killer:, reaction: nil, **opts)
13
- @killer = killer
10
+ include Singleton
14
11
 
15
- @reaction = reaction || proc do |l, k, dj|
16
- k.kill(l.started_at, dj: dj)
17
- end
12
+ attr_accessor :ipc_path, :killer, :thread
18
13
 
19
- @limiter = klass.new(**opts)
20
- @time_to_burn = false
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 new(lifecycle = Delayed::Worker.lifecycle, *_args)
24
- configure_lifecycle(lifecycle)
25
- end
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
- class JobsLimiter < ::WorkerKiller::PumaPlugin
27
+ def start(launcher)
28
+ @runner = launcher.instance_variable_get('@runner')
41
29
 
42
- def initialize(**opts)
43
- super(klass: ::WorkerKiller::CountLimiter, **opts)
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
- class OOMLimiter < ::WorkerKiller::PumaPlugin
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
- def initialize(**opts)
51
- super(klass: ::WorkerKiller::MemoryLimiter, **opts)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module WorkerKiller
4
4
 
5
- VERSION = '1.0.5'
5
+ VERSION = '1.1.0'
6
6
 
7
7
  end
8
8
 
@@ -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: a_value_between(min, max), left: subject.limit) }
7
+ it { is_expected.to have_attributes(min: min, max: max, limit: nil, left: nil) }
8
8
 
9
- it 'expect not to react while less than limit' do
10
- (subject.limit - 1).times do
11
- expect(subject.check).to be_falsey
12
- end
13
- end
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
- it 'expect call reaction when check succeded' do
16
- (subject.limit - 1).times do
17
- expect(subject.check).to be_falsey
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(subject.check).to be_truthy
21
- expect(subject.check).to be_truthy
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
 
@@ -6,7 +6,9 @@ RSpec.describe WorkerKiller::Killer::Puma do
6
6
  end
7
7
  end
8
8
 
9
- let(:killer){ described_class.new }
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(Kernel).to receive(:system).with('pumactl phased-restart').and_return(true)
23
+ expect(Socket).to receive(:unix).with(ipc_path).and_yield(buffer).exactly(4).times
22
24
  expect(Process).to receive(:kill).with(:KILL, anything).exactly(5).times
23
25
 
24
- thread = killer.kill(Time.now)
25
- thread.join
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
@@ -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: a_value_between(min, max)) }
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(subject.limit + 1)
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.5.213977
4
+ version: 1.1.0.214146
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-22 00:00:00.000000000 Z
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
@@ -178,6 +179,7 @@ test_files:
178
179
  - spec/middleware_spec.rb
179
180
  - spec/killer_spec.rb
180
181
  - spec/spec_helper.rb
182
+ - spec/puma_plugin_spec.rb
181
183
  - spec/delayed_job_plugin/oom_limiter_spec.rb
182
184
  - spec/delayed_job_plugin/jobs_limiter_spec.rb
183
185
  - spec/killer/passenger_spec.rb