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,5 +0,0 @@
1
- NSQD_TCP_ADDRESS: '127.0.0.1:4150'
2
- NSQD_HTTP_ADDRESS: '127.0.0.1:4151'
3
- NSQLOOKUPD_TCP_ADDRESS: '127.0.0.1:4160'
4
- NSQLOOKUPD_HTTP_ADDRESS: '127.0.0.1:4161'
5
- FAKE_QUEUE: 'true'
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MessageProcessor
4
- def self.process(message)
5
- FastlyNsq.logger.info("IN PROCESS: #{message}")
6
-
7
- # Do Soemthing with message
8
- end
9
- end
10
-
11
- FastlyNsq.configure do |config|
12
- config.channel = 'my_channel'
13
- config.logger = Logger.new
14
- config.preprocessor = ->(_) { FastlyNsq.logger.info 'PREPROCESSESES' }
15
-
16
- config.listener_config do |lc|
17
- lc.add_topic('some_topic', MessageProcessor)
18
- lc.add_topic('some_other_topic', MessageProcessor)
19
- end
20
- end
data/examples/Rakefile DELETED
@@ -1,41 +0,0 @@
1
- require 'fastly_nsq/rake_task'
2
-
3
- # A list of topics and a channel will need to be passed in to the Rake task
4
- # when it is called.
5
-
6
- desc 'Listen to the messaging queue with the given channel.'
7
-
8
- FastlyNsq::RakeTask.new(:listen_task) do |task|
9
- task.channel = 'some_channel'
10
- task.topics = { 'a_topic' => RespondsToProcess }
11
- end
12
-
13
- # A custom logger and preprocessor
14
-
15
- desc 'Listen to the messaging queue with the given channel.'
16
-
17
- FastlyNsq::RakeTask.new(:listen_task) do |task|
18
- task.channel = 'some_channel'
19
- task.topics = { 'a_topic' => RespondsToProcess }
20
- task.preprocessor = ->(message) { StatsClient.log(message) }
21
- task.logger = Logger.new(STDERR)
22
- end
23
-
24
- #-------------------------------------------------------------------------------
25
- # In Rails, you can include the application environment. The task looks like:
26
-
27
- FastlyNsq::RakeTask.new(listen_task: :environment)
28
-
29
- #-------------------------------------------------------------------------------
30
- # It's also possible to define the rake task to allow for overriding channel
31
- # and topics when calling the rake task:
32
-
33
- FastlyNsq::RakeTask.new(:listen_task, %i[channel topics])
34
-
35
- # Then call the task:
36
- # rake listen_task[my_channel, {topic: Processor}]
37
-
38
- #-------------------------------------------------------------------------------
39
- # Do the same thing and include the environment:
40
-
41
- FastlyNsq::RakeTask.new(:listen_task, %i[channel topics] => :environment)
@@ -1,114 +0,0 @@
1
- module FastlyNsq
2
- module FakeBackend
3
- @@logger = Logger.new(nil)
4
- @@delay = 0.5
5
- @@queue = []
6
-
7
- def self.queue
8
- @@queue
9
- end
10
-
11
- def self.queue=(message)
12
- @@queue = message
13
- end
14
-
15
- def self.reset!
16
- self.queue = []
17
- end
18
-
19
- def self.logger=(logger)
20
- @@logger = logger
21
- end
22
-
23
- def self.logger
24
- @@logger
25
- end
26
-
27
- def self.delay
28
- @@delay
29
- end
30
-
31
- def self.delay=(delay)
32
- @@delay = delay
33
- end
34
-
35
- class Producer
36
- attr_reader :topic
37
-
38
- def initialize(topic:, **)
39
- @topic = topic
40
- end
41
-
42
- def connected?
43
- true
44
- end
45
-
46
- def write(string)
47
- message = Message.new(string, topic: topic)
48
- queue.push(message)
49
- end
50
-
51
- def terminate
52
- # noop
53
- end
54
-
55
- private
56
-
57
- def queue
58
- FakeBackend.queue
59
- end
60
- end
61
-
62
- class Consumer
63
- def initialize(nsqlookupd: nil, topic:, channel:, tls_v1: nil, tls_options: nil); end
64
-
65
- def connected?
66
- true
67
- end
68
-
69
- def pop(delay = FakeBackend.delay)
70
- message = nil
71
-
72
- until message
73
- message = queue.pop
74
- sleep delay
75
- end
76
-
77
- message
78
- end
79
-
80
- def pop_without_blocking
81
- queue.pop
82
- end
83
-
84
- def size
85
- queue.size
86
- end
87
-
88
- def empty?
89
- queue.empty?
90
- end
91
-
92
- def terminate
93
- # noop
94
- end
95
-
96
- private
97
-
98
- def queue
99
- FakeBackend.queue
100
- end
101
- end
102
-
103
- class Message
104
- attr_reader :topic, :body
105
-
106
- def initialize(body, topic: nil)
107
- @topic = topic
108
- @body = body
109
- end
110
-
111
- def finish; end
112
- end
113
- end
114
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FastlyNsq
4
- class Listener
5
- class Config
6
- attr_reader :topic_map
7
-
8
- def initialize
9
- @topic_map = {}
10
- end
11
-
12
- def add_topic(topic_name, processor)
13
- FastlyNsq.logger.info("topic: #{topic_name} : klass #{processor}")
14
- validate topic_name, processor
15
- topic_map[topic_name] = processor
16
- end
17
-
18
- private
19
-
20
- def validate(topic_name, processor)
21
- unless processor.respond_to? :process
22
- error_msg = "ConfigurationError: processor: #{processor} for #{topic_name} does not respond to :process!"
23
- FastlyNsq.logger.error error_msg
24
- raise ::ConfigurationError, error_msg
25
- end
26
-
27
- if topic_map[topic_name]
28
- FastlyNsq.logger.warn("topic: #{topic_name} was added more than once")
29
- end
30
- end
31
- end
32
- end
33
- end
34
-
35
- class ConfigurationError < StandardError; end
@@ -1,78 +0,0 @@
1
- require 'rake'
2
- require 'rake/tasklib'
3
-
4
- module FastlyNsq
5
- class RakeTask < Rake::TaskLib
6
- attr_accessor :name, :channel, :topics, :preprocessor
7
- attr_writer :listener, :logger
8
-
9
- def initialize(*args, &task_block)
10
- @name = args.shift || :begin_listening
11
- add_rake_task_description_if_one_needed
12
-
13
- task(name, *args) do |_, task_args|
14
- RakeFileUtils.send(:verbose, verbose) do
15
- if block_given?
16
- yield(*[self, task_args].slice(0, task_block.arity))
17
- end
18
-
19
- initialize_values task_args
20
- listen_to_configured_topics
21
- end
22
- end
23
- end
24
-
25
- private
26
-
27
- def listen_to_configured_topics
28
- topic_per_thread do |topic, processor|
29
- logger.info "Listening to queue, topic:'#{topic}' and channel: '#{channel}'"
30
- listener.listen_to topic: topic,
31
- channel: channel,
32
- logger: logger,
33
- processor: processor,
34
- preprocessor: preprocessor
35
- logger.info "... done listening on topic:'#{topic}' and channel: '#{channel}'."
36
- end
37
- end
38
-
39
- def require_arg(arg, arg_list)
40
- arg_list.fetch(arg) { raise ArgumentError, "required configuration '#{arg}' is missing." }
41
- end
42
-
43
- def add_rake_task_description_if_one_needed
44
- unless ::Rake.application.last_description
45
- desc 'Listen to NSQ on topic using channel'
46
- end
47
- end
48
-
49
- def topic_per_thread
50
- listener_threads = []
51
- topics.each do |(topic, processor)|
52
- thread = Thread.new do
53
- yield topic, processor
54
- end
55
- thread.abort_on_exception = true
56
- listener_threads << thread
57
- end
58
-
59
- listener_threads.map(&:join)
60
- end
61
-
62
- def listener
63
- @listener || FastlyNsq::Listener
64
- end
65
-
66
- def logger
67
- @logger || FastlyNsq.logger || Logger.new(STDOUT)
68
- end
69
-
70
- def initialize_values(task_args)
71
- @channel ||= require_arg :channel, task_args
72
- @topics ||= require_arg :topics, task_args
73
- @listener ||= task_args[:listener]
74
- @logger ||= task_args[:logger]
75
- @preprocessor ||= task_args[:preprocessor]
76
- end
77
- end
78
- end
@@ -1,36 +0,0 @@
1
- module FastlyNsq
2
- module Strategy
3
- class InvalidParameterError < StandardError; end
4
-
5
- module_function
6
-
7
- def for_queue
8
- real_queue || fake_queue || error
9
- end
10
-
11
- ERR_MESSAGE = "You must set ENV['FAKE_QUEUE'] to either true or false".freeze
12
-
13
- def error
14
- raise InvalidParameterError, ERR_MESSAGE
15
- end
16
-
17
- FALSY_VALUES = [false, 0, '0', 'false', 'FALSE', 'off', 'OFF', nil].freeze
18
- TRUTHY_VALUES = [true, 1, '1', 'true', 'TRUE', 'on', 'ON'].freeze
19
-
20
- def fake_queue
21
- FastlyNsq::FakeBackend if should_use_fake_queue?
22
- end
23
-
24
- def should_use_real_queue?
25
- FALSY_VALUES.include? ENV['FAKE_QUEUE']
26
- end
27
-
28
- def real_queue
29
- Nsq if should_use_real_queue?
30
- end
31
-
32
- def should_use_fake_queue?
33
- TRUTHY_VALUES.include? ENV['FAKE_QUEUE']
34
- end
35
- end
36
- end
@@ -1,72 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe FastlyNsq::Consumer do
4
- let(:channel) { 'star_killer_base' }
5
- let(:topic) { 'death_star' }
6
- let(:consumer) { FastlyNsq::Consumer.new topic: topic, channel: channel }
7
-
8
- describe 'when connected to a backend Consumer' do
9
- let(:backend) { instance_double FastlyNsq::FakeBackend::Consumer, pop: nil, pop_without_blocking: nil, size: nil, terminate: nil, connected?: true }
10
- let(:connector) { double 'Connector strategy', new: backend }
11
-
12
- let(:consumer) do
13
- FastlyNsq::Consumer.new topic: topic, channel: channel, connector: connector
14
- end
15
-
16
- it 'forwards #pop' do
17
- expect(backend).to receive(:pop)
18
- consumer.pop
19
- end
20
-
21
- it 'forwards #pop_without_blocking' do
22
- expect(backend).to receive(:pop_without_blocking)
23
- consumer.pop_without_blocking
24
- end
25
-
26
- it 'forwards #size' do
27
- expect(backend).to receive(:size)
28
- consumer.size
29
- end
30
-
31
- it 'forwards #terminate' do
32
- expect(backend).to receive(:terminate)
33
- consumer.terminate
34
- end
35
- end
36
-
37
- describe 'using strategy to determine the consumer' do
38
- module TestStrategy
39
- module Consumer
40
- @@never_terminated = true
41
-
42
- module_function
43
-
44
- def new(*_)
45
- self
46
- end
47
-
48
- def connected?
49
- true
50
- end
51
-
52
- def terminate
53
- raise 'Already terminated once' unless @@never_terminated
54
- @@never_terminated = false
55
- end
56
-
57
- def was_terminated
58
- !@@never_terminated
59
- end
60
- end
61
- end
62
-
63
- before do
64
- allow(FastlyNsq).to receive(:strategy).and_return(TestStrategy)
65
- end
66
-
67
- it 'instantiates a consumer via Strategy' do
68
- consumer.terminate
69
- expect(TestStrategy::Consumer.was_terminated).to be_truthy
70
- end
71
- end
72
- end
@@ -1,135 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe FastlyNsq::FakeBackend do
4
- describe '@@queue' do
5
- it 'is initalized as an empty array' do
6
- expect(FastlyNsq::FakeBackend.queue).to eq []
7
- end
8
- end
9
-
10
- describe '@@logger' do
11
- after do
12
- FastlyNsq::FakeBackend.logger = Logger.new(nil)
13
- end
14
-
15
- it 'is initalized as an empty Ruby Logger' do
16
- expect(FastlyNsq::FakeBackend.logger).to be_a Logger
17
- end
18
-
19
- it 'can be set and retrieved' do
20
- logger = double('some logger')
21
- FastlyNsq::FakeBackend.logger = logger
22
-
23
- expect(FastlyNsq::FakeBackend.logger).to eq logger
24
- end
25
- end
26
-
27
- describe '.reset!' do
28
- it 'resets the fake message queue' do
29
- FastlyNsq::FakeBackend.queue = ['hello']
30
- expect(FastlyNsq::FakeBackend.queue.size).to eq 1
31
-
32
- FastlyNsq::FakeBackend.reset!
33
-
34
- expect(FastlyNsq::FakeBackend.queue).to be_empty
35
- end
36
- end
37
- end
38
-
39
- RSpec.describe FastlyNsq::FakeBackend::Producer do
40
- let(:topic) { 'death_star' }
41
- let(:producer) { FastlyNsq::FakeBackend::Producer.new topic: topic }
42
-
43
- it 'adds a new message to the queue' do
44
- body = 'hello'
45
-
46
- expect do
47
- producer.write(body)
48
- end.to change { FastlyNsq::FakeBackend.queue.size }.by(1)
49
-
50
- message = FastlyNsq::FakeBackend.queue.shift
51
-
52
- expect(message.topic).to eq(topic)
53
- expect(message.body).to eq(body)
54
- end
55
-
56
- it 'has a `terminate` method which is a noop' do
57
- expect(producer).to respond_to(:terminate)
58
- end
59
- end
60
-
61
- RSpec.describe FastlyNsq::FakeBackend::Message do
62
- describe '#body' do
63
- it 'returns the body of the message' do
64
- topic = 'death_star'
65
- content = 'hello'
66
- producer = FastlyNsq::FakeBackend::Producer.new(
67
- nsqlookupd: ENV.fetch('NSQLOOKUPD_HTTP_ADDRESS'),
68
- topic: topic,
69
- )
70
- producer.write(content)
71
-
72
- message = FastlyNsq::FakeBackend.queue.pop
73
- body = message.body
74
-
75
- expect(content).to eq body
76
- end
77
- end
78
- end
79
-
80
- RSpec.describe FastlyNsq::FakeBackend::Consumer do
81
- let(:topic) { 'death_star' }
82
- let(:channel) { 'star_killer_base' }
83
- let(:consumer) { FastlyNsq::FakeBackend::Consumer.new topic: topic, channel: channel }
84
-
85
- describe 'when there are no messages on the queue' do
86
- it 'tells you there are 0 messages in the queue' do
87
- expect(consumer.size).to eq 0
88
- end
89
-
90
- it 'blocks forever (until timeout) from #pop' do
91
- FastlyNsq::FakeBackend.delay = 0.1
92
- delay = FastlyNsq::FakeBackend.delay + 0.1
93
-
94
- expect do
95
- Timeout.timeout(delay) do
96
- consumer.pop
97
- end
98
- end.to raise_error(Timeout::Error)
99
- end
100
-
101
- it 'returns nil from #pop_without_blocking' do
102
- popped_message = consumer.pop_without_blocking
103
-
104
- expect(popped_message).to be_nil
105
- end
106
- end
107
-
108
- describe 'when there is a message on the queue' do
109
- let(:message) { FastlyNsq::FakeBackend::Message.new 'hello' }
110
- before { FastlyNsq::FakeBackend.queue = [message] }
111
-
112
- it 'tells you there are messages in the queue' do
113
- expect(consumer.size).to eq 1
114
- end
115
-
116
- it 'returns a message immediately from #pop' do
117
- popped_message = consumer.pop
118
-
119
- expect(popped_message).to eq message
120
- end
121
-
122
- it 'returns a message immediately from #pop_without_blocking' do
123
- popped_message = consumer.pop_without_blocking
124
-
125
- expect(popped_message).to eq message
126
- end
127
- end
128
-
129
- describe '#terminate' do
130
- it 'has a terminate method which is a noop' do
131
- consumer = instance_double('FastlyNsq::FakeBackend::Consumer')
132
- allow(consumer).to receive(:terminate)
133
- end
134
- end
135
- end