fastly_nsq 0.13.2 → 1.0.2

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 (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