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.
Files changed (46) hide show
  1. data/Gemfile +2 -0
  2. data/Rakefile +0 -21
  3. data/euston-rabbitmq.gemspec +26 -38
  4. data/lib/euston-rabbitmq/{bindings → euston}/command_handler_binder.rb +2 -2
  5. data/lib/euston-rabbitmq/{errors.rb → euston/errors.rb} +1 -1
  6. data/lib/euston-rabbitmq/{bindings → euston}/event_handler_binder.rb +4 -4
  7. data/lib/euston-rabbitmq/{exchanges.rb → euston/exchanges.rb} +1 -1
  8. data/lib/euston-rabbitmq/{bindings → euston}/handler_binder.rb +2 -1
  9. data/lib/euston-rabbitmq/{queues.rb → euston/queues.rb} +1 -1
  10. data/lib/euston-rabbitmq/{subscriptions/retriable_subscription.rb → euston/retrying_subscription.rb} +17 -16
  11. data/lib/euston-rabbitmq/rabbitmq_client/queue.rb +70 -0
  12. data/lib/euston-rabbitmq/rabbitmq_client/reactive_message.rb +15 -0
  13. data/lib/euston-rabbitmq/{constant_loader.rb → reflection/constant_loader.rb} +0 -0
  14. data/lib/euston-rabbitmq/{handler_finder.rb → reflection/handler_finder.rb} +0 -0
  15. data/lib/euston-rabbitmq/{handler_reference.rb → reflection/handler_reference.rb} +0 -0
  16. data/lib/euston-rabbitmq/version.rb +1 -1
  17. data/lib/euston-rabbitmq.rb +8 -22
  18. data/spec/euston/command_handler_binder_spec.rb +24 -0
  19. data/spec/euston/event_handler_binder_spec.rb +36 -0
  20. data/spec/euston/exchanges_spec.rb +27 -0
  21. data/spec/euston/queues_spec.rb +24 -0
  22. data/spec/euston/retrying_subscription_spec.rb +74 -0
  23. data/spec/rabbitmq_client/queue_spec.rb +69 -0
  24. data/spec/{constant_loader_spec.rb → reflection/constant_loader_spec.rb} +3 -1
  25. data/spec/{handler_finder_spec.rb → reflection/handler_finder_spec.rb} +3 -1
  26. data/spec/spec_helper.rb +19 -52
  27. data/spec/support/filters.rb +10 -0
  28. data/spec/support/queue_subscription_thread_harness.rb +29 -0
  29. data/spec/support/rabbitmqadmin.rb +74 -0
  30. metadata +60 -76
  31. data/Gemfile.lock +0 -76
  32. data/lib/euston-rabbitmq/command_handlers/retry_failed_message.rb +0 -29
  33. data/lib/euston-rabbitmq/event_handlers/message_failure.rb +0 -27
  34. data/lib/euston-rabbitmq/message_buffer.rb +0 -67
  35. data/lib/euston-rabbitmq/message_logger.rb +0 -50
  36. data/lib/euston-rabbitmq/queue.rb +0 -30
  37. data/lib/euston-rabbitmq/read_model/failed_message.rb +0 -36
  38. data/lib/euston-rabbitmq/read_model/message_buffer.rb +0 -57
  39. data/lib/euston-rabbitmq/read_model/message_log.rb +0 -37
  40. data/spec/command_buffer_spec.rb +0 -69
  41. data/spec/event_buffer_spec.rb +0 -69
  42. data/spec/exchange_declaration_spec.rb +0 -28
  43. data/spec/message_failure_spec.rb +0 -77
  44. data/spec/mt_safe_queue_subscription_spec.rb +0 -72
  45. data/spec/safe_queue_subscription_spec.rb +0 -50
  46. 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
@@ -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
@@ -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