fairway 0.1.0 → 0.1.1
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.
- 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
|