worker_roulette 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -121,12 +121,12 @@ end
121
121
  ## Performance
122
122
  Running the performance tests on my laptop, the numbers break down like this:
123
123
  ### Async Api
124
- - Manual: ~4200 read-write round-trips / second
125
- - Pubsub: ~5200 read-write round-trips / second
124
+ - Pubsub: ~5500 read-write round-trips / second
125
+ - Manual: ~4500 read-write round-trips / second
126
126
 
127
127
  ### Sync Api
128
- - Manual: ~1600 read-write round-trips / second
129
- - Pubsub: ~2000 read-write round-trips / second
128
+ - Pubsub: ~2700 read-write round-trips / second
129
+ - Manual: ~2500 read-write round-trips / second
130
130
 
131
131
  To run the perf tests yourself run `bundle exec spec:perf`
132
132
 
@@ -33,8 +33,7 @@ module WorkerRoulette
33
33
  end
34
34
 
35
35
  def self.a_foreman(sender, channel = nil)
36
- raise "WorkerRoulette not Started" unless @foreman_connection_pool
37
- AForeman.new(sender, @foreman_connection_pool, channel)
36
+ foreman(sender, channel)
38
37
  end
39
38
 
40
39
  def self.a_tradesman(channel = nil)
@@ -58,6 +57,29 @@ module WorkerRoulette
58
57
  @redis_config.dup
59
58
  end
60
59
 
60
+ def self.dump(obj)
61
+ Oj.dump(obj)
62
+ rescue Oj::ParseError => e
63
+ {'error' => e, 'unparsable_string' => obj}
64
+ end
65
+
66
+ def self.load(json)
67
+ Oj.load(json)
68
+ rescue Oj::ParseError => e
69
+ {'error' => e, 'unparsable_string' => obj}
70
+ end
71
+
72
+ def self.job_board_key(namespace = nil)
73
+ "#{namespace + ':' if namespace}#{WorkerRoulette::JOB_BOARD}"
74
+ end
75
+
76
+ def self.sender_key(sender, namespace = nil)
77
+ "#{namespace + ':' if namespace}#{sender}"
78
+ end
79
+
80
+ def self.counter_key(sender, namespace = nil)
81
+ "#{namespace + ':' if namespace}counter_key"
82
+ end
61
83
  private
62
84
  def self.new_redis
63
85
  if @evented
@@ -8,13 +8,6 @@ module WorkerRoulette
8
8
  @redis_pubsub.subscribe(@channel)
9
9
  end
10
10
 
11
- def work_orders!(&callback)
12
- Lua.call(self.class.lua_drain_work_orders, [job_board_key, nil], [@namespace]) do |results|
13
- @sender = (results.first || '').split(':').first
14
- callback.call (results[1] || []).map {|work_order| Oj.load(work_order)} if callback
15
- end
16
- end
17
-
18
11
  def unsubscribe(&callback)
19
12
  deferable = @redis_pubsub.unsubscribe(@channel)
20
13
  deferable.callback do
@@ -38,30 +31,5 @@ module WorkerRoulette
38
31
  work_orders! {|work_orders| on_message_callback.call(work_orders, nil, nil)}
39
32
  end
40
33
  end
41
-
42
- def self.lua_drain_work_orders
43
- <<-HERE
44
- local job_board_key = KEYS[1]
45
- local empty = KEYS[2]
46
- local namespace = ARGV[1]
47
-
48
- local function drain_work_orders(job_board_key, namespace)
49
- local sender_key = redis.call('ZRANGE', job_board_key, 0, 0)[1]
50
-
51
- if sender_key == false then
52
- return {}
53
- end
54
-
55
- local results = {}
56
- results[1] = sender_key
57
- results[2] = redis.call('LRANGE', sender_key, 0, -1)
58
- results[3] = redis.call('DEL', sender_key)
59
- results[4] = redis.call('ZREM', job_board_key, sender_key)
60
- return results
61
- end
62
-
63
- return drain_work_orders(job_board_key, namespace)
64
- HERE
65
- end
66
34
  end
67
35
  end
@@ -1,7 +1,6 @@
1
1
  module WorkerRoulette
2
2
  class Foreman
3
3
  attr_reader :sender
4
- COUNTER_KEY = 'counter_key'
5
4
 
6
5
  def initialize(sender, redis_pool, namespace = nil)
7
6
  @sender = sender
@@ -10,41 +9,60 @@ module WorkerRoulette
10
9
  @channel = namespace || WorkerRoulette::JOB_NOTIFICATIONS
11
10
  end
12
11
 
13
- def job_board_key
14
- @job_board_key ||= "#{@namespace + ':' if @namespace}#{WorkerRoulette::JOB_BOARD}"
12
+ def enqueue_work_order(work_order, headers = {}, &callback)
13
+ work_order = {'headers' => default_headers.merge(headers), 'payload' => work_order}
14
+ enqueue_work_order_without_headers(work_order, &callback)
15
15
  end
16
16
 
17
- def sender_key
18
- @sender_key = "#{@namespace + ':' if @namespace}#{@sender}"
17
+ def enqueue_work_order_without_headers(work_order, &callback)
18
+ Lua.call(self.class.lua_enqueue_work_orders, [counter_key, job_board_key, sender_key, @channel],
19
+ [WorkerRoulette.dump(work_order), WorkerRoulette::JOB_NOTIFICATIONS], &callback)
19
20
  end
20
21
 
21
- def counter_key
22
- @counter_key = "#{@namespace + ':' if @namespace}#{COUNTER_KEY}"
22
+ def job_board_key
23
+ @job_board_key ||= WorkerRoulette.job_board_key(@namespace)
23
24
  end
24
25
 
25
- def enqueue_work_order_without_headers(work_order, &callback)
26
- #Caveat Emptor: There is a race condition here, but it not serious;
27
- #the count may be incremented again by another process before the sender
28
- #is added to the job_queue. This is not a big deal bc it just means that
29
- #the sender's queue will be processed one slot behind it's rightful place.
30
- #This does not effect work_order ordering.
31
- @redis_pool.with({}) do |redis|
32
- @count = redis.incr(COUNTER_KEY)
33
- redis.multi do
34
- redis.zadd(job_board_key, @count, @sender)
35
- redis.rpush(sender_key, Oj.dump(work_order))
36
- redis.publish(@channel, WorkerRoulette::JOB_NOTIFICATIONS)
37
- end
38
- end
26
+ def counter_key
27
+ @counter_key ||= WorkerRoulette.counter_key(@namespace)
39
28
  end
40
29
 
41
- def enqueue_work_order(work_order, headers = {}, &callback)
42
- work_order = {'headers' => default_headers.merge(headers), 'payload' => work_order}
43
- enqueue_work_order_without_headers(work_order, &callback)
30
+ private
31
+ def sender_key
32
+ @sender_key = WorkerRoulette.sender_key(sender, @namespace)
44
33
  end
45
34
 
46
35
  def default_headers
47
36
  Hash['sender' => sender]
48
37
  end
38
+
39
+ def self.lua_enqueue_work_orders
40
+ <<-HERE
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
+
49
+ local function enqueue_work_orders(work_order, job_notification)
50
+ local result = sender_key .. ' updated'
51
+ local sender_on_job_board = redis.call('ZSCORE', job_board_key, sender_key)
52
+
53
+ if (sender_on_job_board == false) then
54
+ local count = redis.call('INCR', counter_key)
55
+ local job_added = redis.call('ZADD',job_board_key, count, sender_key)
56
+ result = sender_key .. ' added'
57
+ end
58
+
59
+ local work_added = redis.call('RPUSH',sender_key, work_order)
60
+ local job_board_update = redis.call('PUBLISH', channel, job_notification)
61
+ return result
62
+ end
63
+
64
+ return enqueue_work_orders(work_order, job_notification)
65
+ HERE
66
+ end
49
67
  end
50
68
  end
@@ -4,9 +4,7 @@ module WorkerRoulette
4
4
 
5
5
  def self.call(lua_script, keys_accessed = [], args = [], &callback)
6
6
  WorkerRoulette.tradesman_connection_pool.with do |redis|
7
- results = redis.evalsha(sha(lua_script), keys_accessed.length, *keys_accessed, *args)
8
- results.callback &callback
9
- results.errback {self.eval(redis, lua_script, keys_accessed, args, &callback)}
7
+ results = evalsha(redis, lua_script, keys_accessed, args, &callback)
10
8
  end
11
9
  end
12
10
 
@@ -24,8 +22,25 @@ module WorkerRoulette
24
22
 
25
23
  def self.eval(redis, lua_script, keys_accessed, args, &callback)
26
24
  results = redis.eval(lua_script, keys_accessed.size, *keys_accessed, *args)
27
- results.callback &callback
25
+ results.callback &callback if callback
28
26
  results.errback {|err_msg| raise EM::Hiredis::RedisError.new(err_msg)}
29
27
  end
28
+
29
+ def self.evalsha(redis, lua_script, keys_accessed, args, &callback)
30
+ if redis.class == EM::Hiredis::Client
31
+ results = redis.evalsha(sha(lua_script), keys_accessed.length, *keys_accessed, *args)
32
+ results.callback &callback if callback
33
+ results.errback {self.eval(redis, lua_script, keys_accessed, args, &callback)}
34
+ else
35
+ begin
36
+ results = redis.evalsha(sha(lua_script), keys_accessed, args)
37
+ rescue Redis::CommandError
38
+ results = redis.eval(lua_script, keys_accessed, args)
39
+ ensure
40
+ return callback.call results if callback
41
+ end
42
+ end
43
+ results
44
+ end
30
45
  end
31
46
  end
@@ -8,14 +8,6 @@ module WorkerRoulette
8
8
  @channel = namespace || WorkerRoulette::JOB_NOTIFICATIONS
9
9
  end
10
10
 
11
- def job_board_key
12
- @job_board_key ||= "#{@namespace + ':' if @namespace}#{WorkerRoulette::JOB_BOARD}"
13
- end
14
-
15
- def sender_key
16
- @sender_key = "#{@namespace + ':' if @namespace}#{@sender}"
17
- end
18
-
19
11
  def wait_for_work_orders(on_subscribe_callback = nil, &block)
20
12
  @pubsub_pool.with do |redis|
21
13
  redis.subscribe(@channel) do |on|
@@ -25,15 +17,12 @@ module WorkerRoulette
25
17
  end
26
18
  end
27
19
 
28
- def work_orders!
29
- @client_pool.with do |redis|
30
- get_sender_for_next_job(redis)
31
- results = redis.multi do
32
- redis.lrange(sender_key, 0, -1)
33
- redis.del(sender_key)
34
- redis.zrem(job_board_key, sender_key)
35
- end
36
- ((results || []).first || []).map {|work_order| Oj.load(work_order)}
20
+ def work_orders!(&callback)
21
+ Lua.call(self.class.lua_drain_work_orders, [job_board_key, nil], [@namespace]) do |results|
22
+ @sender = (results.first || '').split(':').first
23
+ work = (results[1] || []).map {|work_order| WorkerRoulette.load(work_order)}
24
+ callback.call work if callback
25
+ work
37
26
  end
38
27
  end
39
28
 
@@ -41,7 +30,40 @@ module WorkerRoulette
41
30
  @pubsub_pool.with {|redis| redis.unsubscribe(@channel)}
42
31
  end
43
32
 
33
+ def job_board_key
34
+ @job_board_key ||= WorkerRoulette.job_board_key(@namespace)
35
+ end
36
+
44
37
  private
38
+ def sender_key
39
+ @sender_key = WorkerRoulette.sender_key(@sender, @namespace)
40
+ end
41
+
42
+ def self.lua_drain_work_orders
43
+ <<-HERE
44
+ local job_board_key = KEYS[1]
45
+ local empty = KEYS[2]
46
+ local namespace = ARGV[1]
47
+
48
+ local function drain_work_orders(job_board_key, namespace)
49
+ local sender_key = redis.call('ZRANGE', job_board_key, 0, 0)[1]
50
+
51
+ if sender_key == false then
52
+ return {}
53
+ end
54
+
55
+ local results = {}
56
+ results[1] = sender_key
57
+ results[2] = redis.call('LRANGE', sender_key, 0, -1)
58
+ results[3] = redis.call('DEL', sender_key)
59
+ results[4] = redis.call('ZREM', job_board_key, sender_key)
60
+ return results
61
+ end
62
+
63
+ return drain_work_orders(job_board_key, namespace)
64
+ HERE
65
+ end
66
+
45
67
  def get_sender_for_next_job(redis)
46
68
  @sender = (redis.zrange(job_board_key, 0, 0) || []).first.to_s
47
69
  end
@@ -1,3 +1,3 @@
1
1
  module WorkerRoulette
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -12,7 +12,7 @@ WorkerRoulette.tradesman_connection_pool.with {|r| r.flushdb}
12
12
  puts "Redis Connection Pool Size: #{REDIS_CONNECTION_POOL_SIZE}"
13
13
 
14
14
  Benchmark.bmbm do |x|
15
- x.report "Time to insert and read #{ITERATIONS} large work_orders" do # ~1600 work_orders / second round trip; 50-50 read-write time; CPU and IO bound
15
+ x.report "Time to insert and read #{ITERATIONS} large work_orders" do # ~2500 work_orders / second round trip; 50-50 read-write time; CPU and IO bound
16
16
  WorkerRoulette.start(size: REDIS_CONNECTION_POOL_SIZE, evented: false)
17
17
  ITERATIONS.times do |iteration|
18
18
  sender = 'sender_' + iteration.to_s
@@ -33,7 +33,7 @@ EM::Hiredis.reconnect_timeout = 0.01
33
33
  WorkerRoulette.tradesman_connection_pool.with {|r| r.flushdb}
34
34
 
35
35
  Benchmark.bmbm do |x|
36
- x.report "Time for tradesmans to enqueue_work_order and read #{ITERATIONS} large work_orders via pubsub" do # ~2000 work_orders / second round trip
36
+ x.report "Time for tradesmans to enqueue_work_order and read #{ITERATIONS} large work_orders via pubsub" do # ~2700 work_orders / second round trip
37
37
  WorkerRoulette.start(size: REDIS_CONNECTION_POOL_SIZE, evented: false)
38
38
  ITERATIONS.times do |iteration|
39
39
  p = -> do
@@ -9,7 +9,7 @@ describe WorkerRoulette do
9
9
  let(:hello_work_order) {Hash['payload' => "hello"]}
10
10
  let(:foreman_work_order) {Hash['payload' => "foreman"]}
11
11
  let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
12
- let(:jsonized_work_orders_with_headers) {[Oj.dump(work_orders_with_headers)]}
12
+ let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
13
13
 
14
14
  let(:redis) {Redis.new(WorkerRoulette.redis_config)}
15
15
 
@@ -17,7 +17,7 @@ describe WorkerRoulette do
17
17
  WorkerRoulette.start(evented: true)
18
18
  end
19
19
 
20
- context Foreman do
20
+ context "Evented Foreman" do
21
21
  let(:subject) {WorkerRoulette.a_foreman(sender)}
22
22
 
23
23
  it "should enqueue work" do
@@ -33,7 +33,7 @@ describe WorkerRoulette do
33
33
  it "should enqueue_work_order two work_orders in the sender's slot in the job board" do
34
34
  subject.enqueue_work_order(work_orders.first) do
35
35
  subject.enqueue_work_order(work_orders.last) do
36
- redis.lrange(sender, 0, -1).should == work_orders.map {|m| Oj.dump(default_headers.merge({'payload' => m})) }
36
+ redis.lrange(sender, 0, -1).should == work_orders.map {|m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) }
37
37
  done
38
38
  end
39
39
  end
@@ -41,7 +41,7 @@ describe WorkerRoulette do
41
41
 
42
42
  it "should enqueue_work_order an array of work_orders without headers in the sender's slot in the job board" do
43
43
  subject.enqueue_work_order_without_headers(work_orders) do
44
- redis.lrange(sender, 0, -1).should == [Oj.dump(work_orders)]
44
+ redis.lrange(sender, 0, -1).should == [WorkerRoulette.dump(work_orders)]
45
45
  done
46
46
  end
47
47
  end
@@ -57,7 +57,7 @@ describe WorkerRoulette do
57
57
  extra_headers = {'foo' => 'bars'}
58
58
  subject.enqueue_work_order(work_orders, extra_headers) do
59
59
  work_orders_with_headers['headers'].merge!(extra_headers)
60
- redis.lrange(sender, 0, -1).should == [Oj.dump(work_orders_with_headers)]
60
+ redis.lrange(sender, 0, -1).should == [WorkerRoulette.dump(work_orders_with_headers)]
61
61
  done
62
62
  end
63
63
  end
@@ -139,7 +139,7 @@ describe WorkerRoulette do
139
139
  end
140
140
  end
141
141
 
142
- context Tradesman do
142
+ context "Evented Tradesman" do
143
143
  let(:foreman) {WorkerRoulette.a_foreman(sender)}
144
144
  let(:subject) {WorkerRoulette.a_tradesman}
145
145
 
@@ -266,16 +266,10 @@ describe WorkerRoulette do
266
266
  end
267
267
  end
268
268
 
269
- it "should return a hash with a string in the payload if OJ cannot parse the json" do
269
+ xit "should return a hash with a string in the payload if OJ cannot parse the json" do
270
270
 
271
271
  end
272
272
 
273
- context "Potential Ack Success/Failure for Processing Queues" do
274
- xit "should not delete the messages from the queue until they have been processed succcesfully"
275
- xit "should checkout a readlock for a queue and put it back when its done processing; lock should expire after 5 minutes?"
276
- xit "should retry doing work on a queue 3 times if it is locked (ex backoff)"
277
- end
278
-
279
273
  context "Failure" do
280
274
  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
281
275
  end
@@ -7,7 +7,7 @@ describe WorkerRoulette do
7
7
  let(:hello_work_order) {Hash['payload' => "hello"]}
8
8
  let(:foreman_work_order) {Hash['payload' => "foreman"]}
9
9
  let(:work_orders_with_headers) {default_headers.merge({'payload' => work_orders})}
10
- let(:jsonized_work_orders_with_headers) {[Oj.dump(work_orders_with_headers)]}
10
+ let(:jsonized_work_orders_with_headers) {[WorkerRoulette.dump(work_orders_with_headers)]}
11
11
 
12
12
  let(:redis) {Redis.new(WorkerRoulette.redis_config)}
13
13
 
@@ -26,20 +26,15 @@ describe WorkerRoulette do
26
26
  subject.sender.should == sender
27
27
  end
28
28
 
29
- it "should be injected with a raw_redis_client so it can do is work" do
30
- Redis.any_instance.should_receive(:rpush)
31
- subject.enqueue_work_order(:whatever)
32
- end
33
-
34
29
  it "should enqueue_work_order two work_orders in the sender's slot in the switchboard" do
35
- subject.enqueue_work_order(work_orders.first)
36
- subject.enqueue_work_order(work_orders.last)
37
- redis.lrange(sender, 0, -1).should == work_orders.map {|m| Oj.dump(default_headers.merge({'payload' => m})) }
30
+ subject.enqueue_work_order(work_orders.first) {}
31
+ subject.enqueue_work_order(work_orders.last) {}
32
+ redis.lrange(sender, 0, -1).should == work_orders.map {|m| WorkerRoulette.dump(default_headers.merge({'payload' => m})) }
38
33
  end
39
34
 
40
35
  it "should enqueue_work_order an array of work_orders without headers in the sender's slot in the switchboard" do
41
36
  subject.enqueue_work_order_without_headers(work_orders)
42
- redis.lrange(sender, 0, -1).should == [Oj.dump(work_orders)]
37
+ redis.lrange(sender, 0, -1).should == [WorkerRoulette.dump(work_orders)]
43
38
  end
44
39
 
45
40
  it "should enqueue_work_order an array of work_orders with default headers in the sender's slot in the switchboard" do
@@ -51,26 +46,24 @@ describe WorkerRoulette do
51
46
  extra_headers = {'foo' => 'bars'}
52
47
  subject.enqueue_work_order(work_orders, extra_headers)
53
48
  work_orders_with_headers['headers'].merge!(extra_headers)
54
- redis.lrange(sender, 0, -1).should == [Oj.dump(work_orders_with_headers)]
49
+ redis.lrange(sender, 0, -1).should == [WorkerRoulette.dump(work_orders_with_headers)]
55
50
  end
56
51
 
57
52
  it "should post the sender's id to the job board with an order number" do
58
53
  subject.enqueue_work_order(work_orders.first)
59
- subject.enqueue_work_order(work_orders.last)
60
- redis.zrange(subject.job_board_key, 0, -1, with_scores: true).should == [[sender.to_s, work_orders.length.to_f]]
61
- end
62
-
63
- it "should post the sender_id and work_orders transactionally" do
64
- Redis.any_instance.should_receive(:multi)
65
- subject.enqueue_work_order(work_orders.first)
54
+ WorkerRoulette.foreman('other_forman').enqueue_work_order(work_orders.last)
55
+ redis.zrange(subject.job_board_key, 0, -1, with_scores: true).should == [[sender, 1.0], ["other_forman", 2.0]]
66
56
  end
67
57
 
68
- it "should generate sequential order numbers" do
58
+ it "should generate a monotically increasing score for senders not on the job board, but not for senders already there" do
59
+ other_forman = WorkerRoulette.foreman('other_forman')
69
60
  redis.get(subject.counter_key).should == nil
70
61
  subject.enqueue_work_order(work_orders.first)
71
62
  redis.get(subject.counter_key).should == "1"
72
63
  subject.enqueue_work_order(work_orders.last)
73
- redis.get(subject.counter_key).should == "2"
64
+ redis.get(subject.counter_key).should == "1"
65
+ other_forman.enqueue_work_order(work_orders.last)
66
+ redis.get(other_forman.counter_key).should == "2"
74
67
  end
75
68
 
76
69
  it "should publish a notification that a new job is ready" do
@@ -104,11 +97,6 @@ describe WorkerRoulette do
104
97
  subject.sender.should == sender
105
98
  end
106
99
 
107
- it "should be injected with a redis client so it can do its work" do
108
- Redis.any_instance.should_receive(:lrange).and_call_original
109
- subject.work_orders!
110
- end
111
-
112
100
  it "should drain one set of work_orders from the sender's slot in the switchboard" do
113
101
  subject.work_orders!.should == [work_orders_with_headers]
114
102
  subject.work_orders!.should == []
@@ -132,11 +120,6 @@ describe WorkerRoulette do
132
120
  redis.zrange(subject.job_board_key, 0, -1).should == [most_recent_sender]
133
121
  end
134
122
 
135
- it "should get the sender and work_order list transactionally" do
136
- Redis.any_instance.should_receive(:multi).and_call_original
137
- subject.work_orders!
138
- end
139
-
140
123
  it "should get the work_orders from the next queue when a new job is ready" do
141
124
  subject.work_orders!
142
125
  subject.should_receive(:work_orders!).and_call_original
@@ -168,9 +151,6 @@ describe WorkerRoulette do
168
151
  end
169
152
  end
170
153
 
171
- it "should checkout a readlock for a queue and put it back when its done processing; lock should expire after 5 minutes?"
172
- it "should eves drop on the job board"
173
-
174
154
  context "Failure" do
175
155
  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
176
156
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: worker_roulette
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -250,7 +250,6 @@ files:
250
250
  - README.md
251
251
  - Rakefile
252
252
  - lib/worker_roulette.rb
253
- - lib/worker_roulette/a_foreman.rb
254
253
  - lib/worker_roulette/a_tradesman.rb
255
254
  - lib/worker_roulette/foreman.rb
256
255
  - lib/worker_roulette/lua.rb
@@ -1,40 +0,0 @@
1
- require_relative './foreman'
2
- module WorkerRoulette
3
- class AForeman < Foreman
4
- def enqueue_work_order_without_headers(work_order, &callback)
5
- Lua.call(self.class.lua_enqueue_work_orders, [COUNTER_KEY, job_board_key, sender_key, @channel],
6
- [@sender, Oj.dump(work_order), WorkerRoulette::JOB_NOTIFICATIONS], &callback)
7
- end
8
-
9
- private
10
- def self.lua_enqueue_work_orders
11
- <<-HERE
12
- local counter_key = KEYS[1]
13
- local job_board_key = KEYS[2]
14
- local sender_key = KEYS[3]
15
- local channel = KEYS[4]
16
-
17
- local sender = ARGV[1]
18
- local work_order = ARGV[2]
19
- local job_notification = ARGV[3]
20
-
21
- local function enqueue_work_orders(sender, work_order, job_notification)
22
- local result = sender .. ' updated'
23
- local sender_on_job_board = redis.call('ZSCORE', job_board_key, sender_key)
24
-
25
- if (sender_on_job_board == false) then
26
- local count = redis.call('INCR', counter_key)
27
- local job_added = redis.call('ZADD',job_board_key, count, sender_key)
28
- result = sender .. ' added'
29
- end
30
-
31
- local work_added = redis.call('RPUSH',sender_key, work_order)
32
- local job_board_update = redis.call('PUBLISH', channel, job_notification)
33
- return result
34
- end
35
-
36
- return enqueue_work_orders(sender, work_order, job_notification)
37
- HERE
38
- end
39
- end
40
- end