shoryuken 2.1.1 → 3.1.3

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +5 -8
  3. data/.rubocop.yml +8 -2
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +167 -0
  6. data/Gemfile +2 -0
  7. data/README.md +16 -155
  8. data/Rakefile +0 -1
  9. data/bin/cli/base.rb +40 -0
  10. data/bin/cli/sqs.rb +204 -0
  11. data/bin/shoryuken +55 -9
  12. data/examples/default_worker.rb +1 -1
  13. data/lib/shoryuken/client.rb +3 -15
  14. data/lib/shoryuken/default_worker_registry.rb +10 -6
  15. data/lib/shoryuken/environment_loader.rb +60 -57
  16. data/lib/shoryuken/fetcher.rb +21 -51
  17. data/lib/shoryuken/launcher.rb +77 -23
  18. data/lib/shoryuken/logging.rb +0 -5
  19. data/lib/shoryuken/manager.rb +54 -205
  20. data/lib/shoryuken/message.rb +4 -13
  21. data/lib/shoryuken/middleware/chain.rb +1 -18
  22. data/lib/shoryuken/middleware/server/auto_delete.rb +3 -8
  23. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +16 -17
  24. data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +37 -22
  25. data/lib/shoryuken/middleware/server/timing.rb +2 -2
  26. data/lib/shoryuken/options.rb +196 -0
  27. data/lib/shoryuken/polling/base.rb +67 -0
  28. data/lib/shoryuken/polling/strict_priority.rb +77 -0
  29. data/lib/shoryuken/polling/weighted_round_robin.rb +66 -0
  30. data/lib/shoryuken/processor.rb +27 -25
  31. data/lib/shoryuken/queue.rb +28 -9
  32. data/lib/shoryuken/runner.rb +131 -0
  33. data/lib/shoryuken/util.rb +1 -9
  34. data/lib/shoryuken/version.rb +1 -1
  35. data/lib/shoryuken/worker.rb +9 -1
  36. data/lib/shoryuken.rb +47 -157
  37. data/shoryuken.gemspec +7 -7
  38. data/spec/integration/launcher_spec.rb +15 -8
  39. data/spec/shoryuken/client_spec.rb +2 -45
  40. data/spec/shoryuken/default_worker_registry_spec.rb +12 -10
  41. data/spec/shoryuken/environment_loader_spec.rb +54 -0
  42. data/spec/shoryuken/fetcher_spec.rb +27 -46
  43. data/spec/shoryuken/manager_spec.rb +80 -93
  44. data/spec/shoryuken/middleware/chain_spec.rb +0 -24
  45. data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
  46. data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +7 -3
  47. data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +56 -33
  48. data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
  49. data/spec/shoryuken/options_spec.rb +100 -0
  50. data/spec/shoryuken/polling/strict_priority_spec.rb +140 -0
  51. data/spec/shoryuken/polling/weighted_round_robin_spec.rb +99 -0
  52. data/spec/shoryuken/processor_spec.rb +20 -37
  53. data/spec/shoryuken/queue_spec.rb +72 -26
  54. data/spec/shoryuken/{cli_spec.rb → runner_spec.rb} +11 -26
  55. data/spec/shoryuken_spec.rb +1 -48
  56. data/spec/spec_helper.rb +14 -20
  57. data/test_workers/endless_interruptive_worker.rb +41 -0
  58. data/test_workers/endless_uninterruptive_worker.rb +44 -0
  59. metadata +39 -34
  60. data/lib/shoryuken/aws_config.rb +0 -64
  61. data/lib/shoryuken/cli.rb +0 -215
  62. data/lib/shoryuken/sns_arn.rb +0 -27
  63. data/lib/shoryuken/topic.rb +0 -17
  64. data/spec/shoryuken/sns_arn_spec.rb +0 -42
  65. data/spec/shoryuken/topic_spec.rb +0 -32
  66. data/spec/shoryuken_endpoint.yml +0 -6
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Shoryuken::Polling::WeightedRoundRobin do
4
+ let(:queue1) { 'shoryuken' }
5
+ let(:queue2) { 'uppercut' }
6
+ let(:queues) { Array.new }
7
+ subject { Shoryuken::Polling::WeightedRoundRobin.new(queues) }
8
+
9
+ describe '#next_queue' do
10
+ it 'cycles' do
11
+ # [shoryuken, 2]
12
+ # [uppercut, 1]
13
+ queues << queue1
14
+ queues << queue1
15
+ queues << queue2
16
+
17
+ expect(subject.next_queue).to eq(queue1)
18
+ expect(subject.next_queue).to eq(queue2)
19
+ expect(subject.next_queue).to eq(queue1)
20
+ end
21
+
22
+ it 'returns nil if there are no active queues' do
23
+ expect(subject.next_queue).to eq(nil)
24
+ end
25
+
26
+ it 'unpauses queues whose pause is expired' do
27
+ # [shoryuken, 2]
28
+ # [uppercut, 1]
29
+ queues << queue1
30
+ queues << queue1
31
+ queues << queue2
32
+
33
+ allow(subject).to receive(:delay).and_return(10)
34
+
35
+ now = Time.now
36
+ allow(Time).to receive(:now).and_return(now)
37
+
38
+ # pause the first queue
39
+ subject.messages_found(queue1, 0)
40
+ expect(subject.next_queue).to eq(queue2)
41
+
42
+ now += 5
43
+ allow(Time).to receive(:now).and_return(now)
44
+
45
+ # pause the second queue
46
+ subject.messages_found(queue2, 0)
47
+ expect(subject.next_queue).to eq(nil)
48
+
49
+ # queue1 should be unpaused now
50
+ now += 6
51
+ allow(Time).to receive(:now).and_return(now)
52
+ expect(subject.next_queue).to eq(queue1)
53
+
54
+ # queue1 should be unpaused and added to the end of queues now
55
+ now += 6
56
+ allow(Time).to receive(:now).and_return(now)
57
+ expect(subject.next_queue).to eq(queue1)
58
+ expect(subject.next_queue).to eq(queue2)
59
+ end
60
+ end
61
+
62
+ describe '#messages_found' do
63
+ it 'pauses a queue if there are no messages found' do
64
+ # [shoryuken, 2]
65
+ # [uppercut, 1]
66
+ queues << queue1
67
+ queues << queue1
68
+ queues << queue2
69
+
70
+ expect(subject).to receive(:pause).with(queue1).and_call_original
71
+ subject.messages_found(queue1, 0)
72
+ expect(subject.instance_variable_get(:@queues)).to eq([queue2])
73
+ end
74
+
75
+ it 'increased the weight if message is found' do
76
+ # [shoryuken, 2]
77
+ # [uppercut, 1]
78
+ queues << queue1
79
+ queues << queue1
80
+ queues << queue2
81
+
82
+ expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2])
83
+ subject.messages_found(queue1, 1)
84
+ expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
85
+ end
86
+
87
+ it 'respects the maximum queue weight' do
88
+ # [shoryuken, 2]
89
+ # [uppercut, 1]
90
+ queues << queue1
91
+ queues << queue1
92
+ queues << queue2
93
+
94
+ subject.messages_found(queue1, 1)
95
+ subject.messages_found(queue1, 1)
96
+ expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
97
+ end
98
+ end
99
+ end
@@ -3,7 +3,7 @@ require 'shoryuken/processor'
3
3
  require 'shoryuken/manager'
4
4
 
5
5
  RSpec.describe Shoryuken::Processor do
6
- let(:manager) { double Shoryuken::Manager, processor_done: nil }
6
+ let(:manager) { double Shoryuken::Manager }
7
7
  let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
8
8
  let(:queue) { 'default' }
9
9
 
@@ -16,14 +16,14 @@ RSpec.describe Shoryuken::Processor do
16
16
  receipt_handle: SecureRandom.uuid
17
17
  end
18
18
 
19
- subject { described_class.new(manager) }
20
-
21
19
  before do
22
20
  allow(manager).to receive(:async).and_return(manager)
23
21
  allow(manager).to receive(:real_thread)
24
22
  allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
25
23
  end
26
24
 
25
+ subject { described_class.new(queue, sqs_msg) }
26
+
27
27
  describe '#process' do
28
28
  it 'parses the body into JSON' do
29
29
  TestWorker.get_shoryuken_options['body_parser'] = :json
@@ -34,7 +34,7 @@ RSpec.describe Shoryuken::Processor do
34
34
 
35
35
  allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
36
36
 
37
- subject.process(queue, sqs_msg)
37
+ subject.process
38
38
  end
39
39
 
40
40
  it 'parses the body calling the proc' do
@@ -44,7 +44,7 @@ RSpec.describe Shoryuken::Processor do
44
44
 
45
45
  allow(sqs_msg).to receive(:body).and_return('test')
46
46
 
47
- subject.process(queue, sqs_msg)
47
+ subject.process
48
48
  end
49
49
 
50
50
  it 'parses the body as text' do
@@ -56,7 +56,7 @@ RSpec.describe Shoryuken::Processor do
56
56
 
57
57
  allow(sqs_msg).to receive(:body).and_return(body)
58
58
 
59
- subject.process(queue, sqs_msg)
59
+ subject.process
60
60
  end
61
61
 
62
62
  it 'parses calling `.load`' do
@@ -72,7 +72,7 @@ RSpec.describe Shoryuken::Processor do
72
72
 
73
73
  allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
74
74
 
75
- subject.process(queue, sqs_msg)
75
+ subject.process
76
76
  end
77
77
 
78
78
  it 'parses calling `.parse`' do
@@ -88,28 +88,21 @@ RSpec.describe Shoryuken::Processor do
88
88
 
89
89
  allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
90
90
 
91
- subject.process(queue, sqs_msg)
91
+ subject.process
92
92
  end
93
93
 
94
94
  context 'when parse errors' do
95
95
  before do
96
96
  TestWorker.get_shoryuken_options['body_parser'] = :json
97
97
 
98
- allow(sqs_msg).to receive(:body).and_return('invalid json')
98
+ allow(sqs_msg).to receive(:body).and_return('invalid JSON')
99
99
  end
100
100
 
101
- it 'logs the error' do
102
- expect(subject.logger).to receive(:error) do |&block|
103
- expect(block.call).
104
- to include("unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
105
- end
106
-
107
- subject.process(queue, sqs_msg) rescue nil
108
- end
101
+ specify do
102
+ expect(subject.logger).to receive(:error).twice
109
103
 
110
- it 're raises the error' do
111
- expect { subject.process(queue, sqs_msg) }.
112
- to raise_error(JSON::ParserError, /unexpected token at 'invalid json'/)
104
+ expect { subject.process }.
105
+ to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
113
106
  end
114
107
  end
115
108
 
@@ -123,7 +116,7 @@ RSpec.describe Shoryuken::Processor do
123
116
 
124
117
  allow(sqs_msg).to receive(:body).and_return(body)
125
118
 
126
- subject.process(queue, sqs_msg)
119
+ subject.process
127
120
  end
128
121
  end
129
122
 
@@ -150,7 +143,7 @@ RSpec.describe Shoryuken::Processor do
150
143
 
151
144
  context 'server' do
152
145
  before do
153
- allow(Shoryuken).to receive(:server?).and_return(true)
146
+ allow(Shoryuken::Options).to receive(:server?).and_return(true)
154
147
  WorkerCalledMiddlewareWorker.instance_variable_set(:@server_chain, nil) # un-memoize middleware
155
148
 
156
149
  Shoryuken.configure_server do |config|
@@ -169,12 +162,10 @@ RSpec.describe Shoryuken::Processor do
169
162
  end
170
163
 
171
164
  it 'invokes middleware' do
172
- expect(manager).to receive(:processor_done).with(queue, subject)
173
-
174
165
  expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
175
166
  expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:called).with(sqs_msg, queue)
176
167
 
177
- subject.process(queue, sqs_msg)
168
+ subject.process
178
169
  end
179
170
  end
180
171
 
@@ -199,12 +190,10 @@ RSpec.describe Shoryuken::Processor do
199
190
  end
200
191
 
201
192
  it "doesn't invoke middleware" do
202
- expect(manager).to receive(:processor_done).with(queue, subject)
203
-
204
193
  expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
205
194
  expect_any_instance_of(WorkerCalledMiddlewareWorker).to_not receive(:called).with(sqs_msg, queue)
206
195
 
207
- subject.process(queue, sqs_msg)
196
+ subject.process
208
197
  end
209
198
  end
210
199
  end
@@ -212,25 +201,21 @@ RSpec.describe Shoryuken::Processor do
212
201
  it 'performs with delete' do
213
202
  TestWorker.get_shoryuken_options['auto_delete'] = true
214
203
 
215
- expect(manager).to receive(:processor_done).with(queue, subject)
216
-
217
204
  expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
218
205
 
219
206
  expect(sqs_queue).to receive(:delete_messages).with(entries: [{ id: '0', receipt_handle: sqs_msg.receipt_handle }])
220
207
 
221
- subject.process(queue, sqs_msg)
208
+ subject.process
222
209
  end
223
210
 
224
211
  it 'performs without delete' do
225
212
  TestWorker.get_shoryuken_options['auto_delete'] = false
226
213
 
227
- expect(manager).to receive(:processor_done).with(queue, subject)
228
-
229
214
  expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
230
215
 
231
216
  expect(sqs_queue).to_not receive(:delete_messages)
232
217
 
233
- subject.process(queue, sqs_msg)
218
+ subject.process
234
219
  end
235
220
 
236
221
  context 'when shoryuken_class header' do
@@ -249,13 +234,11 @@ RSpec.describe Shoryuken::Processor do
249
234
  it 'performs without delete' do
250
235
  Shoryuken.worker_registry.clear # unregister TestWorker
251
236
 
252
- expect(manager).to receive(:processor_done).with(queue, subject)
253
-
254
237
  expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
255
238
 
256
239
  expect(sqs_queue).to_not receive(:delete_messages)
257
240
 
258
- subject.process(queue, sqs_msg)
241
+ subject.process
259
242
  end
260
243
  end
261
244
  end
@@ -1,22 +1,68 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoryuken::Queue do
3
+ RSpec.describe Shoryuken::Queue do
4
4
  let(:credentials) { Aws::Credentials.new('access_key_id', 'secret_access_key') }
5
5
  let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
6
6
  let(:queue_name) { 'shoryuken' }
7
- let(:queue_url) { 'https://eu-west-1.amazonaws.com:6059/123456789012/shoryuken' }
7
+ let(:queue_url) { "https://sqs.eu-west-1.amazonaws.com:6059/0123456789/#{queue_name}" }
8
8
 
9
9
  subject { described_class.new(sqs, queue_name) }
10
- before {
10
+
11
+ before do
11
12
  # Required as Aws::SQS::Client.get_queue_url returns 'String' when responses are stubbed,
12
13
  # which is not accepted by Aws::SQS::Client.get_queue_attributes for :queue_name parameter.
13
14
  allow(subject).to receive(:url).and_return(queue_url)
14
- }
15
+ end
16
+
17
+ describe '#new' do
18
+ context 'when queue url supplied' do
19
+ subject { described_class.new(sqs, queue_url) }
20
+
21
+ specify do
22
+ expect(subject.name).to eq(queue_name)
23
+ end
24
+ end
25
+
26
+ context 'when queue name supplied' do
27
+ subject { described_class.new(sqs, queue_name) }
28
+
29
+ specify do
30
+ expect(subject.name).to eq(queue_name)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#delete_messages' do
36
+ let(:entries) do
37
+ [
38
+ { id: '1', receipt_handle: '1' },
39
+ { id: '2', receipt_handle: '2' }
40
+ ]
41
+ end
42
+
43
+ it 'deletes' do
44
+ expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: []))
45
+
46
+ subject.delete_messages(entries: entries)
47
+ end
48
+
49
+ context 'when it fails' do
50
+ it 'logs the reason' do
51
+ failure = double(id: 'id', code: 'code', message: '...', sender_fault: false)
52
+ logger = double 'Logger'
53
+
54
+ expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: [failure]))
55
+ expect(subject).to receive(:logger).and_return(logger)
56
+ expect(logger).to receive(:error)
57
+
58
+ subject.delete_messages(entries: entries)
59
+ end
60
+ end
61
+ end
15
62
 
16
63
  describe '#send_message' do
17
- before {
18
- allow(subject).to receive(:fifo?).and_return(false)
19
- }
64
+ before { allow(subject).to receive(:fifo?).and_return(false) }
65
+
20
66
  it 'accepts SQS request parameters' do
21
67
  # https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message-instance_method
22
68
  expect(sqs).to receive(:send_message).with(hash_including(message_body: 'msg1'))
@@ -77,24 +123,24 @@ describe Shoryuken::Queue do
77
123
 
78
124
  it 'accepts an array of messages' do
79
125
  options = { entries: [{ id: '0',
80
- message_body: 'msg1',
81
- delay_seconds: 1,
82
- message_attributes: { attr: 'attr1' } },
83
- { id: '1',
84
- message_body: 'msg2',
85
- delay_seconds: 1,
86
- message_attributes: { attr: 'attr2' } }] }
126
+ message_body: 'msg1',
127
+ delay_seconds: 1,
128
+ message_attributes: { attr: 'attr1' } },
129
+ { id: '1',
130
+ message_body: 'msg2',
131
+ delay_seconds: 1,
132
+ message_attributes: { attr: 'attr2' } }] }
87
133
 
88
134
  expect(sqs).to receive(:send_message_batch).with(hash_including(options))
89
135
 
90
136
  subject.send_messages([{ message_body: 'msg1',
91
- delay_seconds: 1,
92
- message_attributes: { attr: 'attr1' }
93
- }, {
94
- message_body: 'msg2',
95
- delay_seconds: 1,
96
- message_attributes: { attr: 'attr2' }
97
- }])
137
+ delay_seconds: 1,
138
+ message_attributes: { attr: 'attr1' }
139
+ }, {
140
+ message_body: 'msg2',
141
+ delay_seconds: 1,
142
+ message_attributes: { attr: 'attr2' }
143
+ }])
98
144
  end
99
145
 
100
146
  context 'when FIFO' do
@@ -125,9 +171,9 @@ describe Shoryuken::Queue do
125
171
  end
126
172
 
127
173
  subject.send_messages([{ message_body: 'msg1',
128
- message_attributes: { attr: 'attr1' },
129
- message_group_id: 'my group',
130
- message_deduplication_id: 'my id' }])
174
+ message_attributes: { attr: 'attr1' },
175
+ message_group_id: 'my group',
176
+ message_deduplication_id: 'my id' }])
131
177
  end
132
178
  end
133
179
  end
@@ -151,13 +197,13 @@ describe Shoryuken::Queue do
151
197
  context 'when queue is FIFO' do
152
198
  let(:fifo) { true }
153
199
 
154
- it { expect(subject.fifo?).to be }
200
+ specify { expect(subject.fifo?).to be }
155
201
  end
156
202
 
157
203
  context 'when queue is not FIFO' do
158
204
  let(:fifo) { false }
159
205
 
160
- it { expect(subject.fifo?).to_not be }
206
+ specify { expect(subject.fifo?).to_not be }
161
207
  end
162
208
  end
163
209
  end
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
- require 'shoryuken/cli'
3
- require 'shoryuken/launcher'
2
+ require 'shoryuken/runner'
4
3
 
5
- RSpec.describe Shoryuken::CLI do
6
- let(:cli) { Shoryuken::CLI.instance }
4
+ # rubocop:disable Metrics/BlockLength
5
+ RSpec.describe Shoryuken::Runner do
6
+ let(:cli) { Shoryuken::Runner.instance }
7
7
 
8
8
  before do
9
9
  # make sure we do not bail
@@ -16,50 +16,35 @@ RSpec.describe Shoryuken::CLI do
16
16
  describe '#run' do
17
17
  let(:launcher) { instance_double('Shoryuken::Launcher') }
18
18
 
19
- before(:each) do
19
+ before do
20
20
  allow(Shoryuken::Launcher).to receive(:new).and_return(launcher)
21
- allow(launcher).to receive(:run).and_raise(Interrupt)
22
- allow(launcher).to receive(:stop)
21
+ allow(launcher).to receive(:start).and_raise(Interrupt)
22
+ allow(launcher).to receive(:stop!)
23
23
  end
24
24
 
25
25
  it 'does not raise' do
26
- expect { cli.run([]) }.to_not raise_error
26
+ expect { cli.run({}) }.to_not raise_error
27
27
  end
28
28
 
29
29
  it 'daemonizes with --daemon --logfile' do
30
- expect(cli).to receive(:celluloid_loaded?).and_return(false)
31
30
  expect(Process).to receive(:daemon)
32
- cli.run(['--daemon', '--logfile', '/dev/null'])
31
+ cli.run(daemon: true, logfile: '/dev/null')
33
32
  end
34
33
 
35
34
  it 'does NOT daemonize with --logfile' do
36
35
  expect(Process).to_not receive(:daemon)
37
- cli.run(['--logfile', '/dev/null'])
36
+ cli.run(logfile: '/dev/null')
38
37
  end
39
38
 
40
39
  it 'writes PID file with --pidfile' do
41
40
  pidfile = instance_double('File')
42
41
  expect(File).to receive(:open).with('/dev/null', 'w').and_yield(pidfile)
43
42
  expect(pidfile).to receive(:puts).with(Process.pid)
44
- cli.run(['--pidfile', '/dev/null'])
43
+ cli.run(pidfile: '/dev/null')
45
44
  end
46
45
  end
47
46
 
48
47
  describe '#daemonize' do
49
- before(:each) do
50
- allow(cli).to receive(:celluloid_loaded?).and_return(false)
51
- end
52
-
53
- it 'raises if logfile is not set' do
54
- expect { cli.send(:daemonize, daemon: true) }.to raise_error(ArgumentError)
55
- end
56
-
57
- it 'raises if Celluloid is already loaded' do
58
- expect(cli).to receive(:celluloid_loaded?).and_return(true)
59
- args = { daemon: true, logfile: '/dev/null' }
60
- expect { cli.send(:daemonize, args) }.to raise_error(RuntimeError)
61
- end
62
-
63
48
  it 'calls Process.daemon' do
64
49
  args = { daemon: true, logfile: '/dev/null' }
65
50
  expect(Process).to receive(:daemon).with(true, true)
@@ -1,51 +1,4 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoryuken do
4
- describe '.register_worker' do
5
- it 'registers a worker' do
6
- described_class.worker_registry.clear
7
- described_class.register_worker('default', TestWorker)
8
- expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
9
- end
10
-
11
- it 'registers a batchable worker' do
12
- described_class.worker_registry.clear
13
- TestWorker.get_shoryuken_options['batch'] = true
14
- described_class.register_worker('default', TestWorker)
15
- expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
16
- end
17
-
18
- it 'allows multiple workers' do
19
- described_class.worker_registry.clear
20
- described_class.register_worker('default', TestWorker)
21
- expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
22
-
23
- class Test2Worker
24
- include Shoryuken::Worker
25
-
26
- shoryuken_options queue: 'default'
27
-
28
- def perform(sqs_msg, body); end
29
- end
30
-
31
- expect(described_class.worker_registry.workers('default')).to eq([Test2Worker])
32
- end
33
-
34
- it 'raises an exception when mixing batchable with non batchable' do
35
- described_class.worker_registry.clear
36
- TestWorker.get_shoryuken_options['batch'] = true
37
- described_class.register_worker('default', TestWorker)
38
-
39
- expect {
40
- class BatchableWorker
41
- include Shoryuken::Worker
42
-
43
- shoryuken_options queue: 'default', batch: true
44
-
45
- def perform(sqs_msg, body); end
46
- end
47
- }.to raise_error("Could not register BatchableWorker for 'default', because TestWorker is already registered for this queue, " \
48
- "and Shoryuken doesn't support a batchable worker for a queue with multiple workers")
49
- end
50
- end
3
+ RSpec.describe Shoryuken do
51
4
  end
data/spec/spec_helper.rb CHANGED
@@ -2,11 +2,10 @@ require 'bundler/setup'
2
2
  Bundler.setup
3
3
 
4
4
  require 'pry-byebug'
5
- require 'celluloid/current'
6
5
  require 'shoryuken'
7
6
  require 'json'
8
- require 'multi_xml'
9
7
  require 'dotenv'
8
+ require 'securerandom'
10
9
  Dotenv.load
11
10
 
12
11
  if ENV['CODECLIMATE_REPO_TOKEN']
@@ -19,16 +18,6 @@ config_file = File.join(File.expand_path('../..', __FILE__), 'spec', 'shoryuken.
19
18
  Shoryuken::EnvironmentLoader.setup_options(config_file: config_file)
20
19
 
21
20
  Shoryuken.logger.level = Logger::UNKNOWN
22
- Celluloid.logger.level = Logger::UNKNOWN
23
-
24
- # I'm not sure whether this is an issue specific to running Shoryuken against github.com/comcast/cmb
25
- # as opposed to AWS itself, but sometimes the receive_messages call returns XML that looks like this:
26
- #
27
- # <ReceiveMessageResponse>\n\t<ReceiveMessageResult>\n\t</ReceiveMessageResult> ... </ReceiveMessageResponse>
28
- #
29
- # The default MultiXML parser is ReXML, which seems to mishandle \n\t chars. Nokogiri seems to be
30
- # the only one that correctly ignore this whitespace.
31
- MultiXml.parser = :nokogiri
32
21
 
33
22
  class TestWorker
34
23
  include Shoryuken::Worker
@@ -40,32 +29,37 @@ end
40
29
 
41
30
  RSpec.configure do |config|
42
31
  # Only run slow tests if SPEC_ALL=true and AWS_ACCESS_KEY_ID is present
43
- # The AWS_ACCESS_KEY_ID checker is because Travis CI does not expose ENV variables to pull requests from forked repositories
32
+ # The AWS_ACCESS_KEY_ID checker is because Travis CI
33
+ # does not expose ENV variables to pull requests from forked repositories
44
34
  # http://docs.travis-ci.com/user/pull-requests/
45
- config.filter_run_excluding slow: true if ENV['SPEC_ALL'] != 'true' || ENV['AWS_ACCESS_KEY_ID'].nil?
35
+ # config.filter_run_excluding slow: true if ENV['SPEC_ALL'] != 'true' || ENV['AWS_ACCESS_KEY_ID'].nil?
36
+ config.filter_run_excluding slow: true
46
37
 
47
38
  config.before do
48
39
  Shoryuken::Client.class_variable_set :@@queues, {}
49
- Shoryuken::Client.class_variable_set :@@visibility_timeouts, {}
50
40
 
51
41
  Shoryuken::Client.sqs = nil
52
- Shoryuken::Client.sqs_resource = nil
53
- Shoryuken::Client.sns = nil
54
42
 
55
- Shoryuken.queues.clear
43
+ Shoryuken.groups.clear
56
44
 
57
45
  Shoryuken.options[:concurrency] = 1
58
46
  Shoryuken.options[:delay] = 1
59
47
  Shoryuken.options[:timeout] = 1
60
48
  Shoryuken.options[:daemon] = nil
61
49
  Shoryuken.options[:logfile] = nil
62
-
63
- Shoryuken.options[:aws].delete(:receive_message)
50
+ Shoryuken.options[:queues] = nil
64
51
 
65
52
  TestWorker.get_shoryuken_options.clear
66
53
  TestWorker.get_shoryuken_options['queue'] = 'default'
67
54
 
55
+ Shoryuken.active_job_queue_name_prefixing = false
56
+
68
57
  Shoryuken.worker_registry.clear
69
58
  Shoryuken.register_worker('default', TestWorker)
59
+
60
+ Aws.config[:stub_responses] = true
61
+
62
+ allow(Concurrent).to receive(:global_io_executor).and_return(Concurrent::ImmediateExecutor.new)
63
+ allow(Shoryuken).to receive(:active_job?).and_return(false)
70
64
  end
71
65
  end
@@ -0,0 +1,41 @@
1
+ class EndlessInterruptiveWorker
2
+ include Shoryuken::Worker
3
+
4
+ # Usage:
5
+ # QUEUE="super-q"
6
+ # MAX_EXECUTION_TIME=2000 QUEUE=$QUEUE \
7
+ # bundle exec ./bin/shoryuken -r ./examples/endless_uninterruptive_worker.rb -q $QUEUE -c 8
8
+
9
+ class << self
10
+ def queue
11
+ ENV['QUEUE'] || 'default'
12
+ end
13
+
14
+ def max_execution_time
15
+ ENV["MAX_EXECUTION_TIME"] ? ENV["MAX_EXECUTION_TIME"].to_i : 100
16
+ end
17
+
18
+ def rng
19
+ @rng ||= Random.new
20
+ end
21
+
22
+ # returns a random number between 0 and 100
23
+ def random_number(hi = 1000)
24
+ (rng.rand * hi).to_i
25
+ end
26
+ end
27
+
28
+ def perform(sqs_msg, body)
29
+ Shoryuken.logger.info("Received message: '#{body}'")
30
+
31
+ execution_ms = self.class.random_number(self.class.max_execution_time)
32
+ Shoryuken.logger.info("Going to sleep for #{execution_ms}ms")
33
+
34
+ new_body = "#{execution_ms}-" + body.to_s
35
+ sleep(execution_ms.to_f / 1000)
36
+
37
+ self.class.perform_async(new_body.slice(0, 512))
38
+ end
39
+
40
+ shoryuken_options queue: queue, auto_delete: true
41
+ end