roundhouse-x 0.1.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.
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