roundhouse-x 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +39 -8
- data/lib/roundhouse.rb +11 -3
- data/lib/roundhouse/api.rb +1 -1
- data/lib/roundhouse/cli.rb +10 -32
- data/lib/roundhouse/fetch.rb +4 -2
- data/lib/roundhouse/launcher.rb +1 -1
- data/lib/roundhouse/monitor.rb +137 -36
- data/lib/roundhouse/processor.rb +2 -3
- data/lib/roundhouse/scheduled.rb +1 -0
- data/lib/roundhouse/script.rb +49 -0
- data/lib/roundhouse/version.rb +1 -1
- data/lib/roundhouse/web.rb +13 -10
- data/roundhouse.gemspec +1 -0
- data/test/test_cli.rb +1 -95
- data/test/test_fetch.rb +13 -45
- data/test/test_monitor.rb +307 -28
- data/test/test_processor.rb +2 -2
- data/web/assets/javascripts/dashboard.js +5 -0
- data/web/views/_summary.erb +20 -0
- data/web/views/busy.erb +0 -2
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e419459121a0e984bdeb39afef3442ff962a37a9
|
4
|
+
data.tar.gz: 8781d7c6f13f23bdc40c2a30311ac73d26476b07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5648e6e5e86ab1e55b5c5e1a6e6f3eb2f660ed7013dc67b9e6b6ebf9910ebf9e2f2b2a5f375b94528c44c9b54d61d4b61e2a3c57510211e1d7aba38e2054ac1b
|
7
|
+
data.tar.gz: 0b8591db28a22914c1eb5e1c0ca65fc90988e6f3b91ac623f07c1c2cca86453f03396112696bce5b0ff64fb1d13592e061266407d30909114aa98258a210f63f
|
data/README.md
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
Roundhouse
|
2
2
|
==============
|
3
3
|
|
4
|
-
Roundhouse is based on Sidekiq 3.5.0 (HEAD: f8ee0896076671b73739099e3c1eb7d8814e407d)
|
4
|
+
Roundhouse is based on Sidekiq 3.5.0 (`HEAD: f8ee0896076671b73739099e3c1eb7d8814e407d`). It
|
5
|
+
dispatches jobs in a very specialized way for a very narrow set of problems. For example,
|
6
|
+
if you don't need to manage a large number of rate-limited access to an external resource,
|
7
|
+
you probably want to use Sidekiq and not Roundhouse.
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
The problem Roundhouse solves resembles that of a load balancing problem: only one worker
|
10
|
+
should be working on a work for a given user. Any other worker can work on any other user's
|
11
|
+
work, as only as no other worker is working on it. We also want to do this fairly so that every
|
12
|
+
user has a chance at getting work done steadily. This requires:
|
9
13
|
|
10
|
-
1. A queue for each user
|
11
|
-
2. A
|
12
|
-
3. A way to track
|
14
|
+
1. A queue for each user. (Which often means a lot of queues)
|
15
|
+
2. A turntable semaphore to control access to queues in a round-robin fashion
|
16
|
+
3. A way to track the status of each queue (active, empty, suspended) and when to push a queue back into the turntable
|
13
17
|
|
14
18
|
Other things Roundhouse should be able to do:
|
15
19
|
|
16
|
-
1. It should be able to run side-by side with Sidekiq client
|
20
|
+
1. It should be able to run side-by side with Sidekiq client.
|
17
21
|
2. Be able to suspend, resume, and defer work
|
18
22
|
3. Metrics to measure lead times and give estimates on when work might be completed
|
19
23
|
4. Coordinate across a number of worker processes, across machines
|
20
24
|
|
25
|
+
Roundhouse is in pre-alpha right now, designed for a specialized need we have. You probably don't want to
|
26
|
+
try this in production.
|
27
|
+
|
21
28
|
Install
|
22
29
|
----------------
|
23
30
|
|
@@ -40,6 +47,30 @@ end
|
|
40
47
|
APIWorker.perform_async(api_token.id, item.id)
|
41
48
|
```
|
42
49
|
|
50
|
+
Why the name 'Roundhouse'?
|
51
|
+
--------------------------
|
52
|
+
|
53
|
+
The name 'Sidekiq' is obviously a play on the word, 'sidekick', both to mean a companion that
|
54
|
+
helps a hero out in the background, and the name of a martial arts kicking technique. It
|
55
|
+
is a great name for a background processing manager that works well with Rails apps.
|
56
|
+
|
57
|
+
When Roundhouse was first concieved, it was meant as a play on the word 'sidekick'. Since Roundhouse
|
58
|
+
controls access to a queue in a round-robin fashion, I thought of a 'roundhouse kick'. Later, while
|
59
|
+
implementing the code, I found out that a 'roundhouse' is an old term in the railway industry.
|
60
|
+
In the old days, you needed a turntable to move trains around, or to reverse their direction.
|
61
|
+
Although in modern times, 'Roundhouse' has come to mean the general facilities to maintain
|
62
|
+
trains, this idea of a turntable at the center of a roundhouse is a powerful metaphor for the way
|
63
|
+
Roundhouse dispatches work to background workers.
|
64
|
+
|
65
|
+
The structure that actually controls access is neither a counting or a binary semaphore. I
|
66
|
+
had originally thought to call it a "queing semaphore' but that confuses a lot of people. The
|
67
|
+
term 'turntable semaphore' better describes this.
|
68
|
+
|
69
|
+
Unfortunately, someone had written a Ruby gem several years ago called RoundhousE. I have no
|
70
|
+
idea what that project does, other than that it has something to do with Ruby and .NET. So for
|
71
|
+
now, this gem is available as `roundhouse-x`. (Yes, that is quite a creative name).
|
72
|
+
|
73
|
+
|
43
74
|
Sidekiq License
|
44
75
|
-----------------
|
45
76
|
|
data/lib/roundhouse.rb
CHANGED
@@ -14,7 +14,6 @@ module Roundhouse
|
|
14
14
|
LICENSE = 'See LICENSE and the LGPL-3.0 for licensing details.'
|
15
15
|
|
16
16
|
DEFAULTS = {
|
17
|
-
queues: [],
|
18
17
|
labels: [],
|
19
18
|
concurrency: 25,
|
20
19
|
require: '.',
|
@@ -33,8 +32,7 @@ module Roundhouse
|
|
33
32
|
}
|
34
33
|
|
35
34
|
DEFAULT_WORKER_OPTIONS = {
|
36
|
-
'retry' => true
|
37
|
-
'queue' => 'default'
|
35
|
+
'retry' => true
|
38
36
|
}
|
39
37
|
|
40
38
|
def self.❨╯°□°❩╯︵┻━┻
|
@@ -93,6 +91,16 @@ module Roundhouse
|
|
93
91
|
end
|
94
92
|
end
|
95
93
|
|
94
|
+
# Suspends a queue
|
95
|
+
def self.suspend_queue(queue_id)
|
96
|
+
self.redis { |conn| Roundhouse::Monitor.suspend(conn, queue_id) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Resumes a queue
|
100
|
+
def self.resume_queue(queue_id)
|
101
|
+
self.redis { |conn| Roundhouse::Monitor.resume(conn, queue_id) }
|
102
|
+
end
|
103
|
+
|
96
104
|
def self.client_middleware
|
97
105
|
@client_chain ||= Middleware::Chain.new
|
98
106
|
yield @client_chain if block_given?
|
data/lib/roundhouse/api.rb
CHANGED
@@ -62,7 +62,7 @@ module Roundhouse
|
|
62
62
|
conn.zcard('retry'.freeze)
|
63
63
|
conn.zcard('dead'.freeze)
|
64
64
|
conn.scard('processes'.freeze)
|
65
|
-
conn.llen(
|
65
|
+
conn.llen(Roundhouse::Monitor::TURNTABLE.freeze)
|
66
66
|
conn.smembers('processes'.freeze)
|
67
67
|
conn.smembers(Roundhouse::Monitor::BUCKETS)
|
68
68
|
end
|
data/lib/roundhouse/cli.rb
CHANGED
@@ -64,7 +64,6 @@ module Roundhouse
|
|
64
64
|
|
65
65
|
logger.info "Running in #{RUBY_DESCRIPTION}"
|
66
66
|
logger.info Roundhouse::LICENSE
|
67
|
-
logger.info "Upgrade to Roundhouse Pro for more features and support: http://roundhouse.org" unless defined?(::Roundhouse::Pro)
|
68
67
|
|
69
68
|
fire_event(:startup)
|
70
69
|
|
@@ -102,18 +101,16 @@ module Roundhouse
|
|
102
101
|
end
|
103
102
|
|
104
103
|
def self.banner
|
105
|
-
%q{
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
sss
|
116
|
-
sss }
|
104
|
+
%q{
|
105
|
+
______ _ _
|
106
|
+
| ___ \ | | |
|
107
|
+
| |_/ /___ _ _ _ __ __| | |__ ___ _ _ ___ ___
|
108
|
+
| // _ \| | | | '_ \ / _` | '_ \ / _ \| | | / __|/ _ \
|
109
|
+
| |\ \ (_) | |_| | | | | (_| | | | | (_) | |_| \__ \ __/
|
110
|
+
\_| \_\___/ \__,_|_| |_|\__,_|_| |_|\___/ \__,_|___/\___|
|
111
|
+
|
112
|
+
|
113
|
+
}
|
117
114
|
end
|
118
115
|
|
119
116
|
def handle_signal(sig)
|
@@ -262,8 +259,6 @@ module Roundhouse
|
|
262
259
|
end
|
263
260
|
|
264
261
|
def validate!
|
265
|
-
options[:queues] << 'default' if options[:queues].empty?
|
266
|
-
|
267
262
|
if !File.exist?(options[:require]) ||
|
268
263
|
(File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
|
269
264
|
logger.info "=================================================================="
|
@@ -303,11 +298,6 @@ module Roundhouse
|
|
303
298
|
opts[:index] = Integer(arg.match(/\d+/)[0])
|
304
299
|
end
|
305
300
|
|
306
|
-
o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
|
307
|
-
queue, weight = arg.split(",")
|
308
|
-
parse_queue opts, queue, weight
|
309
|
-
end
|
310
|
-
|
311
301
|
o.on '-r', '--require [PATH|DIR]', "Location of Rails application with workers or file to require" do |arg|
|
312
302
|
opts[:require] = arg
|
313
303
|
end
|
@@ -368,7 +358,6 @@ module Roundhouse
|
|
368
358
|
if File.exist?(cfile)
|
369
359
|
opts = YAML.load(ERB.new(IO.read(cfile)).result) || opts
|
370
360
|
opts = opts.merge(opts.delete(environment) || {})
|
371
|
-
parse_queues(opts, opts.delete(:queues) || [])
|
372
361
|
else
|
373
362
|
# allow a non-existent config file so Roundhouse
|
374
363
|
# can be deployed by cap with just the defaults.
|
@@ -381,16 +370,5 @@ module Roundhouse
|
|
381
370
|
end
|
382
371
|
opts
|
383
372
|
end
|
384
|
-
|
385
|
-
def parse_queues(opts, queues_and_weights)
|
386
|
-
queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
|
387
|
-
end
|
388
|
-
|
389
|
-
def parse_queue(opts, q, weight=nil)
|
390
|
-
[weight.to_i, 1].max.times do
|
391
|
-
(opts[:queues] ||= []) << q
|
392
|
-
end
|
393
|
-
opts[:strict] = false if weight.to_i > 0
|
394
|
-
end
|
395
373
|
end
|
396
374
|
end
|
data/lib/roundhouse/fetch.rb
CHANGED
@@ -32,7 +32,7 @@ module Roundhouse
|
|
32
32
|
# a new fetch if the current fetch turned up nothing.
|
33
33
|
def fetch
|
34
34
|
watchdog('Fetcher#fetch died') do
|
35
|
-
return if Roundhouse::Fetcher.done?
|
35
|
+
logger.debug 'Fetcher terminating in #fetch' and return if Roundhouse::Fetcher.done?
|
36
36
|
|
37
37
|
begin
|
38
38
|
work = @strategy.retrieve_work
|
@@ -77,6 +77,7 @@ module Roundhouse
|
|
77
77
|
# its mailbox when shutdown starts.
|
78
78
|
def self.done!
|
79
79
|
@done = true
|
80
|
+
Roundhouse.logger.debug 'Fetcher: setting done'
|
80
81
|
end
|
81
82
|
|
82
83
|
def self.reset # testing only
|
@@ -94,10 +95,11 @@ module Roundhouse
|
|
94
95
|
|
95
96
|
class RoundRobinFetch
|
96
97
|
def initialize(options = nil)
|
98
|
+
# ignore options
|
97
99
|
end
|
98
100
|
|
99
101
|
def retrieve_work
|
100
|
-
work = Roundhouse.redis { |conn| Roundhouse::Monitor.
|
102
|
+
work = Roundhouse.redis { |conn| Roundhouse::Monitor.maybe_next_job(conn) }
|
101
103
|
UnitOfWork.new(*work) if work
|
102
104
|
end
|
103
105
|
|
data/lib/roundhouse/launcher.rb
CHANGED
@@ -46,6 +46,7 @@ module Roundhouse
|
|
46
46
|
|
47
47
|
def stop
|
48
48
|
watchdog('Launcher#stop') do
|
49
|
+
logger.debug 'Stopping launcher'
|
49
50
|
@done = true
|
50
51
|
Roundhouse::Fetcher.done!
|
51
52
|
fetcher.terminate if fetcher.alive?
|
@@ -73,7 +74,6 @@ module Roundhouse
|
|
73
74
|
'pid' => $$,
|
74
75
|
'tag' => @options[:tag] || '',
|
75
76
|
'concurrency' => @options[:concurrency],
|
76
|
-
'queues' => @options[:queues].uniq,
|
77
77
|
'labels' => Roundhouse.options[:labels],
|
78
78
|
'identity' => identity,
|
79
79
|
}
|
data/lib/roundhouse/monitor.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'roundhouse/script'
|
2
|
+
|
1
3
|
module Roundhouse
|
2
4
|
# This class implements two things:
|
3
|
-
# 1. A
|
5
|
+
# 1. A turntable semaphore - the fetcher can pop the next available
|
4
6
|
# exclusive right to something (such as API request with a given
|
5
7
|
# auth token)
|
6
8
|
# 2. Track which access right is temporarily suspended
|
@@ -10,25 +12,32 @@ module Roundhouse
|
|
10
12
|
SUSPENDED = -1
|
11
13
|
|
12
14
|
# This helps catch problems with key names at runtime
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
TURNTABLE = 'turntable'.freeze
|
16
|
+
IN_ROTATION = 'inrotation'.freeze
|
17
|
+
BUCKETS = 'buckets'.freeze
|
18
|
+
QUEUE = 'queue'.freeze
|
19
|
+
SCHEDULE = 'schedule'.freeze
|
20
|
+
STATUS = 'status'.freeze
|
21
|
+
|
22
|
+
# Number of seconds to block on turntable
|
23
|
+
TURNTABLE_TIMEOUT = 5
|
18
24
|
|
19
25
|
class << self
|
20
26
|
# Find the first active queue
|
21
|
-
#
|
27
|
+
# Return nil if nothing is there. The fetcher is responsible
|
28
|
+
# for polling.
|
22
29
|
def pop(conn)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
_, q_id = conn.brpop(TURNTABLE, TURNTABLE_TIMEOUT)
|
31
|
+
conn.srem(IN_ROTATION, q_id)
|
32
|
+
return q_id if queue_status(conn, q_id) == ACTIVE
|
33
|
+
return nil
|
27
34
|
end
|
28
35
|
|
36
|
+
# Atomic push
|
37
|
+
# Push queue into turntable if and only if queue status is active
|
29
38
|
def push(conn, q_id)
|
30
|
-
|
31
|
-
conn
|
39
|
+
# NOTE: this version of redis-namespace has a bug when you do keys: argv: params
|
40
|
+
TURNTABLE_PUSH.call conn, [status_bucket(q_id), TURNTABLE, IN_ROTATION], [q_id]
|
32
41
|
end
|
33
42
|
|
34
43
|
# Bulk requeue (push from right). Usually done
|
@@ -37,17 +46,19 @@ module Roundhouse
|
|
37
46
|
conn.rpush("#{QUEUE}:#{q_id}", jobs)
|
38
47
|
end
|
39
48
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
def maybe_next_job(conn)
|
50
|
+
queue_id = pop(conn)
|
51
|
+
return nil unless queue_id
|
52
|
+
|
53
|
+
job = pop_job(conn, queue_id)
|
54
|
+
return queue_id, job if job
|
55
|
+
return nil
|
47
56
|
end
|
48
57
|
|
58
|
+
# Atomically pop job
|
59
|
+
# If nothing is in the queue, set queue status to empty
|
49
60
|
def pop_job(conn, q_id)
|
50
|
-
conn
|
61
|
+
POP_JOB.call conn, ["#{QUEUE}:#{q_id}", status_bucket(q_id)], [q_id]
|
51
62
|
end
|
52
63
|
|
53
64
|
def push_job(conn, payloads)
|
@@ -77,25 +88,20 @@ module Roundhouse
|
|
77
88
|
end
|
78
89
|
|
79
90
|
def resume(conn, q_id)
|
80
|
-
|
81
|
-
set_queue_status(conn, q_id, ACTIVE)
|
82
|
-
conn.lpush(SEMAPHORE, q_id)
|
91
|
+
RESUME_QUEUE.call conn, [status_bucket(q_id), TURNTABLE, IN_ROTATION], [q_id]
|
83
92
|
end
|
84
93
|
|
85
94
|
def queue_status(conn, q_id)
|
86
95
|
conn.hget(status_bucket(q_id), q_id).to_i || EMPTY
|
87
96
|
end
|
88
97
|
|
98
|
+
# Only push onto turntable is status is empty
|
99
|
+
# If empty, push into turntable and set to active
|
100
|
+
# Subsequent job pushes would see the active queue and
|
101
|
+
# not push into turntable
|
102
|
+
# Used after pushing a job, and conditionally putting it into rotation
|
89
103
|
def maybe_add_to_rotation(conn, q_id)
|
90
|
-
|
91
|
-
# sure this is set to ACTIVE after pushing it into the
|
92
|
-
# queuing semaphore. Otherwise, race conditions might
|
93
|
-
# creep in giving this queue an unfair advantage.
|
94
|
-
# See: https://github.com/resque/redis-namespace/blob/master/lib/redis/namespace.rb#L403-L413
|
95
|
-
# See: https://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/
|
96
|
-
return false unless queue_status(conn, q_id) == EMPTY
|
97
|
-
activate(conn, q_id)
|
98
|
-
conn.lpush(SEMAPHORE, q_id)
|
104
|
+
MAYBE_TURNTABLE_PUSH.call conn, [status_bucket(q_id), TURNTABLE, BUCKETS, IN_ROTATION], [q_id, bucket_num(q_id)]
|
99
105
|
end
|
100
106
|
|
101
107
|
def status_bucket(q_id)
|
@@ -106,6 +112,51 @@ module Roundhouse
|
|
106
112
|
q_id.to_i / 1000
|
107
113
|
end
|
108
114
|
|
115
|
+
# TODO: This needs to be in a lua script and atomic
|
116
|
+
# Need to test how well this scales too. Should be testing about
|
117
|
+
# 10,000 queues
|
118
|
+
def rebuild_turntable!
|
119
|
+
Roundhouse.redis do |conn|
|
120
|
+
buckets = conn.smembers(BUCKETS)
|
121
|
+
queues = conn.pipelined do
|
122
|
+
buckets.each { |bucket| conn.hgetall("#{STATUS}:#{bucket}") }
|
123
|
+
end
|
124
|
+
|
125
|
+
all_queue_ids = queues.map(&:keys).flatten
|
126
|
+
queue_len_res = conn.pipelined do
|
127
|
+
all_queue_ids.each { |queue| conn.llen("#{QUEUE}:#{queue}") }
|
128
|
+
end
|
129
|
+
|
130
|
+
queue_len = all_queue_ids.zip(queue_len_res)
|
131
|
+
status = queues.inject({}) { |a,x| a.merge(x) }
|
132
|
+
|
133
|
+
conn.multi do
|
134
|
+
conn.del(TURNTABLE)
|
135
|
+
queue_len.each do |(q_id, len)|
|
136
|
+
s = status[q_id].to_i
|
137
|
+
|
138
|
+
case s
|
139
|
+
when SUSPENDED then next
|
140
|
+
when ACTIVE then
|
141
|
+
if len > 0
|
142
|
+
conn.lpush(TURNTABLE, q_id)
|
143
|
+
conn.sadd(IN_ROTATION, q_id)
|
144
|
+
else
|
145
|
+
set_queue_status(conn, q_id, EMPTY, false)
|
146
|
+
end
|
147
|
+
when EMPTY then
|
148
|
+
next if len <= 0
|
149
|
+
conn.lpush(TURNTABLE, q_id)
|
150
|
+
conn.sadd(IN_ROTATION, q_id)
|
151
|
+
set_queue_status(conn, q_id, ACTIVE, false)
|
152
|
+
else
|
153
|
+
set_queue_status(conn, q_id, SUSPENDED, false)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
109
160
|
private
|
110
161
|
|
111
162
|
def schedule(conn, payloads)
|
@@ -115,10 +166,60 @@ module Roundhouse
|
|
115
166
|
end )
|
116
167
|
end
|
117
168
|
|
118
|
-
def set_queue_status(conn, q_id, status)
|
119
|
-
conn.sadd(BUCKETS, bucket_num(q_id))
|
169
|
+
def set_queue_status(conn, q_id, status, add_bucket = true)
|
170
|
+
conn.sadd(BUCKETS, bucket_num(q_id)) if add_bucket
|
120
171
|
conn.hset(status_bucket(q_id), q_id, status)
|
121
172
|
end
|
122
|
-
end
|
173
|
+
end # class << self
|
174
|
+
|
175
|
+
WOLVERINE_CONFIG = Roundhouse::Script::Configuration.new
|
176
|
+
|
177
|
+
LUA_TURNTABLE_PUSH = <<END
|
178
|
+
local status = redis.call("HGET", KEYS[1], ARGV[1])
|
179
|
+
local in_rotation = redis.call("SISMEMBER", KEYS[3], ARGV[1])
|
180
|
+
if status == "#{ACTIVE}" and in_rotation == 0 then
|
181
|
+
redis.call("SADD", KEYS[3], ARGV[1])
|
182
|
+
return redis.call("LPUSH", KEYS[2], ARGV[1])
|
183
|
+
end
|
184
|
+
return nil
|
185
|
+
END
|
186
|
+
LUA_MAYBE_TURNTABLE_PUSH = <<END
|
187
|
+
local status = redis.call("HGET", KEYS[1], ARGV[1])
|
188
|
+
if status == "#{EMPTY}" or status == nil or status == false then
|
189
|
+
redis.call('SADD', KEYS[3], ARGV[2])
|
190
|
+
redis.call("HSET", KEYS[1], ARGV[1], #{ACTIVE})
|
191
|
+
local in_rotation = redis.call("SISMEMBER", KEYS[4], ARGV[1])
|
192
|
+
if in_rotation == 0 then
|
193
|
+
redis.call('SADD', KEYS[4], ARGV[1])
|
194
|
+
return redis.call("LPUSH", KEYS[2], ARGV[1])
|
123
195
|
end
|
124
196
|
end
|
197
|
+
return nil
|
198
|
+
END
|
199
|
+
LUA_POP_JOB = <<END
|
200
|
+
local job = redis.call("RPOP", KEYS[1])
|
201
|
+
if type(job) == "string" then
|
202
|
+
return job
|
203
|
+
end
|
204
|
+
redis.call("HSET", KEYS[2], ARGV[1], #{EMPTY})
|
205
|
+
return nil
|
206
|
+
END
|
207
|
+
LUA_RESUME_QUEUE = <<END
|
208
|
+
local status = redis.call("HGET", KEYS[1], ARGV[1])
|
209
|
+
if status == "#{SUSPENDED}" then
|
210
|
+
redis.call("HSET", KEYS[1], ARGV[1], #{ACTIVE})
|
211
|
+
local in_rotation = redis.call("SISMEMBER", KEYS[3], ARGV[1])
|
212
|
+
if in_rotation == 0 then
|
213
|
+
redis.call('SADD', KEYS[4], ARGV[1])
|
214
|
+
return redis.call("LPUSH", KEYS[2], ARGV[1])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
return nil
|
218
|
+
END
|
219
|
+
TURNTABLE_PUSH = Roundhouse::Script.new(LUA_TURNTABLE_PUSH.freeze, name: :turntable_push, config: WOLVERINE_CONFIG)
|
220
|
+
MAYBE_TURNTABLE_PUSH = Roundhouse::Script.new(LUA_MAYBE_TURNTABLE_PUSH.freeze, name: :maybe_turntable_push, config: WOLVERINE_CONFIG)
|
221
|
+
POP_JOB = Roundhouse::Script.new(LUA_POP_JOB.freeze, name: :pop_job, config: WOLVERINE_CONFIG)
|
222
|
+
RESUME_QUEUE = Roundhouse::Script.new(LUA_RESUME_QUEUE.freeze, name: :resume_queue, config: WOLVERINE_CONFIG)
|
223
|
+
|
224
|
+
end # Monitor
|
225
|
+
end
|