banter 1.0.5 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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)