fairway 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +6 -6
- data/fairway.gemspec +1 -1
- data/lib/fairway/facet.rb +69 -0
- data/lib/fairway/queue.rb +12 -16
- data/lib/fairway/sidekiq/basic_fetch.rb +1 -20
- data/lib/fairway/version.rb +1 -1
- data/lib/fairway.rb +1 -0
- data/redis/fairway_deliver.lua +42 -8
- data/redis/fairway_priority.lua +29 -0
- data/redis/fairway_pull.lua +77 -10
- data/spec/lib/fairway/facet_spec.rb +95 -0
- data/spec/lib/fairway/queue_spec.rb +102 -35
- metadata +8 -4
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fairway (0.0
|
4
|
+
fairway (0.1.0)
|
5
5
|
activesupport
|
6
6
|
hiredis
|
7
7
|
redis
|
8
|
-
redis-namespace
|
8
|
+
redis-namespace (>= 1.3.0)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: http://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activesupport (3.2.
|
14
|
-
i18n (
|
13
|
+
activesupport (3.2.13)
|
14
|
+
i18n (= 0.6.1)
|
15
15
|
multi_json (~> 1.0)
|
16
16
|
celluloid (0.12.4)
|
17
17
|
facter (>= 1.6.12)
|
@@ -22,8 +22,8 @@ GEM
|
|
22
22
|
hiredis (0.4.5)
|
23
23
|
i18n (0.6.1)
|
24
24
|
multi_json (1.5.0)
|
25
|
-
redis (3.0.
|
26
|
-
redis-namespace (1.
|
25
|
+
redis (3.0.4)
|
26
|
+
redis-namespace (1.3.0)
|
27
27
|
redis (~> 3.0.0)
|
28
28
|
rspec (2.12.0)
|
29
29
|
rspec-core (~> 2.12.0)
|
data/fairway.gemspec
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Fairway
|
2
|
+
class Facet
|
3
|
+
class InvalidPriorityError < Exception; end
|
4
|
+
|
5
|
+
attr_reader :queue, :name
|
6
|
+
|
7
|
+
def initialize(queue, facet_name)
|
8
|
+
@queue = queue
|
9
|
+
@name = facet_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
each_queue do |queue|
|
14
|
+
redis.llen(facet_key(queue))
|
15
|
+
end.sum
|
16
|
+
end
|
17
|
+
|
18
|
+
def priority
|
19
|
+
each_queue do |queue|
|
20
|
+
(redis.hget(priority_key(queue), name) || 1).to_i
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def priority=(priority)
|
25
|
+
validate_priority!(priority)
|
26
|
+
|
27
|
+
each_queue do |queue|
|
28
|
+
scripts.fairway_priority(queue, name, priority)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def facet_key(queue)
|
33
|
+
"#{queue}:#{name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def priority_key(queue)
|
37
|
+
"#{queue}:priorities"
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
other.respond_to?(:queue) &&
|
42
|
+
other.respond_to?(:name) &&
|
43
|
+
queue == other.queue &&
|
44
|
+
name == other.name
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def redis
|
50
|
+
queue.redis
|
51
|
+
end
|
52
|
+
|
53
|
+
def scripts
|
54
|
+
queue.scripts
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_queue(&block)
|
58
|
+
queue.unique_queues.map do |queue|
|
59
|
+
yield(queue)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_priority!(priority)
|
64
|
+
if priority.to_i.to_s != priority.to_s || priority.to_i < 0
|
65
|
+
raise InvalidPriorityError.new("Facet priority must be an integer >= 0")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/fairway/queue.rb
CHANGED
@@ -8,21 +8,19 @@ module Fairway
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def active_facets
|
11
|
-
|
11
|
+
facet_names = unique_queues.map do |queue|
|
12
12
|
redis.smembers("#{queue}:active_facets")
|
13
13
|
end.flatten.uniq
|
14
|
+
|
15
|
+
facet_names.map do |name|
|
16
|
+
Facet.new(self, name)
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
def length
|
17
21
|
redis.mget(unique_queues.map{|q| "#{q}:length" }).sum.to_i
|
18
22
|
end
|
19
23
|
|
20
|
-
def facet_length(facet)
|
21
|
-
each_queue do |queue|
|
22
|
-
redis.llen("#{queue}:#{facet}")
|
23
|
-
end.sum
|
24
|
-
end
|
25
|
-
|
26
24
|
def peek
|
27
25
|
scripts.fairway_peek(@queue_names.shuffle.uniq)
|
28
26
|
end
|
@@ -38,25 +36,23 @@ module Fairway
|
|
38
36
|
queue_names == other.queue_names
|
39
37
|
end
|
40
38
|
|
41
|
-
private
|
42
|
-
|
43
39
|
def unique_queues
|
44
40
|
@queue_names.uniq
|
45
41
|
end
|
46
42
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
def queue_key
|
44
|
+
queue
|
45
|
+
end
|
46
|
+
|
47
|
+
def redis
|
48
|
+
@connection.redis
|
51
49
|
end
|
52
50
|
|
53
51
|
def scripts
|
54
52
|
@connection.scripts
|
55
53
|
end
|
56
54
|
|
57
|
-
|
58
|
-
@connection.redis
|
59
|
-
end
|
55
|
+
private
|
60
56
|
|
61
57
|
def parse_queue_names(names)
|
62
58
|
[].tap do |queues|
|
@@ -26,7 +26,7 @@ module Fairway
|
|
26
26
|
return nil;
|
27
27
|
SCRIPT
|
28
28
|
|
29
|
-
conn.eval(script, queues_cmd
|
29
|
+
conn.eval(script, queues_cmd)
|
30
30
|
end
|
31
31
|
|
32
32
|
if (work)
|
@@ -43,25 +43,6 @@ module Fairway
|
|
43
43
|
def queues_cmd
|
44
44
|
@queues.shuffle.uniq
|
45
45
|
end
|
46
|
-
|
47
|
-
def namespace
|
48
|
-
@namespace ||= begin
|
49
|
-
namespaces = []
|
50
|
-
|
51
|
-
::Sidekiq.redis do |conn|
|
52
|
-
wrapper = conn
|
53
|
-
|
54
|
-
while defined?(wrapper.redis)
|
55
|
-
namespaces.unshift(wrapper.namespace)
|
56
|
-
wrapper = wrapper.redis
|
57
|
-
end
|
58
|
-
|
59
|
-
namespace = namespaces.join(":")
|
60
|
-
namespace += ":" unless namespace.blank?
|
61
|
-
namespace
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
46
|
end
|
66
47
|
end
|
67
48
|
end
|
data/lib/fairway/version.rb
CHANGED
data/lib/fairway.rb
CHANGED
data/redis/fairway_deliver.lua
CHANGED
@@ -3,24 +3,58 @@ local topic = ARGV[1];
|
|
3
3
|
local facet = ARGV[2];
|
4
4
|
local message = ARGV[3];
|
5
5
|
|
6
|
+
local k = function (queue, subkey)
|
7
|
+
return namespace .. queue .. ':' .. subkey;
|
8
|
+
end
|
9
|
+
|
6
10
|
local registered_queues_key = namespace .. 'registered_queues';
|
7
11
|
local registered_queues = redis.call('hgetall', registered_queues_key);
|
8
12
|
|
13
|
+
-- Determine whether or not the message should
|
14
|
+
-- be delivered to each registered queue.
|
9
15
|
for i = 1, #registered_queues, 2 do
|
10
|
-
local
|
11
|
-
local
|
16
|
+
local queue = registered_queues[i];
|
17
|
+
local queue_topic = registered_queues[i+1];
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
-- If the message topic matches the queue topic,
|
20
|
+
-- we deliver the message to the queue.
|
21
|
+
if string.find(topic, queue_topic) then
|
22
|
+
local priorities = k(queue, 'priorities');
|
23
|
+
local active_facets = k(queue, 'active_facets');
|
24
|
+
local round_robin = k(queue, 'facet_queue');
|
25
|
+
local facet_pool = k(queue, 'facet_pool');
|
16
26
|
|
17
|
-
|
18
|
-
|
27
|
+
-- Delivering the message to a queue is as simple as
|
28
|
+
-- pushing it onto the facet's message list, and
|
29
|
+
-- incrementing the length of the queue itself.
|
30
|
+
redis.call('lpush', k(queue, facet), message)
|
31
|
+
redis.call('incr', k(queue, 'length'));
|
19
32
|
|
33
|
+
-- If the facet just became active, we need to add
|
34
|
+
-- the facet to the round-robin queue, so it's
|
35
|
+
-- messages will be processed.
|
20
36
|
if redis.call('sadd', active_facets, facet) == 1 then
|
21
|
-
redis.call('
|
37
|
+
local priority = tonumber(redis.call('hget', priorities, facet)) or 1
|
38
|
+
|
39
|
+
-- If the facet currently has a priority
|
40
|
+
-- we need to jump start the facet by adding
|
41
|
+
-- it to the round-robin queue and updating
|
42
|
+
-- the current priority.
|
43
|
+
if priority > 0 then
|
44
|
+
redis.call('lpush', round_robin, facet);
|
45
|
+
redis.call('hset', facet_pool, facet, 1);
|
46
|
+
|
47
|
+
-- If the facet has no set priority, just set the
|
48
|
+
-- current priority to zero. Since the facet just
|
49
|
+
-- became active, we can be sure it's already zero
|
50
|
+
-- or undefined.
|
51
|
+
else
|
52
|
+
redis.call('hset', facet_pool, facet, 0);
|
53
|
+
end
|
22
54
|
end
|
23
55
|
end
|
24
56
|
end
|
25
57
|
|
58
|
+
-- For any clients listening over pub/sub,
|
59
|
+
-- we should publish the message.
|
26
60
|
redis.call('publish', namespace .. topic, message);
|
@@ -0,0 +1,29 @@
|
|
1
|
+
local namespace = KEYS[1];
|
2
|
+
local queue = ARGV[1];
|
3
|
+
local facet = ARGV[2];
|
4
|
+
local new_priority = tonumber(ARGV[3]);
|
5
|
+
|
6
|
+
local k = function (queue, subkey)
|
7
|
+
return namespace .. queue .. ':' .. subkey;
|
8
|
+
end
|
9
|
+
|
10
|
+
local priorities = k(queue, 'priorities');
|
11
|
+
local round_robin = k(queue, 'facet_queue');
|
12
|
+
local facet_pool = k(queue, 'facet_pool');
|
13
|
+
|
14
|
+
-- Find the current state of the facet for the queue
|
15
|
+
local priority = tonumber(redis.call('hget', priorities, facet)) or 1;
|
16
|
+
local current = tonumber(redis.call('hget', facet_pool, facet));
|
17
|
+
|
18
|
+
-- If priority is currently zero, we need to jump
|
19
|
+
-- start the facet by adding it to the round-robin
|
20
|
+
-- queue and updating the current priority.
|
21
|
+
if new_priority > 0 and priority == 0 and current == 0 then
|
22
|
+
redis.call('lpush', round_robin, facet);
|
23
|
+
redis.call('hset', facet_pool, facet, 1);
|
24
|
+
end
|
25
|
+
|
26
|
+
-- Other than the 0 priority case, we can just
|
27
|
+
-- set the new priority, and the real priority
|
28
|
+
-- will update lazily on pull.
|
29
|
+
redis.call('hset', priorities, facet, new_priority);
|
data/redis/fairway_pull.lua
CHANGED
@@ -1,25 +1,92 @@
|
|
1
1
|
local namespace = KEYS[1];
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
local k = function (queue, subkey)
|
4
|
+
return namespace .. queue .. ':' .. subkey;
|
5
|
+
end
|
6
|
+
|
7
|
+
-- Multiple queues can be passed through
|
8
|
+
-- fairway_pull. We'll loop through all
|
9
|
+
-- provided queues, and return a message
|
10
|
+
-- from the first one that isn't empty.
|
11
|
+
for i, queue in ipairs(ARGV) do
|
12
|
+
local priorities = k(queue, 'priorities');
|
13
|
+
local active_facets = k(queue, 'active_facets');
|
14
|
+
local round_robin = k(queue, 'facet_queue');
|
15
|
+
local facet_pool = k(queue, 'facet_pool');
|
6
16
|
|
7
|
-
|
17
|
+
-- Pull a facet from the round-robin list.
|
18
|
+
-- This list guarantees each active facet will have a
|
19
|
+
-- message pulled from the queue every time through..
|
20
|
+
local facet = redis.call('rpop', round_robin);
|
8
21
|
|
9
22
|
if facet then
|
10
|
-
|
11
|
-
|
23
|
+
-- If we found an active facet, we know the facet
|
24
|
+
-- has at least one message available to be pulled
|
25
|
+
-- from it's message queue.
|
26
|
+
local messages = k(queue, facet);
|
27
|
+
local message = redis.call('rpop', messages);
|
12
28
|
|
13
29
|
if message then
|
14
|
-
redis.call('decr',
|
30
|
+
redis.call('decr', k(queue, 'length'));
|
15
31
|
end
|
16
32
|
|
17
|
-
|
33
|
+
local length = redis.call('llen', messages);
|
34
|
+
|
35
|
+
-- If the length of the facet's message queue
|
36
|
+
-- is empty, then it is no longer active as
|
37
|
+
-- it no longer has any messages.
|
38
|
+
if length == 0 then
|
39
|
+
-- We remove the facet from the set of active
|
40
|
+
-- facets and don't push the facet back on the
|
41
|
+
-- round-robin queue.
|
18
42
|
redis.call('srem', active_facets, facet);
|
43
|
+
|
44
|
+
-- If the facet still has messages to process,
|
45
|
+
-- it remains in the active facet set, and is
|
46
|
+
-- pushed back on the round-robin queue.
|
47
|
+
--
|
48
|
+
-- Additionally, the priority of the facet may
|
49
|
+
-- have changed, so we'll check and update the
|
50
|
+
-- current facet's priority if needed.
|
19
51
|
else
|
20
|
-
redis.call('
|
52
|
+
local priority = tonumber(redis.call('hget', priorities, facet)) or 1
|
53
|
+
local current = tonumber(redis.call('hget', facet_pool, facet))
|
54
|
+
|
55
|
+
-- If the current priority is less than the
|
56
|
+
-- desired priority, let's increase the priority
|
57
|
+
-- by pushing the current facet on the round-robin
|
58
|
+
-- queue twice, and incrementing the current
|
59
|
+
-- priority.
|
60
|
+
--
|
61
|
+
-- Note: If there aren't enough messages left
|
62
|
+
-- on the facet, we don't increase priority.
|
63
|
+
if current < priority and length > current then
|
64
|
+
redis.call('lpush', round_robin, facet);
|
65
|
+
redis.call('lpush', round_robin, facet);
|
66
|
+
redis.call('hset', facet_pool, facet, current + 1);
|
67
|
+
|
68
|
+
-- If the current priority is greater than the
|
69
|
+
-- desired priority, let's decrease the priority
|
70
|
+
-- by not pushing the current facet on the round-robin
|
71
|
+
-- queue, and decrementing the current priority.
|
72
|
+
--
|
73
|
+
-- Note: Also decrement priority if there aren't
|
74
|
+
-- enough messages for the current priority. This
|
75
|
+
-- ensures priority (entries in the round-robin queue)
|
76
|
+
-- never exceeds the number of messages for a given
|
77
|
+
-- facet.
|
78
|
+
elseif current > priority or current > length then
|
79
|
+
redis.call('hset', facet_pool, facet, current - 1);
|
80
|
+
|
81
|
+
-- If the current priority is equals the
|
82
|
+
-- desired priority, let's maintain the current priority
|
83
|
+
-- by pushing the current facet on the round-robin
|
84
|
+
-- queue once.
|
85
|
+
else
|
86
|
+
redis.call('lpush', round_robin, facet);
|
87
|
+
end
|
21
88
|
end
|
22
89
|
|
23
|
-
return {
|
90
|
+
return {queue, message};
|
24
91
|
end
|
25
92
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Fairway
|
4
|
+
describe Facet do
|
5
|
+
let(:connection) { Connection.new }
|
6
|
+
let(:queue) { Queue.new(connection, "myqueue") }
|
7
|
+
let(:message) { { facet: 1, topic: "event:helloworld" } }
|
8
|
+
|
9
|
+
before do
|
10
|
+
Fairway.config.register_queue("myqueue")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#length" do
|
14
|
+
it "returns number of messages queues for a given facet" do
|
15
|
+
connection.deliver(message.merge(facet: 1, message: 1))
|
16
|
+
connection.deliver(message.merge(facet: 1, message: 2))
|
17
|
+
connection.deliver(message.merge(facet: 2, message: 3))
|
18
|
+
|
19
|
+
Facet.new(queue, 1).length.should == 2
|
20
|
+
Facet.new(queue, 2).length.should == 1
|
21
|
+
Facet.new(queue, 3).length.should == 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#priority" do
|
26
|
+
let(:facet) { Facet.new(queue, 1) }
|
27
|
+
|
28
|
+
it "defaults all facets to a priority of 1" do
|
29
|
+
facet.priority.should == [1]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns value of priority set" do
|
33
|
+
facet.priority = 4
|
34
|
+
facet.priority.should == [4]
|
35
|
+
end
|
36
|
+
|
37
|
+
context "multiple queues" do
|
38
|
+
let(:queue) { Queue.new(connection, "myqueue2", "myqueue1") }
|
39
|
+
|
40
|
+
it "returns priority for each queue" do
|
41
|
+
facet.priority.should == [1, 1]
|
42
|
+
|
43
|
+
Facet.new(Queue.new(connection, "myqueue1"), 1).priority = 2
|
44
|
+
|
45
|
+
facet.priority.should == [1, 2]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#priority=" do
|
51
|
+
let(:facet) { Facet.new(queue, 1) }
|
52
|
+
|
53
|
+
it "allows positive integer priority" do
|
54
|
+
facet.priority = 5
|
55
|
+
facet.priority.should == [5]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "doesn't allow non integer priority" do
|
59
|
+
lambda { facet.priority = "hello" }.should raise_error(Facet::InvalidPriorityError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't allow non integer priority" do
|
63
|
+
lambda { facet.priority = "1.23" }.should raise_error(Facet::InvalidPriorityError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "doesn't allow negative priority" do
|
67
|
+
lambda { facet.priority = -1 }.should raise_error(Facet::InvalidPriorityError)
|
68
|
+
end
|
69
|
+
|
70
|
+
context "multiple queues" do
|
71
|
+
let(:queue) { Queue.new(connection, "myqueue2", "myqueue1") }
|
72
|
+
|
73
|
+
it "sets priority on each queue" do
|
74
|
+
facet.priority = 2
|
75
|
+
facet.priority.should == [2, 2]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "equality" do
|
81
|
+
it "should equal facets with same queue and names" do
|
82
|
+
Facet.new(queue, "a").should == Facet.new(queue, "a")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "doesn't equal facets with different queues" do
|
86
|
+
new_queue = Queue.new(connection, "otherqueue")
|
87
|
+
Facet.new(queue, "a").should_not == Queue.new(new_queue, "a")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "doesn't equal queues with different queues" do
|
91
|
+
Facet.new(queue, "a").should_not == Facet.new(queue, "b")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -47,7 +47,10 @@ module Fairway
|
|
47
47
|
|
48
48
|
queue.pull
|
49
49
|
|
50
|
-
queue.active_facets.should == [
|
50
|
+
queue.active_facets.should == [
|
51
|
+
Facet.new(queue, "2"),
|
52
|
+
Facet.new(queue, "3")
|
53
|
+
]
|
51
54
|
end
|
52
55
|
|
53
56
|
context "multiple queues" do
|
@@ -64,39 +67,11 @@ module Fairway
|
|
64
67
|
connection.deliver(message.merge(topic: "event:1", facet: 3, message: 2))
|
65
68
|
connection.deliver(message.merge(topic: "event:2", facet: 3, message: 3))
|
66
69
|
|
67
|
-
queue.active_facets.should == [
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
describe "#facet_length" do
|
73
|
-
it "returns number of messages queues for a given facet" do
|
74
|
-
connection.deliver(message.merge(facet: 1, message: 1))
|
75
|
-
connection.deliver(message.merge(facet: 1, message: 2))
|
76
|
-
connection.deliver(message.merge(facet: 2, message: 3))
|
77
|
-
|
78
|
-
queue.facet_length(1).should == 2
|
79
|
-
queue.facet_length(2).should == 1
|
80
|
-
queue.facet_length(3).should == 0
|
81
|
-
end
|
82
|
-
|
83
|
-
context "multiple queues" do
|
84
|
-
let(:queue) { Queue.new(connection, "myqueue1", "myqueue2") }
|
85
|
-
|
86
|
-
before do
|
87
|
-
Fairway.config.register_queue("myqueue1", "event:1")
|
88
|
-
Fairway.config.register_queue("myqueue2", "event:2")
|
89
|
-
end
|
90
|
-
|
91
|
-
it "sums number of messages for facet across all queues" do
|
92
|
-
connection.deliver(message.merge(topic: "event:1", facet: 1, message: 1))
|
93
|
-
connection.deliver(message.merge(topic: "event:1", facet: 2, message: 2))
|
94
|
-
connection.deliver(message.merge(topic: "event:1", facet: 3, message: 2))
|
95
|
-
connection.deliver(message.merge(topic: "event:2", facet: 3, message: 3))
|
96
|
-
|
97
|
-
queue.facet_length(1).should == 1
|
98
|
-
queue.facet_length(2).should == 1
|
99
|
-
queue.facet_length(3).should == 2
|
70
|
+
queue.active_facets.should == [
|
71
|
+
Facet.new(queue, "1"),
|
72
|
+
Facet.new(queue, "2"),
|
73
|
+
Facet.new(queue, "3")
|
74
|
+
]
|
100
75
|
end
|
101
76
|
end
|
102
77
|
end
|
@@ -132,7 +107,7 @@ module Fairway
|
|
132
107
|
it "removes facet from active list if it becomes empty" do
|
133
108
|
connection.deliver(message)
|
134
109
|
|
135
|
-
queue.active_facets.should == ["1"]
|
110
|
+
queue.active_facets.should == [Facet.new(queue, "1")]
|
136
111
|
queue.pull
|
137
112
|
queue.active_facets.should be_empty
|
138
113
|
end
|
@@ -220,6 +195,98 @@ module Fairway
|
|
220
195
|
end
|
221
196
|
end
|
222
197
|
|
198
|
+
describe "priority" do
|
199
|
+
let(:facet1) { Facet.new(queue, 1) }
|
200
|
+
let(:message1) { message.merge(facet: 1) }
|
201
|
+
let(:message2) { message.merge(facet: 2) }
|
202
|
+
|
203
|
+
it "doesn't pull messages from a facet with priority of 0" do
|
204
|
+
facet1.priority = 0
|
205
|
+
connection.deliver(message1)
|
206
|
+
queue.pull.should be_nil
|
207
|
+
end
|
208
|
+
|
209
|
+
it "lazily adjusts priority on pull" do
|
210
|
+
connection.deliver(message1)
|
211
|
+
connection.deliver(message1)
|
212
|
+
|
213
|
+
facet1.priority = 0
|
214
|
+
|
215
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
216
|
+
queue.pull.should be_nil
|
217
|
+
end
|
218
|
+
|
219
|
+
it "pulls more messages from facets with higher priority" do
|
220
|
+
connection.deliver(message1)
|
221
|
+
connection.deliver(message1)
|
222
|
+
connection.deliver(message2)
|
223
|
+
connection.deliver(message2)
|
224
|
+
connection.deliver(message2)
|
225
|
+
connection.deliver(message1)
|
226
|
+
connection.deliver(message1)
|
227
|
+
connection.deliver(message1)
|
228
|
+
|
229
|
+
facet1.priority = 2
|
230
|
+
|
231
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
232
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
233
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
234
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
235
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
236
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
237
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
238
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
239
|
+
queue.pull.should be_nil
|
240
|
+
end
|
241
|
+
|
242
|
+
it "only pulls messages from higher priority facets if enough messages exist" do
|
243
|
+
connection.deliver(message1)
|
244
|
+
connection.deliver(message1)
|
245
|
+
connection.deliver(message1)
|
246
|
+
connection.deliver(message2)
|
247
|
+
connection.deliver(message2)
|
248
|
+
connection.deliver(message2)
|
249
|
+
connection.deliver(message2)
|
250
|
+
|
251
|
+
facet1.priority = 10
|
252
|
+
|
253
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
254
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
255
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
256
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
257
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
258
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
259
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
260
|
+
queue.pull.should be_nil
|
261
|
+
end
|
262
|
+
|
263
|
+
it "properly increments priority from 0" do
|
264
|
+
facet1.priority = 0
|
265
|
+
|
266
|
+
connection.deliver(message1)
|
267
|
+
|
268
|
+
queue.pull.should be_nil
|
269
|
+
|
270
|
+
facet1.priority = 1
|
271
|
+
|
272
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
273
|
+
end
|
274
|
+
|
275
|
+
it "rapidly changing priority from 0 to 1 works properly" do
|
276
|
+
connection.deliver(message1)
|
277
|
+
|
278
|
+
facet1.priority = 0
|
279
|
+
facet1.priority = 1
|
280
|
+
|
281
|
+
connection.deliver(message1)
|
282
|
+
connection.deliver(message2)
|
283
|
+
|
284
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
285
|
+
queue.pull.should == ["myqueue", message2.to_json]
|
286
|
+
queue.pull.should == ["myqueue", message1.to_json]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
223
290
|
describe "equality" do
|
224
291
|
it "should equal queues with same connection and queue names" do
|
225
292
|
Queue.new(connection, "a", "b", "c").should == Queue.new(connection, "a", "b", "c")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fairway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
requirements:
|
67
67
|
- - ! '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 1.3.0
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -74,7 +74,7 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: 1.3.0
|
78
78
|
description: A fair way to queue work in multi-user systems.
|
79
79
|
email:
|
80
80
|
- john@customer.io
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- lib/fairway/channeled_connection.rb
|
95
95
|
- lib/fairway/config.rb
|
96
96
|
- lib/fairway/connection.rb
|
97
|
+
- lib/fairway/facet.rb
|
97
98
|
- lib/fairway/queue.rb
|
98
99
|
- lib/fairway/scripts.rb
|
99
100
|
- lib/fairway/sidekiq.rb
|
@@ -103,10 +104,12 @@ files:
|
|
103
104
|
- lib/fairway/version.rb
|
104
105
|
- redis/fairway_deliver.lua
|
105
106
|
- redis/fairway_peek.lua
|
107
|
+
- redis/fairway_priority.lua
|
106
108
|
- redis/fairway_pull.lua
|
107
109
|
- spec/lib/fairway/channeled_connection_spec.rb
|
108
110
|
- spec/lib/fairway/config_spec.rb
|
109
111
|
- spec/lib/fairway/connection_spec.rb
|
112
|
+
- spec/lib/fairway/facet_spec.rb
|
110
113
|
- spec/lib/fairway/fetch_spec.rb
|
111
114
|
- spec/lib/fairway/queue_spec.rb
|
112
115
|
- spec/lib/fairway/scripts_spec.rb
|
@@ -142,6 +145,7 @@ test_files:
|
|
142
145
|
- spec/lib/fairway/channeled_connection_spec.rb
|
143
146
|
- spec/lib/fairway/config_spec.rb
|
144
147
|
- spec/lib/fairway/connection_spec.rb
|
148
|
+
- spec/lib/fairway/facet_spec.rb
|
145
149
|
- spec/lib/fairway/fetch_spec.rb
|
146
150
|
- spec/lib/fairway/queue_spec.rb
|
147
151
|
- spec/lib/fairway/scripts_spec.rb
|