worker_roulette 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +3 -1
- data/Gemfile +5 -0
- data/lib/worker_roulette/a_tradesman.rb +4 -2
- data/lib/worker_roulette/foreman.rb +33 -30
- data/lib/worker_roulette/tradesman.rb +42 -38
- data/lib/worker_roulette/version.rb +1 -1
- data/lib/worker_roulette.rb +5 -3
- data/spec/integration/evented_worker_roulette_spec.rb +40 -41
- data/spec/integration/worker_roulette_spec.rb +33 -33
- data/spec/unit/evented_readlock_spec.rb +16 -16
- data/spec/unit/lua_spec.rb +8 -8
- data/spec/unit/readlock_spec.rb +18 -20
- data/worker_roulette.gemspec +10 -11
- metadata +8 -22
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
M2EzZmZkYTllZGYwMjA1MDI1MzVmZDU5ODk1ZGRiNzYzMjQzMjQ1OQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDZmNjhjMTJjZDJhMjJlNzI5MjE2MjllZjFkMGQ1ZDIxODExNTQ4NQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjAyNTBlZDUxNmM5M2Y1MGMwMGYyNzdkNDUzNDEzMGQxNTI0ODY4NTlhY2U1
|
10
|
+
YjRiYjQ1MWI3YzMxNDdmZWM3ZjE1Y2ViZDg1M2FhYTA1ZjhmYzNhYWYwMjY2
|
11
|
+
ZThkODM4ZjY0MmE5NzlmMTdlZTRiMzRhMzUwMWFjMGJkM2NlYTU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODUxMmQwNWNhOWI5NTBkMmQ2NzFlMzUzY2Q4NjZlZTYzZDE4ZmQ2NGZiODJk
|
14
|
+
ZWNkMzc3ZmUxY2UzOTk4OTJjZjlmNDZkMjU4N2Y3ZGVjMmUzYzIxOTdmMzg2
|
15
|
+
MDJlZjAzNGFiN2JjMGMzNWEwYWVlNmY3OTQ4MjE0NjliY2ViOTE=
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require_relative './tradesman'
|
2
2
|
module WorkerRoulette
|
3
3
|
class ATradesman < Tradesman
|
4
|
+
attr_reader :timer
|
5
|
+
|
4
6
|
def wait_for_work_orders(on_subscribe_callback = nil, &on_message_callback)
|
5
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
|
6
8
|
@redis_pubsub.on(:subscribe) {|channel, subscription_count| on_subscribe_callback.call(channel, subscription_count) if on_subscribe_callback}
|
@@ -23,8 +25,8 @@ module WorkerRoulette
|
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
private
|
29
|
+
|
28
30
|
def get_messages(message, channel, on_message_callback)
|
29
31
|
return unless on_message_callback
|
30
32
|
work_orders! do |work_orders_1|
|
@@ -2,6 +2,34 @@ module WorkerRoulette
|
|
2
2
|
class Foreman
|
3
3
|
attr_reader :sender
|
4
4
|
|
5
|
+
LUA_ENQUEUE_WORK_ORDERS = <<-HERE
|
6
|
+
local counter_key = KEYS[1]
|
7
|
+
local job_board_key = KEYS[2]
|
8
|
+
local sender_key = KEYS[3]
|
9
|
+
local channel = KEYS[4]
|
10
|
+
|
11
|
+
local work_order = ARGV[1]
|
12
|
+
local job_notification = ARGV[2]
|
13
|
+
local redis_call = redis.call
|
14
|
+
local zscore = 'ZSCORE'
|
15
|
+
local incr = 'INCR'
|
16
|
+
local zadd = 'ZADD'
|
17
|
+
local rpush = 'RPUSH'
|
18
|
+
local publish = 'PUBLISH'
|
19
|
+
|
20
|
+
local function enqueue_work_orders(work_order, job_notification)
|
21
|
+
if (redis_call(zscore, job_board_key, sender_key) == false) then
|
22
|
+
local count = redis_call(incr, counter_key)
|
23
|
+
redis_call(zadd, job_board_key, count, sender_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
redis_call(rpush,sender_key, work_order)
|
27
|
+
redis_call(publish, channel, job_notification)
|
28
|
+
end
|
29
|
+
|
30
|
+
enqueue_work_orders(work_order, job_notification)
|
31
|
+
HERE
|
32
|
+
|
5
33
|
def initialize(sender, redis_pool, namespace = nil)
|
6
34
|
@sender = sender
|
7
35
|
@namespace = namespace
|
@@ -16,7 +44,7 @@ module WorkerRoulette
|
|
16
44
|
|
17
45
|
def enqueue_work_order_without_headers(work_order, &callback)
|
18
46
|
Lua.call(self.class.lua_enqueue_work_orders, [counter_key, job_board_key, sender_key, @channel],
|
19
|
-
|
47
|
+
[WorkerRoulette.dump(work_order), WorkerRoulette::JOB_NOTIFICATIONS], &callback)
|
20
48
|
end
|
21
49
|
|
22
50
|
def job_board_key
|
@@ -27,7 +55,8 @@ module WorkerRoulette
|
|
27
55
|
@counter_key ||= WorkerRoulette.counter_key(@namespace)
|
28
56
|
end
|
29
57
|
|
30
|
-
|
58
|
+
private
|
59
|
+
|
31
60
|
def sender_key
|
32
61
|
@sender_key = WorkerRoulette.sender_key(sender, @namespace)
|
33
62
|
end
|
@@ -37,33 +66,7 @@ module WorkerRoulette
|
|
37
66
|
end
|
38
67
|
|
39
68
|
def self.lua_enqueue_work_orders
|
40
|
-
|
41
|
-
local counter_key = KEYS[1]
|
42
|
-
local job_board_key = KEYS[2]
|
43
|
-
local sender_key = KEYS[3]
|
44
|
-
local channel = KEYS[4]
|
45
|
-
|
46
|
-
local work_order = ARGV[1]
|
47
|
-
local job_notification = ARGV[2]
|
48
|
-
local redis_call = redis.call
|
49
|
-
local zscore = 'ZSCORE'
|
50
|
-
local incr = 'INCR'
|
51
|
-
local zadd = 'ZADD'
|
52
|
-
local rpush = 'RPUSH'
|
53
|
-
local publish = 'PUBLISH'
|
54
|
-
|
55
|
-
local function enqueue_work_orders(work_order, job_notification)
|
56
|
-
if (redis_call(zscore, job_board_key, sender_key) == false) then
|
57
|
-
local count = redis_call(incr, counter_key)
|
58
|
-
redis_call(zadd, job_board_key, count, sender_key)
|
59
|
-
end
|
60
|
-
|
61
|
-
redis_call(rpush,sender_key, work_order)
|
62
|
-
redis_call(publish, channel, job_notification)
|
63
|
-
end
|
64
|
-
|
65
|
-
enqueue_work_orders(work_order, job_notification)
|
66
|
-
HERE
|
69
|
+
LUA_ENQUEUE_WORK_ORDERS
|
67
70
|
end
|
68
71
|
end
|
69
|
-
end
|
72
|
+
end
|
@@ -1,43 +1,8 @@
|
|
1
1
|
module WorkerRoulette
|
2
2
|
class Tradesman
|
3
3
|
attr_reader :last_sender
|
4
|
-
def initialize(client_pool, pubsub_pool, namespace = nil)
|
5
|
-
@client_pool = client_pool
|
6
|
-
@pubsub_pool = pubsub_pool
|
7
|
-
@namespace = namespace
|
8
|
-
@channel = namespace || WorkerRoulette::JOB_NOTIFICATIONS
|
9
|
-
end
|
10
|
-
|
11
|
-
def wait_for_work_orders(on_subscribe_callback = nil, &block)
|
12
|
-
@pubsub_pool.with do |redis|
|
13
|
-
redis.subscribe(@channel) do |on|
|
14
|
-
on.subscribe {on_subscribe_callback.call if on_subscribe_callback}
|
15
|
-
on.message {block.call(work_orders! + work_orders!) if block}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def work_orders!(&callback)
|
21
|
-
Lua.call(self.class.lua_drain_work_orders, [job_board_key, @last_sender], [nil]) do |results|
|
22
|
-
results ||= []
|
23
|
-
@last_sender = (results.first || '').split(':').first
|
24
|
-
work = (results[1] || []).map {|work_order| WorkerRoulette.load(work_order)}
|
25
|
-
callback.call work if callback
|
26
|
-
work
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def unsubscribe
|
31
|
-
@pubsub_pool.with {|redis| redis.unsubscribe(@channel)}
|
32
|
-
end
|
33
4
|
|
34
|
-
|
35
|
-
@job_board_key ||= WorkerRoulette.job_board_key(@namespace)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
def self.lua_drain_work_orders
|
40
|
-
<<-HERE
|
5
|
+
LUA_DRAIN_WORK_ORDERS = <<-HERE
|
41
6
|
local empty_string = ""
|
42
7
|
local job_board_key = KEYS[1]
|
43
8
|
local last_sender_key = KEYS[2] or empty_string
|
@@ -88,7 +53,46 @@ module WorkerRoulette
|
|
88
53
|
end
|
89
54
|
|
90
55
|
return drain_work_orders(job_board_key, last_sender_key, empty_string)
|
91
|
-
|
56
|
+
HERE
|
57
|
+
|
58
|
+
def initialize(client_pool, pubsub_pool, namespace = nil)
|
59
|
+
@client_pool = client_pool
|
60
|
+
@pubsub_pool = pubsub_pool
|
61
|
+
@namespace = namespace
|
62
|
+
@channel = namespace || WorkerRoulette::JOB_NOTIFICATIONS
|
63
|
+
end
|
64
|
+
|
65
|
+
def wait_for_work_orders(on_subscribe_callback = nil, &block)
|
66
|
+
@pubsub_pool.with do |redis|
|
67
|
+
redis.subscribe(@channel) do |on|
|
68
|
+
on.subscribe {on_subscribe_callback.call if on_subscribe_callback}
|
69
|
+
on.message {block.call(work_orders! + work_orders!) if block}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def work_orders!(&callback)
|
75
|
+
Lua.call(self.class.lua_drain_work_orders, [job_board_key, @last_sender], [nil]) do |results|
|
76
|
+
results ||= []
|
77
|
+
@last_sender = (results.first || '').split(':').first
|
78
|
+
work = (results[1] || []).map {|work_order| WorkerRoulette.load(work_order)}
|
79
|
+
callback.call work if callback
|
80
|
+
work
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def unsubscribe
|
85
|
+
@pubsub_pool.with {|redis| redis.unsubscribe(@channel)}
|
86
|
+
end
|
87
|
+
|
88
|
+
def job_board_key
|
89
|
+
@job_board_key ||= WorkerRoulette.job_board_key(@namespace)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def self.lua_drain_work_orders
|
95
|
+
LUA_DRAIN_WORK_ORDERS
|
92
96
|
end
|
93
97
|
end
|
94
|
-
end
|
98
|
+
end
|
data/lib/worker_roulette.rb
CHANGED
@@ -13,7 +13,7 @@ module WorkerRoulette
|
|
13
13
|
JOB_NOTIFICATIONS = "new_job_ready"
|
14
14
|
|
15
15
|
def self.start(config = {})
|
16
|
-
@redis_config = {host: 'localhost', port: 6379, db: 14, driver: :hiredis, timeout: 5, evented: false, pool_size: 10}.merge(config)
|
16
|
+
@redis_config = { host: 'localhost', port: 6379, db: 14, driver: :hiredis, timeout: 5, evented: false, pool_size: 10 }.merge(config)
|
17
17
|
@pool_config = Hash[size: @redis_config.delete(:pool_size), timeout: @redis_config.delete(:timeout)]
|
18
18
|
@evented = @redis_config.delete(:evented)
|
19
19
|
|
@@ -80,7 +80,9 @@ module WorkerRoulette
|
|
80
80
|
def self.counter_key(sender, namespace = nil)
|
81
81
|
"#{namespace + ':' if namespace}counter_key"
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
|
+
private
|
85
|
+
|
84
86
|
def self.new_redis
|
85
87
|
if @evented
|
86
88
|
require 'eventmachine'
|
@@ -93,7 +95,7 @@ private
|
|
93
95
|
|
94
96
|
def self.new_redis_pubsub
|
95
97
|
if @evented
|
96
|
-
|
98
|
+
new_redis.pubsub
|
97
99
|
else
|
98
100
|
new_redis
|
99
101
|
end
|
@@ -20,19 +20,20 @@ describe WorkerRoulette do
|
|
20
20
|
context "Evented Foreman" do
|
21
21
|
let(:subject) {WorkerRoulette.a_foreman(sender)}
|
22
22
|
|
23
|
-
it "
|
23
|
+
it "enqueues work" do
|
24
24
|
called = false
|
25
|
-
foreman
|
25
|
+
foreman = WorkerRoulette.a_foreman('foreman')
|
26
26
|
foreman.enqueue_work_order('some old fashion work') do |redis_response, stuff|
|
27
27
|
called = true
|
28
28
|
end
|
29
|
-
done(0.1) {called.
|
29
|
+
done(0.1) { expect(called).to be_truthy }
|
30
30
|
end
|
31
31
|
|
32
32
|
it "should enqueue_work_order two work_orders in the sender's slot in the job board" do
|
33
33
|
subject.enqueue_work_order(work_orders.first) do
|
34
34
|
subject.enqueue_work_order(work_orders.last) do
|
35
|
-
|
35
|
+
expected = work_orders.map { |m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) }
|
36
|
+
expect(redis.lrange(sender, 0, -1)).to eq(expected)
|
36
37
|
done
|
37
38
|
end
|
38
39
|
end
|
@@ -40,14 +41,14 @@ describe WorkerRoulette do
|
|
40
41
|
|
41
42
|
it "should enqueue_work_order an array of work_orders without headers in the sender's slot in the job board" do
|
42
43
|
subject.enqueue_work_order_without_headers(work_orders) do
|
43
|
-
redis.lrange(sender, 0, -1).
|
44
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders)])
|
44
45
|
done
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
49
|
it "should enqueue_work_order an array of work_orders with default headers in the sender's slot in the job board" do
|
49
50
|
subject.enqueue_work_order(work_orders) do
|
50
|
-
redis.lrange(sender, 0, -1).
|
51
|
+
expect(redis.lrange(sender, 0, -1)).to eq(jsonized_work_orders_with_headers)
|
51
52
|
done
|
52
53
|
end
|
53
54
|
end
|
@@ -56,7 +57,7 @@ describe WorkerRoulette do
|
|
56
57
|
extra_headers = {'foo' => 'bars'}
|
57
58
|
subject.enqueue_work_order(work_orders, extra_headers) do
|
58
59
|
work_orders_with_headers['headers'].merge!(extra_headers)
|
59
|
-
redis.lrange(sender, 0, -1).
|
60
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders_with_headers)])
|
60
61
|
done
|
61
62
|
end
|
62
63
|
end
|
@@ -66,7 +67,7 @@ describe WorkerRoulette do
|
|
66
67
|
first_foreman.enqueue_work_order('foo') do
|
67
68
|
subject.enqueue_work_order(work_orders.first) do
|
68
69
|
subject.enqueue_work_order(work_orders.last) do
|
69
|
-
redis.zrange(subject.job_board_key, 0, -1, with_scores: true).
|
70
|
+
expect(redis.zrange(subject.job_board_key, 0, -1, with_scores: true)).to eq([["first_foreman", 1.0], ["katie_80", 2.0]])
|
70
71
|
done
|
71
72
|
end
|
72
73
|
end
|
@@ -75,13 +76,13 @@ describe WorkerRoulette do
|
|
75
76
|
|
76
77
|
it "should generate a monotically increasing score for senders not on the job board, but not for senders already there" do
|
77
78
|
first_foreman = WorkerRoulette.a_foreman('first_foreman')
|
78
|
-
redis.get(subject.counter_key).
|
79
|
+
expect(redis.get(subject.counter_key)).to be_nil
|
79
80
|
first_foreman.enqueue_work_order(work_orders.first) do
|
80
|
-
redis.get(subject.counter_key).
|
81
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
81
82
|
first_foreman.enqueue_work_order(work_orders.last) do
|
82
|
-
redis.get(subject.counter_key).
|
83
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
83
84
|
subject.enqueue_work_order(work_orders.first) do
|
84
|
-
redis.get(subject.counter_key).
|
85
|
+
expect(redis.get(subject.counter_key)).to eq("2")
|
85
86
|
done
|
86
87
|
end
|
87
88
|
end
|
@@ -93,7 +94,7 @@ describe WorkerRoulette do
|
|
93
94
|
subscriber = WorkerRoulette.new_redis_pubsub
|
94
95
|
subscriber.subscribe(WorkerRoulette::JOB_NOTIFICATIONS) do |message|
|
95
96
|
subscriber.unsubscribe(WorkerRoulette::JOB_NOTIFICATIONS)
|
96
|
-
message.
|
97
|
+
expect(message).to eq(WorkerRoulette::JOB_NOTIFICATIONS)
|
97
98
|
done
|
98
99
|
end.callback { subject.enqueue_work_order(work_orders) }
|
99
100
|
end
|
@@ -106,7 +107,7 @@ describe WorkerRoulette do
|
|
106
107
|
it "should be working on behalf of a sender" do
|
107
108
|
foreman.enqueue_work_order(work_orders) do
|
108
109
|
subject.work_orders! do |r|
|
109
|
-
subject.last_sender.
|
110
|
+
expect(subject.last_sender).to eq(sender)
|
110
111
|
done
|
111
112
|
end
|
112
113
|
end
|
@@ -116,9 +117,9 @@ describe WorkerRoulette do
|
|
116
117
|
it "should drain one set of work_orders from the sender's slot in the job board" do
|
117
118
|
foreman.enqueue_work_order(work_orders) do
|
118
119
|
subject.work_orders! do |r|
|
119
|
-
r.
|
120
|
-
subject.work_orders! do |r| r.
|
121
|
-
|
120
|
+
expect(r).to eq([work_orders_with_headers])
|
121
|
+
subject.work_orders! do |r| expect(r).to be_empty
|
122
|
+
subject.work_orders! {|r| expect(r).to be_empty; done} #does not throw an error if queue is alreay empty
|
122
123
|
end
|
123
124
|
end
|
124
125
|
end
|
@@ -130,19 +131,19 @@ describe WorkerRoulette do
|
|
130
131
|
most_recent_sender = 'most_recent_sender'
|
131
132
|
most_recent_foreman = WorkerRoulette.a_foreman(most_recent_sender)
|
132
133
|
most_recent_foreman.enqueue_work_order(work_orders) do
|
133
|
-
redis.zrange(subject.job_board_key, 0, -1).
|
134
|
-
subject.work_orders! { redis.zrange(subject.job_board_key, 0, -1).
|
134
|
+
expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([oldest_sender, most_recent_sender])
|
135
|
+
subject.work_orders! { expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([most_recent_sender]); done }
|
135
136
|
end
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
139
140
|
it "should get the work_orders from the next queue when a new job is ready" do
|
140
|
-
subject.
|
141
|
+
expect(subject).to receive(:work_orders!).twice.and_call_original
|
141
142
|
publish = proc {foreman.enqueue_work_order(work_orders)}
|
142
143
|
|
143
144
|
subject.wait_for_work_orders(publish) do |redis_work_orders, message, channel|
|
144
|
-
redis_work_orders.
|
145
|
-
subject.last_sender.
|
145
|
+
expect(redis_work_orders).to eq([work_orders_with_headers])
|
146
|
+
expect(subject.last_sender).to be_nil
|
146
147
|
done
|
147
148
|
end
|
148
149
|
end
|
@@ -160,19 +161,19 @@ describe WorkerRoulette do
|
|
160
161
|
good_publish = proc {good_foreman.enqueue_work_order('some old fashion work')}
|
161
162
|
bad_publish = proc {bad_foreman.enqueue_work_order('evil biddings you should not carry out')}
|
162
163
|
|
163
|
-
tradesman.
|
164
|
-
evil_tradesman.
|
164
|
+
expect(tradesman).to receive(:work_orders!).twice.and_call_original
|
165
|
+
expect(evil_tradesman).to receive(:work_orders!).twice.and_call_original
|
165
166
|
|
166
167
|
#They are double subscribing; is it possible that it is the connection pool?
|
167
168
|
|
168
169
|
tradesman.wait_for_work_orders(good_publish) do |good_work|
|
169
|
-
good_work.to_s.
|
170
|
-
good_work.to_s.
|
170
|
+
expect(good_work.to_s).to match("old fashion")
|
171
|
+
expect(good_work.to_s).not_to match("evil")
|
171
172
|
end
|
172
173
|
|
173
174
|
evil_tradesman.wait_for_work_orders(bad_publish) do |bad_work|
|
174
|
-
bad_work.to_s.
|
175
|
-
bad_work.to_s.
|
175
|
+
expect(bad_work.to_s).not_to match("old fashion")
|
176
|
+
expect(bad_work.to_s).to match("evil")
|
176
177
|
end
|
177
178
|
|
178
179
|
done(0.2)
|
@@ -183,51 +184,49 @@ describe WorkerRoulette do
|
|
183
184
|
subject.wait_for_work_orders(publish) do |redis_work_orders, message, channel|
|
184
185
|
subject.unsubscribe {done}
|
185
186
|
end
|
186
|
-
EM::Hiredis::PubsubClient.
|
187
|
+
expect_any_instance_of(EM::Hiredis::PubsubClient).to receive(:close_connection).and_call_original
|
187
188
|
end
|
188
189
|
|
189
190
|
it "should periodically (random time between 20 and 25 seconds?) poll the job board for new work, in case it missed a notification" do
|
190
|
-
EM::PeriodicTimer.
|
191
|
+
expect(EM::PeriodicTimer).to receive(:new) {|time| expect(time).to be_within(2.5).of(22.5)}
|
191
192
|
publish = proc {foreman.enqueue_work_order('foo')}
|
192
193
|
subject.wait_for_work_orders(publish) {done}
|
193
194
|
end
|
194
195
|
|
195
|
-
|
196
|
+
pending "cancels the old timer when the on_message callback is called" do
|
196
197
|
publish = proc {foreman.enqueue_work_order('foo')}
|
197
198
|
subject.wait_for_work_orders(publish) do
|
198
|
-
subject.send(:timer).
|
199
|
+
expect(subject.send(:timer)).to receive(:cancel).and_call_original
|
199
200
|
done
|
200
201
|
end
|
201
202
|
end
|
202
203
|
|
203
204
|
it "should pull off work orders for more than one sender" do
|
204
|
-
tradesman
|
205
|
+
tradesman = WorkerRoulette.a_tradesman('good_channel')
|
205
206
|
|
206
|
-
good_foreman
|
207
|
-
lazy_foreman
|
207
|
+
good_foreman = WorkerRoulette.a_foreman('good_foreman', 'good_channel')
|
208
|
+
lazy_foreman = WorkerRoulette.a_foreman('lazy_foreman', 'good_channel')
|
208
209
|
|
209
210
|
got_good = false
|
210
211
|
got_lazy = false
|
211
212
|
good_foreman.enqueue_work_order('do good work') do
|
212
213
|
tradesman.work_orders! do |r|
|
213
214
|
got_good = true
|
214
|
-
r.first['payload'].
|
215
|
+
expect(r.first['payload']).to eq('do good work')
|
215
216
|
end
|
216
217
|
end
|
217
218
|
lazy_foreman.enqueue_work_order('just get it done') do
|
218
219
|
tradesman.work_orders! do |r|
|
219
220
|
got_lazy = true
|
220
|
-
r.first['payload'].
|
221
|
+
expect(r.first['payload']).to eq('just get it done')
|
221
222
|
end
|
222
223
|
end
|
223
224
|
|
224
|
-
done(0.2) {(got_good && got_lazy).
|
225
|
+
done(0.2) {expect(got_good && got_lazy).to eq(true)}
|
225
226
|
end
|
226
227
|
end
|
227
228
|
|
228
|
-
|
229
|
-
|
230
|
-
end
|
229
|
+
pending "should return a hash with a string in the payload if OJ cannot parse the json"
|
231
230
|
|
232
231
|
context "Failure" do
|
233
232
|
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; done; end
|
@@ -16,54 +16,54 @@ describe WorkerRoulette do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should exist" do
|
19
|
-
WorkerRoulette.
|
19
|
+
expect(WorkerRoulette).not_to be_nil
|
20
20
|
end
|
21
21
|
|
22
22
|
context Foreman do
|
23
23
|
let(:subject) {WorkerRoulette.foreman(sender)}
|
24
24
|
|
25
25
|
it "should be working on behalf of a sender" do
|
26
|
-
subject.sender.
|
26
|
+
expect(subject.sender).to eq(sender)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should enqueue_work_order two work_orders in the sender's slot in the switchboard" do
|
30
30
|
subject.enqueue_work_order(work_orders.first) {}
|
31
31
|
subject.enqueue_work_order(work_orders.last) {}
|
32
|
-
redis.lrange(sender, 0, -1).
|
32
|
+
expect(redis.lrange(sender, 0, -1)).to eq(work_orders.map {|m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) })
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should enqueue_work_order an array of work_orders without headers in the sender's slot in the switchboard" do
|
36
36
|
subject.enqueue_work_order_without_headers(work_orders)
|
37
|
-
redis.lrange(sender, 0, -1).
|
37
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders)])
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should enqueue_work_order an array of work_orders with default headers in the sender's slot in the switchboard" do
|
41
41
|
subject.enqueue_work_order(work_orders)
|
42
|
-
redis.lrange(sender, 0, -1).
|
42
|
+
expect(redis.lrange(sender, 0, -1)).to eq(jsonized_work_orders_with_headers)
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should enqueue_work_order an array of work_orders with additional headers in the sender's slot in the switchboard" do
|
46
46
|
extra_headers = {'foo' => 'bars'}
|
47
47
|
subject.enqueue_work_order(work_orders, extra_headers)
|
48
48
|
work_orders_with_headers['headers'].merge!(extra_headers)
|
49
|
-
redis.lrange(sender, 0, -1).
|
49
|
+
expect(redis.lrange(sender, 0, -1)).to eq([WorkerRoulette.dump(work_orders_with_headers)])
|
50
50
|
end
|
51
51
|
|
52
52
|
it "should post the sender's id to the job board with an order number" do
|
53
53
|
subject.enqueue_work_order(work_orders.first)
|
54
54
|
WorkerRoulette.foreman('other_forman').enqueue_work_order(work_orders.last)
|
55
|
-
redis.zrange(subject.job_board_key, 0, -1, with_scores: true).
|
55
|
+
expect(redis.zrange(subject.job_board_key, 0, -1, with_scores: true)).to eq([[sender, 1.0], ["other_forman", 2.0]])
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should generate a monotically increasing score for senders not on the job board, but not for senders already there" do
|
59
59
|
other_forman = WorkerRoulette.foreman('other_forman')
|
60
|
-
redis.get(subject.counter_key).
|
60
|
+
expect(redis.get(subject.counter_key)).to be_nil
|
61
61
|
subject.enqueue_work_order(work_orders.first)
|
62
|
-
redis.get(subject.counter_key).
|
62
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
63
63
|
subject.enqueue_work_order(work_orders.last)
|
64
|
-
redis.get(subject.counter_key).
|
64
|
+
expect(redis.get(subject.counter_key)).to eq("1")
|
65
65
|
other_forman.enqueue_work_order(work_orders.last)
|
66
|
-
redis.get(other_forman.counter_key).
|
66
|
+
expect(redis.get(other_forman.counter_key)).to eq("2")
|
67
67
|
end
|
68
68
|
|
69
69
|
it "should publish a notification that a new job is ready" do
|
@@ -80,7 +80,7 @@ describe WorkerRoulette do
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
result.
|
83
|
+
expect(result).to eq(WorkerRoulette::JOB_NOTIFICATIONS)
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -93,27 +93,27 @@ describe WorkerRoulette do
|
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should have a last sender if it found messages" do
|
96
|
-
subject.work_orders!.length.
|
97
|
-
subject.last_sender.
|
96
|
+
expect(subject.work_orders!.length).to eq(1)
|
97
|
+
expect(subject.last_sender).to eq(sender)
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should not have a last sender if it found no messages" do
|
101
|
-
subject.work_orders!.length.
|
102
|
-
subject.work_orders!.length.
|
103
|
-
subject.last_sender.
|
101
|
+
expect(subject.work_orders!.length).to eq(1)
|
102
|
+
expect(subject.work_orders!.length).to eq(0)
|
103
|
+
expect(subject.last_sender).to be_nil
|
104
104
|
end
|
105
105
|
|
106
106
|
it "should drain one set of work_orders from the sender's slot in the switchboard" do
|
107
|
-
subject.work_orders
|
108
|
-
subject.work_orders
|
109
|
-
subject.work_orders
|
107
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
108
|
+
expect(subject.work_orders!).to be_empty
|
109
|
+
expect(subject.work_orders!).to be_empty #does not throw an error if queue is already empty
|
110
110
|
end
|
111
111
|
|
112
112
|
it "should drain all the work_orders from the sender's slot in the switchboard" do
|
113
113
|
foreman.enqueue_work_order(work_orders)
|
114
|
-
subject.work_orders
|
115
|
-
subject.work_orders
|
116
|
-
subject.work_orders
|
114
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers, work_orders_with_headers])
|
115
|
+
expect(subject.work_orders!).to be_empty
|
116
|
+
expect(subject.work_orders!).to be_empty #does not throw an error if queue is already empty
|
117
117
|
end
|
118
118
|
|
119
119
|
it "should take the oldest sender off the job board (FIFO)" do
|
@@ -121,26 +121,26 @@ describe WorkerRoulette do
|
|
121
121
|
most_recent_sender = 'most_recent_sender'
|
122
122
|
most_recent_foreman = WorkerRoulette.foreman(most_recent_sender)
|
123
123
|
most_recent_foreman.enqueue_work_order(work_orders)
|
124
|
-
redis.zrange(subject.job_board_key, 0, -1).
|
124
|
+
expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([oldest_sender, most_recent_sender])
|
125
125
|
subject.work_orders!
|
126
|
-
redis.zrange(subject.job_board_key, 0, -1).
|
126
|
+
expect(redis.zrange(subject.job_board_key, 0, -1)).to eq([most_recent_sender])
|
127
127
|
end
|
128
128
|
|
129
129
|
it "should get the work_orders from the next queue when a new job is ready" do
|
130
130
|
subject.work_orders!
|
131
|
-
subject.
|
131
|
+
expect(subject).to receive(:work_orders!).twice.and_call_original
|
132
132
|
|
133
133
|
publisher = -> {foreman.enqueue_work_order(work_orders); subject.unsubscribe}
|
134
134
|
|
135
135
|
subject.wait_for_work_orders(publisher) do |redis_work_orders|
|
136
|
-
redis_work_orders.
|
137
|
-
subject.last_sender.
|
136
|
+
expect(redis_work_orders).to eq([work_orders_with_headers])
|
137
|
+
expect(subject.last_sender).to be_nil
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
141
|
it "should publish and subscribe on custom channels" do
|
142
142
|
tradesman = WorkerRoulette.tradesman('good_channel')
|
143
|
-
tradesman.
|
143
|
+
expect(tradesman).to receive(:work_orders!).twice.and_call_original
|
144
144
|
|
145
145
|
good_foreman = WorkerRoulette.foreman('foreman', 'good_channel')
|
146
146
|
bad_foreman = WorkerRoulette.foreman('foreman', 'bad_channel')
|
@@ -153,9 +153,9 @@ describe WorkerRoulette do
|
|
153
153
|
end
|
154
154
|
|
155
155
|
tradesman.wait_for_work_orders(publish) do |work|
|
156
|
-
work.to_s.
|
157
|
-
work.to_s.
|
158
|
-
tradesman.last_sender.
|
156
|
+
expect(work.to_s).to match("some old fashion work")
|
157
|
+
expect(work.to_s).not_to match("evil")
|
158
|
+
expect(tradesman.last_sender).to be_nil
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -169,7 +169,7 @@ describe WorkerRoulette do
|
|
169
169
|
Thread.new {WorkerRoulette.tradesman_connection_pool.with {|pooled_redis| pooled_redis.get("foo")}}
|
170
170
|
end.each(&:join)
|
171
171
|
WorkerRoulette.tradesman_connection_pool.with do |pooled_redis|
|
172
|
-
pooled_redis.info["connected_clients"].to_i.
|
172
|
+
expect(pooled_redis.info["connected_clients"].to_i).to be > (WorkerRoulette.pool_size)
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
@@ -24,14 +24,14 @@ module WorkerRoulette
|
|
24
24
|
|
25
25
|
it "should lock a queue when it reads from it" do
|
26
26
|
evented_readlock_preconditions do
|
27
|
-
redis.get(lock_key).
|
27
|
+
expect(redis.get(lock_key)).not_to be_nil
|
28
28
|
done
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
it "should set the lock to expire in 1 second" do
|
33
33
|
evented_readlock_preconditions do
|
34
|
-
redis.ttl(lock_key).
|
34
|
+
expect(redis.ttl(lock_key)).to eq(1)
|
35
35
|
done
|
36
36
|
end
|
37
37
|
end
|
@@ -39,7 +39,7 @@ module WorkerRoulette
|
|
39
39
|
it "should not read a locked queue" do
|
40
40
|
evented_readlock_preconditions do
|
41
41
|
foreman.enqueue_work_order(work_orders) do #locked
|
42
|
-
subject_two.work_orders! {|work
|
42
|
+
subject_two.work_orders! { |work| expect(work).to be_empty; done}
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -48,7 +48,7 @@ module WorkerRoulette
|
|
48
48
|
evented_readlock_preconditions do
|
49
49
|
foreman.enqueue_work_order(work_orders) do #locked
|
50
50
|
number_two.enqueue_work_order(work_orders) do #unlocked
|
51
|
-
subject_two.work_orders!{|work| work.first['headers']['sender'].
|
51
|
+
subject_two.work_orders!{|work| expect(work.first['headers']['sender']).to eq('number_two'); done}
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -57,10 +57,10 @@ module WorkerRoulette
|
|
57
57
|
it "should release its last lock when it asks for its next work order from another sender" do
|
58
58
|
evented_readlock_preconditions do
|
59
59
|
number_two.enqueue_work_order(work_orders) do #unlocked
|
60
|
-
subject.last_sender.
|
60
|
+
expect(subject.last_sender).to eq(sender)
|
61
61
|
subject.work_orders! do |work|
|
62
|
-
work.first['headers']['sender'].
|
63
|
-
redis.get(lock_key).
|
62
|
+
expect(work.first['headers']['sender']).to eq('number_two')
|
63
|
+
expect(redis.get(lock_key)).to be_nil
|
64
64
|
done
|
65
65
|
end
|
66
66
|
end
|
@@ -71,9 +71,9 @@ module WorkerRoulette
|
|
71
71
|
evented_readlock_preconditions do
|
72
72
|
foreman.enqueue_work_order(work_orders) do #locked
|
73
73
|
subject.work_orders! do |work|
|
74
|
-
work.
|
75
|
-
subject.last_sender.
|
76
|
-
redis.get(lock_key).
|
74
|
+
expect(work).to eq([work_orders_with_headers])
|
75
|
+
expect(subject.last_sender).to eq(sender)
|
76
|
+
expect(redis.get(lock_key)).not_to be_nil
|
77
77
|
done
|
78
78
|
end
|
79
79
|
end
|
@@ -83,11 +83,11 @@ module WorkerRoulette
|
|
83
83
|
it "should not take out another lock if there is no work to do" do
|
84
84
|
evented_readlock_preconditions do
|
85
85
|
foreman.enqueue_work_order(work_orders) do #locked
|
86
|
-
subject.work_orders! do |
|
87
|
-
|
86
|
+
subject.work_orders! do |work_order|
|
87
|
+
expect(work_order).to eq([work_orders_with_headers])
|
88
88
|
subject.work_orders! do |work|
|
89
|
-
work.
|
90
|
-
redis.get(lock_key).
|
89
|
+
expect(work).to be_empty
|
90
|
+
expect(redis.get(lock_key)).to be_nil
|
91
91
|
done
|
92
92
|
end
|
93
93
|
end
|
@@ -99,9 +99,9 @@ module WorkerRoulette
|
|
99
99
|
def evented_readlock_preconditions(&spec_block)
|
100
100
|
foreman.enqueue_work_order(work_orders) do
|
101
101
|
subject.work_orders! do |work|
|
102
|
-
work.
|
102
|
+
expect(work).to eq([work_orders_with_headers])
|
103
103
|
spec_block.call
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
107
|
-
end
|
107
|
+
end
|
data/spec/unit/lua_spec.rb
CHANGED
@@ -17,22 +17,22 @@ module WorkerRoulette
|
|
17
17
|
it "should load and call a lua script" do
|
18
18
|
lua_script = 'return redis.call("SET", KEYS[1], ARGV[1])'
|
19
19
|
Lua.call(lua_script, ['foo'], ['daddy']) do |result|
|
20
|
-
Lua.cache.keys.first.
|
21
|
-
Lua.cache.values.first.
|
22
|
-
result.
|
20
|
+
expect(Lua.cache.keys.first).to eq(lua_script)
|
21
|
+
expect(Lua.cache.values.first).to eq(Digest::SHA1.hexdigest(lua_script))
|
22
|
+
expect(result).to eq("OK")
|
23
23
|
done
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should send a sha instead of a script once the script has been cached" do
|
28
28
|
lua_script = 'return KEYS'
|
29
|
-
Lua.
|
29
|
+
expect(Lua).to receive(:eval).and_call_original
|
30
30
|
|
31
31
|
Lua.call(lua_script) do |result|
|
32
|
+
expect(Lua).not_to receive(:eval)
|
32
33
|
|
33
|
-
Lua.
|
34
|
-
|
35
|
-
result.should == []
|
34
|
+
Lua.call(lua_script) do |inner_result|
|
35
|
+
expect(inner_result).to be_empty
|
36
36
|
done
|
37
37
|
end
|
38
38
|
end
|
@@ -46,4 +46,4 @@ module WorkerRoulette
|
|
46
46
|
done
|
47
47
|
end
|
48
48
|
end
|
49
|
-
end
|
49
|
+
end
|
data/spec/unit/readlock_spec.rb
CHANGED
@@ -19,56 +19,54 @@ module WorkerRoulette
|
|
19
19
|
redis.script(:flush)
|
20
20
|
redis.flushdb
|
21
21
|
foreman.enqueue_work_order(work_orders)
|
22
|
-
subject.work_orders
|
22
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should lock a queue when it reads from it" do
|
26
|
-
redis.get(lock_key).
|
26
|
+
expect(redis.get(lock_key)).not_to be_nil
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should set the lock to expire in 1 second" do
|
30
|
-
redis.ttl(lock_key).
|
30
|
+
expect(redis.ttl(lock_key)).to eq(1)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "should not read a locked queue" do
|
34
34
|
foreman.enqueue_work_order(work_orders) #locked
|
35
|
-
subject_two.work_orders
|
35
|
+
expect(subject_two.work_orders!).to be_empty
|
36
36
|
end
|
37
37
|
|
38
38
|
it "should read from the first available queue that is not locked" do
|
39
39
|
foreman.enqueue_work_order(work_orders) #locked
|
40
40
|
number_two.enqueue_work_order(work_orders) #unlocked
|
41
|
-
subject_two.work_orders!.first['headers']['sender'].
|
41
|
+
expect(subject_two.work_orders!.first['headers']['sender']).to eq('number_two')
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should release its previous lock when it asks for work from another sender" do
|
45
45
|
number_two.enqueue_work_order(work_orders) #unlocked
|
46
|
-
subject.last_sender.
|
47
|
-
subject.work_orders!.first['headers']['sender'].
|
48
|
-
redis.get(lock_key).
|
46
|
+
expect(subject.last_sender).to eq(sender)
|
47
|
+
expect(subject.work_orders!.first['headers']['sender']).to eq('number_two')
|
48
|
+
expect(redis.get(lock_key)).to be_nil
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should not release its lock when it asks for work from the same sender" do
|
52
52
|
foreman.enqueue_work_order(work_orders) #locked
|
53
|
-
subject.work_orders
|
54
|
-
subject.last_sender.
|
53
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
54
|
+
expect(subject.last_sender).to eq(sender)
|
55
55
|
|
56
56
|
foreman.enqueue_work_order(work_orders) #locked
|
57
|
-
subject.work_orders
|
58
|
-
subject.last_sender.
|
57
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
58
|
+
expect(subject.last_sender).to eq(sender)
|
59
59
|
|
60
|
-
redis.get(lock_key).
|
60
|
+
expect(redis.get(lock_key)).not_to be_nil
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should release its previous lock if there is no work to do from the same sender" do
|
64
64
|
foreman.enqueue_work_order(work_orders) #locked
|
65
|
-
subject.work_orders
|
66
|
-
subject.work_orders
|
67
|
-
redis.get(lock_key).
|
65
|
+
expect(subject.work_orders!).to eq([work_orders_with_headers])
|
66
|
+
expect(subject.work_orders!).to be_empty
|
67
|
+
expect(redis.get(lock_key)).to be_nil
|
68
68
|
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
end
|
70
|
+
pending "pubsub should clean up one contention or remove the lock on the same sender queue automaticly"
|
73
71
|
end
|
74
|
-
end
|
72
|
+
end
|
data/worker_roulette.gemspec
CHANGED
@@ -4,18 +4,18 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'worker_roulette/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'worker_roulette'
|
8
8
|
spec.version = WorkerRoulette::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.description = %q{
|
12
|
-
spec.summary = %q{
|
13
|
-
spec.homepage =
|
9
|
+
spec.authors = ['Paul Saieg']
|
10
|
+
spec.email = ['classicist@gmail.com']
|
11
|
+
spec.description = %q{Pub Sub Queue for Redis that ensures ordered processing}
|
12
|
+
spec.summary = %q{Pub Sub Queue for Redis that ensures ordered processing}
|
13
|
+
spec.homepage = 'https://github.com/nexiahome/worker_roulette'
|
14
14
|
|
15
15
|
spec.files = `git ls-files`.split($/)
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
18
|
-
spec.require_paths = [
|
18
|
+
spec.require_paths = ['lib']
|
19
19
|
|
20
20
|
spec.add_dependency 'oj'
|
21
21
|
spec.add_dependency 'redis', '~> 3.0.7'
|
@@ -24,14 +24,13 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency 'connection_pool'
|
25
25
|
spec.add_dependency 'eventmachine', '~> 1.0.3'
|
26
26
|
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'bundler'
|
28
|
+
spec.add_development_dependency 'rake'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
30
30
|
spec.add_development_dependency 'pry-debugger'
|
31
31
|
spec.add_development_dependency 'simplecov'
|
32
32
|
spec.add_development_dependency 'simplecov-rcov'
|
33
33
|
spec.add_development_dependency 'rspec_junit_formatter'
|
34
|
-
spec.add_development_dependency 'evented-spec'
|
35
34
|
spec.add_development_dependency 'guard'
|
36
35
|
spec.add_development_dependency 'guard-rspec'
|
37
36
|
end
|
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.7
|
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-
|
11
|
+
date: 2014-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -126,16 +126,16 @@ dependencies:
|
|
126
126
|
name: rspec
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ~>
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 3.0.0
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ~>
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 3.0.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: pry-debugger
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,20 +192,6 @@ dependencies:
|
|
192
192
|
- - ! '>='
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '0'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: evented-spec
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ! '>='
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ! '>='
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
209
195
|
- !ruby/object:Gem::Dependency
|
210
196
|
name: guard
|
211
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -234,7 +220,7 @@ dependencies:
|
|
234
220
|
- - ! '>='
|
235
221
|
- !ruby/object:Gem::Version
|
236
222
|
version: '0'
|
237
|
-
description:
|
223
|
+
description: Pub Sub Queue for Redis that ensures ordered processing
|
238
224
|
email:
|
239
225
|
- classicist@gmail.com
|
240
226
|
executables: []
|
@@ -287,7 +273,7 @@ rubyforge_project:
|
|
287
273
|
rubygems_version: 2.2.2
|
288
274
|
signing_key:
|
289
275
|
specification_version: 4
|
290
|
-
summary:
|
276
|
+
summary: Pub Sub Queue for Redis that ensures ordered processing
|
291
277
|
test_files:
|
292
278
|
- spec/benchmark/perf_test.rb
|
293
279
|
- spec/helpers/.gitkeep
|