worker_roulette 0.1.7 → 0.1.9
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 +8 -8
- data/README.md +31 -88
- data/lib/worker_roulette/foreman.rb +13 -14
- data/lib/worker_roulette/lua.rb +19 -15
- data/lib/worker_roulette/tradesman.rb +100 -72
- data/lib/worker_roulette/version.rb +1 -1
- data/lib/worker_roulette.rb +66 -73
- data/spec/benchmark/irb_demo_runner.rb +39 -0
- data/spec/benchmark/perf_test.rb +22 -26
- data/spec/integration/evented_worker_roulette_spec.rb +165 -186
- data/spec/integration/worker_roulette_spec.rb +123 -145
- data/spec/spec_helper.rb +13 -12
- data/spec/unit/evented_readlock_spec.rb +22 -21
- data/spec/unit/lua_spec.rb +12 -14
- data/spec/unit/readlock_spec.rb +10 -11
- data/worker_roulette.gemspec +1 -0
- metadata +18 -5
- data/lib/worker_roulette/a_tradesman.rb +0 -46
- data/spec/unit/worker_roulette_spec.rb +0 -14
@@ -1,184 +1,162 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
module WorkerRoulette
|
3
|
+
describe WorkerRoulette do
|
4
|
+
let(:sender) {'katie_80'}
|
5
|
+
let(:work_orders) {["hello", "foreman"]}
|
6
|
+
let(:default_headers) {Hash['headers' => {'sender' => sender}]}
|
7
|
+
let(:hello_work_order) {Hash['payload' => "hello"]}
|
8
|
+
let(:foreman_work_order) {Hash['payload' => "foreman"]}
|
9
|
+
let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
|
10
|
+
let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
|
11
|
+
let(:worker_roulette) { WorkerRoulette.start }
|
2
12
|
|
3
|
-
|
4
|
-
let(:sender) {'katie_80'}
|
5
|
-
let(:work_orders) {["hello", "foreman"]}
|
6
|
-
let(:default_headers) {Hash['headers' => {'sender' => sender}]}
|
7
|
-
let(:hello_work_order) {Hash['payload' => "hello"]}
|
8
|
-
let(:foreman_work_order) {Hash['payload' => "foreman"]}
|
9
|
-
let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
|
10
|
-
let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
|
13
|
+
let(:redis) {Redis.new(worker_roulette.redis_config)}
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
before do
|
15
|
-
WorkerRoulette.start
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should exist" do
|
19
|
-
expect(WorkerRoulette).not_to be_nil
|
20
|
-
end
|
21
|
-
|
22
|
-
context Foreman do
|
23
|
-
let(:subject) {WorkerRoulette.foreman(sender)}
|
24
|
-
|
25
|
-
it "should be working on behalf of a sender" do
|
26
|
-
expect(subject.sender).to eq(sender)
|
15
|
+
it "should exist" do
|
16
|
+
expect(worker_roulette).to be_instance_of(WorkerRoulette)
|
27
17
|
end
|
28
18
|
|
29
|
-
|
30
|
-
subject.
|
31
|
-
subject.enqueue_work_order(work_orders.last) {}
|
32
|
-
expect(redis.lrange(sender, 0, -1)).to eq(work_orders.map {|m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) })
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should enqueue_work_order an array of work_orders without headers in the sender's slot in the switchboard" do
|
36
|
-
subject.enqueue_work_order_without_headers(work_orders)
|
37
|
-
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders)])
|
38
|
-
end
|
19
|
+
context Foreman do
|
20
|
+
let(:subject) {worker_roulette.foreman(sender)}
|
39
21
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
22
|
+
it "should be working on behalf of a sender" do
|
23
|
+
expect(subject.sender).to eq(sender)
|
24
|
+
end
|
44
25
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
26
|
+
it "should enqueue_work_order two work_orders in the sender's work queue" do
|
27
|
+
subject.enqueue_work_order(work_orders.first) {}
|
28
|
+
subject.enqueue_work_order(work_orders.last) {}
|
29
|
+
expect(redis.lrange(sender, 0, -1)).to eq(work_orders.map {|m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) })
|
30
|
+
end
|
51
31
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
32
|
+
it "should enqueue_work_order an array of work_orders without headers in the sender's work queue" do
|
33
|
+
subject.enqueue_work_order_without_headers(work_orders)
|
34
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders)])
|
35
|
+
end
|
57
36
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
expect(redis.get(subject.counter_key)).to eq("1")
|
63
|
-
subject.enqueue_work_order(work_orders.last)
|
64
|
-
expect(redis.get(subject.counter_key)).to eq("1")
|
65
|
-
other_forman.enqueue_work_order(work_orders.last)
|
66
|
-
expect(redis.get(other_forman.counter_key)).to eq("2")
|
67
|
-
end
|
37
|
+
it "should enqueue_work_order an array of work_orders with default headers in the sender's work queue" do
|
38
|
+
subject.enqueue_work_order(work_orders)
|
39
|
+
expect(redis.lrange(sender, 0, -1)).to eq(jsonized_work_orders_with_headers)
|
40
|
+
end
|
68
41
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
42
|
+
it "should enqueue_work_order an array of work_orders with additional headers in the sender's work queue" do
|
43
|
+
extra_headers = {'foo' => 'bars'}
|
44
|
+
subject.enqueue_work_order(work_orders, extra_headers)
|
45
|
+
work_orders_with_headers['headers'].merge!(extra_headers)
|
46
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders_with_headers)])
|
47
|
+
end
|
76
48
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
49
|
+
it "should post the sender's id to the job board with an order number" do
|
50
|
+
subject.enqueue_work_order(work_orders.first)
|
51
|
+
worker_roulette.foreman('other_forman').enqueue_work_order(work_orders.last)
|
52
|
+
expect(redis.zrange(subject.job_board_key, 0, -1, with_scores: true)).to eq([[sender, 1.0], ["other_forman", 2.0]])
|
81
53
|
end
|
82
54
|
|
83
|
-
|
55
|
+
it "should generate a monotically increasing score for senders not on the job board, but not for senders already there" do
|
56
|
+
other_forman = worker_roulette.foreman('other_forman')
|
57
|
+
expect(redis.get(subject.counter_key)).to be_nil
|
58
|
+
subject.enqueue_work_order(work_orders.first)
|
59
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
60
|
+
subject.enqueue_work_order(work_orders.last)
|
61
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
62
|
+
other_forman.enqueue_work_order(work_orders.last)
|
63
|
+
expect(redis.get(other_forman.counter_key)).to eq("2")
|
64
|
+
end
|
84
65
|
end
|
85
|
-
end
|
86
66
|
|
87
|
-
|
88
|
-
|
89
|
-
|
67
|
+
context Tradesman do
|
68
|
+
let(:foreman) {worker_roulette.foreman(sender)}
|
69
|
+
let(:subject) {worker_roulette.tradesman}
|
90
70
|
|
91
|
-
|
92
|
-
|
93
|
-
|
71
|
+
before do
|
72
|
+
foreman.enqueue_work_order(work_orders)
|
73
|
+
end
|
94
74
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
75
|
+
it "should have a last sender if it found messages" do
|
76
|
+
expect(subject.work_orders!.length).to eq(1)
|
77
|
+
expect(subject.last_sender).to eq(sender)
|
78
|
+
end
|
99
79
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
80
|
+
it "should not have a last sender if it found no messages" do
|
81
|
+
expect(subject.work_orders!.length).to eq(1)
|
82
|
+
expect(subject.work_orders!.length).to eq(0)
|
83
|
+
expect(subject.last_sender).to be_nil
|
84
|
+
end
|
105
85
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
86
|
+
it "should drain one set of work_orders from the sender's work queue" do
|
87
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
88
|
+
expect(subject.work_orders!).to be_empty
|
89
|
+
expect(subject.work_orders!).to be_empty #does not throw an error if queue is already empty
|
90
|
+
end
|
111
91
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
92
|
+
it "should drain all the work_orders from the sender's work queue" do
|
93
|
+
foreman.enqueue_work_order(work_orders)
|
94
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers, work_orders_with_headers])
|
95
|
+
expect(subject.work_orders!).to be_empty
|
96
|
+
expect(subject.work_orders!).to be_empty #does not throw an error if queue is already empty
|
97
|
+
end
|
118
98
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
it "should take the oldest sender off the job board (FIFO)" do
|
100
|
+
oldest_sender = sender.to_s
|
101
|
+
most_recent_sender = 'most_recent_sender'
|
102
|
+
most_recent_foreman = worker_roulette.foreman(most_recent_sender)
|
103
|
+
most_recent_foreman.enqueue_work_order(work_orders)
|
104
|
+
expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([oldest_sender, most_recent_sender])
|
105
|
+
subject.work_orders!
|
106
|
+
expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([most_recent_sender])
|
107
|
+
end
|
128
108
|
|
129
|
-
|
130
|
-
|
131
|
-
|
109
|
+
it "should get the work_orders from the next queue when a new job is ready, then poll for new work" do
|
110
|
+
subject.work_orders!
|
111
|
+
expect(subject).to receive(:work_orders!).and_call_original
|
112
|
+
expect(subject.timer).to receive(:after)
|
132
113
|
|
133
|
-
|
114
|
+
foreman.enqueue_work_order(work_orders)
|
134
115
|
|
135
|
-
|
136
|
-
|
137
|
-
|
116
|
+
subject.wait_for_work_orders do |redis_work_orders|
|
117
|
+
expect(redis_work_orders).to eq([work_orders_with_headers])
|
118
|
+
expect(subject.last_sender).to eq('katie_80')
|
119
|
+
end
|
138
120
|
end
|
139
|
-
end
|
140
|
-
|
141
|
-
it "should publish and subscribe on custom channels" do
|
142
|
-
tradesman = WorkerRoulette.tradesman('good_channel')
|
143
|
-
expect(tradesman).to receive(:work_orders!).twice.and_call_original
|
144
121
|
|
145
|
-
|
146
|
-
|
122
|
+
it "should publish and subscribe on custom channels" do
|
123
|
+
tradesman = worker_roulette.tradesman('good_channel')
|
124
|
+
expect(tradesman).to receive(:work_orders!).and_call_original
|
147
125
|
|
126
|
+
good_foreman = worker_roulette.foreman('foreman', 'good_channel')
|
127
|
+
bad_foreman = worker_roulette.foreman('foreman', 'bad_channel')
|
148
128
|
|
149
|
-
publish = -> do
|
150
129
|
good_foreman.enqueue_work_order('some old fashion work')
|
151
130
|
bad_foreman.enqueue_work_order('evil biddings you should not carry out')
|
152
|
-
tradesman.unsubscribe
|
153
|
-
end
|
154
131
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
132
|
+
tradesman.wait_for_work_orders do |work|
|
133
|
+
expect(work.to_s).to match("some old fashion work")
|
134
|
+
expect(work.to_s).not_to match("evil")
|
135
|
+
expect(tradesman.last_sender).to eq('foreman')
|
136
|
+
end
|
159
137
|
end
|
160
|
-
end
|
161
138
|
|
162
|
-
|
163
|
-
|
164
|
-
|
139
|
+
context "Failure" do
|
140
|
+
it "should not put the sender_id and work_orders back if processing fails bc new work_orders may have been processed while that process failed" do; end
|
141
|
+
end
|
165
142
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
143
|
+
context "Concurrent Access" do
|
144
|
+
it "should pool its connections" do
|
145
|
+
Array.new(100) do
|
146
|
+
Thread.new {worker_roulette.tradesman_connection_pool.with {|pooled_redis| pooled_redis.get("foo")}}
|
147
|
+
end.each(&:join)
|
148
|
+
worker_roulette.tradesman_connection_pool.with do |pooled_redis|
|
149
|
+
expect(pooled_redis.info["connected_clients"].to_i).to be > (worker_roulette.pool_size)
|
150
|
+
end
|
173
151
|
end
|
174
|
-
end
|
175
152
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
153
|
+
#This may be fixed soon (10 Feb 2014 - https://github.com/redis/redis-rb/pull/389 and https://github.com/redis/redis-rb/issues/364)
|
154
|
+
it "should not be fork() proof -- forking reconnects need to be handled in the calling code (until redis gem is udpated, then we should be fork-proof)" do
|
155
|
+
instance = WorkerRoulette.start
|
156
|
+
instance.tradesman_connection_pool.with {|pooled_redis| pooled_redis.get("foo")}
|
157
|
+
fork do
|
158
|
+
expect {instance.tradesman_connection_pool.with {|pooled_redis| pooled_redis.get("foo")}}.to raise_error(Redis::InheritedError)
|
159
|
+
end
|
182
160
|
end
|
183
161
|
end
|
184
162
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
1
|
+
module WorkerRoulette
|
2
|
+
require 'worker_roulette'
|
3
|
+
require 'evented-spec'
|
4
|
+
require 'rspec'
|
5
|
+
require 'pry'
|
5
6
|
|
6
|
-
require File.expand_path(File.join("..", "..", "lib", "worker_roulette.rb"), __FILE__)
|
7
|
-
include WorkerRoulette
|
7
|
+
require File.expand_path(File.join("..", "..", "lib", "worker_roulette.rb"), __FILE__)
|
8
8
|
|
9
|
-
Dir[File.join(File.dirname(__FILE__), 'helpers', '**/*.rb')].sort.each { |file| require file.gsub(".rb", "")}
|
9
|
+
Dir[File.join(File.dirname(__FILE__), 'helpers', '**/*.rb')].sort.each { |file| require file.gsub(".rb", "")}
|
10
10
|
|
11
|
-
EM::Hiredis.reconnect_timeout = 0.01
|
11
|
+
EM::Hiredis.reconnect_timeout = 0.01
|
12
12
|
|
13
|
-
RSpec.configure do |c|
|
14
|
-
|
15
|
-
|
13
|
+
RSpec.configure do |c|
|
14
|
+
c.after(:each) do
|
15
|
+
Redis.new(WorkerRoulette.start.redis_config).flushdb
|
16
|
+
end
|
16
17
|
end
|
17
|
-
end
|
18
|
+
end
|
@@ -1,23 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
module WorkerRoulette
|
3
|
-
|
3
|
+
describe "Evented Read Lock" do
|
4
4
|
include EventedSpec::EMSpec
|
5
5
|
|
6
|
-
let(:redis) {Redis.new(WorkerRoulette.redis_config)}
|
6
|
+
let(:redis) {Redis.new(WorkerRoulette.start.redis_config)}
|
7
7
|
let(:sender) {'katie_80'}
|
8
8
|
let(:work_orders) {"hellot"}
|
9
9
|
let(:lock_key) {"L*:#{sender}"}
|
10
10
|
let(:default_headers) {Hash['headers' => {'sender' => sender}]}
|
11
11
|
let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
|
12
12
|
let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
|
13
|
-
let(:
|
14
|
-
let(:
|
15
|
-
let(:
|
16
|
-
let(:
|
13
|
+
let(:worker_roulette) { WorkerRoulette.start(evented: true) }
|
14
|
+
let(:foreman) {worker_roulette.foreman(sender)}
|
15
|
+
let(:number_two) {worker_roulette.foreman('number_two')}
|
16
|
+
let(:subject) {worker_roulette.tradesman}
|
17
|
+
let(:subject_two) {worker_roulette.tradesman}
|
18
|
+
let(:lua) { Lua.new(worker_roulette.tradesman_connection_pool) }
|
17
19
|
|
18
20
|
em_before do
|
19
|
-
|
20
|
-
Lua.clear_cache!
|
21
|
+
lua.clear_cache!
|
21
22
|
redis.script(:flush)
|
22
23
|
redis.flushdb
|
23
24
|
end
|
@@ -29,9 +30,9 @@ module WorkerRoulette
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
it "should set the lock to expire in
|
33
|
+
it "should set the lock to expire in 3 seconds" do
|
33
34
|
evented_readlock_preconditions do
|
34
|
-
expect(redis.ttl(lock_key)).to eq(
|
35
|
+
expect(redis.ttl(lock_key)).to eq(3)
|
35
36
|
done
|
36
37
|
end
|
37
38
|
end
|
@@ -45,13 +46,13 @@ module WorkerRoulette
|
|
45
46
|
end
|
46
47
|
|
47
48
|
it "should read from the first available queue that is not locked" do
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
evented_readlock_preconditions do
|
50
|
+
foreman.enqueue_work_order(work_orders) do #locked
|
51
|
+
number_two.enqueue_work_order(work_orders) do #unlocked
|
51
52
|
subject_two.work_orders!{|work| expect(work.first['headers']['sender']).to eq('number_two'); done}
|
52
53
|
end
|
53
|
-
|
54
|
-
|
54
|
+
end
|
55
|
+
end
|
55
56
|
end
|
56
57
|
|
57
58
|
it "should release its last lock when it asks for its next work order from another sender" do
|
@@ -94,13 +95,13 @@ module WorkerRoulette
|
|
94
95
|
end
|
95
96
|
end
|
96
97
|
end
|
97
|
-
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
def evented_readlock_preconditions(&spec_block)
|
100
|
+
foreman.enqueue_work_order(work_orders) do
|
101
|
+
subject.work_orders! do |work|
|
102
|
+
expect(work).to eq([work_orders_with_headers])
|
103
|
+
spec_block.call
|
104
|
+
end
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
data/spec/unit/lua_spec.rb
CHANGED
@@ -2,23 +2,21 @@ require 'spec_helper'
|
|
2
2
|
module WorkerRoulette
|
3
3
|
describe Lua do
|
4
4
|
include EventedSpec::EMSpec
|
5
|
-
let(:
|
6
|
-
|
7
|
-
|
8
|
-
WorkerRoulette.start(evented: true)
|
9
|
-
end
|
5
|
+
let(:worker_roulette) {WorkerRoulette.start(evented: true)}
|
6
|
+
let(:lua) { Lua.new(worker_roulette.tradesman_connection_pool) }
|
7
|
+
let(:redis) {Redis.new(worker_roulette.redis_config)}
|
10
8
|
|
11
9
|
before do
|
12
|
-
|
10
|
+
lua.clear_cache!
|
13
11
|
redis.script(:flush)
|
14
12
|
redis.flushdb
|
15
13
|
end
|
16
14
|
|
17
15
|
it "should load and call a lua script" do
|
18
16
|
lua_script = 'return redis.call("SET", KEYS[1], ARGV[1])'
|
19
|
-
|
20
|
-
expect(
|
21
|
-
expect(
|
17
|
+
lua.call(lua_script, ['foo'], ['daddy']) do |result|
|
18
|
+
expect(lua.cache.keys.first).to eq(lua_script)
|
19
|
+
expect(lua.cache.values.first).to eq(Digest::SHA1.hexdigest(lua_script))
|
22
20
|
expect(result).to eq("OK")
|
23
21
|
done
|
24
22
|
end
|
@@ -26,12 +24,12 @@ module WorkerRoulette
|
|
26
24
|
|
27
25
|
it "should send a sha instead of a script once the script has been cached" do
|
28
26
|
lua_script = 'return KEYS'
|
29
|
-
expect(
|
27
|
+
expect(lua).to receive(:eval).and_call_original
|
30
28
|
|
31
|
-
|
32
|
-
expect(
|
29
|
+
lua.call(lua_script) do |result|
|
30
|
+
expect(lua).not_to receive(:eval)
|
33
31
|
|
34
|
-
|
32
|
+
lua.call(lua_script) do |inner_result|
|
35
33
|
expect(inner_result).to be_empty
|
36
34
|
done
|
37
35
|
end
|
@@ -40,7 +38,7 @@ module WorkerRoulette
|
|
40
38
|
|
41
39
|
it "should raise an error to the caller if the script fails in redis" do
|
42
40
|
lua_script = 'this is junk'
|
43
|
-
#
|
41
|
+
# lua.call(lua_script)
|
44
42
|
# rspec cannot test this bc of the callbacks, but if you have doubts,
|
45
43
|
# uncomment the line above and watch it fail
|
46
44
|
done
|
data/spec/unit/readlock_spec.rb
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
module WorkerRoulette
|
3
3
|
describe "Read Lock" do
|
4
|
-
let(:
|
4
|
+
let(:worker_roulette) {WorkerRoulette.start(evented: false)}
|
5
|
+
let(:redis) {Redis.new(worker_roulette.redis_config)}
|
5
6
|
let(:sender) {'katie_80'}
|
6
7
|
let(:work_orders) {"hellot"}
|
7
8
|
let(:lock_key) {"L*:#{sender}"}
|
8
9
|
let(:default_headers) {Hash['headers' => {'sender' => sender}]}
|
9
10
|
let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
|
10
11
|
let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
|
11
|
-
let(:foreman) {
|
12
|
-
let(:number_two) {
|
13
|
-
let(:subject) {
|
14
|
-
let(:subject_two) {
|
12
|
+
let(:foreman) {worker_roulette.foreman(sender)}
|
13
|
+
let(:number_two) {worker_roulette.foreman('number_two')}
|
14
|
+
let(:subject) {worker_roulette.tradesman}
|
15
|
+
let(:subject_two) {worker_roulette.tradesman}
|
16
|
+
let(:lua) { Lua.new(worker_roulette.tradesman_connection_pool) }
|
15
17
|
|
16
18
|
before do
|
17
|
-
|
18
|
-
Lua.clear_cache!
|
19
|
+
lua.clear_cache!
|
19
20
|
redis.script(:flush)
|
20
21
|
redis.flushdb
|
21
22
|
foreman.enqueue_work_order(work_orders)
|
@@ -26,8 +27,8 @@ module WorkerRoulette
|
|
26
27
|
expect(redis.get(lock_key)).not_to be_nil
|
27
28
|
end
|
28
29
|
|
29
|
-
it "should set the lock to expire in
|
30
|
-
expect(redis.ttl(lock_key)).to eq(
|
30
|
+
it "should set the lock to expire in 3 second" do
|
31
|
+
expect(redis.ttl(lock_key)).to eq(3)
|
31
32
|
end
|
32
33
|
|
33
34
|
it "should not read a locked queue" do
|
@@ -66,7 +67,5 @@ module WorkerRoulette
|
|
66
67
|
expect(subject.work_orders!).to be_empty
|
67
68
|
expect(redis.get(lock_key)).to be_nil
|
68
69
|
end
|
69
|
-
|
70
|
-
pending "pubsub should clean up one contention or remove the lock on the same sender queue automaticly"
|
71
70
|
end
|
72
71
|
end
|
data/worker_roulette.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency 'em-hiredis', '~> 0.2.1'
|
24
24
|
spec.add_dependency 'connection_pool'
|
25
25
|
spec.add_dependency 'eventmachine', '~> 1.0.3'
|
26
|
+
spec.add_dependency 'timers', '~> 3.0.1'
|
26
27
|
|
27
28
|
spec.add_development_dependency 'bundler'
|
28
29
|
spec.add_development_dependency 'rake'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: worker_roulette
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Saieg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ~>
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.0.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timers
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.0.1
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.0.1
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: bundler
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -236,11 +250,11 @@ files:
|
|
236
250
|
- README.md
|
237
251
|
- Rakefile
|
238
252
|
- lib/worker_roulette.rb
|
239
|
-
- lib/worker_roulette/a_tradesman.rb
|
240
253
|
- lib/worker_roulette/foreman.rb
|
241
254
|
- lib/worker_roulette/lua.rb
|
242
255
|
- lib/worker_roulette/tradesman.rb
|
243
256
|
- lib/worker_roulette/version.rb
|
257
|
+
- spec/benchmark/irb_demo_runner.rb
|
244
258
|
- spec/benchmark/perf_test.rb
|
245
259
|
- spec/helpers/.gitkeep
|
246
260
|
- spec/integration/evented_worker_roulette_spec.rb
|
@@ -249,7 +263,6 @@ files:
|
|
249
263
|
- spec/unit/evented_readlock_spec.rb
|
250
264
|
- spec/unit/lua_spec.rb
|
251
265
|
- spec/unit/readlock_spec.rb
|
252
|
-
- spec/unit/worker_roulette_spec.rb
|
253
266
|
- worker_roulette.gemspec
|
254
267
|
homepage: https://github.com/nexiahome/worker_roulette
|
255
268
|
licenses: []
|
@@ -275,6 +288,7 @@ signing_key:
|
|
275
288
|
specification_version: 4
|
276
289
|
summary: Pub Sub Queue for Redis that ensures ordered processing
|
277
290
|
test_files:
|
291
|
+
- spec/benchmark/irb_demo_runner.rb
|
278
292
|
- spec/benchmark/perf_test.rb
|
279
293
|
- spec/helpers/.gitkeep
|
280
294
|
- spec/integration/evented_worker_roulette_spec.rb
|
@@ -283,4 +297,3 @@ test_files:
|
|
283
297
|
- spec/unit/evented_readlock_spec.rb
|
284
298
|
- spec/unit/lua_spec.rb
|
285
299
|
- spec/unit/readlock_spec.rb
|
286
|
-
- spec/unit/worker_roulette_spec.rb
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require_relative './tradesman'
|
2
|
-
module WorkerRoulette
|
3
|
-
class ATradesman < Tradesman
|
4
|
-
attr_reader :timer
|
5
|
-
|
6
|
-
def wait_for_work_orders(on_subscribe_callback = nil, &on_message_callback)
|
7
|
-
@redis_pubsub ||= WorkerRoulette.new_redis_pubsub #cannot use connection pool bc redis expects each obj to own its own pubsub connection for the life of the subscription
|
8
|
-
@redis_pubsub.on(:subscribe) {|channel, subscription_count| on_subscribe_callback.call(channel, subscription_count) if on_subscribe_callback}
|
9
|
-
@redis_pubsub.on(:message) {|channel, message| get_messages(message, channel, on_message_callback)}
|
10
|
-
set_timer(on_message_callback);
|
11
|
-
@redis_pubsub.subscribe(@channel)
|
12
|
-
end
|
13
|
-
|
14
|
-
def unsubscribe(&callback)
|
15
|
-
deferable = @redis_pubsub.unsubscribe(@channel)
|
16
|
-
deferable.callback do
|
17
|
-
@redis_pubsub.close_connection
|
18
|
-
@redis_pubsub = nil
|
19
|
-
callback.call
|
20
|
-
end
|
21
|
-
deferable.errback do
|
22
|
-
@redis_pubsub.close_connection
|
23
|
-
@redis_pubsub = nil
|
24
|
-
callback.call
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def get_messages(message, channel, on_message_callback)
|
31
|
-
return unless on_message_callback
|
32
|
-
work_orders! do |work_orders_1|
|
33
|
-
work_orders! do |work_orders|
|
34
|
-
on_message_callback.call(work_orders_1 + work_orders, message, channel)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def set_timer(on_message_callback)
|
40
|
-
return if (@timer || !on_message_callback)
|
41
|
-
@timer = EM::PeriodicTimer.new(rand(20..25)) do
|
42
|
-
work_orders! {|work_orders| on_message_callback.call(work_orders, nil, nil)}
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|