resqued 0.9.0 → 0.11.0
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 +5 -5
- data/CHANGES.md +23 -0
- data/docs/signals.md +4 -0
- data/exe/resqued +41 -22
- data/lib/resqued.rb +5 -5
- data/lib/resqued/config.rb +7 -7
- data/lib/resqued/config/after_fork.rb +1 -1
- data/lib/resqued/config/base.rb +1 -1
- data/lib/resqued/config/before_fork.rb +1 -1
- data/lib/resqued/config/worker.rb +13 -13
- data/lib/resqued/daemon.rb +1 -0
- data/lib/resqued/exec_on_hup.rb +43 -0
- data/lib/resqued/listener.rb +51 -48
- data/lib/resqued/listener_pool.rb +97 -0
- data/lib/resqued/listener_proxy.rb +40 -31
- data/lib/resqued/listener_state.rb +8 -0
- data/lib/resqued/logging.rb +15 -8
- data/lib/resqued/master.rb +94 -98
- data/lib/resqued/master_state.rb +73 -0
- data/lib/resqued/procline_version.rb +2 -2
- data/lib/resqued/sleepy.rb +6 -4
- data/lib/resqued/test_case.rb +3 -3
- data/lib/resqued/version.rb +1 -1
- data/lib/resqued/worker.rb +21 -14
- data/spec/fixtures/test_case_after_fork_raises.rb +5 -2
- data/spec/fixtures/test_case_before_fork_raises.rb +4 -1
- data/spec/fixtures/test_case_environment.rb +3 -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 +27 -27
- data/spec/resqued/config/fork_event_spec.rb +8 -8
- data/spec/resqued/config/worker_spec.rb +68 -55
- data/spec/resqued/config_spec.rb +6 -6
- data/spec/resqued/sleepy_spec.rb +10 -11
- data/spec/resqued/test_case_spec.rb +7 -7
- data/spec/spec_helper.rb +6 -1
- data/spec/support/custom_matchers.rb +10 -2
- 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 +57 -36
- data/exe/resqued-listener +0 -6
@@ -1,12 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "spec_helper"
|
2
|
+
require "resqued/config/after_fork"
|
3
|
+
require "resqued/config/before_fork"
|
4
|
+
require "resqued/runtime_info"
|
5
5
|
|
6
6
|
describe do
|
7
7
|
before { evaluator.apply(config) }
|
8
8
|
|
9
|
-
context
|
9
|
+
context "after_fork" do
|
10
10
|
# Run the after_fork block.
|
11
11
|
#
|
12
12
|
# after_fork do |resque_worker|
|
@@ -26,13 +26,13 @@ describe do
|
|
26
26
|
end
|
27
27
|
END_CONFIG
|
28
28
|
|
29
|
-
let(:evaluator) { Resqued::Config::AfterFork.new(:
|
29
|
+
let(:evaluator) { Resqued::Config::AfterFork.new(worker: worker) }
|
30
30
|
let(:worker) { FakeResqueWorker.new }
|
31
31
|
|
32
32
|
it { expect(worker.token).to eq(:called) }
|
33
33
|
end
|
34
34
|
|
35
|
-
context
|
35
|
+
context "before_fork" do
|
36
36
|
# Run the before_fork block.
|
37
37
|
#
|
38
38
|
# before_fork do
|
@@ -56,7 +56,7 @@ describe do
|
|
56
56
|
end
|
57
57
|
END_CONFIG
|
58
58
|
|
59
|
-
let(:evaluator) { $before_fork_called = false
|
59
|
+
let(:evaluator) { $before_fork_called = false; Resqued::Config::BeforeFork.new(resqued: resqued) }
|
60
60
|
let(:resqued) { Resqued::RuntimeInfo.new }
|
61
61
|
|
62
62
|
it { expect($before_fork_called).to eq(true) }
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "spec_helper"
|
2
|
+
require "resqued/config/worker"
|
3
3
|
|
4
4
|
describe Resqued::Config::Worker do
|
5
5
|
# Create a bunch of Resqued::Worker objects from
|
@@ -15,7 +15,7 @@ describe Resqued::Config::Worker do
|
|
15
15
|
#
|
16
16
|
# ignore calls to any other top-level method.
|
17
17
|
|
18
|
-
let(:evaluator) { described_class.new(:
|
18
|
+
let(:evaluator) { described_class.new(worker_class: FakeWorker) }
|
19
19
|
let(:result) { evaluator.apply(config) }
|
20
20
|
module FakeWorker
|
21
21
|
def self.new(options)
|
@@ -23,7 +23,7 @@ describe Resqued::Config::Worker do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
context
|
26
|
+
context "individual" do
|
27
27
|
let(:config) { <<-END_CONFIG }
|
28
28
|
before_fork { }
|
29
29
|
after_fork { }
|
@@ -35,25 +35,38 @@ describe Resqued::Config::Worker do
|
|
35
35
|
after_fork { } # So that we don't rely on `workers`'s result falling through.
|
36
36
|
END_CONFIG
|
37
37
|
it { expect(result.size).to eq(6) }
|
38
|
-
it { expect(result[0]).to eq(:
|
39
|
-
it { expect(result[1]).to eq(:
|
40
|
-
it { expect(result[2]).to eq(:
|
41
|
-
it { expect(result[3]).to eq(:
|
42
|
-
it { expect(result[4]).to eq(:
|
43
|
-
it { expect(result[5]).to eq(:
|
38
|
+
it { expect(result[0]).to eq(queues: ["a"]) }
|
39
|
+
it { expect(result[1]).to eq(queues: ["a"]) }
|
40
|
+
it { expect(result[2]).to eq(queues: ["b"]) }
|
41
|
+
it { expect(result[3]).to eq(queues: ["c", "d"]) }
|
42
|
+
it { expect(result[4]).to eq(queues: ["d", "c"], interval: 3) }
|
43
|
+
it { expect(result[5]).to eq(queues: ["*"]) }
|
44
44
|
end
|
45
45
|
|
46
|
-
context
|
46
|
+
context "concise pool" do
|
47
47
|
let(:config) { <<-END_CONFIG }
|
48
48
|
worker_pool 2, 'a', 'b', 'c', :interval => 1
|
49
49
|
END_CONFIG
|
50
|
-
it
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
it do
|
51
|
+
expect(result).to eq([
|
52
|
+
{ queues: ["a", "b", "c"], interval: 1 },
|
53
|
+
{ queues: ["a", "b", "c"], interval: 1 },
|
54
|
+
])
|
55
|
+
end
|
54
56
|
end
|
55
57
|
|
56
|
-
context
|
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
|
+
|
69
|
+
context "pool (hash for concurrency)" do
|
57
70
|
let(:config) { <<-END_CONFIG }
|
58
71
|
before_fork { }
|
59
72
|
after_fork { }
|
@@ -64,15 +77,15 @@ describe Resqued::Config::Worker do
|
|
64
77
|
after_fork { } # So that we don't rely on `worker_pool`'s result falling through.
|
65
78
|
END_CONFIG
|
66
79
|
it { expect(result.size).to eq(20) }
|
67
|
-
it { expect(result[0]).to eq(:
|
68
|
-
it { expect(result[3]).to eq(:
|
69
|
-
it { expect(result[4]).to eq(:
|
70
|
-
it { expect(result[9]).to eq(:
|
71
|
-
it { expect(result[10]).to eq(:
|
72
|
-
it { expect(result[19]).to eq(:
|
80
|
+
it { expect(result[0]).to eq(queues: ["a", "b1", "b2", "c"], interval: 1) }
|
81
|
+
it { expect(result[3]).to eq(queues: ["a", "b1", "b2", "c"], interval: 1) }
|
82
|
+
it { expect(result[4]).to eq(queues: ["b1", "b2", "c"], interval: 1) }
|
83
|
+
it { expect(result[9]).to eq(queues: ["b1", "b2", "c"], interval: 1) }
|
84
|
+
it { expect(result[10]).to eq(queues: ["c"], interval: 1) }
|
85
|
+
it { expect(result[19]).to eq(queues: ["c"], interval: 1) }
|
73
86
|
end
|
74
87
|
|
75
|
-
context
|
88
|
+
context "pool, with implied queue" do
|
76
89
|
let(:config) { <<-END_CONFIG }
|
77
90
|
before_fork { }
|
78
91
|
after_fork { }
|
@@ -80,11 +93,11 @@ describe Resqued::Config::Worker do
|
|
80
93
|
after_fork { } # So that we don't rely on `worker_pool`'s result falling through.
|
81
94
|
END_CONFIG
|
82
95
|
it { expect(result.size).to eq(20) }
|
83
|
-
it { expect(result[0]).to eq(:
|
84
|
-
it { expect(result[19]).to eq(:
|
96
|
+
it { expect(result[0]).to eq(queues: ["*"]) }
|
97
|
+
it { expect(result[19]).to eq(queues: ["*"]) }
|
85
98
|
end
|
86
99
|
|
87
|
-
context
|
100
|
+
context "pool, with fewer queues than workers" do
|
88
101
|
let(:config) { <<-END_CONFIG }
|
89
102
|
before_fork { }
|
90
103
|
after_fork { }
|
@@ -93,13 +106,13 @@ describe Resqued::Config::Worker do
|
|
93
106
|
after_fork { } # So that we don't rely on `worker_pool`'s result falling through.
|
94
107
|
END_CONFIG
|
95
108
|
it { expect(result.size).to eq(20) }
|
96
|
-
it { expect(result[0]).to eq(:
|
97
|
-
it { expect(result[9]).to eq(:
|
98
|
-
it { expect(result[10]).to eq(:
|
99
|
-
it { expect(result[19]).to eq(:
|
109
|
+
it { expect(result[0]).to eq(queues: ["a"]) }
|
110
|
+
it { expect(result[9]).to eq(queues: ["a"]) }
|
111
|
+
it { expect(result[10]).to eq(queues: ["*"]) }
|
112
|
+
it { expect(result[19]).to eq(queues: ["*"]) }
|
100
113
|
end
|
101
114
|
|
102
|
-
context
|
115
|
+
context "pool, with more queues than workers" do
|
103
116
|
let(:config) { <<-END_CONFIG }
|
104
117
|
before_fork { }
|
105
118
|
after_fork { }
|
@@ -110,48 +123,48 @@ describe Resqued::Config::Worker do
|
|
110
123
|
it { expect(result.size).to eq(20) }
|
111
124
|
end
|
112
125
|
|
113
|
-
context
|
126
|
+
context "pool, with shuffled queues" do
|
114
127
|
let(:config) { <<-END_CONFIG }
|
115
|
-
worker_pool
|
116
|
-
queue 'a', :count =>
|
117
|
-
queue 'b', :count =>
|
128
|
+
worker_pool 200, :shuffle_queues => true
|
129
|
+
queue 'a', :count => 100
|
130
|
+
queue 'b', :count => 150
|
118
131
|
END_CONFIG
|
119
|
-
it { expect(result.size).to eq(
|
120
|
-
it { (0..
|
121
|
-
it { (
|
122
|
-
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(["*"]) } }
|
123
136
|
it { result.each { |x| expect(x).not_to have_key(:shuffle_queues) } }
|
124
137
|
it do
|
125
|
-
shuffled_queues = result.take(
|
126
|
-
expect(shuffled_queues.sort.uniq).to eq([
|
138
|
+
shuffled_queues = result.take(100).map { |x| x[:queues] }
|
139
|
+
expect(shuffled_queues.sort.uniq).to eq([["a", "b"], ["b", "a"]]) # Some of the queues should be shuffled
|
127
140
|
end
|
128
141
|
end
|
129
142
|
|
130
|
-
context
|
143
|
+
context "multiple worker configs" do
|
131
144
|
let(:config) { <<-END_CONFIG }
|
132
145
|
worker 'one'
|
133
146
|
worker 'two'
|
134
147
|
worker_pool 2
|
135
148
|
END_CONFIG
|
136
149
|
it { expect(result.size).to eq(4) }
|
137
|
-
it { expect(result[0]).to eq(:
|
138
|
-
it { expect(result[1]).to eq(:
|
139
|
-
it { expect(result[2]).to eq(:
|
140
|
-
it { expect(result[3]).to eq(:
|
150
|
+
it { expect(result[0]).to eq(queues: ["one"]) }
|
151
|
+
it { expect(result[1]).to eq(queues: ["two"]) }
|
152
|
+
it { expect(result[2]).to eq(queues: ["*"]) }
|
153
|
+
it { expect(result[3]).to eq(queues: ["*"]) }
|
141
154
|
end
|
142
155
|
|
143
|
-
context
|
156
|
+
context "worker factory" do
|
144
157
|
let(:config) { <<-END_CONFIG }
|
145
158
|
worker_factory { |queues| queues }
|
146
159
|
worker 'a'
|
147
160
|
END_CONFIG
|
148
161
|
|
149
162
|
it { expect(result.size).to eq(1) }
|
150
|
-
it { expect(result[0].reject { |k, _| k == :worker_factory}).to eq(:
|
151
|
-
it { expect(result[0][:worker_factory].call(result[0][:queues])).to eq([
|
163
|
+
it { expect(result[0].reject { |k, _| k == :worker_factory }).to eq(queues: ["a"]) }
|
164
|
+
it { expect(result[0][:worker_factory].call(result[0][:queues])).to eq(["a"]) }
|
152
165
|
end
|
153
166
|
|
154
|
-
context
|
167
|
+
context "worker factory with pool" do
|
155
168
|
let(:config) { <<-END_CONFIG }
|
156
169
|
worker_factory { |queues| queues }
|
157
170
|
worker_pool 1
|
@@ -159,15 +172,15 @@ describe Resqued::Config::Worker do
|
|
159
172
|
END_CONFIG
|
160
173
|
|
161
174
|
it { expect(result.size).to eq(1) }
|
162
|
-
it { expect(result[0].reject { |k, _| k == :worker_factory}).to eq(:
|
163
|
-
it { expect(result[0][:worker_factory].call(result[0][:queues])).to eq([
|
175
|
+
it { expect(result[0].reject { |k, _| k == :worker_factory }).to eq(queues: ["a"]) }
|
176
|
+
it { expect(result[0][:worker_factory].call(result[0][:queues])).to eq(["a"]) }
|
164
177
|
end
|
165
178
|
|
166
|
-
context
|
167
|
-
let(:evaluator) { described_class.new(:
|
179
|
+
context "with default options" do
|
180
|
+
let(:evaluator) { described_class.new(worker_class: FakeWorker, config: "something") }
|
168
181
|
let(:config) { <<-END_CONFIG }
|
169
182
|
worker 'a', :interval => 1
|
170
183
|
END_CONFIG
|
171
|
-
it { expect(result[0]).to eq(:
|
184
|
+
it { expect(result[0]).to eq(queues: ["a"], interval: 1, config: "something") }
|
172
185
|
end
|
173
186
|
end
|
data/spec/resqued/config_spec.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "fileutils"
|
4
|
+
require "tmpdir"
|
5
5
|
|
6
|
-
require
|
6
|
+
require "resqued/config"
|
7
7
|
|
8
8
|
describe Resqued::Config do
|
9
9
|
context do
|
@@ -19,8 +19,8 @@ describe Resqued::Config do
|
|
19
19
|
end
|
20
20
|
let(:config) { Resqued::Config.new([@config_file]) }
|
21
21
|
|
22
|
-
it("can require_relative") { config.build_workers
|
23
|
-
it("does not override require_relative in required files") { config.build_workers
|
22
|
+
it("can require_relative") { config.build_workers; expect($test_val).to eq(:ok) }
|
23
|
+
it("does not override require_relative in required files") { config.build_workers; expect($other_test_val).to eq(:ok) }
|
24
24
|
|
25
25
|
def make_file(dir, relative_path, content)
|
26
26
|
File.join(dir, relative_path).tap do |path|
|
data/spec/resqued/sleepy_spec.rb
CHANGED
@@ -1,34 +1,33 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'thread'
|
1
|
+
require "spec_helper"
|
2
|
+
require "resqued/sleepy"
|
4
3
|
|
5
4
|
describe Resqued::Sleepy do
|
6
5
|
include Resqued::Sleepy
|
7
6
|
|
8
|
-
it
|
7
|
+
it "sleeps" do
|
9
8
|
expect { yawn(0.2) }.to run_for(0.2)
|
10
9
|
end
|
11
10
|
|
12
|
-
it
|
13
|
-
Thread.new { sleep 0.1
|
11
|
+
it "wakes on `awake`" do
|
12
|
+
Thread.new { sleep 0.1; awake }
|
14
13
|
expect { yawn(2.0) }.to run_for(0.1)
|
15
14
|
end
|
16
15
|
|
17
|
-
it
|
16
|
+
it "wakes on IO" do
|
18
17
|
rd, wr = IO.pipe
|
19
|
-
Thread.new { sleep 0.1
|
18
|
+
Thread.new { sleep 0.1; wr.write(".") }
|
20
19
|
expect { yawn(2.0, rd) }.to run_for(0.1)
|
21
20
|
end
|
22
21
|
|
23
|
-
it
|
22
|
+
it "does not sleep if duration is 0" do
|
24
23
|
expect { yawn(-0.000001) }.to run_for(0.0)
|
25
24
|
end
|
26
25
|
|
27
|
-
it
|
26
|
+
it "does not sleep if duration is negative" do
|
28
27
|
expect { yawn(0) }.to run_for(0.0)
|
29
28
|
end
|
30
29
|
|
31
|
-
it
|
30
|
+
it "sleeps if io is nil" do
|
32
31
|
expect { yawn(0.5, nil) }.to run_for(0.5)
|
33
32
|
end
|
34
33
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "spec_helper"
|
2
|
+
require "resqued/test_case"
|
3
3
|
|
4
4
|
describe Resqued::TestCase do
|
5
5
|
let(:test_case) { Object.new.extend(the_module) }
|
6
6
|
|
7
|
-
context
|
7
|
+
context "LoadConfig" do
|
8
8
|
let(:the_module) { described_class::LoadConfig }
|
9
|
-
it { expect { test_case.assert_resqued
|
10
|
-
it { expect { test_case.assert_resqued
|
11
|
-
it { expect { test_case.assert_resqued
|
12
|
-
it { expect { test_case.assert_resqued
|
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(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
|
+
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
@@ -8,7 +8,11 @@ module CustomMatchers
|
|
8
8
|
class RunFor
|
9
9
|
def initialize(expected_duration)
|
10
10
|
@expected_duration = expected_duration
|
11
|
-
@epsilon
|
11
|
+
@epsilon = 0.01
|
12
|
+
end
|
13
|
+
|
14
|
+
def supports_block_expectations?
|
15
|
+
true
|
12
16
|
end
|
13
17
|
|
14
18
|
def within(epsilon)
|
@@ -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
|