msgr 0.15.2.1.b175 → 0.15.2.1.b178

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
  SHA256:
3
- metadata.gz: 42747ff1144b8ca1909b6e590abeeba4c01d6987cc30e9a0f4e0d4af35e3eb68
4
- data.tar.gz: 485f6a9381d87a4806da5bd49b35577a7ca88adcdd2f814721d28bbe3bf6ba41
3
+ metadata.gz: 1c2aba57e013a4e5972ec47493539cb0b8a0ccfafd94279fc8040000aab0a8f5
4
+ data.tar.gz: c6d5c25f205476087f183d1b59abcad36d1bf1877ebf0f1015e870d0ccb302c7
5
5
  SHA512:
6
- metadata.gz: 0811f5754236dabc42b98f7b7af105589058515a6cddc93b764b86bf9a7c8d93ab1fbc926cd5c3aec39846cf3e1a1725ce658beba942f357b5fe360db1d94968
7
- data.tar.gz: 2f3e9772b9ad8d24fab608cfa5ac59193a594dd5b29a9274af8f9ea06ec52e5a9e02daee910c6e2d28f4554e22e82b2d40b9e5b641a8ee8e55dac30a57ddfbd4
6
+ metadata.gz: 1fd16f2b6f7414ef21dab90dad31170c0de7dc833f2a6f1737d47ff45476505aa179a4d184c27bb92446570225aa8b8a478d99690992c980199e43a07be8168e
7
+ data.tar.gz: fcc9d1e8251987c80958538c468c7c20fb9a526b0f4b777a08bc26f8cfacf1a4f58c22b76ad06230a79c657a7a1e594f5453b5080b2c04da44576ab621188129
data/README.md CHANGED
@@ -83,7 +83,34 @@ class TestController < ApplicationController
83
83
  end
84
84
  ```
85
85
 
86
- ## Msgr and fork web server like unicorn
86
+ ## Advanced configuration
87
+
88
+ ### Manual message acknowledgement
89
+
90
+ Per default messages are automatically acknowledged, if no (n)ack is sent explicitly by the consumer. This can be disabled by setting the `auto_ack` attribute to `false`.
91
+
92
+ ```ruby
93
+ class TestConsumer < Msgr::Consumer
94
+ self.auto_ack = false
95
+
96
+ def index
97
+ data = { fuubar: 'abc' }
98
+
99
+ publish data, to: 'local.test.another_action'
100
+ end
101
+ end
102
+ ```
103
+
104
+
105
+ ### Prefetch count
106
+
107
+ Per default each message queue has a prefetch count of 1. This value can be changed when specifying the messaging routes:
108
+
109
+ ```ruby
110
+ route 'local.test.index', to: 'test#index', prefetch: 42
111
+ ```
112
+
113
+ ### Msgr and fork web server like unicorn
87
114
 
88
115
  Per default msgr opens the rabbitmq connect when rails is loaded. If you use a multi-process web server that preloads the application (like unicorn) will lead to unexpected behavior. In this case adjust `config/rabbitmq.yml` and adjust `autostart = false`:
89
116
 
data/lib/msgr/binding.rb CHANGED
@@ -4,18 +4,19 @@ module Msgr
4
4
  class Binding
5
5
  include Logging
6
6
 
7
- attr_reader :queue, :subscription, :connection, :route, :dispatcher
7
+ attr_reader :queue, :subscription, :connection, :channel, :route, :dispatcher
8
8
 
9
9
  def initialize(connection, route, dispatcher)
10
10
  @connection = connection
11
+ @channel = connection.channel(prefetch: route.prefetch)
11
12
  @route = route
12
13
  @dispatcher = dispatcher
13
- @queue = connection.queue(route.name)
14
+ @queue = @channel.queue(route.name)
14
15
 
15
16
  route.keys.each do |key|
16
17
  log(:debug) { "Bind #{key} to #{queue.name}." }
17
18
 
18
- queue.bind connection.exchange, routing_key: key
19
+ queue.bind @channel.exchange, routing_key: key
19
20
  end
20
21
 
21
22
  subscribe
@@ -43,7 +44,7 @@ module Msgr
43
44
  def subscribe
44
45
  @subscription = queue.subscribe(manual_ack: true) do |*args|
45
46
  begin
46
- dispatcher.call Message.new(connection, *args, route)
47
+ dispatcher.call Message.new(channel, *args, route)
47
48
  rescue => err
48
49
  log(:error) do
49
50
  "Rescued error from subscribe: #{err.class.name}: " \
@@ -0,0 +1,65 @@
1
+ module Msgr
2
+ class Channel
3
+ include Logging
4
+
5
+ EXCHANGE_NAME = 'msgr'
6
+
7
+ attr_reader :config, :channel
8
+
9
+ def initialize(config, connection)
10
+ @config = config
11
+ @channel = connection.create_channel
12
+ end
13
+
14
+ def prefetch(count)
15
+ @channel.prefetch count
16
+ end
17
+
18
+ def exchange
19
+ @exchange ||= begin
20
+ @channel.topic(prefix(EXCHANGE_NAME), durable: true).tap do |ex|
21
+ log(:debug) do
22
+ "Created exchange #{ex.name} (type: #{ex.type}, " \
23
+ "durable: #{ex.durable?}, auto_delete: #{ex.auto_delete?})"
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def queue(name)
30
+ @channel.queue(prefix(name), durable: true).tap do |queue|
31
+ log(:debug) do
32
+ "Create queue #{queue.name} (durable: #{queue.durable?}, " \
33
+ "auto_delete: #{queue.auto_delete?})"
34
+ end
35
+ end
36
+ end
37
+
38
+ def prefix(name)
39
+ if config[:prefix].present?
40
+ "#{config[:prefix]}-#{name}"
41
+ else
42
+ name
43
+ end
44
+ end
45
+
46
+ def ack(delivery_tag)
47
+ @channel.ack delivery_tag
48
+ log(:debug) { "Acked message: #{delivery_tag}" }
49
+ end
50
+
51
+ def nack(delivery_tag)
52
+ @channel.nack delivery_tag, false, true
53
+ log(:debug) { "Nacked message: #{delivery_tag}" }
54
+ end
55
+
56
+ def reject(delivery_tag, requeue = true)
57
+ @channel.reject delivery_tag, requeue
58
+ log(:debug) { "Rejected message: #{delivery_tag}" }
59
+ end
60
+
61
+ def close
62
+ @channel.close if @channel.open?
63
+ end
64
+ end
65
+ end
@@ -8,14 +8,13 @@ module Msgr
8
8
  class Connection
9
9
  include Logging
10
10
 
11
- EXCHANGE_NAME = 'msgr'
12
-
13
11
  attr_reader :uri, :config
14
12
 
15
13
  def initialize(uri, config, dispatcher)
16
14
  @uri = uri
17
15
  @config = config
18
16
  @dispatcher = dispatcher
17
+ @channels = []
19
18
  end
20
19
 
21
20
  def running?
@@ -38,12 +37,15 @@ module Msgr
38
37
  connection
39
38
  end
40
39
 
41
- def channel
42
- @channel ||= begin
43
- channel = connection.create_channel
44
- channel.prefetch 1
45
- channel
46
- end
40
+ def channel(prefetch: 1)
41
+ channel = Msgr::Channel.new(config, connection)
42
+ channel.prefetch(prefetch)
43
+ @channels << channel
44
+ channel
45
+ end
46
+
47
+ def exchange
48
+ @exchange ||= channel.exchange
47
49
  end
48
50
 
49
51
  def release
@@ -71,34 +73,6 @@ module Msgr
71
73
  @bindings ||= []
72
74
  end
73
75
 
74
- def prefix(name)
75
- if config[:prefix].present?
76
- "#{config[:prefix]}-#{name}"
77
- else
78
- name
79
- end
80
- end
81
-
82
- def exchange
83
- @exchange ||= begin
84
- channel.topic(prefix(EXCHANGE_NAME), durable: true).tap do |ex|
85
- log(:debug) do
86
- "Created exchange #{ex.name} (type: #{ex.type}, " \
87
- "durable: #{ex.durable?}, auto_delete: #{ex.auto_delete?})"
88
- end
89
- end
90
- end
91
- end
92
-
93
- def queue(name)
94
- channel.queue(prefix(name), durable: true).tap do |queue|
95
- log(:debug) do
96
- "Create queue #{queue.name} (durable: #{queue.durable?}, " \
97
- "auto_delete: #{queue.auto_delete?})"
98
- end
99
- end
100
- end
101
-
102
76
  def bind(routes)
103
77
  if routes.empty?
104
78
  log(:warn) do
@@ -110,23 +84,8 @@ module Msgr
110
84
  end
111
85
  end
112
86
 
113
- def ack(delivery_tag)
114
- channel.ack delivery_tag
115
- log(:debug) { "Acked message: #{delivery_tag}" }
116
- end
117
-
118
- def nack(delivery_tag)
119
- channel.nack delivery_tag, false, true
120
- log(:debug) { "Nacked message: #{delivery_tag}" }
121
- end
122
-
123
- def reject(delivery_tag, requeue = true)
124
- channel.reject delivery_tag, requeue
125
- log(:debug) { "Rejected message: #{delivery_tag}" }
126
- end
127
-
128
87
  def close
129
- channel.close if @channel && @channel.open?
88
+ @channels.each(&:close)
130
89
  connection.close if @connection
131
90
  log(:debug) { 'Closed.' }
132
91
  end
data/lib/msgr/consumer.rb CHANGED
@@ -9,6 +9,16 @@ module Msgr
9
9
  delegate :action, to: :'@message.route'
10
10
  delegate :consumer, to: :'@message.consumer'
11
11
 
12
+ class << self
13
+ def auto_ack?
14
+ @auto_ack || @auto_ack.nil?
15
+ end
16
+
17
+ def auto_ack=(val)
18
+ @auto_ack = val
19
+ end
20
+ end
21
+
12
22
  def dispatch(message)
13
23
  @message = message
14
24
 
@@ -37,8 +37,8 @@ module Msgr
37
37
 
38
38
  consumer_class.new.dispatch message
39
39
 
40
- # Acknowledge message unless it is already acknowledged.
41
- message.ack unless message.acked?
40
+ # Acknowledge message unless it is already acknowledged or auto_ack is disabled.
41
+ message.ack unless message.acked? or not consumer_class.auto_ack?
42
42
  rescue => error
43
43
  message.nack unless message.acked?
44
44
 
data/lib/msgr/message.rb CHANGED
@@ -4,8 +4,8 @@ module Msgr
4
4
  class Message
5
5
  attr_reader :delivery_info, :metadata, :payload, :route
6
6
 
7
- def initialize(connection, delivery_info, metadata, payload, route)
8
- @connection = connection
7
+ def initialize(channel, delivery_info, metadata, payload, route)
8
+ @channel = channel
9
9
  @delivery_info = delivery_info
10
10
  @metadata = metadata
11
11
  @payload = payload
@@ -40,7 +40,7 @@ module Msgr
40
40
  return if acked?
41
41
 
42
42
  @acked = true
43
- @connection.ack delivery_info.delivery_tag
43
+ @channel.ack delivery_info.delivery_tag
44
44
  end
45
45
 
46
46
  # Send negative message acknowledge to broker unless
@@ -52,7 +52,7 @@ module Msgr
52
52
  return if acked?
53
53
 
54
54
  @acked = true
55
- @connection.nack delivery_info.delivery_tag
55
+ @channel.nack delivery_info.delivery_tag
56
56
  end
57
57
  end
58
58
  end
data/lib/msgr/route.rb CHANGED
@@ -31,6 +31,10 @@ module Msgr
31
31
  end
32
32
  alias routing_keys keys
33
33
 
34
+ def prefetch
35
+ @opts[:prefetch] || 1
36
+ end
37
+
34
38
  def add(key)
35
39
  raise ArgumentError.new 'Routing key required.' unless key.present?
36
40
 
data/lib/msgr.rb CHANGED
@@ -11,6 +11,7 @@ require 'active_support/core_ext/hash/keys'
11
11
  require 'msgr/logging'
12
12
  require 'msgr/client'
13
13
  require 'msgr/binding'
14
+ require 'msgr/channel'
14
15
  require 'msgr/connection'
15
16
  require 'msgr/consumer'
16
17
  require 'msgr/dispatcher'
@@ -24,8 +24,8 @@ describe Msgr::Dispatcher do
24
24
  allow(t).to receive(:action).and_return 'index'
25
25
  end
26
26
  end
27
- let(:connection) do
28
- double(:connection).tap do |c|
27
+ let(:channel) do
28
+ double(:channel).tap do |c|
29
29
  allow(c).to receive(:ack)
30
30
  end
31
31
  end
@@ -40,7 +40,7 @@ describe Msgr::Dispatcher do
40
40
  allow(metadata).to receive(:content_type).and_return('text/plain')
41
41
  end
42
42
  end
43
- let(:message) { Msgr::Message.new connection, delivery_info, metadata, payload, route }
43
+ let(:message) { Msgr::Message.new channel, delivery_info, metadata, payload, route }
44
44
  let(:action) { -> { dispatcher.call message } }
45
45
 
46
46
  it 'should consume message' do
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class MsgrAutoAckConsumer < Msgr::Consumer
6
+ self.auto_ack = true
7
+
8
+ def index
9
+ end
10
+ end
11
+
12
+ class MsgrManualAckConsumer < Msgr::Consumer
13
+ self.auto_ack = false
14
+
15
+ def index
16
+ end
17
+ end
18
+
19
+ describe Msgr::Dispatcher do
20
+ let(:config) { {} }
21
+ let(:args) { [config] }
22
+ let(:dispatcher) { Msgr::Dispatcher.new(*args) }
23
+ subject { dispatcher }
24
+
25
+ describe 'dispatch' do
26
+ it 'should ack messages automatically if auto_ack is enabled' do
27
+ route_db = double('Route', :consumer => 'MsgrAutoAckConsumer', :action => :index)
28
+ msg_db = double('Message', :route => route_db, :acked? => false)
29
+ expect(msg_db).to receive(:ack)
30
+ expect(msg_db).not_to receive(:nack)
31
+
32
+ dispatcher.dispatch(msg_db)
33
+ end
34
+
35
+ it 'should not ack messages if auto_ack is disabled' do
36
+ route_db = double('Route', :consumer => 'MsgrManualAckConsumer', :action => :index)
37
+ msg_db = double('Message', :route => route_db, :acked? => false)
38
+ expect(msg_db).not_to receive(:ack)
39
+ expect(msg_db).not_to receive(:nack)
40
+
41
+ dispatcher.dispatch(msg_db)
42
+ end
43
+ end
44
+ end
@@ -16,6 +16,14 @@ class MsgrTestConsumer < Msgr::Consumer
16
16
  end
17
17
  end
18
18
 
19
+ class MsgrPrefetchTestConsumer < Msgr::Consumer
20
+ self.auto_ack = false
21
+
22
+ def index
23
+ Receiver.batch message
24
+ end
25
+ end
26
+
19
27
  describe Msgr do
20
28
  let(:queue) { Queue.new }
21
29
  let(:client) { Msgr::Client.new size: 1, prefix: SecureRandom.hex(2) }
@@ -24,6 +32,7 @@ describe Msgr do
24
32
  client.routes.configure do
25
33
  route 'test.index', to: 'msgr_test#index'
26
34
  route 'test.error', to: 'msgr_test#error'
35
+ route 'test.batch', to: 'msgr_prefetch_test#index', prefetch: 2
27
36
  end
28
37
 
29
38
  client.start
@@ -49,4 +58,27 @@ describe Msgr do
49
58
 
50
59
  Timeout.timeout(4) { queue.pop }
51
60
  end
61
+
62
+ it 'should receive 2 messages when prefetch is set to 2' do
63
+ expect(Receiver).to receive(:batch).twice { |msg| queue << msg }
64
+
65
+ 2.times { client.publish 'Payload', to: 'test.batch' }
66
+
67
+ 2.times { Timeout.timeout(4) { queue.pop } }
68
+ end
69
+
70
+ it 'should not bulk ack all unacknowledged messages when acknowledging the last one' do
71
+ expect(Receiver).to receive(:batch).exactly(3).times { |msg| queue << msg }
72
+
73
+ 2.times { client.publish 'Payload', to: 'test.batch' }
74
+
75
+ messages = 2.times.map { Timeout.timeout(4) { queue.pop } }
76
+ messages[1].ack
77
+ messages[0].nack
78
+
79
+ # Test whether the nacked message gets redelivered. In this case, it was not acked when acknowledging the other message
80
+ message = Timeout.timeout(4) { queue.pop }
81
+ expect(message.payload).to eq(messages[0].payload)
82
+ expect(message.delivery_info.redelivered).to eq(true)
83
+ end
52
84
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msgr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.2.1.b175
4
+ version: 0.15.2.1.b178
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-19 00:00:00.000000000 Z
11
+ date: 2017-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -93,6 +93,7 @@ files:
93
93
  - gemfiles/Gemfile.rails-master
94
94
  - lib/msgr.rb
95
95
  - lib/msgr/binding.rb
96
+ - lib/msgr/channel.rb
96
97
  - lib/msgr/client.rb
97
98
  - lib/msgr/connection.rb
98
99
  - lib/msgr/consumer.rb
@@ -139,6 +140,7 @@ files:
139
140
  - spec/msgr/msgr/client_spec.rb
140
141
  - spec/msgr/msgr/connection_spec.rb
141
142
  - spec/msgr/msgr/consumer_spec.rb
143
+ - spec/msgr/msgr/dispatcher_spec.rb
142
144
  - spec/msgr/msgr/route_spec.rb
143
145
  - spec/msgr/msgr/routes_spec.rb
144
146
  - spec/msgr/msgr_spec.rb
@@ -202,6 +204,7 @@ test_files:
202
204
  - spec/msgr/msgr/client_spec.rb
203
205
  - spec/msgr/msgr/connection_spec.rb
204
206
  - spec/msgr/msgr/consumer_spec.rb
207
+ - spec/msgr/msgr/dispatcher_spec.rb
205
208
  - spec/msgr/msgr/route_spec.rb
206
209
  - spec/msgr/msgr/routes_spec.rb
207
210
  - spec/msgr/msgr_spec.rb