sidekiq-process_manager 1.1.1 → 1.1.2

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: 51d57e09aba69d64225948647f2f95625c668b44eb16e514b5ab4daf8e064e3c
4
- data.tar.gz: 73d49c20ce5bf06a8d84a168d8e1afa2b04883ea1656d3d4e0ac96b66855140c
3
+ metadata.gz: dc25ffae388e5ca7ad73f0976d00cf09ec84124b2284a000af1dc0970cf87c20
4
+ data.tar.gz: 94637ae3cfa8a6fa991e95ee434c8410936c62263695f36b6db8c2b664d1f439
5
5
  SHA512:
6
- metadata.gz: 2f35e8a6ccbbcbaa15fbbbd62658bd79e4337e675a0c4a776e8dc2c6457bb957ed5ba9880336532a68f54e5ea13890917d758d5f84a2395130c0c52a5166a7cc
7
- data.tar.gz: dd4fa55ec09584a890e5125d93a64d15924b138583d23e91331a1818bec0c58dfa0cdd81ab58bfab37951a645ed41e149fa04996ec89d11c85c0362204c21be4
6
+ metadata.gz: 32bba980929441cb06022c829f73e7a7d8ee41de0dba07c98f44cb1f2c339dab87ec21dae8b1847ae2967e5771fb6506dd09a42a9540d784df4defd9f820f664
7
+ data.tar.gz: e1dc4e4e0e502b0747932b5b12011acfcb04535baeaae835027c50e1ec3ae1ff6ea7c0f6317469a8c4e11f8d9d9bfe40c530064ad4da5b4354231af1182776c6
data/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.1.2
8
+
9
+ ### Added
10
+ - Added thread to monitor child processes to make sure they exit after a SIGTERM signal has been sent. If a process does not exit after the configured Sidekiq timeout time, then it will be killed with a SIGKILL signal.
11
+
12
+ ### Changed
13
+ - A SIGINT sent to the manager process will sent SIGTERM to the child processes to give them a chance to shutdown gracefully.
14
+
7
15
  ## 1.1.1
8
16
 
9
17
  ### Added
data/README.md CHANGED
@@ -8,7 +8,7 @@ This gem provides a command line script for managing [sidekiq](https://github.co
8
8
 
9
9
  The sidekiq processes can all be managed by sending signals to the manager process. This process simply forwards the signals on to the child processes, allowing you to control the sidekiq processes as you normally would.
10
10
 
11
- If one of the sidekiq processes dies unexpectedly, the process manager automatically starts a new sidekiq process to replace it.
11
+ If one of the sidekiq processes exits unexpectedly, the process manager automatically starts a new sidekiq process to replace it.
12
12
 
13
13
  ## Pre-Forking
14
14
 
@@ -44,7 +44,7 @@ For a Rails application, you would normally want to preboot the `config/boot.rb`
44
44
 
45
45
  ## Memory Bloat
46
46
 
47
- You can also specify a maximum memory footprint that you want to allow for each child process. You can use this feature to automatically guard against poorly designed workers that bloat the Ruby memory heap. Note that you can also use an external process monitor to kill processes with memory bloat; the process manager will restart any process regardless of how it dies.
47
+ You can also specify a maximum memory footprint that you want to allow for each child process. You can use this feature to automatically guard against poorly designed workers that bloat the Ruby memory heap. Note that you can also use an external process monitor to kill processes with memory bloat; the process manager will restart any process regardless of how it exits.
48
48
 
49
49
  ## Usage
50
50
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.1.2
@@ -72,7 +72,7 @@ module Sidekiq
72
72
  signal = @signal_pipe_read.gets.strip
73
73
  send_signal_to_children(signal.to_sym)
74
74
  rescue => e
75
- log_error("Error handling signal #{signal}: #{e.message}")
75
+ log_warning("Error sending signal #{signal} to child processes: #{e.message}")
76
76
  end
77
77
  end
78
78
  end
@@ -81,6 +81,7 @@ module Sidekiq
81
81
  [:INT, :TERM, :USR1, :USR2, :TSTP, :TTIN].each do |signal|
82
82
  ::Signal.trap(signal) do
83
83
  if ::Process.pid == master_pid
84
+ signal = :TERM if signal == :INT
84
85
  @signal_pipe_write.puts(signal)
85
86
  end
86
87
  end
@@ -88,8 +89,12 @@ module Sidekiq
88
89
 
89
90
  # Ensure that child processes receive the term signal when the master process exits.
90
91
  at_exit do
91
- if ::Process.pid == master_pid && @process_count > 0
92
- send_signal_to_children(:TERM)
92
+ if ::Process.pid == master_pid
93
+ if @process_count > 0
94
+ send_signal_to_children(:TERM)
95
+ end
96
+ wait_for_children_to_exit
97
+ log_info("Process manager exiting")
93
98
  end
94
99
  end
95
100
 
@@ -104,9 +109,8 @@ module Sidekiq
104
109
 
105
110
  start_memory_monitor if @max_memory
106
111
 
107
- log_info("Process manager started with pid #{::Process.pid}")
112
+ log_info("Process manager started")
108
113
  monitor_child_processes
109
- log_info("Process manager #{::Process.pid} exiting")
110
114
  end
111
115
 
112
116
  # Helper to wait on the manager to wait on child processes to start up.
@@ -237,14 +241,24 @@ module Sidekiq
237
241
  log_info("Process manager trapped signal #{signal}")
238
242
  @process_count = 0 if signal == :INT || signal == :TERM
239
243
  pids.each do |pid|
240
- begin
241
- log_info("Sending signal #{signal} to sidekiq process #{pid}")
242
- ::Process.kill(signal, pid)
243
- rescue => e
244
- log_warning("Error sending signal #{signal} to sidekiq process #{pid}: #{e.inspect}")
244
+ send_signal_to_pid(signal, pid)
245
+ end
246
+ end
247
+
248
+ def send_signal_to_pid(signal, pid)
249
+ signal = signal.to_sym
250
+ begin
251
+ log_info("Sending signal #{signal} to sidekiq process #{pid}")
252
+ ::Process.kill(signal, pid)
253
+ if [:TERM, :INT].include?(signal)
254
+ Thread.new do
255
+ Thread.current.name = "pid-#{pid}-killer"
256
+ ensure_pid_dies(pid)
257
+ end
245
258
  end
259
+ rescue Errno::ESRCH
260
+ # The process is already dead
246
261
  end
247
- wait_for_children_to_exit(pids) if @process_count == 0
248
262
  end
249
263
 
250
264
  def start_memory_monitor
@@ -259,12 +273,8 @@ module Sidekiq
259
273
  begin
260
274
  memory = GetProcessMem.new(pid)
261
275
  if memory.bytes > @max_memory
262
- log_warning("Kill bloated sidekiq process #{pid}: #{memory.mb.round}mb used")
263
- begin
264
- ::Process.kill(:TERM, pid)
265
- rescue Errno::ESRCH
266
- # The process is already dead
267
- end
276
+ log_warning("Killing bloated sidekiq process #{pid}: #{memory.mb.round}mb used")
277
+ send_signal_to_pid(:TERM, pid)
268
278
  break
269
279
  end
270
280
  rescue => e
@@ -281,22 +291,13 @@ module Sidekiq
281
291
  end
282
292
  end
283
293
 
284
- def wait_for_children_to_exit(pids)
285
- timeout = monotonic_time + (sidekiq_options[:timeout] || 25).to_f
294
+ def wait_for_children_to_exit
286
295
  pids.each do |pid|
287
- while monotonic_time < timeout
288
- break unless process_alive?(pid)
296
+ while process_alive?(pid)
289
297
  sleep(0.01)
290
298
  end
291
299
  end
292
-
293
- pids.each do |pid|
294
- begin
295
- ::Process.kill(:INT, pid) if process_alive?(pid)
296
- rescue
297
- # Ignore errors so we can continue to kill other processes.
298
- end
299
- end
300
+ log_info("All sidekiq processes have exited")
300
301
  end
301
302
 
302
303
  def process_alive?(pid)
@@ -308,6 +309,24 @@ module Sidekiq
308
309
  end
309
310
  end
310
311
 
312
+ def ensure_pid_dies(pid)
313
+ # Wait for the process to die, or kill it after a timeout.
314
+ timeout = (sidekiq_options[:timeout] || 25).to_f
315
+ end_time = monotonic_time + timeout
316
+ while monotonic_time < end_time && process_alive?(pid)
317
+ sleep(0.1)
318
+ end
319
+
320
+ if process_alive?(pid)
321
+ begin
322
+ ::Process.kill(:KILL, pid)
323
+ log_warning("Sidekiq process #{pid} failed to exit after #{timeout} seconds; killed with SIGKILL")
324
+ rescue Errno::ESRCH
325
+ # The process is already dead
326
+ end
327
+ end
328
+ end
329
+
311
330
  # Listen for child processes dying and restart if necessary.
312
331
  def monitor_child_processes
313
332
  loop do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-process_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-19 00:00:00.000000000 Z
11
+ date: 2023-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq