msgr 0.15.2.1.b175 → 0.15.2.1.b178

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