resqued 0.10.1 → 0.10.2

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: 477007ffc7e6d2630b794ef021d1bbe2bc4f2cc5fc5df95fbd91b92f2e6121fe
4
- data.tar.gz: 39531525a5afdd44ab077f69355d5b17a615f6430c21171bbd8cddc36adbaf66
3
+ metadata.gz: bc14cf354d4cd292119aa6e6f06f0c59dd09faf982fe10707e69c5bbb3ef56e4
4
+ data.tar.gz: 7bad129217778e76f14c1dc6a732025d5cc0bba17baaee9d81ee5a1648768edc
5
5
  SHA512:
6
- metadata.gz: e0f13ad32bc91c04058201882a00a65d4f67da7f3fadd19cac227eaca76d1816b47987f517a53d2ec03de9c39b3ff2c7ed6b2956e44ff0890cea0eb467085adc
7
- data.tar.gz: 6c8399d793a329ae717d18c38dfca7e1f5b286e218e6ae39b5ce9deaed26b12a325d7e49292f5687a5ecb1e3814360fe9d5c6b670e0a5d49ed01473b978008c4
6
+ metadata.gz: e8b5e943ec7612022b8a937c6a380b1d396be6cd2c5ebad13239961232c992dcce54d42e807c1d52f036f102defe4492f4fd498f0299c33e40e5095dc26ab48f
7
+ data.tar.gz: a26e43be4a408d59d8db061c00b112e45a6fec8b6ba318e145cbac1e53b4087b12f9b2ba3510fa118714aeb8b0117673c90b6f7fb987ca028a8962c85f594e5f
data/CHANGES.md CHANGED
@@ -1,5 +1,9 @@
1
1
  Starting with version 0.6.1, resqued uses semantic versioning to indicate incompatibilities between the master process, listener process, and configuration.
2
2
 
3
+ v0.10.2
4
+ -------
5
+ * Shut down cleanly even if there are other stray child processes of the master. (#59)
6
+
3
7
  v0.10.1
4
8
  -------
5
9
  * Avoid deadlock if a listener stops responding. (#58)
@@ -25,6 +25,11 @@ module Resqued
25
25
  @listener_proxies.size
26
26
  end
27
27
 
28
+ # Public: Are the listeners all gone?
29
+ def empty?
30
+ @listener_proxies.empty?
31
+ end
32
+
28
33
  # Public: Initialize a new listener, run it, and record it as the current listener. Returns its ListenerProxy.
29
34
  def start!
30
35
  listener_state = ListenerState.new
@@ -187,7 +187,7 @@ module Resqued
187
187
  end
188
188
 
189
189
  def reap_all_listeners(waitpid_flags = 0)
190
- loop do
190
+ until @listeners.empty?
191
191
  begin
192
192
  lpid, status = Process.waitpid2(-1, waitpid_flags)
193
193
  return unless lpid
@@ -203,9 +203,11 @@ module Resqued
203
203
  @state.clear_last_good!
204
204
  end
205
205
 
206
- dead_listener = @listeners.delete(lpid)
207
- listener_status dead_listener, "stop"
208
- dead_listener.dispose
206
+ if dead_listener = @listeners.delete(lpid)
207
+ listener_status dead_listener, "stop"
208
+ dead_listener.dispose
209
+ end
210
+
209
211
  write_procline
210
212
  rescue Errno::ECHILD
211
213
  return
@@ -1,3 +1,3 @@
1
1
  module Resqued
2
- VERSION = '0.10.1'
2
+ VERSION = '0.10.2'
3
3
  end
@@ -0,0 +1,85 @@
1
+ require "spec_helper"
2
+ require "timeout"
3
+
4
+ describe "Resqued master with an extra child process" do
5
+ include ResquedPath
6
+
7
+ # Starts resqued with an extra child process.
8
+ def start_resqued_with_extra_child
9
+ shim_path = File.expand_path("../support/extra-child-shim", File.dirname(__FILE__))
10
+
11
+ config_path = File.join(SPEC_TEMPDIR, "config.rb")
12
+ File.write(config_path, <<-CONFIG)
13
+ before_fork { File.write(ENV["LISTENER_PIDFILE"], $$.to_s) }
14
+ CONFIG
15
+
16
+ logfile = File.join(SPEC_TEMPDIR, "resqued.log")
17
+ File.write(logfile, "") # truncate it
18
+
19
+ env = {
20
+ "LISTENER_PIDFILE" => listener_pidfile,
21
+ "EXTRA_CHILD_PIDFILE" => extra_child_pidfile,
22
+ }
23
+
24
+ pid = spawn(env, shim_path, resqued_path, "--logfile", logfile, config_path)
25
+ sleep 1.0
26
+ pid
27
+ end
28
+
29
+ let(:extra_child_pidfile) { File.join(SPEC_TEMPDIR, "extra-child.pid") }
30
+ def extra_child_pid
31
+ File.read(extra_child_pidfile).to_i
32
+ end
33
+
34
+ let(:listener_pidfile) { File.join(File.join(SPEC_TEMPDIR, "listener.pid")) }
35
+ def listener_pid
36
+ File.read(listener_pidfile).to_i
37
+ end
38
+
39
+ before do
40
+ File.unlink(extra_child_pidfile) rescue nil
41
+ File.unlink(listener_pidfile) rescue nil
42
+ @resqued_pid = start_resqued_with_extra_child
43
+ end
44
+
45
+ after do
46
+ kill_safely(:TERM) { @resqued_pid }
47
+ kill_safely(:KILL) { extra_child_pid }
48
+ sleep 0.1
49
+ kill_safely(:KILL) { @resqued_pid }
50
+ end
51
+
52
+ it "doesn't exit when listener dies unexpectedly" do
53
+ # Kill off the listener process.
54
+ first_listener_pid = listener_pid
55
+ Process.kill :QUIT, first_listener_pid
56
+ # Let Resqued::Backoff decide it's OK to start the listener again.
57
+ sleep 2.5
58
+ # Resqued should start a new listener to replace the dead one.
59
+ expect(listener_pid).not_to eq(first_listener_pid)
60
+ end
61
+
62
+ it "exits when listeners have all exited during shutdown" do
63
+ # Do a normal shutdown.
64
+ Process.kill :QUIT, @resqued_pid
65
+ # Expect the resqued process to exit.
66
+ expect(Timeout.timeout(5.0) { Process.waitpid(@resqued_pid) }).to eq(@resqued_pid)
67
+ end
68
+
69
+ it "doesn't crash when extra child exits" do
70
+ # Kill off the extra child process. Resqued should wait on it, but not exit.
71
+ Process.kill :KILL, extra_child_pid
72
+ sleep 1.0
73
+ # The resqued process should not have exited.
74
+ expect(Process.waitpid(@resqued_pid, Process::WNOHANG)).to be_nil
75
+ expect(Process.kill(0, @resqued_pid)).to eq(1)
76
+ end
77
+
78
+ def kill_safely(signal)
79
+ return unless pid = yield
80
+
81
+ Process.kill(signal, pid)
82
+ rescue Errno::ESRCH, Errno::ENOENT
83
+ # Process isn't there anymore, or pidfile isn't there. :+1:
84
+ end
85
+ end
@@ -0,0 +1,63 @@
1
+ require "spec_helper"
2
+
3
+ describe "Resqued can restart" do
4
+ include ResquedPath
5
+
6
+ it "expect to be able to restart" do
7
+ start_resqued
8
+ expect_running listener: "listener #1"
9
+ restart_resqued
10
+ expect_running listener: "listener #2"
11
+ stop_resqued
12
+ expect_not_running
13
+ end
14
+
15
+ after do
16
+ begin
17
+ Process.kill(:QUIT, @pid) if @pid
18
+ rescue Errno::ESRCH
19
+ end
20
+ end
21
+
22
+ def expect_running(listener:)
23
+ processes = list_processes
24
+ expect(processes).to include(is_resqued_master)
25
+ listeners = processes.select { |p| p[:ppid] == @pid }.map { |p| p[:args] }
26
+ expect(listeners).to all(match(/#{listener}/)).and(satisfy { |l| l.size == 1 })
27
+ end
28
+
29
+ def expect_not_running
30
+ processes = list_processes
31
+ expect(processes).not_to include(is_resqued_master)
32
+ end
33
+
34
+ def start_resqued
35
+ # Don't configure any workers. That way, we don't need to have redis running.
36
+ config_path = File.join(SPEC_TEMPDIR, "config.rb")
37
+ File.write(config_path, "")
38
+
39
+ logfile = File.join(SPEC_TEMPDIR, "resqued.log")
40
+ File.write(logfile, "") # truncate it
41
+
42
+ @pid = spawn resqued_path, "--logfile", logfile, config_path
43
+ sleep 1.0
44
+ end
45
+
46
+ def restart_resqued
47
+ Process.kill(:HUP, @pid)
48
+ sleep 1.0
49
+ end
50
+
51
+ def stop_resqued
52
+ Process.kill(:TERM, @pid)
53
+ sleep 1.0
54
+ end
55
+
56
+ def list_processes
57
+ `ps axo pid,ppid,args`.lines.map { |line| pid, ppid, args = line.strip.split(/\s+/, 3); { pid: pid.to_i, ppid: ppid.to_i, args: args } }
58
+ end
59
+
60
+ def is_resqued_master
61
+ satisfy { |p| p[:pid] == @pid && p[:args] =~ /resqued-/ }
62
+ end
63
+ end
@@ -1 +1,5 @@
1
1
  require "support/custom_matchers"
2
+ require "support/resqued_path"
3
+
4
+ SPEC_TEMPDIR = File.expand_path("../tmp/spec", File.dirname(__FILE__))
5
+ FileUtils.mkpath(SPEC_TEMPDIR)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ extra_child_pid = fork { sleep(100); exit! }
4
+ File.write(ENV["EXTRA_CHILD_PIDFILE"], extra_child_pid.to_s)
5
+
6
+ exec(*ARGV)
@@ -0,0 +1,11 @@
1
+ module ResquedPath
2
+ def resqued_path
3
+ return @resqued_path if @resqued_path
4
+
5
+ @resqued_path = File.expand_path("../../gemfiles/bin/resqued", File.dirname(__FILE__))
6
+ unless File.executable?(@resqued_path)
7
+ @resqued_path = File.expand_path("../../bin/resqued", File.dirname(__FILE__))
8
+ end
9
+ @resqued_path
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resqued
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Burke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-04 00:00:00.000000000 Z
11
+ date: 2020-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio
@@ -137,6 +137,8 @@ files:
137
137
  - spec/fixtures/test_case_clean.rb
138
138
  - spec/fixtures/test_case_environment.rb
139
139
  - spec/fixtures/test_case_no_workers.rb
140
+ - spec/integration/master_inherits_child_spec.rb
141
+ - spec/integration/restart_spec.rb
140
142
  - spec/resqued/backoff_spec.rb
141
143
  - spec/resqued/config/fork_event_spec.rb
142
144
  - spec/resqued/config/worker_spec.rb
@@ -146,7 +148,8 @@ files:
146
148
  - spec/resqued/test_case_spec.rb
147
149
  - spec/spec_helper.rb
148
150
  - spec/support/custom_matchers.rb
149
- - spec/test_restart.sh
151
+ - spec/support/extra-child-shim
152
+ - spec/support/resqued_path.rb
150
153
  homepage: https://github.com/spraints/resqued
151
154
  licenses:
152
155
  - MIT
@@ -172,8 +175,11 @@ specification_version: 4
172
175
  summary: Daemon of resque workers
173
176
  test_files:
174
177
  - spec/spec_helper.rb
175
- - spec/test_restart.sh
178
+ - spec/integration/restart_spec.rb
179
+ - spec/integration/master_inherits_child_spec.rb
176
180
  - spec/support/custom_matchers.rb
181
+ - spec/support/resqued_path.rb
182
+ - spec/support/extra-child-shim
177
183
  - spec/fixtures/test_case_clean.rb
178
184
  - spec/fixtures/test_case_before_fork_raises.rb
179
185
  - spec/fixtures/test_case_environment.rb
@@ -1,84 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -e
4
- set -o nounset
5
-
6
- WORKDIR="$(mktemp)"
7
- PIDFILE="${WORKDIR}/resqued.pid"
8
- CONFIG="${WORKDIR}/config.rb"
9
-
10
- # mktemp makes a file, but we want a dir.
11
- rm -f "$WORKDIR"
12
- mkdir "$WORKDIR"
13
-
14
- set -x
15
- cd "$(dirname "$0")/.."
16
-
17
- main() {
18
- trap cleanup EXIT
19
-
20
- configure_resqued
21
- start_resqued
22
- restart_resqued
23
- stop_resqued
24
- }
25
-
26
- configure_resqued() {
27
- # Don't configure any workers. That way, we don't need to have redis running.
28
- touch "${CONFIG}"
29
- }
30
-
31
- start_resqued() {
32
- bin_resqued=bin/resqued
33
- if [ -x gemfiles/bin/resqued ]; then
34
- bin_resqued=gemfiles/bin/resqued
35
- fi
36
- $bin_resqued --pidfile "${PIDFILE}" "${CONFIG}" &
37
- sleep 1
38
- echo expect to find the master process and the first listener
39
- running # set -e will make the test fail if it's not running
40
- ps axo pid,args -H | grep [r]esqued-
41
- ps axo pid,args | grep [r]esqued- | grep -q 'listener #1.*running'
42
- }
43
-
44
- restart_resqued() {
45
- local pid="$(cat "${PIDFILE}")"
46
- kill -HUP "$pid"
47
- sleep 1
48
- echo expect to find the master process and the second listener
49
- running
50
- ps axo pid,args -H | grep [r]esqued-
51
- ps axo pid,args | grep [r]esqued- | grep -qv 'listener #1'
52
- ps axo pid,args | grep [r]esqued- | grep -q 'listener #2.*running'
53
- }
54
-
55
- stop_resqued() {
56
- local pid="$(cat "${PIDFILE}")"
57
- kill -TERM "$pid"
58
- sleep 1
59
- echo expect everything to be stopped
60
- if running >&/dev/null; then
61
- echo "expected resqued to be stopped"
62
- false
63
- fi
64
- ps axo pid,args -H | grep [r]esqued- || true
65
- test -z "$(ps axo pid,args | grep [r]esqued-)"
66
- }
67
-
68
- running() {
69
- set -e
70
- test -f "${PIDFILE}"
71
- local pid="$(cat "${PIDFILE}")"
72
- kill -0 "$pid"
73
- ps o pid,args "$pid"
74
- }
75
-
76
- cleanup() {
77
- while running >&/dev/null; do
78
- kill -TERM "$(cat "${PIDFILE}")"
79
- sleep 2
80
- done
81
- rm -rfv "${WORKDIR}" || rm -rf "${WORKDIR}"
82
- }
83
-
84
- main