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.
@@ -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