fastly_nsq 0.13.2 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. data/.env +4 -0
  3. data/.overcommit.yml +3 -3
  4. data/.rubocop.yml +11 -1
  5. data/.travis.yml +8 -1
  6. data/Gemfile +9 -0
  7. data/README.md +52 -82
  8. data/Rakefile +2 -0
  9. data/bin/fastly_nsq +1 -0
  10. data/docker-compose.yml +23 -0
  11. data/examples/.sample.env +0 -3
  12. data/fastly_nsq.gemspec +7 -8
  13. data/lib/fastly_nsq.rb +44 -50
  14. data/lib/fastly_nsq/cli.rb +20 -14
  15. data/lib/fastly_nsq/consumer.rb +26 -30
  16. data/lib/fastly_nsq/feeder.rb +16 -0
  17. data/lib/fastly_nsq/http/nsqd.rb +7 -1
  18. data/lib/fastly_nsq/http/nsqlookupd.rb +1 -1
  19. data/lib/fastly_nsq/launcher.rb +31 -23
  20. data/lib/fastly_nsq/listener.rb +34 -103
  21. data/lib/fastly_nsq/manager.rb +48 -72
  22. data/lib/fastly_nsq/message.rb +2 -0
  23. data/lib/fastly_nsq/messenger.rb +5 -5
  24. data/lib/fastly_nsq/priority_queue.rb +12 -0
  25. data/lib/fastly_nsq/priority_thread_pool.rb +32 -0
  26. data/lib/fastly_nsq/producer.rb +52 -32
  27. data/lib/fastly_nsq/testing.rb +239 -0
  28. data/lib/fastly_nsq/tls_options.rb +2 -0
  29. data/lib/fastly_nsq/version.rb +3 -1
  30. data/spec/{lib/fastly_nsq/cli_spec.rb → cli_spec.rb} +2 -0
  31. data/spec/consumer_spec.rb +59 -0
  32. data/spec/fastly_nsq_spec.rb +72 -0
  33. data/spec/feeder_spec.rb +22 -0
  34. data/spec/{lib/fastly_nsq/http → http}/nsqd_spec.rb +1 -1
  35. data/spec/{lib/fastly_nsq/http → http}/nsqlookupd_spec.rb +1 -1
  36. data/spec/{lib/fastly_nsq/http_spec.rb → http_spec.rb} +3 -1
  37. data/spec/integration_spec.rb +48 -0
  38. data/spec/launcher_spec.rb +50 -0
  39. data/spec/listener_spec.rb +184 -0
  40. data/spec/manager_spec.rb +111 -0
  41. data/spec/matchers/delegate.rb +32 -0
  42. data/spec/{lib/fastly_nsq/message_spec.rb → message_spec.rb} +2 -0
  43. data/spec/{lib/fastly_nsq/messenger_spec.rb → messenger_spec.rb} +7 -5
  44. data/spec/priority_thread_pool_spec.rb +19 -0
  45. data/spec/producer_spec.rb +94 -0
  46. data/spec/spec_helper.rb +32 -28
  47. data/spec/support/http.rb +37 -0
  48. data/spec/support/webmock.rb +22 -0
  49. data/spec/{lib/fastly_nsq/tls_options_spec.rb → tls_options_spec.rb} +2 -0
  50. metadata +54 -96
  51. data/env_configuration_for_local_gem_tests.yml +0 -5
  52. data/example_config_class.rb +0 -20
  53. data/examples/Rakefile +0 -41
  54. data/lib/fastly_nsq/fake_backend.rb +0 -114
  55. data/lib/fastly_nsq/listener/config.rb +0 -35
  56. data/lib/fastly_nsq/rake_task.rb +0 -78
  57. data/lib/fastly_nsq/strategy.rb +0 -36
  58. data/spec/lib/fastly_nsq/consumer_spec.rb +0 -72
  59. data/spec/lib/fastly_nsq/fake_backend_spec.rb +0 -135
  60. data/spec/lib/fastly_nsq/fastly_nsq_spec.rb +0 -10
  61. data/spec/lib/fastly_nsq/launcher_spec.rb +0 -56
  62. data/spec/lib/fastly_nsq/listener_spec.rb +0 -213
  63. data/spec/lib/fastly_nsq/manager_spec.rb +0 -127
  64. data/spec/lib/fastly_nsq/producer_spec.rb +0 -60
  65. data/spec/lib/fastly_nsq/rake_task_spec.rb +0 -142
  66. data/spec/lib/fastly_nsq/strategy_spec.rb +0 -36
  67. data/spec/lib/fastly_nsq_spec.rb +0 -18
  68. data/spec/support/env_helpers.rb +0 -15
@@ -1,10 +0,0 @@
1
- require 'spec_helper'
2
- require 'fastly_nsq'
3
-
4
- RSpec.describe FastlyNsq do
5
- it 'has a version number' do
6
- version = FastlyNsq.const_get('VERSION')
7
-
8
- expect(version).not_to be_empty
9
- end
10
- end
@@ -1,56 +0,0 @@
1
- require 'spec_helper'
2
- require 'fastly_nsq/launcher'
3
-
4
- RSpec.describe FastlyNsq::Launcher do
5
- let(:launcher) { FastlyNsq::Launcher.new options }
6
- let(:manager) { instance_double 'Manager', start: nil, quiet: nil, stop: nil }
7
- let(:thread) { instance_double 'Thread' }
8
- let(:options) { { joe: 'biden', timeout: 9 } }
9
-
10
- before do
11
- allow(FastlyNsq::Manager).to receive(:new).and_return(manager)
12
- allow(launcher).to receive(:safe_thread).and_return(thread)
13
- end
14
-
15
- it 'creates a manager with correct options' do
16
- expect(FastlyNsq::Manager).to have_received(:new).with(options)
17
- end
18
-
19
- describe '#run' do
20
- it 'creates a heartbeat thread' do
21
- launcher.run
22
- expect(launcher).to have_received(:safe_thread).with('heartbeat')
23
- expect(launcher.stopping?).to eq false
24
- end
25
-
26
- it 'starts the setup manager' do
27
- launcher.run
28
- expect(manager).to have_received(:start)
29
- expect(launcher.stopping?).to eq false
30
- end
31
- end
32
-
33
- describe '#quiet' do
34
- it 'quites the manager and sets done' do
35
- expect(launcher.stopping?).to eq false
36
- launcher.quiet
37
- expect(manager).to have_received(:quiet)
38
- expect(launcher.stopping?).to eq true
39
- end
40
- end
41
-
42
- describe '#stop' do
43
- it 'stops the manager within a deadline' do
44
- now = Time.now
45
- allow(Time).to receive(:now).and_return(now)
46
- launcher.stop
47
- expect(manager).to have_received(:stop).with(now + options[:timeout])
48
- end
49
-
50
- it 'quites the manager' do
51
- launcher.stop
52
- expect(manager).to have_received(:quiet)
53
- expect(launcher.stopping?).to eq true
54
- end
55
- end
56
- end
@@ -1,213 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe FastlyNsq::Listener do
4
- let(:topic) { 'testing_topic' }
5
- let(:channel) { 'testing_channel' }
6
- let(:consumer) { FastlyNsq::FakeBackend::Consumer.new topic: topic, channel: channel }
7
- let(:logger) { double 'Logger', info: nil, debug: nil, error: nil }
8
-
9
- module TestMessageProcessor
10
- @@messages_processed = []
11
- Message = Struct.new(:body, :topic) do
12
- def finish
13
- @did_finish = true
14
- end
15
- end
16
-
17
- def self.process(incoming_message)
18
- @@messages_processed.push Message.new(incoming_message.to_s)
19
- end
20
-
21
- def self.messages_processed
22
- @@messages_processed
23
- end
24
-
25
- def self.clear
26
- @@messages_processed = []
27
- end
28
- end
29
-
30
- let(:listener) do
31
- FastlyNsq::Listener.new topic: topic,
32
- processor: TestMessageProcessor,
33
- consumer: consumer,
34
- logger: logger
35
- end
36
-
37
- let(:message) { TestMessageProcessor::Message.new 'this is message body' }
38
- let(:messages_processed) { TestMessageProcessor.messages_processed }
39
- let(:expected_message) { TestMessageProcessor::Message.new 'this is message body' }
40
- let(:expected_messages) { [expected_message] }
41
-
42
- describe 'instantiating without a consumer' do
43
- it 'instantiates a consumer, passing the topic and channel' do
44
- allow(FastlyNsq::Consumer).to receive(:new)
45
-
46
- FastlyNsq::Listener.new topic: topic,
47
- channel: channel,
48
- processor: TestMessageProcessor,
49
- consumer: nil
50
-
51
- expect(FastlyNsq::Consumer).to have_received(:new).
52
- with(topic: topic, channel: channel)
53
- end
54
- end
55
-
56
- context 'when not passed a manager' do
57
- it 'creates a blank manager' do
58
- expect(listener.identity[:manager]).to_not be_nil
59
- end
60
- end
61
-
62
- context 'when using the fake queue and it is empty', fake_queue: true do
63
- before do
64
- TestMessageProcessor.clear
65
- FastlyNsq::FakeBackend.delay = 0.1
66
- end
67
-
68
- it 'blocks on the process for longer than the check cycle' do
69
- delay = FastlyNsq::FakeBackend.delay + 0.1
70
-
71
- expect do
72
- Timeout.timeout(delay) do
73
- listener.go run_once: true
74
- end
75
- end.to raise_error(Timeout::Error)
76
- end
77
- end
78
-
79
- describe 'when processing next message' do
80
- before(:each) do
81
- TestMessageProcessor.clear
82
- allow(consumer).to receive(:pop).and_return(message)
83
- end
84
-
85
- it 'processes the next message' do
86
- listener.go run_once: true
87
-
88
- expect(messages_processed).to eql(expected_messages)
89
- end
90
-
91
- it 'finishes the message' do
92
- allow(message).to receive(:finish)
93
-
94
- listener.go run_once: true
95
-
96
- expect(message).to have_received(:finish).once
97
- end
98
-
99
- it 'logs info for the message body' do
100
- allow(logger).to receive(:info)
101
- listener.go run_once: true
102
-
103
- expect(logger).to have_received(:info).once.with(/\[NSQ\] Message received on topic \[#{topic}\]: #{message.body}/)
104
- end
105
-
106
- context 'when preprocessor is provided' do
107
- it 'calls the preprocessor' do
108
- preprocessor_was_called = false
109
- preprocessor = ->(*_args) { preprocessor_was_called = true }
110
-
111
- listener = FastlyNsq::Listener.new topic: topic,
112
- processor: TestMessageProcessor,
113
- consumer: consumer,
114
- logger: logger,
115
- preprocessor: preprocessor
116
-
117
- listener.go run_once: true
118
- expect(preprocessor_was_called).to be_truthy
119
- end
120
- end
121
-
122
- context 'when running as a thread' do
123
- let(:manager) { double 'Manager', listener_stopped: nil, listener_killed: nil }
124
- let(:thread) { double 'FakeThread', raise: nil, kill: nil, status: 'fake_thread' }
125
- let(:listener) do
126
- FastlyNsq::Listener.new topic: topic,
127
- processor: TestMessageProcessor,
128
- logger: logger,
129
- consumer: consumer,
130
- manager: manager
131
- end
132
-
133
- describe 'shutdown' do
134
- it 'informs the manager of a shutdown when run once' do
135
- listener.go run_once: true
136
-
137
- expect(manager).to have_received(:listener_stopped).with(listener)
138
- end
139
- end
140
-
141
- before do
142
- allow(listener).to receive(:safe_thread).and_return(thread)
143
- end
144
-
145
- it 'starts and provide status' do
146
- listener.start
147
- expect(logger).to have_received(:info)
148
- expect(listener.status).to eq 'fake_thread'
149
- end
150
-
151
- it 'can describe itself' do
152
- id = listener.identity
153
- expect(id[:consumer]).to_not be_nil
154
- expect(id[:manager]).to be manager
155
- expect(id[:preprocessor]).to be_nil
156
- expect(id[:processor]).to_not be_nil
157
- expect(id[:topic]).to_not be_nil
158
- end
159
-
160
- it 'can be cleanly duplicated' do
161
- new_listener = listener.reset_then_dup
162
-
163
- expect(listener.identity).to eq new_listener.identity
164
- end
165
-
166
- it 'can be terminated' do
167
- listener.start
168
- state = listener.instance_variable_get(:@done)
169
- expect(state).to eq false
170
- consumer = listener.instance_variable_get(:@consumer)
171
- allow(consumer).to receive(:terminate)
172
- listener.terminate
173
-
174
- state = listener.instance_variable_get(:@done)
175
- expect(logger).to have_received(:info).thrice
176
- expect(consumer).to have_received(:terminate)
177
- expect(state).to eq true
178
- end
179
-
180
- it 'raise in terminate to interupt blocking on an empty queue' do
181
- listener.start
182
- listener.terminate
183
-
184
- expect(consumer.empty?).to be true
185
- expect(thread).to have_received(:raise)
186
- end
187
-
188
- it 'terminate allows messages to be completed' do
189
- 100.times { FastlyNsq::Messenger.deliver(on_topic: topic, message: 'test_message') }
190
- listener.start
191
- listener.terminate
192
-
193
- expect(consumer.empty?).to be false
194
- expect(thread).to_not have_received(:raise)
195
- end
196
-
197
- it 'can be killed' do
198
- listener.start
199
- state = listener.instance_variable_get(:@done)
200
- expect(state).to eq false
201
- consumer = listener.instance_variable_get(:@consumer)
202
- allow(consumer).to receive(:terminate)
203
- listener.kill
204
-
205
- state = listener.instance_variable_get(:@done)
206
- expect(logger).to have_received(:info).thrice
207
- expect(consumer).to have_received(:terminate)
208
- expect(thread).to have_received(:raise).with(FastlyNsq::Shutdown)
209
- expect(state).to eq true
210
- end
211
- end
212
- end
213
- end
@@ -1,127 +0,0 @@
1
- require 'spec_helper'
2
- require 'fastly_nsq/manager'
3
-
4
- RSpec.describe FastlyNsq::Manager do
5
- class TestProcessor
6
- def self.process(message)
7
- FastlyNsq.logger.info("IN PROCESS: #{message}")
8
- end
9
- end
10
-
11
- let(:listener_1) { instance_double 'Listener1', new: nil, start: nil, terminate: nil, kill: nil, reset_then_dup: listener_dup }
12
- let(:listener_2) { instance_double 'Listener2', new: nil, start: nil, terminate: nil, kill: nil, reset_then_dup: listener_dup }
13
- let(:listener_dup) { instance_double 'ListenerDup', start: nil }
14
- let(:manager) { FastlyNsq::Manager.new options }
15
- let(:options) { { joe: 'biden' } }
16
-
17
- let(:configed_topics) do
18
- [
19
- { topic: 'warm_topic', klass: TestProcessor },
20
- { topic: 'cool_topic', klass: TestProcessor },
21
- ]
22
- end
23
-
24
- before do
25
- logger = Logger.new(STDOUT)
26
- logger.level = Logger::FATAL
27
-
28
- FastlyNsq.configure do |config|
29
- config.channel = 'william'
30
- config.logger = logger
31
- config.listener_config do |lc|
32
- configed_topics.each do |t|
33
- lc.add_topic t[:topic], t[:klass]
34
- end
35
- end
36
- end
37
-
38
- allow(FastlyNsq::Listener).to receive(:new).and_return(listener_1, listener_2)
39
- manager.start
40
- end
41
-
42
- after do
43
- FastlyNsq.reset_config
44
- end
45
-
46
- describe '#start' do
47
- it 'sets up each configured listener' do
48
- configed_topics.each do |t|
49
- expect(FastlyNsq::Listener).to have_received(:new).with(
50
- channel: FastlyNsq.channel,
51
- manager: manager,
52
- preprocessor: FastlyNsq.preprocessor,
53
- processor: t[:klass],
54
- topic: t[:topic],
55
- )
56
- end
57
- end
58
-
59
- it 'starts all listeners' do
60
- expect(listener_1).to have_received(:start)
61
- expect(listener_2).to have_received(:start)
62
- end
63
-
64
- it 'populates @listeners with all created listeners' do
65
- expect(manager.listeners).to eq Set.new([listener_1, listener_2])
66
- end
67
- end
68
-
69
- describe '#quiet' do
70
- it 'does nothing if stopping' do
71
- manager.instance_variable_set(:@done, true)
72
- manager.quiet
73
- expect(listener_1).to_not have_received(:terminate)
74
- expect(listener_2).to_not have_received(:terminate)
75
- end
76
-
77
- it 'terminates all listeners' do
78
- manager.quiet
79
- expect(listener_1).to have_received(:terminate)
80
- expect(listener_2).to have_received(:terminate)
81
- end
82
- end
83
-
84
- describe '#stop' do
85
- it 'does nothing if no listeners exist post quiet' do
86
- allow(manager).to receive(:quiet)
87
- allow(manager).to receive(:hard_shutdown)
88
- manager.instance_variable_set(:@listeners, Set.new)
89
-
90
- manager.stop(Time.now)
91
-
92
- expect(manager).to have_received(:quiet)
93
- expect(manager).to_not have_received(:hard_shutdown)
94
- end
95
-
96
- it 'forces shutdown if listeners remain after deadline' do
97
- allow(manager).to receive(:hard_shutdown).and_call_original
98
-
99
- manager.stop(Time.now)
100
-
101
- expect(manager).to have_received(:hard_shutdown)
102
- expect(listener_1).to have_received(:kill)
103
- expect(listener_2).to have_received(:kill)
104
- end
105
- end
106
-
107
- describe '#listener_stopped' do
108
- it 'removes listeners from set of listeners' do
109
- manager.listener_stopped(listener_1)
110
- expect(manager.listeners).to eq Set.new([listener_2])
111
- end
112
- end
113
-
114
- describe '#listener_killed' do
115
- it 'removes listeners from set of listeners' do
116
- manager.quiet # mark for stopping
117
- manager.listener_killed(listener_1)
118
- expect(manager.listeners).to eq Set.new([listener_2])
119
- end
120
-
121
- it 'creates and starts replacements if not stopping' do
122
- manager.listener_killed(listener_1)
123
- expect(manager.listeners).to eq Set.new([listener_dup, listener_2])
124
- expect(listener_dup).to have_received(:start)
125
- end
126
- end
127
- end
@@ -1,60 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe FastlyNsq::Producer do
4
- let(:topic) { 'death_star' }
5
- let(:producer) { FastlyNsq::Producer.new(topic: topic) }
6
-
7
- describe 'when connector connects to a backend Producer' do
8
- let(:backend) { instance_double FastlyNsq::FakeBackend::Producer, write: nil, terminate: nil, connected?: true }
9
- let(:connector) { double 'Connector', new: backend }
10
- let(:producer) do
11
- FastlyNsq::Producer.new topic: topic, connector: connector
12
- end
13
-
14
- it 'forwards #write' do
15
- expect(backend).to receive(:write).with("it's a message")
16
- producer.write "it's a message"
17
- end
18
-
19
- it 'forwards #terminate' do
20
- expect(backend).to receive(:terminate)
21
- producer.terminate
22
- end
23
- end
24
-
25
- describe 'using the default connector' do
26
- module TestStrategy
27
- module Producer
28
- @@never_terminated = true
29
-
30
- module_function
31
-
32
- def new(*_)
33
- self
34
- end
35
-
36
- def connected?
37
- true
38
- end
39
-
40
- def terminate
41
- raise 'Already terminated once' unless @@never_terminated
42
- @@never_terminated = false
43
- end
44
-
45
- def was_terminated
46
- !@@never_terminated
47
- end
48
- end
49
- end
50
-
51
- before do
52
- allow(FastlyNsq).to receive(:strategy).and_return(TestStrategy)
53
- end
54
-
55
- it 'instantiates a producer via Strategy' do
56
- producer.terminate
57
- expect(TestStrategy::Producer.was_terminated).to be_truthy
58
- end
59
- end
60
- end