banter 1.0.5 → 1.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd09b360f3b05bd5eface4120f9d009441365f68
4
- data.tar.gz: 29d94a28bcebce70d175d53e16ca6173b75b05f9
3
+ metadata.gz: 7eddb3c0d8d9801ac79fe7b7ca04c7f892a4cc0a
4
+ data.tar.gz: 03fd47b4fe7849b469ace3f1b94ca3640857cd6d
5
5
  SHA512:
6
- metadata.gz: dc29fc38a2604b657ff4ff8e103cb33c591bfd15e0a58a96105d35f879a404e4a1b09aa26c1f93239b371829476cc62817ace3c1593120d8027f7901a736f7fb
7
- data.tar.gz: 0f94f10f1627c52cbc79be446544311f722eb1dbbee5cac85276453f9e942619dda5e208d45b5c321b0f7a0247a0b69fd8fbd9af971e8bd5b5b49c03b04eacc9
6
+ metadata.gz: fb0cf4082a90fd66772bb400106d77a2898557984b67b25a2363234b2ee5959ea199b80b41397098dbb1488d449d6178268f6af24e48722c8a71716ddfe4c2f9
7
+ data.tar.gz: f70da6ed9736961564bb996489927b4d5835005b11d57ac8c2ace0c11915747a63bcf1e244fb0f49549c91af72d7ec86537a365b00db6c282c00eb00ae75f2f9
@@ -6,7 +6,7 @@ module Banter
6
6
  # being either consumed or discarded.
7
7
  # @default 1 hour
8
8
  mattr_accessor :default_queue_ttl
9
- @@default_queue_ttl = 1.hour
9
+ @@default_queue_ttl = 36000 # 10 hours in seconds
10
10
 
11
11
  # The exchange to publish (and subscribe) on RabbitMQ
12
12
  # @default true
@@ -44,6 +44,9 @@ module Banter
44
44
  mattr_accessor :web_enabled
45
45
  @@web_enabled = false
46
46
 
47
+ mattr_accessor :application_name
48
+ @@application_name = nil
49
+
47
50
  def self.configure_with(environment_name, yaml_file = nil)
48
51
  @@yaml_file = yaml_file.nil? ? "config/banter.yml" : yaml_file
49
52
  @@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
@@ -73,12 +76,5 @@ module Banter
73
76
  end
74
77
  end
75
78
 
76
- def self.application_name
77
- @application_name ||= if defined?(Rails)
78
- Rails.application.class.name.to_s.gsub("::Application", '')
79
- else
80
- 'banter'
81
- end.downcase
82
- end
83
79
  end
84
80
  end
@@ -104,9 +104,11 @@ module Banter
104
104
  end
105
105
 
106
106
  def teardown
107
- # FIX!!! -thl
108
- # How can I check to see if the connection is valid quickly without trying a close on the connection?
109
- @connection.close
107
+ begin
108
+ @connection.close
109
+ rescue => e
110
+ Banter::RabbitLogger.log(Logger::WARN, "RabbitMQ teardown failed: #{e.message}: #{e.backtrace}")
111
+ end
110
112
  @publisher = nil
111
113
  end
112
114
 
@@ -13,6 +13,7 @@ module Banter
13
13
  config.after_initialize do
14
14
  Banter.logger = config.banter.logger if config.banter.logger
15
15
 
16
+ Banter::Configuration.application_name = Rails.application.class.name.to_s.gsub("::Application", '').downcase
16
17
  if Banter::Configuration.web_enabled
17
18
  require 'banter/web'
18
19
  end
@@ -10,17 +10,17 @@ module Banter
10
10
 
11
11
  attr_reader :worker_class
12
12
 
13
- def initialize(worker_class, request_key, queue, durable = true, topic = nil)
13
+ def initialize(worker_class, request_key, queue_name, durable = true, topic = nil)
14
14
  @topic = topic || Banter::Configuration.topic_prefix
15
15
  @request_key = request_key
16
16
  @worker_class = worker_class
17
- @queue_name = queue
18
17
  @durable = durable
19
- @subscriber = ::Banter::Server::RabbitMQSubscriber.new(@request_key, worker_class.queue_ttl, @durable, @topic)
18
+ @queue_name = queue_name
19
+ @subscriber = ::Banter::Server::RabbitMQSubscriber.new(@request_key, @queue_name, worker_class.queue_ttl, @durable, @topic)
20
20
  end
21
21
 
22
22
  def start
23
- @subscriber.start(@queue_name, false) do |delivery_info, properties, envelope|
23
+ @subscriber.start(false) do |delivery_info, properties, envelope|
24
24
  message_received(delivery_info, properties, envelope)
25
25
  true
26
26
  end
@@ -7,7 +7,7 @@ module Banter
7
7
  attr_reader :channel
8
8
  attr_reader :ttl
9
9
 
10
- def initialize(routing_key, ttl, durable = true, topic = Banter::Configuration.topic_prefix)
10
+ def initialize(routing_key, queue_name, ttl, durable = true, topic = Banter::Configuration.topic_prefix)
11
11
  @initial_key = routing_key
12
12
  @durable = durable
13
13
  @topic = topic
@@ -18,12 +18,13 @@ module Banter
18
18
  else
19
19
  @routing_key = "#{@topic}.#"
20
20
  end
21
+
22
+ @queue_name = queue_name
21
23
  end
22
24
 
23
- # name - used to ensure that certain consumers are actually listening to an exchange
24
25
  # pass in a lambda for this method to work. We might only want to expose the content instead of
25
26
  # all 3 chunks.
26
- def start(name, blocking=false)
27
+ def start(blocking=false)
27
28
  @connection = Bunny.new(Configuration.connection)
28
29
  begin
29
30
  @connection.start
@@ -35,23 +36,29 @@ module Banter
35
36
  @channel = @connection.create_channel
36
37
  @exchange = @channel.topic(@topic, :durable => @durable, :auto_delete => false)
37
38
 
38
- # FIX!!! -thl
39
- # Need to ensure that the ids for a server will be reproducible in case a server
40
- # goes down and has to get restarted.
41
- if @initial_key.present?
42
- @queue = "#{@initial_key}.#{name}"
43
- else
44
- @queue = "#{name}"
45
- end
46
-
47
39
  queue_arguments = {}
48
40
  queue_arguments["x-dead-letter-exchange"] = Configuration.dead_letter_queue if Configuration.dead_letter_queue.present?
49
41
  queue_arguments["x-message-ttl"] = ttl * 1000
50
- @listener = @channel.queue(@queue, arguments: queue_arguments).bind(@exchange, routing_key: @routing_key, exclusive: false)
42
+
43
+ # FIX!!! -thl
44
+ # Something is wrong with basic_qos because the server cannot be quickly bounced when this happens, and for messages to be
45
+ # quickly picked up. It seems sporadic. It could be a timing thing but I am unsure.
46
+ # Only grab 1 message at a time, otherwise, we will have to write some extra conditions to reject
47
+ # all the other messages that they already pulled down, which then could make the subscriber
48
+ # run out of memory, etc
49
+ # @channel.basic_qos(1)
50
+
51
+ # Need to mark the queue as durable
52
+ rabbit_queue = @channel.queue(@queue_name, durable: @durable, exclusive: false, arguments: queue_arguments)
53
+ # rabbit_queue = @channel.queue(@queue_name, arguments: queue_arguments)
54
+
55
+ @listener = rabbit_queue.bind(@exchange, routing_key: @routing_key, exclusive: false)
51
56
 
52
57
  # Parameters for subscribe that might be useful:
53
58
  # :block=>true - Used for long running consumer applications. (backend servers?)
54
- @consumer = @listener.subscribe(consumer_tag: name, block: blocking)
59
+
60
+ @consumer = @listener.subscribe(consumer_tag: @queue_name, manual_ack: true, block: blocking)
61
+ # @consumer = @listener.subscribe(consumer_tag: @queue_name, block: blocking)
55
62
 
56
63
  @consumer.on_delivery do |delivery_info, properties, contents|
57
64
  Banter::RabbitLogger.log(Logger::DEBUG, "Message delivery with contents: #{contents}")
@@ -63,13 +70,20 @@ module Banter
63
70
  Banter::RabbitLogger.log_receive(delivery_info[:routing_key], message)
64
71
  yield delivery_info, properties, message
65
72
  Banter::RabbitLogger.log_complete(delivery_info[:routing_key], message)
73
+ Banter::RabbitLogger.log(Logger::DEBUG, "Message acknowledged with tag #{delivery_info.delivery_tag}")
74
+ # Need to acknowledge the message for the next message to come down.
75
+ @channel.ack(delivery_info.delivery_tag)
66
76
  true
67
77
  end
68
78
  end
69
79
 
70
80
  def teardown
71
- @consumer.cancel if @consumer.present?
72
- @connection.close if @connection.present?
81
+ begin
82
+ @consumer.cancel if @consumer.present?
83
+ @connection.close if @connection.present?
84
+ rescue => e
85
+ Airbrake.notify(e, params: { error: e.message }, environment_name: ENV['RAILS_ENV'], backtrace: caller)
86
+ end
73
87
  end
74
88
  end
75
89
  end
@@ -88,7 +88,6 @@ module Banter
88
88
  Banter::CLI.instance.remove_pid
89
89
  end
90
90
 
91
-
92
91
  def set_information
93
92
  @process_name = $0
94
93
  @pid = Process.pid
@@ -96,15 +95,13 @@ module Banter
96
95
  end
97
96
 
98
97
  def create_queue_listeners(subscriber)
99
- routing_key = subscriber.subscribed_key
100
- subscribed_queue_name = subscriber.subscribed_queue
101
-
102
- if subscribed_queue_name.blank?
103
- raise ArgumentError.new("Queue Name must be provided in #{subscriber.name} using `subscribe_to routing_key, on: queue_name`")
104
- end
98
+ application_name = Banter::Configuration.application_name
99
+ routing_key = subscriber.subscribed_key
100
+ durable = subscriber.durable
101
+ queue_name = subscriber.generated_queue_name(routing_key, application_name)
105
102
 
106
- STDOUT.puts "Setting up listener for request_key: #{routing_key} and queue:#{subscribed_queue_name}"
107
- ClientQueueListener.new(subscriber, routing_key, subscribed_queue_name)
103
+ STDOUT.puts "Setting up listener for request_key: #{routing_key} and queue:#{application_name}"
104
+ ClientQueueListener.new(subscriber, routing_key, queue_name, durable)
108
105
  end
109
106
  end
110
107
  end
@@ -8,7 +8,7 @@ module Banter
8
8
  class Subscriber
9
9
  @@registered_subscribers = []
10
10
 
11
- class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue, :queue_ttl
11
+ class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue, :queue_ttl, :application_name, :durable
12
12
  attr_accessor :delivery_routing_data, :delivery_properties, :context
13
13
 
14
14
  def self.inherited(klass)
@@ -18,18 +18,20 @@ module Banter
18
18
  # Specify the routing key that the subscriber class should listen to.
19
19
  # @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
20
20
  # @param [Hash] options subscription options
21
- # @option [String] :on Optionally specify a queue. If not provided, queue name is generated from the routing key
21
+ # @option [String] :durable sets the subscriber to be a durable subscriber (one that survives reboots). This currently defaults to true.
22
22
  # @option [Integer] :queue_ttl Time, in seconds, that the message lives on the queue before being either consumer by a subscriber or being discarded.
23
23
  # If not specified, then Banter::Configuration.default_queue_ttl is used
24
24
 
25
25
  def self.subscribe_to(routing_key_name, options = {})
26
- options.assert_valid_keys(:on, :queue_ttl)
26
+ options.assert_valid_keys(:durable, :queue_ttl)
27
27
  unless validate_routing_key_name(routing_key_name)
28
28
  raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
29
29
  end
30
30
  self.subscribed_key = routing_key_name
31
- self.subscribed_queue = generated_queue_name(routing_key_name, options[:on])
31
+ self.application_name = generated_application_name(options[:on])
32
+ self.subscribed_queue = generated_queue_name(routing_key_name, self.application_name)
32
33
  self.queue_ttl = options[:queue_ttl] || Banter::Configuration.default_queue_ttl
34
+ self.durable = options.key?(:durable) ? options[:durable] : true
33
35
  end
34
36
 
35
37
  # Sets the validator for payload
@@ -113,9 +115,23 @@ module Banter
113
115
  key.match(/\A([a-z_]+\.?)*([a-z\_]+)\Z/).present?
114
116
  end
115
117
 
116
- def self.generated_queue_name(routing_key, queue_name)
117
- return queue_name if queue_name.present?
118
- [Banter::Configuration.application_name.to_s.gsub(/[^\w\_]/, ''), routing_key.gsub(".", '_')].reject(&:blank?).join('_')
118
+ def self.generated_application_name(application_name)
119
+ return application_name if application_name.present?
120
+
121
+ Banter::Configuration.application_name.to_s.gsub(/[^\w\_ ]/, '')
122
+ end
123
+
124
+ def self.generated_queue_name(routing_key, application_name)
125
+ name = ""
126
+ if routing_key.present?
127
+ name = routing_key
128
+ if application_name
129
+ name += "_#{application_name}"
130
+ end
131
+ else
132
+ name = application_name
133
+ end
134
+ name
119
135
  end
120
136
 
121
137
  end
@@ -1,3 +1,3 @@
1
1
  module Banter
2
- VERSION = "1.0.5"
2
+ VERSION = "1.1.1"
3
3
  end
@@ -49,7 +49,7 @@ describe Banter::Publisher do
49
49
 
50
50
  specify {expect { result }.not_to raise_error }
51
51
  end
52
-
52
+
53
53
  context "duplicate entries" do
54
54
  let(:result) { publisher.delay_messages { operations } }
55
55
 
@@ -88,4 +88,23 @@ describe Banter::Publisher do
88
88
  end
89
89
 
90
90
  end
91
+
92
+ describe "#teardown" do
93
+ context "should not fail" do
94
+ before do
95
+ allow(Banter::RabbitLogger).to receive(:log).with(Logger::WARN, /RabbitMQ teardown failed/)
96
+ end
97
+
98
+ it do
99
+ expect{publisher.send(:teardown)}.not_to raise_error
100
+ end
101
+
102
+ it do
103
+ publisher.send(:teardown)
104
+ expect(Banter::RabbitLogger).to have_received(:log).exactly(1).times
105
+ end
106
+ end
107
+
108
+ end
109
+
91
110
  end
@@ -15,6 +15,7 @@ describe Banter::Server::ClientQueueListener do
15
15
  Banter::Configuration.web_enabled = true
16
16
  allow(BanterWorker).to receive(:current).and_return(banter_worker)
17
17
  }
18
+ let!(:application_name) { "testapplication" }
18
19
  let(:banter_message) { BanterMessage.last }
19
20
 
20
21
  after(:each) { Banter::Configuration.web_enabled = false }
@@ -22,7 +23,7 @@ describe Banter::Server::ClientQueueListener do
22
23
  def self.banter_message_fields_are_correct(status, error_message = nil)
23
24
  it { expect(BanterMessage.count).to eq 1 }
24
25
  it { expect(banter_message.subscriber_class).to eq('Klass') }
25
- it { expect(banter_message.queue_name).to eq('banter_test_foo') }
26
+ it { expect(banter_message.queue_name).to eq("test.foo_#{application_name}") }
26
27
  it { expect(banter_message.subscribed_key).to eq('test.foo') }
27
28
  it { expect(banter_message.payload).to eq({ "hello" => "moto" }) }
28
29
  it { expect(banter_message.started_at.to_s).to eq(Time.now.to_s) }
@@ -39,6 +40,7 @@ describe Banter::Server::ClientQueueListener do
39
40
  end
40
41
 
41
42
  before do
43
+ Banter::Configuration.application_name = application_name
42
44
  context_before
43
45
  Object.const_set :Klass, Class.new(Banter::Subscriber)
44
46
  validators.each do |validator|
@@ -1,5 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Banter::Server::RabbitMQSubscriber do
4
- subject { Banter::Server::RabbitMQSubscriber.new( routing_key ) }
4
+ let(:durable) { true }
5
+ let(:routing_key) { 'test.me' }
6
+ let(:application_name) { 'queue_spec'}
7
+ let(:queue_name) { "test.me_#{application_name}" }
8
+ let(:ttl) { 100000 }
9
+ subject { Banter::Server::RabbitMQSubscriber.new( routing_key, queue_name, ttl, durable) }
10
+
11
+ before do
12
+ Banter::Configuration.application_name = application_name
13
+ end
14
+
15
+ describe "#start" do
16
+ before do
17
+ end
18
+
19
+ context "mark queue as durable" do
20
+
21
+ end
22
+
23
+ end
24
+
5
25
  end
@@ -5,15 +5,18 @@ require 'spec_helper'
5
5
 
6
6
  describe Banter::Server::SubscriberServer do
7
7
  subject { Banter::Server::SubscriberServer.instance }
8
+ let!(:application_name) { "testsubscriber" }
9
+ let!(:durable) { true }
8
10
  describe '#set_workers' do
9
11
  before {
12
+ Banter::Configuration.application_name = application_name
10
13
  allow(Banter::Server::ClientQueueListener).to receive(:new)
11
14
  subject.set_workers([MyTestSubscriber1, MyTestSubscriber2])
12
15
  }
13
16
 
14
17
  it "creates queue listeners" do
15
- expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1, "foo.bar.one", 'banter_foo_bar_one')
16
- expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2, "foo.bar.two", 'banter_foo_bar_two')
18
+ expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1, "foo.bar.one", "foo.bar.one_#{application_name}", durable)
19
+ expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2, "foo.bar.two", "foo.bar.two_#{application_name}", durable)
17
20
  end
18
21
  end
19
22
 
@@ -3,6 +3,11 @@ require 'spec_helper'
3
3
  describe Banter::Subscriber do
4
4
  before(:each) { Object.const_set :Klass, Class.new(Banter::Subscriber) }
5
5
  after(:each) { Object.send :remove_const, :Klass }
6
+ let!(:application_name) { "app_name"}
7
+
8
+ before do
9
+ Banter::Configuration.application_name = application_name
10
+ end
6
11
 
7
12
  describe "#routing_key" do
8
13
  subject { Banter::Subscriber.new(routing_data, properties, {}).routing_key }
@@ -205,44 +210,42 @@ describe Banter::Subscriber do
205
210
  end
206
211
 
207
212
  describe "#generated_queue_name" do
208
- before(:each) { allow(Banter::Configuration).to receive(:application_name).and_return("app_name")}
209
213
 
210
- it { expect(Klass.send(:generated_queue_name, 'abcdef', 'queue_name')).to eq('queue_name') }
211
- it { expect(Klass.send(:generated_queue_name, 'abcdef', nil)).to eq('app_name_abcdef') }
212
- it { expect(Klass.send(:generated_queue_name, 'abcdef.abcd', nil)).to eq('app_name_abcdef_abcd') }
213
- it { expect(Klass.send(:generated_queue_name, 'a.b.c', nil)).to eq('app_name_a_b_c') }
214
+ it { expect(Klass.send(:generated_queue_name, 'abcdef', 'queue_name')).to eq('abcdef_queue_name') }
215
+ it { expect(Klass.send(:generated_queue_name, 'abcdef', nil)).to eq('abcdef') }
216
+ it { expect(Klass.send(:generated_queue_name, 'abcdef.abcd', nil)).to eq('abcdef.abcd') }
217
+ it { expect(Klass.send(:generated_queue_name, 'a.b.c', nil)).to eq('a.b.c') }
214
218
  end
215
219
 
216
220
  describe '.subscribe_to' do
217
- before(:each) { allow(Banter::Configuration).to receive(:application_name).and_return("app_name")}
218
221
  context 'routing_key without queue_name' do
219
222
  before { Klass.subscribe_to 'a.b.c' }
220
223
  it 'sets the routing key and queue name' do
221
224
  expect(Klass.subscribed_key).to eq('a.b.c')
222
- expect(Klass.subscribed_queue).to eq('app_name_a_b_c')
225
+ expect(Klass.subscribed_queue).to eq('a.b.c_app_name')
223
226
  end
224
227
  end
225
228
 
226
229
  context 'routing_key with queue_name' do
227
- before { Klass.subscribe_to 'a.b.c', on: 'foo_bar' }
230
+ before { Klass.subscribe_to 'a.b.c' }
228
231
  it 'sets the routing key and queue name' do
229
232
  expect(Klass.subscribed_key).to eq('a.b.c')
230
- expect(Klass.subscribed_queue).to eq('foo_bar')
233
+ expect(Klass.subscribed_queue).to eq('a.b.c_app_name')
231
234
  end
232
235
  end
233
236
 
234
237
  context 'underscore in routing key' do
235
- before { Klass.subscribe_to 'a_b.c', on: 'foo_bar' }
238
+ before { Klass.subscribe_to 'a_b.c' }
236
239
  it 'is allowed' do
237
240
  expect(Klass.subscribed_key).to eq('a_b.c')
238
- expect(Klass.subscribed_queue).to eq('foo_bar')
241
+ expect(Klass.subscribed_queue).to eq('a_b.c_app_name')
239
242
  end
240
243
  end
241
244
 
242
245
  context 'invalid routing key' do
243
246
  it 'sets the routing key and queue name' do
244
247
  expect {
245
- Klass.subscribe_to 'a.b.c.', on: 'foo_bar'
248
+ Klass.subscribe_to 'a.b.c.'
246
249
  }.to raise_error(ArgumentError)
247
250
  end
248
251
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: banter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Honest Company
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-08-28 00:00:00.000000000 Z
14
+ date: 2014-09-18 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -288,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
288
  version: '0'
289
289
  requirements: []
290
290
  rubyforge_project:
291
- rubygems_version: 2.3.0
291
+ rubygems_version: 2.2.2
292
292
  signing_key:
293
293
  specification_version: 4
294
294
  summary: Library for pub-sub (Message Bus)