resqued 0.10.1 → 0.10.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: 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