roundhouse-x 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -53,9 +53,6 @@ module Roundhouse
53
53
  execute_job(worker, cloned(msg['args']))
54
54
  end
55
55
  end
56
-
57
- # Put the queue back into rotation
58
- Roundhouse.redis { |conn| Roundhouse::Monitor.push(conn, queue) }
59
56
  rescue Roundhouse::Shutdown
60
57
  # Had to force kill this job because it didn't finish
61
58
  # within the timeout. Don't acknowledge the work since
@@ -65,6 +62,8 @@ module Roundhouse
65
62
  handle_exception(ex, msg || { :message => msgstr })
66
63
  raise
67
64
  ensure
65
+ # Put the queue back into rotation
66
+ Roundhouse.redis { |conn| Roundhouse::Monitor.push(conn, queue) }
68
67
  work.acknowledge if ack
69
68
  end
70
69
 
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'roundhouse'
2
3
  require 'roundhouse/util'
3
4
  require 'roundhouse/actor'
@@ -0,0 +1,49 @@
1
+ require 'wolverine/script'
2
+ require 'wolverine/lua_error'
3
+
4
+ module Roundhouse
5
+ # Modifies the Shopify Wolverine library. The Roundhouse::Script will
6
+ # take a string and load it up to redis directly. No method-missing, no
7
+ # autoloading, no templating.
8
+ # Optionally pass a :name, so we can collect stats. Otherwise it will be the SHA1 hash
9
+ class Script < Wolverine::Script
10
+ def initialize content, options = {}
11
+ @content = content
12
+ @digest = Digest::SHA1.hexdigest @content
13
+ @config = options[:config] || fail('Must supply a Wolverine::Configuration object')
14
+ @name = options[:name] || @digest
15
+ end
16
+
17
+ def statsd
18
+ "Wolverine.#{@name}"
19
+ end
20
+
21
+ def instrument eval_type
22
+ ret = nil
23
+ runtime = Benchmark.realtime { ret = yield }
24
+ @config.instrumentation.call @name, runtime, eval_type
25
+ ret
26
+ end
27
+
28
+ class Configuration
29
+ attr_reader :instrumentation
30
+
31
+ def initialize(&blk)
32
+ @instrumentation = blk || proc { |script_name, runtime, eval_type| Roundhouse.logger.info "REDIS LUA [#{eval_type}]: #{script_name} #{runtime}" }
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ # Mock the Wolverine class
39
+ class Wolverine
40
+ class << self
41
+ # If we added Roundhouse into a Rails app and we fully configure StatsD and
42
+ # Wolverine, then we don't want this proxy to interfere
43
+ unless defined?(statsd_enabled?)
44
+ def statsd_enabled?
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Roundhouse
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -127,7 +127,6 @@ module Roundhouse
127
127
  redirect_with_query("#{root_path}morgue")
128
128
  end
129
129
 
130
-
131
130
  get '/retries' do
132
131
  @count = (params[:count] || 25).to_i
133
132
  (@current_page, @total_size, @retries) = page("retry", params[:page], @count)
@@ -218,15 +217,19 @@ module Roundhouse
218
217
  content_type :json
219
218
  Roundhouse.dump_json(
220
219
  roundhouse: {
221
- processed: roundhouse_stats.processed,
222
- failed: roundhouse_stats.failed,
223
- busy: roundhouse_stats.workers_size,
224
- processes: roundhouse_stats.processes_size,
225
- enqueued: roundhouse_stats.enqueued,
226
- scheduled: roundhouse_stats.scheduled_size,
227
- retries: roundhouse_stats.retry_size,
228
- dead: roundhouse_stats.dead_size,
229
- default_latency: roundhouse_stats.default_queue_latency
220
+ processed: roundhouse_stats.processed,
221
+ failed: roundhouse_stats.failed,
222
+ busy: roundhouse_stats.workers_size,
223
+ processes: roundhouse_stats.processes_size,
224
+ enqueued: roundhouse_stats.enqueued,
225
+ scheduled: roundhouse_stats.scheduled_size,
226
+ retries: roundhouse_stats.retry_size,
227
+ dead: roundhouse_stats.dead_size,
228
+ in_rotation: roundhouse_stats.in_rotation,
229
+ num_queues: roundhouse_stats.num_queues,
230
+ num_empty_queues: roundhouse_stats.num_empty_queues,
231
+ num_suspended_queues: roundhouse_stats.num_suspended_queues,
232
+ avg_queue_len: roundhouse_stats.avg_queue_len,
230
233
  },
231
234
  redis: redis_stats
232
235
  )
data/roundhouse.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency 'connection_pool', '~> 2.2', '>= 2.2.0'
21
21
  gem.add_dependency 'celluloid', '~> 0.17.0'
22
22
  gem.add_dependency 'json', '~> 1.0'
23
+ gem.add_dependency 'wolverine', '~> 0.3.3'
23
24
  gem.add_development_dependency 'sinatra', '~> 1.4', '>= 1.4.6'
24
25
  gem.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0'
25
26
  gem.add_development_dependency 'rake', '~> 10.0'
data/test/test_cli.rb CHANGED
@@ -41,11 +41,6 @@ class TestCli < Roundhouse::Test
41
41
  assert_equal 60, Roundhouse.options[:concurrency]
42
42
  end
43
43
 
44
- it 'changes queues' do
45
- @cli.parse(['roundhouse', '-q', 'foo', '-r', './test/fake_env.rb'])
46
- assert_equal ['foo'], Roundhouse.options[:queues]
47
- end
48
-
49
44
  it 'accepts a process index' do
50
45
  @cli.parse(['roundhouse', '-i', '7', '-r', './test/fake_env.rb'])
51
46
  assert_equal 7, Roundhouse.options[:index]
@@ -56,41 +51,11 @@ class TestCli < Roundhouse::Test
56
51
  assert_equal 7, Roundhouse.options[:index]
57
52
  end
58
53
 
59
- it 'sets strictly ordered queues if weights are not present' do
60
- @cli.parse(['roundhouse', '-q', 'foo', '-q', 'bar', '-r', './test/fake_env.rb'])
61
- assert_equal true, !!Roundhouse.options[:strict]
62
- end
63
-
64
- it 'does not set strictly ordered queues if weights are present' do
65
- @cli.parse(['roundhouse', '-q', 'foo,3', '-r', './test/fake_env.rb'])
66
- assert_equal false, !!Roundhouse.options[:strict]
67
- end
68
-
69
- it 'does not set strictly ordered queues if weights are present with multiple queues' do
70
- @cli.parse(['roundhouse', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
71
- assert_equal false, !!Roundhouse.options[:strict]
72
- end
73
-
74
54
  it 'changes timeout' do
75
55
  @cli.parse(['roundhouse', '-t', '30', '-r', './test/fake_env.rb'])
76
56
  assert_equal 30, Roundhouse.options[:timeout]
77
57
  end
78
58
 
79
- it 'handles multiple queues with weights' do
80
- @cli.parse(['roundhouse', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
81
- assert_equal %w(foo foo foo bar), Roundhouse.options[:queues]
82
- end
83
-
84
- it 'handles queues with multi-word names' do
85
- @cli.parse(['roundhouse', '-q', 'queue_one', '-q', 'queue-two', '-r', './test/fake_env.rb'])
86
- assert_equal %w(queue_one queue-two), Roundhouse.options[:queues]
87
- end
88
-
89
- it 'handles queues with dots in the name' do
90
- @cli.parse(['roundhouse', '-q', 'foo.bar', '-r', './test/fake_env.rb'])
91
- assert_equal ['foo.bar'], Roundhouse.options[:queues]
92
- end
93
-
94
59
  it 'sets verbose' do
95
60
  old = Roundhouse.logger.level
96
61
  @cli.parse(['roundhouse', '-v', '-r', './test/fake_env.rb'])
@@ -195,11 +160,6 @@ class TestCli < Roundhouse::Test
195
160
  it 'sets logfile' do
196
161
  assert_equal '/tmp/roundhouse.log', Roundhouse.options[:logfile]
197
162
  end
198
-
199
- it 'sets queues' do
200
- assert_equal 2, Roundhouse.options[:queues].count { |q| q == 'very_often' }
201
- assert_equal 1, Roundhouse.options[:queues].count { |q| q == 'seldom' }
202
- end
203
163
  end
204
164
 
205
165
  describe 'with env based config file' do
@@ -234,11 +194,6 @@ class TestCli < Roundhouse::Test
234
194
  it 'sets logfile' do
235
195
  assert_equal '/tmp/roundhouse.log', Roundhouse.options[:logfile]
236
196
  end
237
-
238
- it 'sets queues' do
239
- assert_equal 2, Roundhouse.options[:queues].count { |q| q == 'very_often' }
240
- assert_equal 1, Roundhouse.options[:queues].count { |q| q == 'seldom' }
241
- end
242
197
  end
243
198
 
244
199
  describe 'with an empty config file' do
@@ -287,9 +242,7 @@ class TestCli < Roundhouse::Test
287
242
  '-e', 'snoop',
288
243
  '-c', '100',
289
244
  '-r', @tmp_lib_path,
290
- '-P', @tmp_path,
291
- '-q', 'often,7',
292
- '-q', 'seldom,3'])
245
+ '-P', @tmp_path])
293
246
  end
294
247
 
295
248
  after do
@@ -312,53 +265,6 @@ class TestCli < Roundhouse::Test
312
265
  it 'uses pidfile flag' do
313
266
  assert_equal @tmp_path, Roundhouse.options[:pidfile]
314
267
  end
315
-
316
- it 'sets queues' do
317
- assert_equal 7, Roundhouse.options[:queues].count { |q| q == 'often' }
318
- assert_equal 3, Roundhouse.options[:queues].count { |q| q == 'seldom' }
319
- end
320
- end
321
-
322
- describe 'Roundhouse::CLI#parse_queues' do
323
- describe 'when weight is present' do
324
- it 'concatenates queues by factor of weight and sets strict to false' do
325
- opts = { strict: true }
326
- @cli.__send__ :parse_queues, opts, [['often', 7], ['repeatedly', 3]]
327
- @cli.__send__ :parse_queues, opts, [['once']]
328
- assert_equal (%w[often] * 7 + %w[repeatedly] * 3 + %w[once]), opts[:queues]
329
- assert !opts[:strict]
330
- end
331
- end
332
-
333
- describe 'when weight is not present' do
334
- it 'returns queues and sets strict' do
335
- opts = { strict: true }
336
- @cli.__send__ :parse_queues, opts, [['once'], ['one_time']]
337
- @cli.__send__ :parse_queues, opts, [['einmal']]
338
- assert_equal %w[once one_time einmal], opts[:queues]
339
- assert opts[:strict]
340
- end
341
- end
342
- end
343
-
344
- describe 'Roundhouse::CLI#parse_queue' do
345
- describe 'when weight is present' do
346
- it 'concatenates queue to opts[:queues] weight number of times and sets strict to false' do
347
- opts = { strict: true }
348
- @cli.__send__ :parse_queue, opts, 'often', 7
349
- assert_equal %w[often] * 7, opts[:queues]
350
- assert !opts[:strict]
351
- end
352
- end
353
-
354
- describe 'when weight is not present' do
355
- it 'concatenates queue to opts[:queues] once and leaves strict true' do
356
- opts = { strict: true }
357
- @cli.__send__ :parse_queue, opts, 'once', nil
358
- assert_equal %w[once], opts[:queues]
359
- assert opts[:strict]
360
- end
361
- end
362
268
  end
363
269
  end
364
270
 
data/test/test_fetch.rb CHANGED
@@ -18,7 +18,7 @@ class TestFetcher < Roundhouse::Test
18
18
  describe 'when nominal' do
19
19
  before do
20
20
  Roundhouse.redis do |conn|
21
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 100)
21
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 100)
22
22
  conn.hset(Roundhouse::Monitor.status_bucket(100), 100, Roundhouse::Monitor::ACTIVE)
23
23
  conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'msg')
24
24
  end
@@ -41,68 +41,48 @@ class TestFetcher < Roundhouse::Test
41
41
  describe 'with empty status' do
42
42
  before do
43
43
  Roundhouse.redis do |conn|
44
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 100)
45
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 200)
44
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 100)
45
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 200)
46
46
  conn.hset(Roundhouse::Monitor.status_bucket(100), 100, Roundhouse::Monitor::EMPTY)
47
47
  conn.hset(Roundhouse::Monitor.status_bucket(200), 200, Roundhouse::Monitor::ACTIVE)
48
48
  conn.lpush("#{Roundhouse::Monitor::QUEUE}:200", 'msg-200')
49
49
  end
50
50
  end
51
51
 
52
- it 'skips queues with empty status' do
52
+ it 'should return nil' do
53
53
  fetch = Roundhouse::RoundRobinFetch.new
54
54
  uow = fetch.retrieve_work
55
- refute_nil uow
56
- assert_equal '200', uow.queue_id
57
- assert_equal 'msg-200', uow.message
58
- q = Roundhouse::Queue.new(200)
59
- assert_equal 0, q.size
60
- uow.requeue
61
- assert_equal 1, q.size
62
- assert_nil uow.acknowledge
55
+ assert_nil uow
63
56
  end
64
57
  end
65
58
 
66
59
  describe 'with active status and empty queue' do
67
60
  before do
68
61
  Roundhouse.redis do |conn|
69
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 100)
70
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 200)
62
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 100)
63
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 200)
71
64
  conn.hset(Roundhouse::Monitor.status_bucket(100), 100, Roundhouse::Monitor::ACTIVE)
72
65
  conn.hset(Roundhouse::Monitor.status_bucket(200), 200, Roundhouse::Monitor::ACTIVE)
73
66
  conn.lpush("#{Roundhouse::Monitor::QUEUE}:200", 'msg-200')
74
67
  end
75
68
  end
76
69
 
77
- it 'skips queues with empty status' do
70
+ it 'should return nil' do
78
71
  empty_queue = Roundhouse::Queue.new(100)
79
72
  assert_equal 0, empty_queue.size
80
73
  assert_equal :active, empty_queue.status
81
74
 
82
75
  fetch = Roundhouse::RoundRobinFetch.new
83
76
  uow = fetch.retrieve_work
84
- refute_nil uow
85
- assert_equal '200', uow.queue_id
86
- assert_equal 'msg-200', uow.message
87
-
88
- # After fetching, should set the active status
89
- # of an empty queue to empty
90
- assert_equal :empty, empty_queue.status
91
-
92
- # The filled queue should act as normal
93
- q = Roundhouse::Queue.new(200)
94
- assert_equal 0, q.size
95
- uow.requeue
96
- assert_equal 1, q.size
97
- assert_nil uow.acknowledge
77
+ assert_nil uow
98
78
  end
99
79
  end
100
80
 
101
81
  describe 'with suspended status' do
102
82
  before do
103
83
  Roundhouse.redis do |conn|
104
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 100)
105
- conn.lpush(Roundhouse::Monitor::SEMAPHORE, 200)
84
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 100)
85
+ conn.lpush(Roundhouse::Monitor::TURNTABLE, 200)
106
86
  conn.hset(Roundhouse::Monitor.status_bucket(100), 100, Roundhouse::Monitor::SUSPENDED)
107
87
  conn.hset(Roundhouse::Monitor.status_bucket(200), 200, Roundhouse::Monitor::ACTIVE)
108
88
  conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'msg-100')
@@ -110,22 +90,10 @@ class TestFetcher < Roundhouse::Test
110
90
  end
111
91
  end
112
92
 
113
- it 'skips queues with suspended status' do
93
+ it 'should return nil' do
114
94
  fetch = Roundhouse::RoundRobinFetch.new
115
95
  uow = fetch.retrieve_work
116
- refute_nil uow
117
- assert_equal '200', uow.queue_id
118
- assert_equal 'msg-200', uow.message
119
-
120
- # Should not touch the work in suspended queue
121
- suspended_q = Roundhouse::Queue.new(100)
122
- assert_equal 1, suspended_q.size
123
-
124
- q = Roundhouse::Queue.new(200)
125
- assert_equal 0, q.size
126
- uow.requeue
127
- assert_equal 1, q.size
128
- assert_nil uow.acknowledge
96
+ assert_nil uow
129
97
  end
130
98
  end
131
99
 
data/test/test_monitor.rb CHANGED
@@ -15,73 +15,102 @@ class TestMonitor < Roundhouse::Test
15
15
  it 'should pop the next available job from the next available queue' do
16
16
  Roundhouse.redis do |conn|
17
17
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
18
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
19
+
18
20
  assert_equal '100', Roundhouse::Monitor.pop(conn)
21
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
19
22
  end
20
23
  end
21
24
  end
22
25
 
23
26
  describe 'with queue with empty status' do
24
- it 'should skip' do
27
+ it 'should return nil' do
25
28
  Roundhouse.redis do |conn|
26
29
  Roundhouse::Monitor.activate(conn, '200')
27
30
  Roundhouse::Monitor.push(conn, '200')
28
31
  Roundhouse::Monitor.set_queue_is_empty(conn, '200')
29
32
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
33
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
30
34
 
31
- assert_equal '100', Roundhouse::Monitor.pop(conn)
35
+ assert_equal nil, Roundhouse::Monitor.pop(conn)
36
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
32
37
  end
33
38
  end
34
39
  end
35
40
 
36
41
  describe 'with empty queue with active status' do
37
- it 'should not skip' do
42
+ it 'should return queue_id' do
38
43
  Roundhouse.redis do |conn|
39
44
  Roundhouse::Monitor.activate(conn, '200')
40
45
  Roundhouse::Monitor.push(conn, '200')
41
46
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
47
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
42
48
 
43
49
  assert_equal '200', Roundhouse::Monitor.pop(conn)
50
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
44
51
  end
45
52
  end
46
53
  end
47
54
 
48
55
  describe 'with queue with suspended status' do
49
- it 'should skip' do
56
+ it 'should return nil' do
50
57
  Roundhouse.redis do |conn|
51
58
  Roundhouse::Monitor.activate(conn, '200')
52
59
  Roundhouse::Monitor.push(conn, '200')
53
60
  Roundhouse::Monitor.suspend(conn, '200')
54
61
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
62
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
55
63
 
56
- assert_equal '100', Roundhouse::Monitor.pop(conn)
64
+ assert_equal nil, Roundhouse::Monitor.pop(conn)
65
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 200)
57
66
  end
58
67
  end
59
68
  end
60
69
 
61
- describe 'when no active queue is in semaphore' do
62
- it 'should block' do
63
- skip 'do not know how to test this, and this may not be the right design'
70
+ describe 'when nothing is in the turntable' do
71
+ it "should block for #{Roundhouse::Monitor::TURNTABLE_TIMEOUT} seconds and return nil" do
72
+ skip 'skipping slow tests' unless ENV['SLOW']
73
+ Roundhouse.redis do |conn|
74
+ assert_equal nil, Roundhouse::Monitor.pop(conn)
75
+ end
64
76
  end
65
77
  end
66
78
  end # #pop
67
79
 
68
80
  describe '#push' do
69
81
  describe 'with active queue' do
70
- it 'should push into semaphore' do
82
+ it 'should push into turntable' do
71
83
  Roundhouse.redis do |conn|
72
84
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
73
85
 
74
- assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
86
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, -1, -1)
87
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
88
+ end
89
+ end
90
+
91
+ describe 'when already in turntable' do
92
+ it 'should not push into turntable' do
93
+ Roundhouse.redis do |conn|
94
+ Roundhouse::Monitor.activate(conn, 100)
95
+ Roundhouse::Monitor.push(conn, 100)
96
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
97
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
98
+
99
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
100
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
101
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
102
+ end
75
103
  end
76
104
  end
77
105
  end
78
106
 
79
107
  describe 'with queue with empty status' do
80
- it 'should not push into semaphore' do
108
+ it 'should not push into turntable' do
81
109
  Roundhouse.redis do |conn|
82
110
  Roundhouse::Monitor.set_queue_is_empty(conn, '200')
83
111
 
84
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
112
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
113
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
85
114
  end
86
115
  end
87
116
  end
@@ -92,7 +121,8 @@ class TestMonitor < Roundhouse::Test
92
121
  Roundhouse::Monitor.suspend(conn, '200')
93
122
  Roundhouse::Monitor.push(conn, '200')
94
123
 
95
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
124
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
125
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
96
126
  end
97
127
  end
98
128
  end
@@ -104,7 +134,11 @@ class TestMonitor < Roundhouse::Test
104
134
  Roundhouse.redis do |conn|
105
135
  Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
106
136
  assert conn.lrange("#{Roundhouse::Monitor::QUEUE}:100", 0, 0).first
137
+
107
138
  assert Roundhouse::Monitor.pop_job conn, 100
139
+
140
+ # Should still be active
141
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
108
142
  end
109
143
  end
110
144
  end
@@ -112,8 +146,14 @@ class TestMonitor < Roundhouse::Test
112
146
  describe 'without jobs in queue' do
113
147
  it 'should return nil' do
114
148
  Roundhouse.redis do |conn|
149
+ Roundhouse::Monitor.activate(conn, 100)
150
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
115
151
  refute conn.lrange("#{Roundhouse::Monitor::QUEUE}:100", 0, 0).first
152
+
116
153
  refute Roundhouse::Monitor.pop_job conn, 100
154
+
155
+ # Should be set to inactive
156
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
117
157
  end
118
158
  end
119
159
  end
@@ -142,31 +182,55 @@ class TestMonitor < Roundhouse::Test
142
182
 
143
183
  describe '#maybe_add_to_rotation' do
144
184
  describe 'with active queue' do
145
- it 'should not rotate queue into semaphore' do
185
+ it 'should not rotate queue into turntable' do
146
186
  Roundhouse.redis do |conn|
147
187
  Roundhouse::Monitor.activate(conn, 100)
148
188
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
149
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
189
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, 0)
190
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
150
191
 
151
192
  Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
152
193
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
153
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
194
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, 0)
195
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
154
196
  end
155
197
  end
156
198
  end
157
199
 
158
200
  describe 'with empty queue' do
159
- it 'should rotate queue into semaphore' do
201
+ it 'should rotate queue into turntable' do
160
202
  Roundhouse.redis do |conn|
161
203
  Roundhouse::Monitor.set_queue_is_empty(conn, 100)
162
204
  assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
163
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
205
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
206
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
164
207
 
165
208
  Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
166
209
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
167
- assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
210
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, 0)
211
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
168
212
  end
169
213
  end
214
+
215
+ describe 'when already in rotation' do
216
+ it 'should not rotate queue into turntable' do
217
+ Roundhouse.redis do |conn|
218
+ Roundhouse::Monitor.activate(conn, 100)
219
+ Roundhouse::Monitor.push(conn, 100)
220
+ Roundhouse::Monitor.set_queue_is_empty(conn, 100)
221
+
222
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
223
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
224
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
225
+
226
+ Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
227
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
228
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
229
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
230
+ end
231
+ end
232
+
233
+ end
170
234
  end
171
235
 
172
236
  describe 'with suspended queue' do
@@ -174,11 +238,13 @@ class TestMonitor < Roundhouse::Test
174
238
  Roundhouse.redis do |conn|
175
239
  Roundhouse::Monitor.suspend(conn, 100)
176
240
  assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
177
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
241
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
242
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
178
243
 
179
244
  Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
180
245
  assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
181
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
246
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
247
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, 100)
182
248
  end
183
249
  end
184
250
  end
@@ -213,15 +279,33 @@ class TestMonitor < Roundhouse::Test
213
279
 
214
280
  describe '#resume' do
215
281
  describe 'with suspended queue' do
216
- it 'should reactive queue and put it into rotation' do
282
+ it 'should reactivate queue and put it into rotation' do
217
283
  Roundhouse.redis do |conn|
218
284
  Roundhouse::Monitor.suspend(conn, 100)
219
285
  assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
220
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
286
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
221
287
 
222
288
  Roundhouse::Monitor.resume(conn, 100)
223
289
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
224
- assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
290
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
291
+ end
292
+ end
293
+
294
+ describe 'when queue is already in rotation' do
295
+ it 'should reactivate but not put into queue' do
296
+ Roundhouse.redis do |conn|
297
+ Roundhouse::Monitor.activate(conn, 100)
298
+ Roundhouse::Monitor.push(conn, 100) # Puts it into rotation
299
+
300
+ Roundhouse::Monitor.suspend(conn, 100)
301
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
302
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
303
+
304
+ Roundhouse::Monitor.resume(conn, 100)
305
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
306
+ # There should only be a single queue 100 in the turntable
307
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::TURNTABLE, 0, -1)
308
+ end
225
309
  end
226
310
  end
227
311
  end
@@ -231,11 +315,11 @@ class TestMonitor < Roundhouse::Test
231
315
  Roundhouse.redis do |conn|
232
316
  Roundhouse::Monitor.set_queue_is_empty(conn, 100)
233
317
  assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
234
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
318
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, -1, -1)
235
319
 
236
320
  Roundhouse::Monitor.resume(conn, 100)
237
321
  assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
238
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
322
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, -1, -1)
239
323
  end
240
324
  end
241
325
  end
@@ -245,14 +329,209 @@ class TestMonitor < Roundhouse::Test
245
329
  Roundhouse.redis do |conn|
246
330
  Roundhouse::Monitor.activate(conn, 100)
247
331
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
248
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
332
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, -1, -1)
249
333
 
250
334
  Roundhouse::Monitor.resume(conn, 100)
251
335
  assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
252
- assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
336
+ assert_equal [], conn.lrange(Roundhouse::Monitor::TURNTABLE, -1, -1)
253
337
  end
254
338
  end
255
339
  end
256
340
  end # #resume
341
+
342
+ describe '#rebuild_turntable!' do
343
+ describe 'with active-status queue' do
344
+ before do
345
+ Roundhouse.redis { |conn| Roundhouse::Monitor.activate(conn, 100) }
346
+ end
347
+
348
+ describe 'with someting in the queue' do
349
+ it 'should put queue into turntable' do
350
+ Roundhouse.redis { |conn| conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'something') }
351
+ Roundhouse::Monitor.rebuild_turntable!
352
+
353
+ Roundhouse.redis do |conn|
354
+ # Should still be active
355
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
356
+ # Should be in turntable
357
+ assert_equal 1, conn.llen(Roundhouse::Monitor::TURNTABLE)
358
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
359
+ assert_equal '100', conn.rpop(Roundhouse::Monitor::TURNTABLE)
360
+ end
361
+ end
362
+
363
+ end
364
+
365
+ describe 'without anything in queue' do
366
+ it 'should set queue to empty and not put into turntable' do
367
+ Roundhouse::Monitor.rebuild_turntable!
368
+
369
+ Roundhouse.redis do |conn|
370
+ # Should set to empty
371
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
372
+ # Should not be in turntable
373
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
374
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ describe 'with empty-status queue' do
381
+ before do
382
+ Roundhouse.redis { |conn| Roundhouse::Monitor.set_queue_is_empty(conn, 100) }
383
+ end
384
+
385
+ describe 'with someting in the queue' do
386
+ it 'should activate queue and put queue into turntable' do
387
+ Roundhouse.redis { |conn| conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'something') }
388
+ Roundhouse::Monitor.rebuild_turntable!
389
+
390
+ Roundhouse.redis do |conn|
391
+ # Should be set to active
392
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
393
+ # Should be in turntable
394
+ assert_equal 1, conn.llen(Roundhouse::Monitor::TURNTABLE)
395
+ assert conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
396
+ assert_equal '100', conn.rpop(Roundhouse::Monitor::TURNTABLE)
397
+ end
398
+ end
399
+
400
+ end
401
+
402
+ describe 'without anything in queue' do
403
+ it 'should not put into turntable' do
404
+ Roundhouse::Monitor.rebuild_turntable!
405
+
406
+ Roundhouse.redis do |conn|
407
+ # Should still be empty
408
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
409
+ # Should not be in turntable
410
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
411
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
412
+ end
413
+ end
414
+ end
415
+ end
416
+
417
+ describe 'with suspended-status queue' do
418
+ before do
419
+ Roundhouse.redis { |conn| Roundhouse::Monitor.suspend(conn, 100) }
420
+ end
421
+
422
+ describe 'with something in the queue' do
423
+ it 'should not put queue into turntable' do
424
+ Roundhouse.redis { |conn| conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'something') }
425
+ Roundhouse::Monitor.rebuild_turntable!
426
+
427
+ Roundhouse.redis do |conn|
428
+ # Should still be suspended
429
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
430
+ # Should not be in turntable
431
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
432
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
433
+ end
434
+ end
435
+
436
+ end
437
+
438
+ describe 'without anything in queue' do
439
+ it 'should not put into turntable' do
440
+ Roundhouse::Monitor.rebuild_turntable!
441
+
442
+ Roundhouse.redis do |conn|
443
+ # Should still be suspended
444
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
445
+ # Should not be in turntable
446
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
447
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
448
+ end
449
+ end
450
+ end
451
+ end
452
+
453
+ describe 'with unknown-status queue' do
454
+ before do
455
+ Roundhouse.redis { |conn| Roundhouse::Monitor.send(:set_queue_status, conn, 100, -2000) }
456
+ end
457
+
458
+ describe 'with something in the queue' do
459
+ it 'should not put queue into turntable' do
460
+ Roundhouse.redis { |conn| conn.lpush("#{Roundhouse::Monitor::QUEUE}:100", 'something') }
461
+ Roundhouse::Monitor.rebuild_turntable!
462
+
463
+ Roundhouse.redis do |conn|
464
+ # Should be suspended
465
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
466
+ # Should not be in turntable
467
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
468
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
469
+ end
470
+ end
471
+
472
+ end
473
+
474
+ describe 'without anything in queue' do
475
+ it 'should not put into turntable' do
476
+ Roundhouse::Monitor.rebuild_turntable!
477
+
478
+ Roundhouse.redis do |conn|
479
+ # Should be suspended
480
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
481
+ # Should not be in turntable
482
+ assert_equal 0, conn.llen(Roundhouse::Monitor::TURNTABLE)
483
+ refute conn.sismember(Roundhouse::Monitor::IN_ROTATION, '100')
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ it 'scaling test' do
490
+ skip 'slow test' unless ENV['SLOW']
491
+ require 'benchmark'
492
+
493
+ num_queues = 10_000
494
+ num_active = (num_queues * 0.7).to_i
495
+ num_empty = (num_queues * 0.2).to_i
496
+ num_suspended = num_queues - num_active - num_empty
497
+ active_with_empty = (num_active * 0.25).to_i
498
+ active_with_active = num_active - active_with_empty
499
+ empty_with_something = (num_empty * 0.25).to_i
500
+ empty_with_empty = num_empty - empty_with_something
501
+
502
+ states = \
503
+ Array.new(active_with_empty) { :active_with_empty } +
504
+ Array.new(active_with_active) { :active_with_active } +
505
+ Array.new(empty_with_something) { :empty_with_something } +
506
+ Array.new(empty_with_empty) { :empty_with_empty } +
507
+ Array.new(num_suspended) { :suspended }
508
+
509
+ rand_msgs = -> { Array.new(rand(3) + 1) { 'msg' } }
510
+ puts "\nScaling Test:\nSetting up"
511
+ Roundhouse.redis do |conn|
512
+ conn.multi do
513
+ states.shuffle.each_with_index do |state, q_id|
514
+ case state
515
+ when :active_with_empty then
516
+ Roundhouse::Monitor.activate(conn, q_id)
517
+ when :empty_with_empty then
518
+ Roundhouse::Monitor.set_queue_is_empty(conn, q_id)
519
+ when :suspended then
520
+ Roundhouse::Monitor.set_queue_is_empty(conn, q_id)
521
+ when :active_with_active then
522
+ Roundhouse::Monitor.activate(conn, q_id)
523
+ conn.lpush("#{Roundhouse::Monitor::QUEUE}:#{q_id}", rand_msgs.())
524
+ when :empty_with_something then
525
+ Roundhouse::Monitor.set_queue_is_empty(conn, q_id)
526
+ conn.lpush("#{Roundhouse::Monitor::QUEUE}:#{q_id}", rand_msgs.())
527
+ end
528
+ end
529
+ end
530
+ end
531
+
532
+ res = Benchmark.measure { Roundhouse::Monitor.rebuild_turntable! }
533
+ puts res, ''
534
+ end
535
+ end # #rebuild_turntable!
257
536
  end
258
537
  end