euston-rabbitmq 1.0.1-java → 1.0.2-java
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.
- data/Gemfile +2 -0
- data/Rakefile +0 -21
- data/euston-rabbitmq.gemspec +26 -38
- data/lib/euston-rabbitmq/{bindings → euston}/command_handler_binder.rb +2 -2
- data/lib/euston-rabbitmq/{errors.rb → euston/errors.rb} +1 -1
- data/lib/euston-rabbitmq/{bindings → euston}/event_handler_binder.rb +4 -4
- data/lib/euston-rabbitmq/{exchanges.rb → euston/exchanges.rb} +1 -1
- data/lib/euston-rabbitmq/{bindings → euston}/handler_binder.rb +2 -1
- data/lib/euston-rabbitmq/{queues.rb → euston/queues.rb} +1 -1
- data/lib/euston-rabbitmq/{subscriptions/retriable_subscription.rb → euston/retrying_subscription.rb} +17 -16
- data/lib/euston-rabbitmq/rabbitmq_client/queue.rb +70 -0
- data/lib/euston-rabbitmq/rabbitmq_client/reactive_message.rb +15 -0
- data/lib/euston-rabbitmq/{constant_loader.rb → reflection/constant_loader.rb} +0 -0
- data/lib/euston-rabbitmq/{handler_finder.rb → reflection/handler_finder.rb} +0 -0
- data/lib/euston-rabbitmq/{handler_reference.rb → reflection/handler_reference.rb} +0 -0
- data/lib/euston-rabbitmq/version.rb +1 -1
- data/lib/euston-rabbitmq.rb +8 -22
- data/spec/euston/command_handler_binder_spec.rb +24 -0
- data/spec/euston/event_handler_binder_spec.rb +36 -0
- data/spec/euston/exchanges_spec.rb +27 -0
- data/spec/euston/queues_spec.rb +24 -0
- data/spec/euston/retrying_subscription_spec.rb +74 -0
- data/spec/rabbitmq_client/queue_spec.rb +69 -0
- data/spec/{constant_loader_spec.rb → reflection/constant_loader_spec.rb} +3 -1
- data/spec/{handler_finder_spec.rb → reflection/handler_finder_spec.rb} +3 -1
- data/spec/spec_helper.rb +19 -52
- data/spec/support/filters.rb +10 -0
- data/spec/support/queue_subscription_thread_harness.rb +29 -0
- data/spec/support/rabbitmqadmin.rb +74 -0
- metadata +60 -76
- data/Gemfile.lock +0 -76
- data/lib/euston-rabbitmq/command_handlers/retry_failed_message.rb +0 -29
- data/lib/euston-rabbitmq/event_handlers/message_failure.rb +0 -27
- data/lib/euston-rabbitmq/message_buffer.rb +0 -67
- data/lib/euston-rabbitmq/message_logger.rb +0 -50
- data/lib/euston-rabbitmq/queue.rb +0 -30
- data/lib/euston-rabbitmq/read_model/failed_message.rb +0 -36
- data/lib/euston-rabbitmq/read_model/message_buffer.rb +0 -57
- data/lib/euston-rabbitmq/read_model/message_log.rb +0 -37
- data/spec/command_buffer_spec.rb +0 -69
- data/spec/event_buffer_spec.rb +0 -69
- data/spec/exchange_declaration_spec.rb +0 -28
- data/spec/message_failure_spec.rb +0 -77
- data/spec/mt_safe_queue_subscription_spec.rb +0 -72
- data/spec/safe_queue_subscription_spec.rb +0 -50
- data/spec/support/factories.rb +0 -18
@@ -1,50 +0,0 @@
|
|
1
|
-
module Euston
|
2
|
-
module RabbitMq
|
3
|
-
class MessageLogger
|
4
|
-
class << self
|
5
|
-
def commands_logger channel = nil
|
6
|
-
@command_logger ||= MessageLogger.new channel, :commands
|
7
|
-
end
|
8
|
-
|
9
|
-
def events_logger channel = nil
|
10
|
-
@event_logger ||= MessageLogger.new channel, :events
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
include Euston::RabbitMq::Exchanges
|
15
|
-
include Euston::RabbitMq::Queues
|
16
|
-
|
17
|
-
def initialize channel, exchange_name
|
18
|
-
@channel = channel
|
19
|
-
@exchange = get_exchange channel, exchange_name
|
20
|
-
@read_model = Euston::RabbitMq::ReadModel::MessageLog.send exchange_name
|
21
|
-
|
22
|
-
@queue = get_queue channel, "#{exchange_name}_log"
|
23
|
-
@queue.bind @exchange, :routing_key => "#{exchange_name}.#"
|
24
|
-
|
25
|
-
@queue.when(:message_decode_failed => method(:log_failure),
|
26
|
-
:message_failed => method(:handle_failure),
|
27
|
-
:message_received => method(:write_message_to_log))
|
28
|
-
|
29
|
-
@queue.safe_subscribe
|
30
|
-
end
|
31
|
-
|
32
|
-
def handle_failure(message, error, header)
|
33
|
-
log_failure message, error
|
34
|
-
header.ack
|
35
|
-
end
|
36
|
-
|
37
|
-
def log_failure message, error
|
38
|
-
text = "A log queue subscription failed. [Error] #{error.message} [Payload] #{message}"
|
39
|
-
err = Euston::RabbitMq::MessageDecodeFailedError.new text
|
40
|
-
err.set_backtrace error.backtrace
|
41
|
-
|
42
|
-
Safely.report! err
|
43
|
-
end
|
44
|
-
|
45
|
-
def write_message_to_log message
|
46
|
-
@read_model.log_new_message message
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module AMQP
|
2
|
-
class Queue
|
3
|
-
include Hollywood
|
4
|
-
|
5
|
-
def safe_subscribe
|
6
|
-
cb = Hollywood.instance_method(:callback).bind self
|
7
|
-
|
8
|
-
self.subscribe :ack => true do |header, body|
|
9
|
-
rt = ::RobustThread.new(:args => [header, body], :label => "#{self.name} queue subscriber") do |header, body|
|
10
|
-
begin
|
11
|
-
message = ::ActiveSupport::JSON.decode body
|
12
|
-
message.recursive_symbolize_keys!
|
13
|
-
|
14
|
-
begin
|
15
|
-
cb.call :message_received, message
|
16
|
-
header.ack
|
17
|
-
rescue Euston::EventStore::ConcurrencyError
|
18
|
-
header.reject :requeue => true
|
19
|
-
rescue StandardError => e
|
20
|
-
cb.call :message_failed, message, e, header
|
21
|
-
end
|
22
|
-
rescue StandardError => e
|
23
|
-
cb.call :message_decode_failed, body, e
|
24
|
-
header.ack
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Euston
|
2
|
-
module RabbitMq
|
3
|
-
module ReadModel
|
4
|
-
class FailedMessage
|
5
|
-
def initialize
|
6
|
-
name = 'failed_messages'
|
7
|
-
|
8
|
-
mongodb = Euston::RabbitMq.event_store_mongodb
|
9
|
-
mongodb.create_collection name unless mongodb.collection_names.include? name
|
10
|
-
|
11
|
-
@collection = mongodb.collection name
|
12
|
-
@collection.ensure_index [ ['failure_timestamp', Mongo::ASCENDING] ], :unique => false, :name => 'failed_messages_failure_timestamp_index'
|
13
|
-
end
|
14
|
-
|
15
|
-
def get_by_id id
|
16
|
-
@collection.find_one({ '_id' => id })
|
17
|
-
end
|
18
|
-
|
19
|
-
def find_all
|
20
|
-
@collection.find({}, { :sort => [ 'failure_timestamp', Mongo::DESCENDING ] })
|
21
|
-
end
|
22
|
-
|
23
|
-
def log_failure failure
|
24
|
-
failure.recursive_stringify_keys!
|
25
|
-
failure['_id'] = failure.delete 'message_id'
|
26
|
-
|
27
|
-
@collection.save(failure, :safe => { :fsync => true })
|
28
|
-
end
|
29
|
-
|
30
|
-
def remove_by_id id
|
31
|
-
@collection.remove({ '_id' => id }, :safe => { :fsync => true })
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module Euston
|
2
|
-
module RabbitMq
|
3
|
-
module ReadModel
|
4
|
-
class MessageBuffer
|
5
|
-
class << self
|
6
|
-
def commands
|
7
|
-
@commands ||= MessageBuffer.new "buffered_commands"
|
8
|
-
end
|
9
|
-
|
10
|
-
def events
|
11
|
-
@events ||= MessageBuffer.new "buffered_events"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize name
|
16
|
-
mongodb = Euston::RabbitMq.event_store_mongodb
|
17
|
-
mongodb.create_collection name unless mongodb.collection_names.include? name
|
18
|
-
|
19
|
-
@collection = mongodb.collection name
|
20
|
-
@collection.ensure_index [ ['next_attempt', Mongo::ASCENDING] ], :unique => false, :name => "#{name}_next_attempt_index"
|
21
|
-
end
|
22
|
-
|
23
|
-
def buffer_new_message message
|
24
|
-
@collection.save({ '_id' => message[:headers][:id],
|
25
|
-
'type' => message[:headers][:type],
|
26
|
-
'next_attempt' => Time.now.to_f,
|
27
|
-
'json' => ::ActiveSupport::JSON.encode(message) }, :safe => { :fsync => true })
|
28
|
-
end
|
29
|
-
|
30
|
-
def find_next_message
|
31
|
-
query = { 'next_attempt' => { '$lte' => Time.now.to_f } }
|
32
|
-
@collection.find_one(query)
|
33
|
-
end
|
34
|
-
|
35
|
-
def find_due_messages
|
36
|
-
query = { 'next_attempt' => { '$lte' => Time.now.to_f } }
|
37
|
-
order = [ 'next_attempt', Mongo::ASCENDING ]
|
38
|
-
|
39
|
-
@collection.find(query).sort(order)
|
40
|
-
end
|
41
|
-
|
42
|
-
def get_by_id id
|
43
|
-
@collection.find_one({ '_id' => id })
|
44
|
-
end
|
45
|
-
|
46
|
-
def set_next_attempt message
|
47
|
-
@collection.update({ '_id' => message['_id'] },
|
48
|
-
{ '$set' => { 'next_attempt' => Time.now.to_f + 10 } }, :safe => { :fsync => true })
|
49
|
-
end
|
50
|
-
|
51
|
-
def remove_published_message id
|
52
|
-
@collection.remove({ '_id' => id }, :safe => { :fsync => true })
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Euston
|
2
|
-
module RabbitMq
|
3
|
-
module ReadModel
|
4
|
-
class MessageLog
|
5
|
-
class << self
|
6
|
-
def commands
|
7
|
-
@commands ||= MessageLog.new "command_log"
|
8
|
-
end
|
9
|
-
|
10
|
-
def events
|
11
|
-
@events ||= MessageLog.new "event_log"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize name
|
16
|
-
mongodb = Euston::RabbitMq.event_store_mongodb
|
17
|
-
mongodb.create_collection name unless mongodb.collection_names.include? name
|
18
|
-
|
19
|
-
@collection = mongodb.collection name
|
20
|
-
@collection.ensure_index [ ['timestamp', Mongo::ASCENDING] ], :unique => false, :name => "#{name}_timestamp_index"
|
21
|
-
end
|
22
|
-
|
23
|
-
def find_all
|
24
|
-
@collection.find({}, { :sort => [ 'timestamp', Mongo::DESCENDING ] })
|
25
|
-
end
|
26
|
-
|
27
|
-
def log_new_message message
|
28
|
-
@collection.save({ '_id' => message[:headers][:id],
|
29
|
-
'type' => message[:headers][:type],
|
30
|
-
'version' => message[:headers][:version],
|
31
|
-
'timestamp' => Time.now.to_f,
|
32
|
-
'json' => ::ActiveSupport::JSON.encode(message) }, :safe => { :fsync => true })
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/spec/command_buffer_spec.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
2
|
-
|
3
|
-
unless RUBY_PLATFORM == 'java'
|
4
|
-
describe 'command buffer' do
|
5
|
-
include EustonRmqSpec
|
6
|
-
|
7
|
-
context 'the command buffer is created' do
|
8
|
-
include Euston::RabbitMq::Queues
|
9
|
-
|
10
|
-
amqp_before do
|
11
|
-
@buffer = Euston::RabbitMq::MessageBuffer.new @channel, :commands
|
12
|
-
@queue = get_queue @channel, :commands_buffer
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'declares the commands buffer queue' do
|
16
|
-
done(0.5) {
|
17
|
-
@queue.name.should == 'commands_buffer'
|
18
|
-
@queue.opts.should include(:durable => true)
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'binds the command buffer queue to the commands exchange with a wildcard routing key' do
|
23
|
-
received = []
|
24
|
-
data = Factory.command
|
25
|
-
|
26
|
-
delayed(1) {
|
27
|
-
exchange = @channel.find_exchange 'commands'
|
28
|
-
@queue.when(:message_received => ->(message) { received << message })
|
29
|
-
exchange.publish ::ActiveSupport::JSON.encode(data), :routing_key => "commands.#{Euston.uuid.generate}"
|
30
|
-
}
|
31
|
-
|
32
|
-
done(2) {
|
33
|
-
received.should have(1).item
|
34
|
-
received.first.should == data
|
35
|
-
}
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context 'a command is pushed into the buffer by client code' do
|
40
|
-
amqp_before :each do
|
41
|
-
@buffer = Euston::RabbitMq::MessageBuffer.new @channel, :commands
|
42
|
-
@command = Factory.command
|
43
|
-
@buffer.push @command
|
44
|
-
@read_model = Euston::RabbitMq::ReadModel::MessageBuffer.new 'buffered_commands'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'writes the command to persistent storage' do
|
48
|
-
@read_model.find_due_messages.to_a.should have(1).items
|
49
|
-
|
50
|
-
due_command = @read_model.find_due_messages.first
|
51
|
-
due_command['_id'].should == @command[:headers][:id]
|
52
|
-
due_command['type'].should == @command[:headers][:type]
|
53
|
-
due_command['json'].should == ::ActiveSupport::JSON.encode(@command)
|
54
|
-
|
55
|
-
done(0.3)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'removes the command from the buffer storage when it is obtained from the buffer queue' do
|
59
|
-
delayed(1) {
|
60
|
-
@buffer.dispatch_due_messages
|
61
|
-
}
|
62
|
-
|
63
|
-
done(3) {
|
64
|
-
@read_model.get_by_id(@command[:headers][:id]).should be_nil
|
65
|
-
}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/spec/event_buffer_spec.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
2
|
-
|
3
|
-
unless RUBY_PLATFORM == 'java'
|
4
|
-
describe 'event buffer' do
|
5
|
-
include EustonRmqSpec
|
6
|
-
|
7
|
-
context 'the event buffer is created' do
|
8
|
-
include Euston::RabbitMq::Queues
|
9
|
-
|
10
|
-
amqp_before do
|
11
|
-
@buffer = Euston::RabbitMq::MessageBuffer.new @channel, :events
|
12
|
-
@queue = get_queue @channel, :events_buffer
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'declares the events buffer queue' do
|
16
|
-
done(0.5) {
|
17
|
-
@queue.name.should == 'events_buffer'
|
18
|
-
@queue.opts.should include(:durable => true)
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'binds the event buffer queue to the events exchange with a wildcard routing key' do
|
23
|
-
received = []
|
24
|
-
data = Factory.command
|
25
|
-
|
26
|
-
delayed(0.3) {
|
27
|
-
exchange = @channel.find_exchange 'events'
|
28
|
-
@queue.when(:message_received => ->(message) { received << message })
|
29
|
-
exchange.publish ::ActiveSupport::JSON.encode(data), :routing_key => "events.#{Euston.uuid.generate}"
|
30
|
-
}
|
31
|
-
|
32
|
-
done(2) {
|
33
|
-
received.should have(1).item
|
34
|
-
received.first.should == data
|
35
|
-
}
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context 'an event is pushed into the buffer by client code' do
|
40
|
-
amqp_before :each do
|
41
|
-
@buffer = Euston::RabbitMq::MessageBuffer.new @channel, :events
|
42
|
-
@command = Factory.command
|
43
|
-
@buffer.push @command
|
44
|
-
@read_model = Euston::RabbitMq::ReadModel::MessageBuffer.new 'buffered_events'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'writes the event to persistent storage' do
|
48
|
-
@read_model.find_due_messages.to_a.should have(1).items
|
49
|
-
|
50
|
-
due_command = @read_model.find_due_messages.first
|
51
|
-
due_command['_id'].should == @command[:headers][:id]
|
52
|
-
due_command['type'].should == @command[:headers][:type]
|
53
|
-
due_command['json'].should == ::ActiveSupport::JSON.encode(@command)
|
54
|
-
|
55
|
-
done(0.3)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'removes the event from the buffer storage when it is obtained from the buffer queue' do
|
59
|
-
delayed(1) {
|
60
|
-
@buffer.dispatch_due_messages
|
61
|
-
}
|
62
|
-
|
63
|
-
done(3) {
|
64
|
-
@read_model.get_by_id(@command[:headers][:id]).should be_nil
|
65
|
-
}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
2
|
-
|
3
|
-
unless RUBY_PLATFORM == 'java'
|
4
|
-
describe 'exchange declaration' do
|
5
|
-
include EustonRmqSpec
|
6
|
-
|
7
|
-
context 'declaring the commands exchange' do
|
8
|
-
include Euston::RabbitMq::Exchanges
|
9
|
-
it 'declares the commands exchange' do
|
10
|
-
exchange = get_exchange @channel, :commands
|
11
|
-
|
12
|
-
done(0.3) {
|
13
|
-
exchange.name.should == 'commands'
|
14
|
-
exchange.should be_durable
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'declares the events exchange' do
|
19
|
-
exchange = get_exchange @channel, :events
|
20
|
-
|
21
|
-
done(0.3) {
|
22
|
-
exchange.name.should == 'events'
|
23
|
-
exchange.should be_durable
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
2
|
-
|
3
|
-
unless RUBY_PLATFORM == 'java'
|
4
|
-
module MessageFailureTracking
|
5
|
-
module FailingCommandHandlers
|
6
|
-
class DeliverWidget
|
7
|
-
class << self
|
8
|
-
attr_accessor :fail_next
|
9
|
-
end
|
10
|
-
|
11
|
-
include Euston::CommandHandler
|
12
|
-
|
13
|
-
version 1 do |headers, command|
|
14
|
-
if self.class.fail_next
|
15
|
-
raise 'User-generated failure'
|
16
|
-
else
|
17
|
-
publish headers, command
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe 'message failure tracking' do
|
25
|
-
include EustonRmqSpec
|
26
|
-
include Euston::RabbitMq::Exchanges
|
27
|
-
include Euston::RabbitMq::Queues
|
28
|
-
|
29
|
-
amqp_before do
|
30
|
-
@exchange = get_exchange @channel, :commands
|
31
|
-
@handler = ::MessageFailureTracking::FailingCommandHandlers::DeliverWidget
|
32
|
-
|
33
|
-
command_handlers = Euston::RabbitMq::CommandHandlerBindings.new @channel
|
34
|
-
command_handlers.add_namespace Euston::RabbitMq::CommandHandlers
|
35
|
-
command_handlers.add_namespace ::MessageFailureTracking::FailingCommandHandlers
|
36
|
-
command_handlers.finalize_bindings
|
37
|
-
|
38
|
-
event_handlers = Euston::RabbitMq::EventHandlerBindings.new @channel
|
39
|
-
event_handlers.add_namespace Euston::RabbitMq::EventHandlers
|
40
|
-
event_handlers.finalize_bindings
|
41
|
-
|
42
|
-
@command = { :headers => { :id => Euston.uuid.generate,
|
43
|
-
:type => 'deliver_widget',
|
44
|
-
:version => 1 },
|
45
|
-
:body => { :id => Euston.uuid.generate,
|
46
|
-
:name => 'Bill Smith' } }
|
47
|
-
end
|
48
|
-
|
49
|
-
def publish
|
50
|
-
json = ::ActiveSupport::JSON.encode(@command)
|
51
|
-
opts = default_publish_options.merge(:routing_key => 'commands.deliver_widget')
|
52
|
-
delayed(0.3) { @exchange.publish json, opts }
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'a failing command is published' do
|
56
|
-
it 'moves the message to the failed-messages queue' do
|
57
|
-
@handler.fail_next = true
|
58
|
-
received = 0
|
59
|
-
|
60
|
-
delayed(0.3) {
|
61
|
-
@queue = get_queue @channel, :message_failure
|
62
|
-
@queue.when(:message_received => ->(message) { received = received + 1 })
|
63
|
-
|
64
|
-
publish
|
65
|
-
}
|
66
|
-
|
67
|
-
done(3) {
|
68
|
-
received.should == 1
|
69
|
-
}
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'a failed message is retried' do
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|