worker_killer 1.0.5.213977 → 1.1.0.214146

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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