resqued 0.10.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGES.md +21 -0
- data/docs/signals.md +4 -0
- data/lib/resqued/config/worker.rb +1 -1
- data/lib/resqued/listener.rb +4 -1
- data/lib/resqued/listener_pool.rb +5 -0
- data/lib/resqued/listener_proxy.rb +4 -1
- data/lib/resqued/master.rb +7 -5
- data/lib/resqued/version.rb +1 -1
- data/lib/resqued/worker.rb +5 -1
- data/spec/integration/listener_still_starting_spec.rb +24 -0
- data/spec/integration/master_inherits_child_spec.rb +85 -0
- data/spec/integration/restart_spec.rb +21 -0
- data/spec/resqued/backoff_spec.rb +8 -8
- data/spec/resqued/config/worker_spec.rb +19 -8
- data/spec/resqued/test_case_spec.rb +2 -2
- data/spec/spec_helper.rb +5 -0
- data/spec/support/custom_matchers.rb +9 -1
- data/spec/support/extra-child-shim +6 -0
- data/spec/support/resqued_integration_helpers.rb +50 -0
- data/spec/support/resqued_path.rb +11 -0
- metadata +32 -29
- data/spec/test_restart.sh +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b8956c8d1035f7289519c1bb7c136efa41fafde26f9caec7feb514b09270132
|
4
|
+
data.tar.gz: 05150d5f65763b0712b7d272cd44bc328b7a302042cc6fc98f5d34424aaca15d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12b640ca8b2d567dffd82e19e150e984461ba42c7b143650162b31e8812c31c47b9a1813c9f54df72f1cd19ccd51f80fa8f71dcc74c5805e5453c3798f5707a2
|
7
|
+
data.tar.gz: 355f43fe2e0d2b6d91b440e9bd7a7cd921c5134b17d4e934a06cbe33e52c5feef99fd8b14e93706a6e78b3c2f04e1fbc98386eba711e550855a5c023b0c57d4e
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,26 @@
|
|
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.11.1
|
4
|
+
-------
|
5
|
+
* Fix a crash during shutdown. (#62)
|
6
|
+
|
7
|
+
v0.11.0
|
8
|
+
-------
|
9
|
+
* Ignore SIGHUP in Listener and Worker processes. (#61)
|
10
|
+
|
11
|
+
v0.10.3
|
12
|
+
-------
|
13
|
+
* Fix a timing related crash during reload. (#60)
|
14
|
+
|
15
|
+
v0.10.2
|
16
|
+
-------
|
17
|
+
* Shut down cleanly even if there are other stray child processes of the master. (#59)
|
18
|
+
|
19
|
+
v0.10.1
|
20
|
+
-------
|
21
|
+
* Avoid deadlock if a listener stops responding. (#58)
|
22
|
+
* When using 'percent' for a queue in a worker pool, always assign at least one worker. (#57)
|
23
|
+
|
3
24
|
v0.10.0
|
4
25
|
-------
|
5
26
|
* Master process restarts itself (#51), so that it doesn't continue running stale code indefinitely. This will help with the rollout process when changes like #50 are introduced, so that the master process will catch up. The risk of this is that the master process might not be able to be restarted, which would lead to it crashing. The mostly likely way for that to happen is if you try to roll back your version of resqued to 0.9.0 or earlier. If you need to do that, ensure that your process monitor (systemd, god, etc.) is able to restart the master process. You can disable the new behavior by passing `--no-exec-on-hup`.
|
data/docs/signals.md
CHANGED
@@ -34,6 +34,8 @@ The Listener process forwards `SIGCONT` to all of its workers.
|
|
34
34
|
|
35
35
|
The Listener process handles `SIGINT`, `SIGTERM`, and `SIGQUIT`. When it receives one of these signals, it goes into shutdown mode. It sends the received signal to all of its workers. When all workers have exited, the Listener process exits.
|
36
36
|
|
37
|
+
The Listener process handles `SIGHUP` and does nothing. This makes it easier to reload resqued in a docker container, since many container platforms will send a requested signal to all processes in the container.
|
38
|
+
|
37
39
|
## Worker
|
38
40
|
|
39
41
|
The Worker process uses resque's signal handling. Resque 1.23.0 handles the following signals:
|
@@ -44,3 +46,5 @@ The Worker process uses resque's signal handling. Resque 1.23.0 handles the foll
|
|
44
46
|
* `USR1`: Kill the forked child immediately, continue processing jobs.
|
45
47
|
* `USR2`: Don't process any new jobs
|
46
48
|
* `CONT`: Start processing jobs again after a USR2
|
49
|
+
|
50
|
+
Resqued leaves a handler for `HUP` in place that does nothing. This makes it easier to reload resqued in a docker container, since many container platforms will send a requested signal to all processes in the container.
|
@@ -112,7 +112,7 @@ module Resqued
|
|
112
112
|
elsif value.is_a?(1.class)
|
113
113
|
value < @pool_size ? value : @pool_size
|
114
114
|
elsif value.is_a?(Float) && value >= 0.0 && value <= 1.0
|
115
|
-
(@pool_size * value).to_i
|
115
|
+
[(@pool_size * value).to_i, 1].max
|
116
116
|
else
|
117
117
|
raise TypeError, "Unknown concurrency value: #{value.inspect}"
|
118
118
|
end
|
data/lib/resqued/listener.rb
CHANGED
@@ -63,12 +63,13 @@ module Resqued
|
|
63
63
|
end
|
64
64
|
|
65
65
|
SIGNALS = [:CONT, :QUIT, :INT, :TERM].freeze
|
66
|
-
ALL_SIGNALS = SIGNALS + [:CHLD]
|
66
|
+
ALL_SIGNALS = SIGNALS + [:CHLD, :HUP]
|
67
67
|
|
68
68
|
SIGNAL_QUEUE = [] # rubocop: disable Style/MutableConstant
|
69
69
|
|
70
70
|
# Public: Run the main loop.
|
71
71
|
def run
|
72
|
+
trap(:HUP) {} # ignore this, in case it trickles in from the master.
|
72
73
|
trap(:CHLD) { awake }
|
73
74
|
SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal; awake } }
|
74
75
|
@socket.close_on_exec = true
|
@@ -85,6 +86,8 @@ module Resqued
|
|
85
86
|
|
86
87
|
write_procline("shutdown")
|
87
88
|
burn_down_workers(exit_signal || :QUIT)
|
89
|
+
@socket&.close
|
90
|
+
@socket = nil
|
88
91
|
end
|
89
92
|
|
90
93
|
# Private.
|
@@ -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
|
@@ -99,7 +99,10 @@ module Resqued
|
|
99
99
|
def worker_finished(pid)
|
100
100
|
return if @state.master_socket.nil?
|
101
101
|
|
102
|
-
@state.master_socket.
|
102
|
+
@state.master_socket.write_nonblock("#{pid}\n")
|
103
|
+
rescue IO::WaitWritable
|
104
|
+
log "Couldn't tell #{@state.pid} that #{pid} exited!"
|
105
|
+
# Ignore it, maybe the next time it'll work.
|
103
106
|
rescue Errno::EPIPE
|
104
107
|
@state.master_socket.close
|
105
108
|
@state.master_socket = nil
|
data/lib/resqued/master.rb
CHANGED
@@ -187,7 +187,7 @@ module Resqued
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def reap_all_listeners(waitpid_flags = 0)
|
190
|
-
|
190
|
+
until @listeners.empty?
|
191
191
|
begin
|
192
192
|
lpid, status = Process.waitpid2(-1, waitpid_flags)
|
193
193
|
return unless lpid
|
@@ -200,12 +200,14 @@ module Resqued
|
|
200
200
|
end
|
201
201
|
|
202
202
|
if @listeners.last_good_pid == lpid
|
203
|
-
@
|
203
|
+
@listeners.clear_last_good!
|
204
|
+
end
|
205
|
+
|
206
|
+
if dead_listener = @listeners.delete(lpid)
|
207
|
+
listener_status dead_listener, "stop"
|
208
|
+
dead_listener.dispose
|
204
209
|
end
|
205
210
|
|
206
|
-
dead_listener = @listeners.delete(lpid)
|
207
|
-
listener_status dead_listener, "stop"
|
208
|
-
dead_listener.dispose
|
209
211
|
write_procline
|
210
212
|
rescue Errno::ECHILD
|
211
213
|
return
|
data/lib/resqued/version.rb
CHANGED
data/lib/resqued/worker.rb
CHANGED
@@ -91,7 +91,11 @@ module Resqued
|
|
91
91
|
else
|
92
92
|
# In case we get a signal before resque is ready for it.
|
93
93
|
Resqued::Listener::ALL_SIGNALS.each { |signal| trap(signal, "DEFAULT") }
|
94
|
-
|
94
|
+
# Continue ignoring SIGHUP, though.
|
95
|
+
trap(:HUP) {}
|
96
|
+
# If we get a QUIT during boot, just spin back down.
|
97
|
+
trap(:QUIT) { exit! 0 }
|
98
|
+
|
95
99
|
$0 = "STARTING RESQUE FOR #{queues.join(',')}"
|
96
100
|
resque_worker = @worker_factory.call(queues)
|
97
101
|
@config.after_fork(resque_worker)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Listener still starting on SIGHUP" do
|
4
|
+
include ResquedIntegrationHelpers
|
5
|
+
|
6
|
+
it "expect master not to crash" do
|
7
|
+
start_resqued config: <<-CONFIG
|
8
|
+
before_fork do
|
9
|
+
sleep 1
|
10
|
+
end
|
11
|
+
CONFIG
|
12
|
+
expect_running listener: "listener #1"
|
13
|
+
restart_resqued
|
14
|
+
sleep 2
|
15
|
+
expect_running listener: "listener #2"
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
begin
|
20
|
+
Process.kill(:QUIT, @pid) if @pid
|
21
|
+
rescue Errno::ESRCH
|
22
|
+
end
|
23
|
+
end
|
24
|
+
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 2.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,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Resqued can restart" do
|
4
|
+
include ResquedIntegrationHelpers
|
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
|
+
end
|
@@ -6,7 +6,7 @@ describe Resqued::Backoff do
|
|
6
6
|
let(:backoff) { described_class.new(min: 0.5, max: 64.0) }
|
7
7
|
|
8
8
|
it "can start on the first try" do
|
9
|
-
expect(backoff.wait?).to
|
9
|
+
expect(backoff.wait?).to be_falsey
|
10
10
|
end
|
11
11
|
|
12
12
|
it "has no waiting at first" do
|
@@ -15,42 +15,42 @@ describe Resqued::Backoff do
|
|
15
15
|
|
16
16
|
context "after expected exits" do
|
17
17
|
before { 3.times { backoff.started } }
|
18
|
-
it { expect(backoff.wait?).to
|
18
|
+
it { expect(backoff.wait?).to be true }
|
19
19
|
it { expect(backoff.how_long?).to be_close_to(0.5) }
|
20
20
|
end
|
21
21
|
|
22
22
|
context "after one quick exit" do
|
23
23
|
before { 1.times { backoff.started; backoff.died } }
|
24
|
-
it { expect(backoff.wait?).to
|
24
|
+
it { expect(backoff.wait?).to be true }
|
25
25
|
it { expect(backoff.how_long?).to be_close_to(1.0) }
|
26
26
|
end
|
27
27
|
|
28
28
|
context "after two quick starts" do
|
29
29
|
before { 2.times { backoff.started; backoff.died } }
|
30
|
-
it { expect(backoff.wait?).to
|
30
|
+
it { expect(backoff.wait?).to be true }
|
31
31
|
it { expect(backoff.how_long?).to be_close_to(2.0) }
|
32
32
|
end
|
33
33
|
|
34
34
|
context "after five quick starts" do
|
35
35
|
before { 6.times { backoff.started; backoff.died } }
|
36
|
-
it { expect(backoff.wait?).to
|
36
|
+
it { expect(backoff.wait?).to be true }
|
37
37
|
it { expect(backoff.how_long?).to be_close_to(32.0) }
|
38
38
|
end
|
39
39
|
|
40
40
|
context "after six quick starts" do
|
41
41
|
before { 7.times { backoff.started; backoff.died } }
|
42
|
-
it { expect(backoff.wait?).to
|
42
|
+
it { expect(backoff.wait?).to be true }
|
43
43
|
it { expect(backoff.how_long?).to be_close_to(64.0) }
|
44
44
|
end
|
45
45
|
|
46
46
|
context "does not wait longer than 64s" do
|
47
47
|
before { 8.times { backoff.started; backoff.died } }
|
48
|
-
it { expect(backoff.wait?).to
|
48
|
+
it { expect(backoff.wait?).to be true }
|
49
49
|
it { expect(backoff.how_long?).to be_close_to(64.0) }
|
50
50
|
it "and resets after an expected exit" do
|
51
51
|
backoff.started
|
52
52
|
backoff.started
|
53
|
-
expect(backoff.wait?).to
|
53
|
+
expect(backoff.wait?).to be true
|
54
54
|
expect(backoff.how_long?).to be_close_to(0.5)
|
55
55
|
end
|
56
56
|
end
|
@@ -55,6 +55,17 @@ describe Resqued::Config::Worker do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
context "small pool with percent" do
|
59
|
+
let(:config) { <<-END_CONFIG }
|
60
|
+
worker_pool 2
|
61
|
+
queue "a"
|
62
|
+
queue "b", :percent => 45
|
63
|
+
END_CONFIG
|
64
|
+
it { expect(result.size).to eq(2) }
|
65
|
+
it { expect(result[0]).to eq(queues: ["a", "b"]) }
|
66
|
+
it { expect(result[1]).to eq(queues: ["a"]) }
|
67
|
+
end
|
68
|
+
|
58
69
|
context "pool (hash for concurrency)" do
|
59
70
|
let(:config) { <<-END_CONFIG }
|
60
71
|
before_fork { }
|
@@ -114,17 +125,17 @@ describe Resqued::Config::Worker do
|
|
114
125
|
|
115
126
|
context "pool, with shuffled queues" do
|
116
127
|
let(:config) { <<-END_CONFIG }
|
117
|
-
worker_pool
|
118
|
-
queue 'a', :count =>
|
119
|
-
queue 'b', :count =>
|
128
|
+
worker_pool 200, :shuffle_queues => true
|
129
|
+
queue 'a', :count => 100
|
130
|
+
queue 'b', :count => 150
|
120
131
|
END_CONFIG
|
121
|
-
it { expect(result.size).to eq(
|
122
|
-
it { (0..
|
123
|
-
it { (
|
124
|
-
it { (
|
132
|
+
it { expect(result.size).to eq(200) }
|
133
|
+
it { (0..99).each { |i| expect(result[i][:queues].sort).to eq(["a", "b"]) } }
|
134
|
+
it { (100..149).each { |i| expect(result[i][:queues]).to eq(["b"]) } }
|
135
|
+
it { (150..199).each { |i| expect(result[i][:queues]).to eq(["*"]) } }
|
125
136
|
it { result.each { |x| expect(x).not_to have_key(:shuffle_queues) } }
|
126
137
|
it do
|
127
|
-
shuffled_queues = result.take(
|
138
|
+
shuffled_queues = result.take(100).map { |x| x[:queues] }
|
128
139
|
expect(shuffled_queues.sort.uniq).to eq([["a", "b"], ["b", "a"]]) # Some of the queues should be shuffled
|
129
140
|
end
|
130
141
|
end
|
@@ -7,8 +7,8 @@ describe Resqued::TestCase do
|
|
7
7
|
context "LoadConfig" do
|
8
8
|
let(:the_module) { described_class::LoadConfig }
|
9
9
|
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_clean.rb" }.not_to raise_error }
|
10
|
-
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_before_fork_raises.rb" }.to raise_error }
|
11
|
-
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_after_fork_raises.rb" }.to raise_error }
|
10
|
+
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_before_fork_raises.rb" }.to raise_error(RuntimeError) }
|
11
|
+
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_after_fork_raises.rb" }.to raise_error(RuntimeError) }
|
12
12
|
it { expect { test_case.assert_resqued "spec/fixtures/test_case_environment.rb", "spec/fixtures/test_case_no_workers.rb" }.not_to raise_error }
|
13
13
|
end
|
14
14
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,6 +11,10 @@ module CustomMatchers
|
|
11
11
|
@epsilon = 0.01
|
12
12
|
end
|
13
13
|
|
14
|
+
def supports_block_expectations?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
14
18
|
def within(epsilon)
|
15
19
|
@epsilon = epsilon
|
16
20
|
self
|
@@ -24,9 +28,13 @@ module CustomMatchers
|
|
24
28
|
@epsilon >= diff
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
31
|
+
def failure_message
|
28
32
|
"Expected block to run for #{@expected_duration} +/-#{@epsilon} seconds, but it ran for #{@actual_duration} seconds."
|
29
33
|
end
|
34
|
+
|
35
|
+
def failure_message_when_negated
|
36
|
+
"Expected block not to run for #{@expected_duration} +/-#{@epsilon} seconds, but it ran for #{@actual_duration} seconds."
|
37
|
+
end
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ResquedIntegrationHelpers
|
2
|
+
include ResquedPath
|
3
|
+
|
4
|
+
def start_resqued(config: "", debug: false)
|
5
|
+
# Don't configure any workers. That way, we don't need to have redis running.
|
6
|
+
config_path = File.join(SPEC_TEMPDIR, "config.rb")
|
7
|
+
File.write(config_path, config)
|
8
|
+
|
9
|
+
@pid =
|
10
|
+
if debug
|
11
|
+
spawn resqued_path, config_path
|
12
|
+
else
|
13
|
+
logfile = File.join(SPEC_TEMPDIR, "resqued.log")
|
14
|
+
File.write(logfile, "") # truncate it
|
15
|
+
|
16
|
+
spawn resqued_path, "--logfile", logfile, config_path
|
17
|
+
end
|
18
|
+
sleep 1.0
|
19
|
+
end
|
20
|
+
|
21
|
+
def restart_resqued
|
22
|
+
Process.kill(:HUP, @pid)
|
23
|
+
sleep 1.0
|
24
|
+
end
|
25
|
+
|
26
|
+
def expect_running(listener:)
|
27
|
+
processes = list_processes
|
28
|
+
expect(processes).to include(is_resqued_master)
|
29
|
+
listeners = processes.select { |p| p[:ppid] == @pid }.map { |p| p[:args] }
|
30
|
+
expect(listeners).to all(match(/#{listener}/)).and(satisfy { |l| l.size == 1 })
|
31
|
+
end
|
32
|
+
|
33
|
+
def expect_not_running
|
34
|
+
processes = list_processes
|
35
|
+
expect(processes).not_to include(is_resqued_master)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop_resqued
|
39
|
+
Process.kill(:TERM, @pid)
|
40
|
+
sleep 1.0
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_processes
|
44
|
+
`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 } }
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_resqued_master
|
48
|
+
satisfy { |p| p[:pid] == @pid && p[:args] =~ /resqued-/ }
|
49
|
+
end
|
50
|
+
end
|
@@ -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.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Burke
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kgio
|
@@ -56,36 +56,30 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 13.0.1
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 13.0.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '2.0'
|
76
|
-
- - "<"
|
73
|
+
- - '='
|
77
74
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
75
|
+
version: 3.9.0
|
79
76
|
type: :development
|
80
77
|
prerelease: false
|
81
78
|
version_requirements: !ruby/object:Gem::Requirement
|
82
79
|
requirements:
|
83
|
-
- -
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '2.0'
|
86
|
-
- - "<"
|
80
|
+
- - '='
|
87
81
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
82
|
+
version: 3.9.0
|
89
83
|
- !ruby/object:Gem::Dependency
|
90
84
|
name: rubocop
|
91
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,6 +137,9 @@ files:
|
|
143
137
|
- spec/fixtures/test_case_clean.rb
|
144
138
|
- spec/fixtures/test_case_environment.rb
|
145
139
|
- spec/fixtures/test_case_no_workers.rb
|
140
|
+
- spec/integration/listener_still_starting_spec.rb
|
141
|
+
- spec/integration/master_inherits_child_spec.rb
|
142
|
+
- spec/integration/restart_spec.rb
|
146
143
|
- spec/resqued/backoff_spec.rb
|
147
144
|
- spec/resqued/config/fork_event_spec.rb
|
148
145
|
- spec/resqued/config/worker_spec.rb
|
@@ -152,7 +149,9 @@ files:
|
|
152
149
|
- spec/resqued/test_case_spec.rb
|
153
150
|
- spec/spec_helper.rb
|
154
151
|
- spec/support/custom_matchers.rb
|
155
|
-
- spec/
|
152
|
+
- spec/support/extra-child-shim
|
153
|
+
- spec/support/resqued_integration_helpers.rb
|
154
|
+
- spec/support/resqued_path.rb
|
156
155
|
homepage: https://github.com/spraints/resqued
|
157
156
|
licenses:
|
158
157
|
- MIT
|
@@ -172,24 +171,28 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
171
|
- !ruby/object:Gem::Version
|
173
172
|
version: '0'
|
174
173
|
requirements: []
|
175
|
-
|
176
|
-
rubygems_version: 2.5.2.1
|
174
|
+
rubygems_version: 3.0.3
|
177
175
|
signing_key:
|
178
176
|
specification_version: 4
|
179
177
|
summary: Daemon of resque workers
|
180
178
|
test_files:
|
181
|
-
- spec/resqued/sleepy_spec.rb
|
182
|
-
- spec/resqued/backoff_spec.rb
|
183
|
-
- spec/resqued/config/worker_spec.rb
|
184
|
-
- spec/resqued/config/fork_event_spec.rb
|
185
|
-
- spec/resqued/start_ctx_spec.rb
|
186
|
-
- spec/resqued/config_spec.rb
|
187
|
-
- spec/resqued/test_case_spec.rb
|
188
179
|
- spec/spec_helper.rb
|
180
|
+
- spec/integration/restart_spec.rb
|
181
|
+
- spec/integration/listener_still_starting_spec.rb
|
182
|
+
- spec/integration/master_inherits_child_spec.rb
|
183
|
+
- spec/support/resqued_integration_helpers.rb
|
184
|
+
- spec/support/custom_matchers.rb
|
185
|
+
- spec/support/resqued_path.rb
|
186
|
+
- spec/support/extra-child-shim
|
187
|
+
- spec/fixtures/test_case_clean.rb
|
189
188
|
- spec/fixtures/test_case_before_fork_raises.rb
|
190
189
|
- spec/fixtures/test_case_environment.rb
|
191
190
|
- spec/fixtures/test_case_no_workers.rb
|
192
191
|
- spec/fixtures/test_case_after_fork_raises.rb
|
193
|
-
- spec/
|
194
|
-
- spec/
|
195
|
-
- spec/
|
192
|
+
- spec/resqued/config_spec.rb
|
193
|
+
- spec/resqued/config/fork_event_spec.rb
|
194
|
+
- spec/resqued/config/worker_spec.rb
|
195
|
+
- spec/resqued/sleepy_spec.rb
|
196
|
+
- spec/resqued/test_case_spec.rb
|
197
|
+
- spec/resqued/start_ctx_spec.rb
|
198
|
+
- spec/resqued/backoff_spec.rb
|
data/spec/test_restart.sh
DELETED
@@ -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
|