message-driver 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +160 -0
- data/.travis.yml +5 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -8
- data/Guardfile +14 -7
- data/README.md +2 -0
- data/Rakefile +16 -10
- data/examples/basic_producer_and_consumer/Gemfile +2 -2
- data/examples/basic_producer_and_consumer/common.rb +3 -3
- data/examples/basic_producer_and_consumer/consumer.rb +5 -5
- data/examples/basic_producer_and_consumer/producer.rb +7 -7
- data/features/CHANGELOG.md +5 -0
- data/features/message_consumers/transactional_ack_consumers.feature +1 -0
- data/features/rabbitmq_specific_features/publisher_acknowledgements.feature +51 -0
- data/features/step_definitions/error_handling_steps.rb +2 -2
- data/features/step_definitions/logging_steps.rb +4 -4
- data/features/step_definitions/message_consumers_steps.rb +8 -8
- data/features/step_definitions/rabbitmq_specific_steps.rb +10 -0
- data/features/step_definitions/steps.rb +15 -15
- data/features/support/env.rb +3 -0
- data/features/support/firewall_helper.rb +5 -6
- data/features/support/message_table_matcher.rb +3 -4
- data/features/support/no_error_matcher.rb +3 -3
- data/features/support/test_runner.rb +11 -3
- data/features/support/transforms.rb +1 -1
- data/lib/message-driver.rb +1 -1
- data/lib/message_driver.rb +0 -1
- data/lib/message_driver/adapters/base.rb +10 -10
- data/lib/message_driver/adapters/bunny_adapter.rb +57 -30
- data/lib/message_driver/adapters/in_memory_adapter.rb +4 -5
- data/lib/message_driver/adapters/stomp_adapter.rb +4 -6
- data/lib/message_driver/broker.rb +5 -4
- data/lib/message_driver/client.rb +6 -6
- data/lib/message_driver/destination.rb +2 -2
- data/lib/message_driver/errors.rb +1 -4
- data/lib/message_driver/logging.rb +1 -1
- data/lib/message_driver/message.rb +2 -2
- data/lib/message_driver/subscription.rb +1 -1
- data/lib/message_driver/version.rb +1 -1
- data/lib/{message_driver/vendor → vendor}/.document +0 -0
- data/lib/vendor/nesty.rb +1 -0
- data/lib/vendor/nesty/nested_error.rb +28 -0
- data/message-driver.gemspec +15 -14
- data/spec/integration/bunny/amqp_integration_spec.rb +43 -43
- data/spec/integration/bunny/bunny_adapter_spec.rb +117 -101
- data/spec/integration/in_memory/in_memory_adapter_spec.rb +35 -35
- data/spec/integration/stomp/stomp_adapter_spec.rb +42 -42
- data/spec/spec_helper.rb +4 -1
- data/spec/support/shared/adapter_examples.rb +7 -7
- data/spec/support/shared/client_ack_examples.rb +6 -6
- data/spec/support/shared/context_examples.rb +4 -4
- data/spec/support/shared/destination_examples.rb +10 -10
- data/spec/support/shared/subscription_examples.rb +29 -29
- data/spec/support/shared/transaction_examples.rb +10 -10
- data/spec/units/message_driver/adapters/base_spec.rb +19 -19
- data/spec/units/message_driver/broker_spec.rb +57 -58
- data/spec/units/message_driver/client_spec.rb +84 -84
- data/spec/units/message_driver/destination_spec.rb +4 -4
- data/spec/units/message_driver/message_spec.rb +19 -19
- data/spec/units/message_driver/subscription_spec.rb +4 -4
- data/test_lib/broker_config.rb +2 -2
- metadata +27 -6
- data/lib/message_driver/vendor/nesty.rb +0 -1
- data/lib/message_driver/vendor/nesty/nested_error.rb +0 -26
data/features/CHANGELOG.md
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
@bunny
|
2
|
+
Feature: Publisher Acknowledgements
|
3
|
+
|
4
|
+
RabbitMQ supports confirmation of published messages
|
5
|
+
See http://www.rabbitmq.com/confirms.html for details.
|
6
|
+
|
7
|
+
Verifying the publish of a single message and of a group of messages is
|
8
|
+
supported as described below.
|
9
|
+
|
10
|
+
Background:
|
11
|
+
Given I am connected to the broker
|
12
|
+
And I have a destination :publish_ack with no messages on it
|
13
|
+
|
14
|
+
Scenario: Publishing a single message with confirmations turned on
|
15
|
+
When I execute the following code
|
16
|
+
""""ruby
|
17
|
+
publish(:publish_ack, "Test Message", {}, confirm: true)
|
18
|
+
"""
|
19
|
+
|
20
|
+
Then I expect all the publishes to have been acknowledged
|
21
|
+
And I expect to find the following message on :publish_ack
|
22
|
+
| body |
|
23
|
+
| Test Message |
|
24
|
+
|
25
|
+
|
26
|
+
Scenario: Publishing a single message where confirmations are turned on by the destination
|
27
|
+
When I execute the following code
|
28
|
+
""""ruby
|
29
|
+
my_new_destination = MessageDriver::Client.dynamic_destination(:publish_ack, {}, {confirm: true})
|
30
|
+
my_new_destination.publish("Test Message")
|
31
|
+
"""
|
32
|
+
|
33
|
+
Then I expect all the publishes to have been acknowledged
|
34
|
+
And I expect to find the following message on :publish_ack
|
35
|
+
| body |
|
36
|
+
| Test Message |
|
37
|
+
|
38
|
+
|
39
|
+
Scenario: Publishing a batch of messages with confirmations turned on
|
40
|
+
When I execute the following code
|
41
|
+
""""ruby
|
42
|
+
with_message_transaction(type: :confirm_and_wait) do
|
43
|
+
50.times do |i|
|
44
|
+
publish(:publish_ack, "Test Message #{i}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
"""
|
48
|
+
|
49
|
+
Then I expect all the publishes to have been acknowledged
|
50
|
+
And I expect that we are not in transaction mode
|
51
|
+
And I expect to find 50 messages on :publish_ack
|
@@ -1,22 +1,22 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
-
LOG_FILE_NAME =
|
3
|
+
LOG_FILE_NAME = 'cucumber_log_file.log'
|
4
4
|
|
5
5
|
Given(/^I am logging to a log file(?: at the (#{STRING_OR_SYM}) level)?$/) do |level|
|
6
6
|
step "an empty file named \"#{LOG_FILE_NAME}\""
|
7
7
|
in_current_dir do
|
8
8
|
@logger = Logger.new(LOG_FILE_NAME)
|
9
9
|
end
|
10
|
-
step "I set the log level to #{level ||
|
10
|
+
step "I set the log level to #{level || 'info'}"
|
11
11
|
@orig_logger, MessageDriver.logger = MessageDriver.logger, @logger
|
12
12
|
end
|
13
13
|
|
14
14
|
Given(/^I set the log level to (#{STRING_OR_SYM})$/) do |level|
|
15
|
-
level = level ? level.to_s.upcase :
|
15
|
+
level = level ? level.to_s.upcase : 'INFO'
|
16
16
|
@logger.level = Logger::SEV_LABEL.find_index(level)
|
17
17
|
end
|
18
18
|
|
19
|
-
Then
|
19
|
+
Then 'the log file should contain:' do |string|
|
20
20
|
step "the file \"#{LOG_FILE_NAME}\" should contain:", string
|
21
21
|
end
|
22
22
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Given
|
1
|
+
Given 'I have a message consumer' do |src|
|
2
2
|
test_runner.run_config_code(src)
|
3
3
|
expect(test_runner).to have_no_errors
|
4
4
|
end
|
@@ -7,18 +7,18 @@ Given(/^I subscribe to (#{STRING_OR_SYM}) with (#{STRING_OR_SYM})$/) do |destina
|
|
7
7
|
MessageDriver::Client[test_runner.broker_name].subscribe(destination, consumer)
|
8
8
|
end
|
9
9
|
|
10
|
-
Given
|
10
|
+
Given 'I create a subscription' do |src|
|
11
11
|
test_runner.run_test_code("@subscription = #{src}")
|
12
12
|
expect(test_runner).to have_no_errors
|
13
13
|
end
|
14
14
|
|
15
|
-
When
|
16
|
-
test_runner.run_test_code(
|
17
|
-
step
|
15
|
+
When 'I cancel the subscription' do
|
16
|
+
test_runner.run_test_code('@subscription.unsubscribe')
|
17
|
+
step 'I allow for processing'
|
18
18
|
end
|
19
19
|
|
20
|
-
When
|
21
|
-
step
|
22
|
-
step
|
20
|
+
When 'I let the subscription process' do
|
21
|
+
step 'I allow for processing'
|
22
|
+
step 'I cancel the subscription'
|
23
23
|
expect(test_runner).to have_no_errors
|
24
24
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Then 'I expect all the publishes to have been acknowledged' do
|
2
|
+
ctx = test_runner.fetch_current_adapter_context
|
3
|
+
expect(ctx.channel).to be_using_publisher_confirms
|
4
|
+
expect(ctx.channel.unconfirmed_set).to be_empty
|
5
|
+
end
|
6
|
+
|
7
|
+
Then 'I expect that we are not in transaction mode' do
|
8
|
+
ctx = test_runner.fetch_current_adapter_context
|
9
|
+
expect(ctx).not_to be_transactional
|
10
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
Given
|
1
|
+
Given 'I am connected to the broker' do
|
2
2
|
MessageDriver::Broker.configure(test_runner.broker_name, broker_config)
|
3
3
|
end
|
4
4
|
|
5
5
|
Given(/^I am connected to a broker named (#{STRING_OR_SYM})$/) do |broker_name|
|
6
6
|
test_runner.broker_name = broker_name
|
7
|
-
step
|
7
|
+
step 'I am connected to the broker'
|
8
8
|
end
|
9
9
|
|
10
|
-
Given
|
11
|
-
step
|
10
|
+
Given 'the following broker configuration' do |src|
|
11
|
+
step 'I am connected to the broker'
|
12
12
|
test_runner.run_config_code(src)
|
13
13
|
end
|
14
14
|
|
15
|
-
Given
|
15
|
+
Given 'I configure my broker as follows' do |src|
|
16
16
|
test_runner.run_config_code(src)
|
17
17
|
end
|
18
18
|
|
@@ -23,14 +23,14 @@ Given(/^I have a destination (#{STRING_OR_SYM})$/) do |destination|
|
|
23
23
|
end
|
24
24
|
|
25
25
|
Given(/^I have a destination (#{STRING_OR_SYM}) with no messages on it$/) do |destination|
|
26
|
-
dest = destination.
|
26
|
+
dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
|
27
27
|
step "I have a destination #{dest}"
|
28
28
|
test_runner.purge_destination(destination)
|
29
29
|
end
|
30
30
|
|
31
31
|
Given(/^I have the following messages? on (#{STRING_OR_SYM})$/) do |destination, table|
|
32
32
|
test_runner.purge_destination(destination)
|
33
|
-
dest = destination.
|
33
|
+
dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
|
34
34
|
step "I send the following messages to #{dest}", table
|
35
35
|
end
|
36
36
|
|
@@ -44,15 +44,15 @@ When(/^I send the following messages? to (#{STRING_OR_SYM})$/) do |destination,
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
When
|
47
|
+
When 'I execute the following code' do |src|
|
48
48
|
test_runner.run_test_code(src)
|
49
49
|
end
|
50
50
|
|
51
|
-
When
|
51
|
+
When 'I reset the context' do
|
52
52
|
MessageDriver::Client[test_runner.broker_name].current_adapter_context.invalidate
|
53
53
|
end
|
54
54
|
|
55
|
-
When
|
55
|
+
When 'I allow for processing' do
|
56
56
|
test_runner.pause_if_needed
|
57
57
|
end
|
58
58
|
|
@@ -70,7 +70,7 @@ Then(/^I expect to find the following (#{NUMBER}) messages? on (#{STRING_OR_SYM}
|
|
70
70
|
end
|
71
71
|
|
72
72
|
Then(/^I expect to find the following message on (#{STRING_OR_SYM})$/) do |destination, table|
|
73
|
-
dest = destination.
|
73
|
+
dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
|
74
74
|
step "I expect to find the following 1 message on #{dest}", table
|
75
75
|
end
|
76
76
|
|
@@ -87,13 +87,13 @@ Then(/^I expect it to raise a (.*?) error$/) do |error_type|
|
|
87
87
|
test_runner.raised_error = nil
|
88
88
|
end
|
89
89
|
|
90
|
-
Then
|
90
|
+
Then 'I expect to have no errors' do
|
91
91
|
expect(test_runner).to have_no_errors
|
92
92
|
end
|
93
93
|
|
94
|
-
Then
|
95
|
-
step
|
96
|
-
step
|
94
|
+
Then 'I expect the following check to pass' do |src|
|
95
|
+
step 'I execute the following code', src
|
96
|
+
step 'I expect to have no errors'
|
97
97
|
end
|
98
98
|
|
99
99
|
Before do |current_scenario|
|
data/features/support/env.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module FirewallHelper
|
2
|
-
|
3
2
|
def self.port
|
4
3
|
BrokerConfig.current_adapter_port
|
5
4
|
end
|
@@ -7,18 +6,18 @@ module FirewallHelper
|
|
7
6
|
COMMANDS = {
|
8
7
|
darwin: {
|
9
8
|
setup: [
|
10
|
-
|
9
|
+
'lunchy stop rabbit'
|
11
10
|
],
|
12
11
|
teardown: [
|
13
|
-
|
12
|
+
'lunchy start rabbit'
|
14
13
|
]
|
15
14
|
},
|
16
15
|
linux: {
|
17
16
|
setup: [
|
18
|
-
|
17
|
+
'sudo service rabbitmq-server stop'
|
19
18
|
],
|
20
19
|
teardown: [
|
21
|
-
|
20
|
+
'sudo service rabbitmq-server start'
|
22
21
|
]
|
23
22
|
}
|
24
23
|
}
|
@@ -49,7 +48,7 @@ module FirewallHelper
|
|
49
48
|
end
|
50
49
|
|
51
50
|
def darwin?
|
52
|
-
system(
|
51
|
+
system('uname | grep Darwin')
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
@@ -4,10 +4,9 @@ RSpec::Matchers.define :match_message_table do |expected_tbl|
|
|
4
4
|
end
|
5
5
|
|
6
6
|
define_method :messages_to_hash do |messages|
|
7
|
-
messages.
|
8
|
-
expected_tbl.headers.
|
9
|
-
|
10
|
-
memo
|
7
|
+
messages.map do |msg|
|
8
|
+
expected_tbl.headers.each_with_object({}) do |method, hash|
|
9
|
+
hash[method] = msg.send(method)
|
11
10
|
end
|
12
11
|
end
|
13
12
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
RSpec::Matchers.define :have_no_errors do
|
2
2
|
match do |test_runner|
|
3
|
-
test_runner.raised_error
|
3
|
+
test_runner.raised_error.nil?
|
4
4
|
end
|
5
5
|
|
6
6
|
failure_message_for_should do |test_runner|
|
7
7
|
err = test_runner.raised_error
|
8
8
|
filtered = (err.backtrace || []).reject do |line|
|
9
|
-
Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.
|
9
|
+
Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
|
10
10
|
end
|
11
|
-
(["#{err.class}: #{err
|
11
|
+
(["#{err.class}: #{err}"]+filtered).join("\n ")
|
12
12
|
end
|
13
13
|
end
|
@@ -26,10 +26,14 @@ class TestRunner
|
|
26
26
|
destination = fetch_destination(destination_name)
|
27
27
|
pause_if_needed
|
28
28
|
result = []
|
29
|
-
|
29
|
+
loop do
|
30
30
|
msg = destination.pop_message
|
31
|
-
|
32
|
-
|
31
|
+
if msg.nil?
|
32
|
+
break
|
33
|
+
else
|
34
|
+
result << msg
|
35
|
+
end
|
36
|
+
end
|
33
37
|
result
|
34
38
|
end
|
35
39
|
|
@@ -53,6 +57,10 @@ class TestRunner
|
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
60
|
+
def fetch_current_adapter_context
|
61
|
+
MessageDriver::Client[self.broker_name].current_adapter_context
|
62
|
+
end
|
63
|
+
|
56
64
|
def publish_table_to_destination(destination, table)
|
57
65
|
table.hashes.each do |msg|
|
58
66
|
destination.publish(msg[:body], msg[:headers]||{}, msg[:properties]||{})
|
data/lib/message-driver.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'message_driver'
|
data/lib/message_driver.rb
CHANGED
@@ -9,8 +9,8 @@ module MessageDriver
|
|
9
9
|
@contexts ||= []
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
raise
|
12
|
+
def initialize(_broker, _configuration)
|
13
|
+
raise 'Must be implemented in subclass'
|
14
14
|
end
|
15
15
|
|
16
16
|
def new_context
|
@@ -20,7 +20,7 @@ module MessageDriver
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def build_context
|
23
|
-
raise
|
23
|
+
raise 'Must be implemented in subclass'
|
24
24
|
end
|
25
25
|
|
26
26
|
def reset_after_tests
|
@@ -47,20 +47,20 @@ module MessageDriver
|
|
47
47
|
@valid = true
|
48
48
|
end
|
49
49
|
|
50
|
-
def publish(
|
51
|
-
raise
|
50
|
+
def publish(_destination, _body, _headers={}, _properties={})
|
51
|
+
raise 'Must be implemented in subclass'
|
52
52
|
end
|
53
53
|
|
54
|
-
def pop_message(
|
55
|
-
raise
|
54
|
+
def pop_message(_destination, _options={})
|
55
|
+
raise 'Must be implemented in subclass'
|
56
56
|
end
|
57
57
|
|
58
|
-
def subscribe(
|
58
|
+
def subscribe(_destination, _options={}, &_consumer)
|
59
59
|
raise "#subscribe is not supported by #{adapter.class}"
|
60
60
|
end
|
61
61
|
|
62
|
-
def create_destination(
|
63
|
-
raise
|
62
|
+
def create_destination(_name, _dest_options={}, _message_props={})
|
63
|
+
raise 'Must be implemented in subclass'
|
64
64
|
end
|
65
65
|
|
66
66
|
def valid?
|
@@ -46,13 +46,13 @@ module MessageDriver
|
|
46
46
|
|
47
47
|
class QueueDestination < Destination
|
48
48
|
def after_initialize(adapter_context)
|
49
|
-
|
49
|
+
if @dest_options[:no_declare]
|
50
|
+
raise MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true' if @name.empty?
|
51
|
+
raise MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true' if @dest_options[:bindings]
|
52
|
+
else
|
50
53
|
adapter_context.with_channel(false) do |ch|
|
51
54
|
bunny_queue(ch, true)
|
52
55
|
end
|
53
|
-
else
|
54
|
-
raise MessageDriver::Error, "server-named queues must be declared, but you provided :no_declare => true" if @name.empty?
|
55
|
-
raise MessageDriver::Error, "queues with bindings must be declared, but you provided :no_declare => true" if @dest_options[:bindings]
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -60,7 +60,7 @@ module MessageDriver
|
|
60
60
|
queue = channel.queue(@name, @dest_options)
|
61
61
|
if initialize
|
62
62
|
@name = queue.name
|
63
|
-
if bindings = @dest_options[:bindings]
|
63
|
+
if (bindings = @dest_options[:bindings])
|
64
64
|
bindings.each do |bnd|
|
65
65
|
raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
|
66
66
|
queue.bind(bnd[:source], bnd[:args]||{})
|
@@ -71,10 +71,10 @@ module MessageDriver
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def exchange_name
|
74
|
-
|
74
|
+
''
|
75
75
|
end
|
76
76
|
|
77
|
-
def routing_key(
|
77
|
+
def routing_key(_properties)
|
78
78
|
@name
|
79
79
|
end
|
80
80
|
|
@@ -93,14 +93,14 @@ module MessageDriver
|
|
93
93
|
|
94
94
|
class ExchangeDestination < Destination
|
95
95
|
def after_initialize(adapter_context)
|
96
|
-
if declare = @dest_options[:declare]
|
96
|
+
if (declare = @dest_options[:declare])
|
97
97
|
adapter_context.with_channel(false) do |ch|
|
98
98
|
type = declare.delete(:type)
|
99
|
-
raise MessageDriver::Error,
|
99
|
+
raise MessageDriver::Error, 'you must provide a valid exchange type' unless type
|
100
100
|
ch.exchange_declare(@name, type, declare)
|
101
101
|
end
|
102
102
|
end
|
103
|
-
if bindings = @dest_options[:bindings]
|
103
|
+
if (bindings = @dest_options[:bindings])
|
104
104
|
adapter_context.with_channel(false) do |ch|
|
105
105
|
bindings.each do |bnd|
|
106
106
|
raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
|
@@ -113,7 +113,7 @@ module MessageDriver
|
|
113
113
|
|
114
114
|
class Subscription < Subscription::Base
|
115
115
|
def start
|
116
|
-
raise MessageDriver::Error,
|
116
|
+
raise MessageDriver::Error, 'subscriptions are only supported with QueueDestinations' unless destination.is_a? QueueDestination
|
117
117
|
@sub_ctx = adapter.new_subscription_context(self)
|
118
118
|
@error_handler = options[:error_handler]
|
119
119
|
@ack_mode = case options[:ack]
|
@@ -141,10 +141,11 @@ module MessageDriver
|
|
141
141
|
end
|
142
142
|
|
143
143
|
private
|
144
|
+
|
144
145
|
def start_subscription
|
145
146
|
@sub_ctx.with_channel do |ch|
|
146
147
|
queue = destination.bunny_queue(@sub_ctx.channel)
|
147
|
-
if options.
|
148
|
+
if options.key? :prefetch_size
|
148
149
|
ch.prefetch(options[:prefetch_size])
|
149
150
|
end
|
150
151
|
@bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
|
@@ -213,7 +214,7 @@ module MessageDriver
|
|
213
214
|
def stop
|
214
215
|
begin
|
215
216
|
super
|
216
|
-
@connection.close
|
217
|
+
@connection.close unless @connection.nil?
|
217
218
|
rescue => e
|
218
219
|
logger.error "error while attempting connection close\n#{exception_to_str(e)}"
|
219
220
|
ensure
|
@@ -263,34 +264,48 @@ module MessageDriver
|
|
263
264
|
def begin_transaction(options={})
|
264
265
|
raise MessageDriver::TransactionError, "you can't begin another transaction, you are already in one!" if in_transaction?
|
265
266
|
@in_transaction = true
|
267
|
+
@in_confirms_transaction = true if options[:type] == :confirm_and_wait
|
266
268
|
end
|
267
269
|
|
268
270
|
def commit_transaction(channel_commit=false)
|
269
271
|
raise MessageDriver::TransactionError, "you can't finish the transaction unless you already in one!" if !in_transaction? && !channel_commit
|
270
272
|
begin
|
271
|
-
if
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
@
|
273
|
+
if @in_confirms_transaction
|
274
|
+
wait_for_confirms(@channel) unless @rollback_only
|
275
|
+
else
|
276
|
+
if is_transactional? && valid? && !@need_channel_reset
|
277
|
+
handle_errors do
|
278
|
+
if @rollback_only
|
279
|
+
@channel.tx_rollback
|
280
|
+
else
|
281
|
+
@channel.tx_commit
|
282
|
+
end
|
277
283
|
end
|
278
284
|
end
|
279
285
|
end
|
280
286
|
ensure
|
281
287
|
@rollback_only = false
|
282
288
|
@in_transaction = false
|
289
|
+
@in_confirms_transaction = false
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def wait_for_confirms(channel)
|
294
|
+
until channel.unconfirmed_set.empty?
|
295
|
+
channel.wait_for_confirms
|
283
296
|
end
|
284
297
|
end
|
298
|
+
private :wait_for_confirms
|
285
299
|
|
286
300
|
def rollback_transaction
|
287
301
|
@rollback_only = true
|
288
302
|
commit_transaction
|
289
303
|
end
|
290
304
|
|
291
|
-
def
|
305
|
+
def transactional?
|
292
306
|
@is_transactional
|
293
307
|
end
|
308
|
+
alias_method :is_transactional?, :transactional?
|
294
309
|
|
295
310
|
def in_transaction?
|
296
311
|
@in_transaction
|
@@ -298,8 +313,14 @@ module MessageDriver
|
|
298
313
|
|
299
314
|
def publish(destination, body, headers={}, properties={})
|
300
315
|
exchange, routing_key, props = *destination.publish_params(headers, properties)
|
316
|
+
confirm = props.delete(:confirm)
|
317
|
+
confirm = false if confirm.nil?
|
301
318
|
with_channel(true) do |ch|
|
319
|
+
if confirm == true
|
320
|
+
ch.confirm_select unless ch.using_publisher_confirms?
|
321
|
+
end
|
302
322
|
ch.basic_publish(body, exchange, routing_key, props)
|
323
|
+
ch.wait_for_confirms if confirm == true
|
303
324
|
end
|
304
325
|
end
|
305
326
|
|
@@ -309,7 +330,7 @@ module MessageDriver
|
|
309
330
|
with_channel(false) do |ch|
|
310
331
|
queue = ch.queue(destination.name, passive: true)
|
311
332
|
|
312
|
-
message = queue.pop(ack:
|
333
|
+
message = queue.pop(ack: options.fetch(:client_ack, false))
|
313
334
|
if message.nil? || message[0].nil?
|
314
335
|
nil
|
315
336
|
else
|
@@ -322,14 +343,14 @@ module MessageDriver
|
|
322
343
|
true
|
323
344
|
end
|
324
345
|
|
325
|
-
def ack_message(message,
|
346
|
+
def ack_message(message, _options={})
|
326
347
|
with_channel(true) do |ch|
|
327
348
|
ch.ack(message.delivery_tag)
|
328
349
|
end
|
329
350
|
end
|
330
351
|
|
331
352
|
def nack_message(message, options={})
|
332
|
-
requeue = options[:requeue].
|
353
|
+
requeue = options[:requeue].is_a?(FalseClass) ? false : true
|
333
354
|
with_channel(true) do |ch|
|
334
355
|
ch.reject(message.delivery_tag, requeue)
|
335
356
|
end
|
@@ -371,7 +392,7 @@ module MessageDriver
|
|
371
392
|
rescue Bunny::ChannelLevelException => e
|
372
393
|
@need_channel_reset = true
|
373
394
|
@rollback_only = true if in_transaction?
|
374
|
-
if e.
|
395
|
+
if e.is_a? Bunny::NotFound
|
375
396
|
raise MessageDriver::QueueNotFound.new(e.to_s, e)
|
376
397
|
else
|
377
398
|
raise MessageDriver::WrappedError.new(e.to_s, e)
|
@@ -389,12 +410,18 @@ module MessageDriver
|
|
389
410
|
|
390
411
|
def with_channel(require_commit=true)
|
391
412
|
raise MessageDriver::TransactionRollbackOnly if @rollback_only
|
392
|
-
raise MessageDriver::Error,
|
413
|
+
raise MessageDriver::Error, 'this adapter context is not valid!' unless valid?
|
393
414
|
@channel = adapter.connection.create_channel if @channel.nil?
|
394
415
|
reset_channel if @need_channel_reset
|
395
|
-
if in_transaction?
|
396
|
-
@
|
397
|
-
|
416
|
+
if in_transaction?
|
417
|
+
if @in_confirms_transaction
|
418
|
+
@channel.confirm_select unless @channel.using_publisher_confirmations?
|
419
|
+
else
|
420
|
+
unless is_transactional?
|
421
|
+
@channel.tx_select
|
422
|
+
@is_transactional = true
|
423
|
+
end
|
424
|
+
end
|
398
425
|
end
|
399
426
|
handle_errors do
|
400
427
|
result = yield @channel
|
@@ -430,10 +457,10 @@ module MessageDriver
|
|
430
457
|
end
|
431
458
|
|
432
459
|
def validate_bunny_version
|
433
|
-
required = Gem::Requirement.create('>= 1.
|
460
|
+
required = Gem::Requirement.create('>= 1.2.2')
|
434
461
|
current = Gem::Version.create(Bunny::VERSION)
|
435
462
|
unless required.satisfied_by? current
|
436
|
-
raise MessageDriver::Error,
|
463
|
+
raise MessageDriver::Error, 'bunny 1.2.2 or later is required for the bunny adapter'
|
437
464
|
end
|
438
465
|
end
|
439
466
|
end
|