roundhouse-x 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +16 -0
  4. data/3.0-Upgrade.md +70 -0
  5. data/Changes.md +1127 -0
  6. data/Gemfile +27 -0
  7. data/LICENSE +7 -0
  8. data/README.md +52 -0
  9. data/Rakefile +9 -0
  10. data/bin/roundhouse +19 -0
  11. data/bin/roundhousectl +93 -0
  12. data/lib/generators/roundhouse/templates/worker.rb.erb +9 -0
  13. data/lib/generators/roundhouse/templates/worker_spec.rb.erb +6 -0
  14. data/lib/generators/roundhouse/templates/worker_test.rb.erb +8 -0
  15. data/lib/generators/roundhouse/worker_generator.rb +49 -0
  16. data/lib/roundhouse/actor.rb +39 -0
  17. data/lib/roundhouse/api.rb +859 -0
  18. data/lib/roundhouse/cli.rb +396 -0
  19. data/lib/roundhouse/client.rb +210 -0
  20. data/lib/roundhouse/core_ext.rb +105 -0
  21. data/lib/roundhouse/exception_handler.rb +30 -0
  22. data/lib/roundhouse/fetch.rb +154 -0
  23. data/lib/roundhouse/launcher.rb +98 -0
  24. data/lib/roundhouse/logging.rb +104 -0
  25. data/lib/roundhouse/manager.rb +236 -0
  26. data/lib/roundhouse/middleware/chain.rb +149 -0
  27. data/lib/roundhouse/middleware/i18n.rb +41 -0
  28. data/lib/roundhouse/middleware/server/active_record.rb +13 -0
  29. data/lib/roundhouse/middleware/server/logging.rb +40 -0
  30. data/lib/roundhouse/middleware/server/retry_jobs.rb +206 -0
  31. data/lib/roundhouse/monitor.rb +124 -0
  32. data/lib/roundhouse/paginator.rb +42 -0
  33. data/lib/roundhouse/processor.rb +159 -0
  34. data/lib/roundhouse/rails.rb +24 -0
  35. data/lib/roundhouse/redis_connection.rb +77 -0
  36. data/lib/roundhouse/scheduled.rb +115 -0
  37. data/lib/roundhouse/testing/inline.rb +28 -0
  38. data/lib/roundhouse/testing.rb +193 -0
  39. data/lib/roundhouse/util.rb +68 -0
  40. data/lib/roundhouse/version.rb +3 -0
  41. data/lib/roundhouse/web.rb +264 -0
  42. data/lib/roundhouse/web_helpers.rb +249 -0
  43. data/lib/roundhouse/worker.rb +90 -0
  44. data/lib/roundhouse.rb +177 -0
  45. data/roundhouse.gemspec +27 -0
  46. data/test/config.yml +9 -0
  47. data/test/env_based_config.yml +11 -0
  48. data/test/fake_env.rb +0 -0
  49. data/test/fixtures/en.yml +2 -0
  50. data/test/helper.rb +49 -0
  51. data/test/test_api.rb +521 -0
  52. data/test/test_cli.rb +389 -0
  53. data/test/test_client.rb +294 -0
  54. data/test/test_exception_handler.rb +55 -0
  55. data/test/test_fetch.rb +206 -0
  56. data/test/test_logging.rb +34 -0
  57. data/test/test_manager.rb +169 -0
  58. data/test/test_middleware.rb +160 -0
  59. data/test/test_monitor.rb +258 -0
  60. data/test/test_processor.rb +176 -0
  61. data/test/test_rails.rb +23 -0
  62. data/test/test_redis_connection.rb +127 -0
  63. data/test/test_retry.rb +390 -0
  64. data/test/test_roundhouse.rb +87 -0
  65. data/test/test_scheduled.rb +120 -0
  66. data/test/test_scheduling.rb +75 -0
  67. data/test/test_testing.rb +78 -0
  68. data/test/test_testing_fake.rb +240 -0
  69. data/test/test_testing_inline.rb +65 -0
  70. data/test/test_util.rb +18 -0
  71. data/test/test_web.rb +605 -0
  72. data/test/test_web_helpers.rb +52 -0
  73. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  74. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  75. data/web/assets/images/logo.png +0 -0
  76. data/web/assets/images/status/active.png +0 -0
  77. data/web/assets/images/status/idle.png +0 -0
  78. data/web/assets/images/status-sd8051fd480.png +0 -0
  79. data/web/assets/javascripts/application.js +83 -0
  80. data/web/assets/javascripts/dashboard.js +300 -0
  81. data/web/assets/javascripts/locales/README.md +27 -0
  82. data/web/assets/javascripts/locales/jquery.timeago.ar.js +96 -0
  83. data/web/assets/javascripts/locales/jquery.timeago.bg.js +18 -0
  84. data/web/assets/javascripts/locales/jquery.timeago.bs.js +49 -0
  85. data/web/assets/javascripts/locales/jquery.timeago.ca.js +18 -0
  86. data/web/assets/javascripts/locales/jquery.timeago.cs.js +18 -0
  87. data/web/assets/javascripts/locales/jquery.timeago.cy.js +20 -0
  88. data/web/assets/javascripts/locales/jquery.timeago.da.js +18 -0
  89. data/web/assets/javascripts/locales/jquery.timeago.de.js +18 -0
  90. data/web/assets/javascripts/locales/jquery.timeago.el.js +18 -0
  91. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +20 -0
  92. data/web/assets/javascripts/locales/jquery.timeago.en.js +20 -0
  93. data/web/assets/javascripts/locales/jquery.timeago.es.js +18 -0
  94. data/web/assets/javascripts/locales/jquery.timeago.et.js +18 -0
  95. data/web/assets/javascripts/locales/jquery.timeago.fa.js +22 -0
  96. data/web/assets/javascripts/locales/jquery.timeago.fi.js +28 -0
  97. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +16 -0
  98. data/web/assets/javascripts/locales/jquery.timeago.fr.js +17 -0
  99. data/web/assets/javascripts/locales/jquery.timeago.he.js +18 -0
  100. data/web/assets/javascripts/locales/jquery.timeago.hr.js +49 -0
  101. data/web/assets/javascripts/locales/jquery.timeago.hu.js +18 -0
  102. data/web/assets/javascripts/locales/jquery.timeago.hy.js +18 -0
  103. data/web/assets/javascripts/locales/jquery.timeago.id.js +18 -0
  104. data/web/assets/javascripts/locales/jquery.timeago.it.js +16 -0
  105. data/web/assets/javascripts/locales/jquery.timeago.ja.js +19 -0
  106. data/web/assets/javascripts/locales/jquery.timeago.ko.js +17 -0
  107. data/web/assets/javascripts/locales/jquery.timeago.lt.js +20 -0
  108. data/web/assets/javascripts/locales/jquery.timeago.mk.js +20 -0
  109. data/web/assets/javascripts/locales/jquery.timeago.nl.js +20 -0
  110. data/web/assets/javascripts/locales/jquery.timeago.no.js +18 -0
  111. data/web/assets/javascripts/locales/jquery.timeago.pl.js +31 -0
  112. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +16 -0
  113. data/web/assets/javascripts/locales/jquery.timeago.pt.js +16 -0
  114. data/web/assets/javascripts/locales/jquery.timeago.ro.js +18 -0
  115. data/web/assets/javascripts/locales/jquery.timeago.rs.js +49 -0
  116. data/web/assets/javascripts/locales/jquery.timeago.ru.js +34 -0
  117. data/web/assets/javascripts/locales/jquery.timeago.sk.js +18 -0
  118. data/web/assets/javascripts/locales/jquery.timeago.sl.js +44 -0
  119. data/web/assets/javascripts/locales/jquery.timeago.sv.js +18 -0
  120. data/web/assets/javascripts/locales/jquery.timeago.th.js +20 -0
  121. data/web/assets/javascripts/locales/jquery.timeago.tr.js +16 -0
  122. data/web/assets/javascripts/locales/jquery.timeago.uk.js +34 -0
  123. data/web/assets/javascripts/locales/jquery.timeago.uz.js +19 -0
  124. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +20 -0
  125. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +20 -0
  126. data/web/assets/stylesheets/application.css +746 -0
  127. data/web/assets/stylesheets/bootstrap.css +9 -0
  128. data/web/locales/cs.yml +68 -0
  129. data/web/locales/da.yml +68 -0
  130. data/web/locales/de.yml +69 -0
  131. data/web/locales/el.yml +68 -0
  132. data/web/locales/en.yml +77 -0
  133. data/web/locales/es.yml +69 -0
  134. data/web/locales/fr.yml +69 -0
  135. data/web/locales/hi.yml +75 -0
  136. data/web/locales/it.yml +69 -0
  137. data/web/locales/ja.yml +69 -0
  138. data/web/locales/ko.yml +68 -0
  139. data/web/locales/nl.yml +68 -0
  140. data/web/locales/no.yml +69 -0
  141. data/web/locales/pl.yml +59 -0
  142. data/web/locales/pt-br.yml +68 -0
  143. data/web/locales/pt.yml +67 -0
  144. data/web/locales/ru.yml +75 -0
  145. data/web/locales/sv.yml +68 -0
  146. data/web/locales/ta.yml +75 -0
  147. data/web/locales/zh-cn.yml +68 -0
  148. data/web/locales/zh-tw.yml +68 -0
  149. data/web/views/_footer.erb +22 -0
  150. data/web/views/_job_info.erb +84 -0
  151. data/web/views/_nav.erb +66 -0
  152. data/web/views/_paging.erb +23 -0
  153. data/web/views/_poll_js.erb +5 -0
  154. data/web/views/_poll_link.erb +7 -0
  155. data/web/views/_status.erb +4 -0
  156. data/web/views/_summary.erb +40 -0
  157. data/web/views/busy.erb +90 -0
  158. data/web/views/dashboard.erb +75 -0
  159. data/web/views/dead.erb +34 -0
  160. data/web/views/layout.erb +31 -0
  161. data/web/views/morgue.erb +71 -0
  162. data/web/views/queue.erb +45 -0
  163. data/web/views/queues.erb +27 -0
  164. data/web/views/retries.erb +74 -0
  165. data/web/views/retry.erb +34 -0
  166. data/web/views/scheduled.erb +54 -0
  167. data/web/views/scheduled_job_info.erb +8 -0
  168. metadata +404 -0
@@ -0,0 +1,258 @@
1
+ require_relative 'helper'
2
+ #require 'roundhouse/api'
3
+ require 'roundhouse/monitor'
4
+
5
+ require 'active_support/core_ext/numeric/time'
6
+
7
+ class TestMonitor < Roundhouse::Test
8
+ describe 'Roundhouse::Monitor' do
9
+ before do
10
+ Roundhouse.redis(&:flushdb)
11
+ end
12
+
13
+ describe '#pop' do
14
+ describe 'when nominal' do
15
+ it 'should pop the next available job from the next available queue' do
16
+ Roundhouse.redis do |conn|
17
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
18
+ assert_equal '100', Roundhouse::Monitor.pop(conn)
19
+ end
20
+ end
21
+ end
22
+
23
+ describe 'with queue with empty status' do
24
+ it 'should skip' do
25
+ Roundhouse.redis do |conn|
26
+ Roundhouse::Monitor.activate(conn, '200')
27
+ Roundhouse::Monitor.push(conn, '200')
28
+ Roundhouse::Monitor.set_queue_is_empty(conn, '200')
29
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
30
+
31
+ assert_equal '100', Roundhouse::Monitor.pop(conn)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe 'with empty queue with active status' do
37
+ it 'should not skip' do
38
+ Roundhouse.redis do |conn|
39
+ Roundhouse::Monitor.activate(conn, '200')
40
+ Roundhouse::Monitor.push(conn, '200')
41
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
42
+
43
+ assert_equal '200', Roundhouse::Monitor.pop(conn)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'with queue with suspended status' do
49
+ it 'should skip' do
50
+ Roundhouse.redis do |conn|
51
+ Roundhouse::Monitor.activate(conn, '200')
52
+ Roundhouse::Monitor.push(conn, '200')
53
+ Roundhouse::Monitor.suspend(conn, '200')
54
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
55
+
56
+ assert_equal '100', Roundhouse::Monitor.pop(conn)
57
+ end
58
+ end
59
+ end
60
+
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'
64
+ end
65
+ end
66
+ end # #pop
67
+
68
+ describe '#push' do
69
+ describe 'with active queue' do
70
+ it 'should push into semaphore' do
71
+ Roundhouse.redis do |conn|
72
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
73
+
74
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe 'with queue with empty status' do
80
+ it 'should not push into semaphore' do
81
+ Roundhouse.redis do |conn|
82
+ Roundhouse::Monitor.set_queue_is_empty(conn, '200')
83
+
84
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'with queue with suspended status' do
90
+ it 'should skip' do
91
+ Roundhouse.redis do |conn|
92
+ Roundhouse::Monitor.suspend(conn, '200')
93
+ Roundhouse::Monitor.push(conn, '200')
94
+
95
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
96
+ end
97
+ end
98
+ end
99
+ end # #push
100
+
101
+ describe '#pop_job' do
102
+ describe 'with jobs in queue' do
103
+ it 'should pop the next job and return data' do
104
+ Roundhouse.redis do |conn|
105
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 100, 'class' => 'MyWorker', 'args' => [1]}]
106
+ assert conn.lrange("#{Roundhouse::Monitor::QUEUE}:100", 0, 0).first
107
+ assert Roundhouse::Monitor.pop_job conn, 100
108
+ end
109
+ end
110
+ end
111
+
112
+ describe 'without jobs in queue' do
113
+ it 'should return nil' do
114
+ Roundhouse.redis do |conn|
115
+ refute conn.lrange("#{Roundhouse::Monitor::QUEUE}:100", 0, 0).first
116
+ refute Roundhouse::Monitor.pop_job conn, 100
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#push_job' do
123
+ it 'should add bucket and push to queue' do
124
+ Roundhouse.redis do |conn|
125
+ bucket_num = Roundhouse::Monitor.bucket_num(2500)
126
+ refute conn.sismember(Roundhouse::Monitor::BUCKETS, bucket_num)
127
+ refute conn.lrange("#{Roundhouse::Monitor::QUEUE}:2500", 0, 0).first
128
+
129
+ Roundhouse::Monitor.push_job conn, [{'queue_id' => 2500, 'class' => 'MyWorker', 'args' => [1]}]
130
+
131
+ # We should see the bucket registered
132
+ assert conn.sismember(Roundhouse::Monitor::BUCKETS, bucket_num)
133
+ assert conn.lrange("#{Roundhouse::Monitor::QUEUE}:2500", 0, 0).first
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+ describe '#requeue' do
140
+ it 'should requeue items'
141
+ end
142
+
143
+ describe '#maybe_add_to_rotation' do
144
+ describe 'with active queue' do
145
+ it 'should not rotate queue into semaphore' do
146
+ Roundhouse.redis do |conn|
147
+ Roundhouse::Monitor.activate(conn, 100)
148
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
149
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
150
+
151
+ Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
152
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
153
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
154
+ end
155
+ end
156
+ end
157
+
158
+ describe 'with empty queue' do
159
+ it 'should rotate queue into semaphore' do
160
+ Roundhouse.redis do |conn|
161
+ Roundhouse::Monitor.set_queue_is_empty(conn, 100)
162
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
163
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
164
+
165
+ Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
166
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
167
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
168
+ end
169
+ end
170
+ end
171
+
172
+ describe 'with suspended queue' do
173
+ it 'should not rotate queue into semaphore' do
174
+ Roundhouse.redis do |conn|
175
+ Roundhouse::Monitor.suspend(conn, 100)
176
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
177
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
178
+
179
+ Roundhouse::Monitor.maybe_add_to_rotation(conn, 100)
180
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
181
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, 0, 0)
182
+ end
183
+ end
184
+ end
185
+ end # maybe_add_to_rotation
186
+
187
+ describe '#set_queue_is_empty' do
188
+ it 'should set queue as empty' do
189
+ Roundhouse.redis do |conn|
190
+ Roundhouse::Monitor.set_queue_is_empty(conn, 100)
191
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
192
+ end
193
+ end
194
+ end # #set_queue_is_empty
195
+
196
+ describe '#activate' do
197
+ it 'should set queue as active' do
198
+ Roundhouse.redis do |conn|
199
+ Roundhouse::Monitor.activate(conn, 100)
200
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
201
+ end
202
+ end
203
+ end # #activate
204
+
205
+ describe '#suspend' do
206
+ it 'should set queue as suspended' do
207
+ Roundhouse.redis do |conn|
208
+ Roundhouse::Monitor.suspend(conn, 100)
209
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
210
+ end
211
+ end
212
+ end # #suspend
213
+
214
+ describe '#resume' do
215
+ describe 'with suspended queue' do
216
+ it 'should reactive queue and put it into rotation' do
217
+ Roundhouse.redis do |conn|
218
+ Roundhouse::Monitor.suspend(conn, 100)
219
+ assert_equal Roundhouse::Monitor::SUSPENDED, Roundhouse::Monitor.queue_status(conn, 100)
220
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
221
+
222
+ Roundhouse::Monitor.resume(conn, 100)
223
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
224
+ assert_equal ['100'], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
225
+ end
226
+ end
227
+ end
228
+
229
+ describe 'with empty queue' do
230
+ it 'should not resume' do
231
+ Roundhouse.redis do |conn|
232
+ Roundhouse::Monitor.set_queue_is_empty(conn, 100)
233
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
234
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
235
+
236
+ Roundhouse::Monitor.resume(conn, 100)
237
+ assert_equal Roundhouse::Monitor::EMPTY, Roundhouse::Monitor.queue_status(conn, 100)
238
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
239
+ end
240
+ end
241
+ end
242
+
243
+ describe 'with active queue' do
244
+ it 'should not resume' do
245
+ Roundhouse.redis do |conn|
246
+ Roundhouse::Monitor.activate(conn, 100)
247
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
248
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
249
+
250
+ Roundhouse::Monitor.resume(conn, 100)
251
+ assert_equal Roundhouse::Monitor::ACTIVE, Roundhouse::Monitor.queue_status(conn, 100)
252
+ assert_equal [], conn.lrange(Roundhouse::Monitor::SEMAPHORE, -1, -1)
253
+ end
254
+ end
255
+ end
256
+ end # #resume
257
+ end
258
+ end
@@ -0,0 +1,176 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse/processor'
3
+ require 'roundhouse/fetch'
4
+ require 'roundhouse/cli'
5
+
6
+ class TestProcessor < Roundhouse::Test
7
+ TestException = Class.new(StandardError)
8
+ TEST_EXCEPTION = TestException.new("kerboom!")
9
+
10
+ describe 'with mock setup' do
11
+ before do
12
+ $invokes = 0
13
+ @boss = Minitest::Mock.new
14
+ @processor = ::Roundhouse::Processor.new(@boss)
15
+ Celluloid.logger = nil
16
+ Roundhouse.redis = REDIS
17
+ end
18
+
19
+ class MockWorker
20
+ include Roundhouse::Worker
21
+ def perform(args)
22
+ raise TEST_EXCEPTION if args == 'boom'
23
+ args.pop if args.is_a? Array
24
+ $invokes += 1
25
+ end
26
+ end
27
+
28
+ def work(msg, full_queue_name='queue:100')
29
+ Roundhouse::RoundRobinFetch::UnitOfWork.new(full_queue_name, msg)
30
+ end
31
+
32
+ it 'processes as expected' do
33
+ # Queue needs to be active or it won't be put back into rotation
34
+ Roundhouse.redis { |conn| Roundhouse::Monitor.activate(conn, 100) }
35
+
36
+ msg = Roundhouse.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
37
+ actor = Minitest::Mock.new
38
+ actor.expect(:processor_done, nil, [@processor])
39
+ actor.expect(:real_thread, nil, [nil, Thread])
40
+ @boss.expect(:async, actor, [])
41
+ @boss.expect(:async, actor, [])
42
+ @processor.process(work(msg))
43
+
44
+ # After being finished, the q_id should be put back into the semaphore
45
+ queue_id = Roundhouse.redis { |conn| conn.lrange('semaphore', 0, 0).first }
46
+ assert_equal '100', queue_id
47
+
48
+ @boss.verify
49
+ assert_equal 1, $invokes
50
+ end
51
+
52
+ it 'executes a worker as expected' do
53
+ worker = Minitest::Mock.new
54
+ worker.expect(:perform, nil, [1, 2, 3])
55
+ @processor.execute_job(worker, [1, 2, 3])
56
+ end
57
+
58
+ it 'passes exceptions to ExceptionHandler' do
59
+ actor = Minitest::Mock.new
60
+ actor.expect(:real_thread, nil, [nil, Thread])
61
+ @boss.expect(:async, actor, [])
62
+ msg = Roundhouse.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
63
+ begin
64
+ @processor.process(work(msg))
65
+ flunk "Expected #process to raise exception"
66
+ rescue TestException
67
+ end
68
+
69
+ assert_equal 0, $invokes
70
+ end
71
+
72
+ it 're-raises exceptions after handling' do
73
+ msg = Roundhouse.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
74
+ re_raise = false
75
+ actor = Minitest::Mock.new
76
+ actor.expect(:real_thread, nil, [nil, Thread])
77
+ @boss.expect(:async, actor, [])
78
+
79
+ begin
80
+ @processor.process(work(msg))
81
+ rescue TestException
82
+ re_raise = true
83
+ end
84
+
85
+ assert re_raise, "does not re-raise exceptions after handling"
86
+ end
87
+
88
+ it 'does not modify original arguments' do
89
+ msg = { 'class' => MockWorker.to_s, 'args' => [['myarg']] }
90
+ msgstr = Roundhouse.dump_json(msg)
91
+ processor = ::Roundhouse::Processor.new(@boss)
92
+ actor = Minitest::Mock.new
93
+ actor.expect(:processor_done, nil, [processor])
94
+ actor.expect(:real_thread, nil, [nil, Thread])
95
+ @boss.expect(:async, actor, [])
96
+ @boss.expect(:async, actor, [])
97
+ processor.process(work(msgstr))
98
+ assert_equal [['myarg']], msg['args']
99
+ end
100
+
101
+ describe 'stats' do
102
+ before do
103
+ Roundhouse.redis {|c| c.flushdb }
104
+ end
105
+
106
+ def with_expire(time)
107
+ begin
108
+ old = Roundhouse::Processor::STATS_TIMEOUT
109
+ silence_warnings { Roundhouse::Processor.const_set(:STATS_TIMEOUT, time) }
110
+ yield
111
+ ensure
112
+ silence_warnings { Roundhouse::Processor.const_set(:STATS_TIMEOUT, old) }
113
+ end
114
+ end
115
+
116
+ describe 'when successful' do
117
+ let(:processed_today_key) { "stat:processed:#{Time.now.utc.to_date}" }
118
+
119
+ def successful_job
120
+ msg = Roundhouse.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
121
+ actor = Minitest::Mock.new
122
+ actor.expect(:real_thread, nil, [nil, Thread])
123
+ actor.expect(:processor_done, nil, [@processor])
124
+ @boss.expect(:async, actor, [])
125
+ @boss.expect(:async, actor, [])
126
+ @processor.process(work(msg))
127
+ end
128
+
129
+ it 'increments processed stat' do
130
+ successful_job
131
+ assert_equal 1, Roundhouse::Stats.new.processed
132
+ end
133
+
134
+ it 'expires processed stat' do
135
+ successful_job
136
+ assert_equal Roundhouse::Processor::STATS_TIMEOUT, Roundhouse.redis { |conn| conn.ttl(processed_today_key) }
137
+ end
138
+
139
+ it 'increments date processed stat' do
140
+ successful_job
141
+ assert_equal 1, Roundhouse.redis { |conn| conn.get(processed_today_key) }.to_i
142
+ end
143
+ end
144
+
145
+ describe 'when failed' do
146
+ let(:failed_today_key) { "stat:failed:#{Time.now.utc.to_date}" }
147
+
148
+ def failed_job
149
+ actor = Minitest::Mock.new
150
+ actor.expect(:real_thread, nil, [nil, Thread])
151
+ @boss.expect(:async, actor, [])
152
+ msg = Roundhouse.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
153
+ begin
154
+ @processor.process(work(msg))
155
+ rescue TestException
156
+ end
157
+ end
158
+
159
+ it 'increments failed stat' do
160
+ failed_job
161
+ assert_equal 1, Roundhouse::Stats.new.failed
162
+ end
163
+
164
+ it 'increments date failed stat' do
165
+ failed_job
166
+ assert_equal 1, Roundhouse.redis { |conn| conn.get(failed_today_key) }.to_i
167
+ end
168
+
169
+ it 'expires failed stat' do
170
+ failed_job
171
+ assert_equal Roundhouse::Processor::STATS_TIMEOUT, Roundhouse.redis { |conn| conn.ttl(failed_today_key) }
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse'
3
+ require 'roundhouse/web_helpers'
4
+
5
+ $HAS_AJ = true
6
+ begin
7
+ require 'active_job'
8
+ rescue LoadError
9
+ $HAS_AJ = false
10
+ end
11
+
12
+ class TestRails < Roundhouse::Test
13
+
14
+ describe 'ActiveJob' do
15
+ it 'does not allow Roundhouse::Worker in AJ::Base classes' do
16
+ ex = assert_raises ArgumentError do
17
+ c = Class.new(ActiveJob::Base)
18
+ c.send(:include, Roundhouse::Worker)
19
+ end
20
+ assert_includes ex.message, "cannot include"
21
+ end if $HAS_AJ
22
+ end
23
+ end
@@ -0,0 +1,127 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse/redis_connection'
3
+
4
+ class TestRedisConnection < Roundhouse::Test
5
+
6
+ describe ".create" do
7
+
8
+ it "creates a pooled redis connection" do
9
+ pool = Roundhouse::RedisConnection.create
10
+ assert_equal Redis, pool.checkout.class
11
+ end
12
+
13
+ describe "network_timeout" do
14
+ it "sets a custom network_timeout if specified" do
15
+ pool = Roundhouse::RedisConnection.create(:network_timeout => 8)
16
+ redis = pool.checkout
17
+
18
+ assert_equal 8, redis.client.timeout
19
+ end
20
+
21
+ it "uses the default network_timeout if none specified" do
22
+ pool = Roundhouse::RedisConnection.create
23
+ redis = pool.checkout
24
+
25
+ assert_equal 5, redis.client.timeout
26
+ end
27
+ end
28
+
29
+ describe "namespace" do
30
+ it "uses a given :namespace" do
31
+ pool = Roundhouse::RedisConnection.create(:namespace => "xxx")
32
+ assert_equal "xxx", pool.checkout.namespace
33
+ end
34
+
35
+ it "uses given :namespace over :namespace from Roundhouse.options" do
36
+ Roundhouse.options[:namespace] = "xxx"
37
+ pool = Roundhouse::RedisConnection.create(:namespace => "yyy")
38
+ assert_equal "yyy", pool.checkout.namespace
39
+ end
40
+ end
41
+
42
+ describe "socket path" do
43
+ it "uses a given :path" do
44
+ pool = Roundhouse::RedisConnection.create(:path => "/var/run/redis.sock")
45
+ assert_equal "unix", pool.checkout.client.scheme
46
+ assert_equal "redis:///var/run/redis.sock/0", pool.checkout.client.id
47
+ end
48
+
49
+ it "uses a given :path and :db" do
50
+ pool = Roundhouse::RedisConnection.create(:path => "/var/run/redis.sock", :db => 8)
51
+ assert_equal "unix", pool.checkout.client.scheme
52
+ assert_equal "redis:///var/run/redis.sock/8", pool.checkout.client.id
53
+ end
54
+ end
55
+
56
+ describe "pool_timeout" do
57
+ it "uses a given :timeout over the default of 1" do
58
+ pool = Roundhouse::RedisConnection.create(:pool_timeout => 5)
59
+
60
+ assert_equal 5, pool.instance_eval{ @timeout }
61
+ end
62
+
63
+ it "uses the default timeout of 1 if no override" do
64
+ pool = Roundhouse::RedisConnection.create
65
+
66
+ assert_equal 1, pool.instance_eval{ @timeout }
67
+ end
68
+ end
69
+ end
70
+
71
+ describe ".determine_redis_provider" do
72
+
73
+ before do
74
+ @old_env = ENV.to_hash
75
+ end
76
+
77
+ after do
78
+ ENV.update(@old_env)
79
+ end
80
+
81
+ def with_env_var(var, uri, skip_provider=false)
82
+ vars = ['REDISTOGO_URL', 'REDIS_PROVIDER', 'REDIS_URL'] - [var]
83
+ vars.each do |v|
84
+ next if skip_provider
85
+ ENV[v] = nil
86
+ end
87
+ ENV[var] = uri
88
+ assert_equal uri, Roundhouse::RedisConnection.__send__(:determine_redis_provider)
89
+ ENV[var] = nil
90
+ end
91
+
92
+ describe "with REDISTOGO_URL and a parallel REDIS_PROVIDER set" do
93
+ it "sets connection URI to the provider" do
94
+ uri = 'redis://roundhouse-redis-provider:6379/0'
95
+ provider = 'SIDEKIQ_REDIS_PROVIDER'
96
+
97
+ ENV['REDIS_PROVIDER'] = provider
98
+ ENV[provider] = uri
99
+ ENV['REDISTOGO_URL'] = 'redis://redis-to-go:6379/0'
100
+ with_env_var provider, uri, true
101
+
102
+ ENV[provider] = nil
103
+ end
104
+ end
105
+
106
+ describe "with REDIS_PROVIDER set" do
107
+ it "sets connection URI to the provider" do
108
+ uri = 'redis://roundhouse-redis-provider:6379/0'
109
+ provider = 'SIDEKIQ_REDIS_PROVIDER'
110
+
111
+ ENV['REDIS_PROVIDER'] = provider
112
+ ENV[provider] = uri
113
+
114
+ with_env_var provider, uri, true
115
+
116
+ ENV[provider] = nil
117
+ end
118
+ end
119
+
120
+ describe "with REDIS_URL set" do
121
+ it "sets connection URI to custom uri" do
122
+ with_env_var 'REDIS_URL', 'redis://redis-uri:6379/0'
123
+ end
124
+ end
125
+
126
+ end
127
+ end