euston-daemons 1.0.0-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 (34) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +101 -0
  3. data/LICENSE +19 -0
  4. data/Rakefile +161 -0
  5. data/euston-daemons.gemspec +69 -0
  6. data/lib/euston-daemons.rb +15 -0
  7. data/lib/euston-daemons/command_processor_daemon/config/environment.rb +14 -0
  8. data/lib/euston-daemons/command_processor_daemon/lib/components/command_handler_component.rb +55 -0
  9. data/lib/euston-daemons/command_processor_daemon/lib/daemon.rb +43 -0
  10. data/lib/euston-daemons/command_processor_daemon/lib/settings.rb +19 -0
  11. data/lib/euston-daemons/command_processor_daemon/rake_task.rb +31 -0
  12. data/lib/euston-daemons/event_processor_daemon/config/environment.rb +20 -0
  13. data/lib/euston-daemons/event_processor_daemon/lib/components/event_handler_component.rb +72 -0
  14. data/lib/euston-daemons/event_processor_daemon/lib/daemon.rb +72 -0
  15. data/lib/euston-daemons/event_processor_daemon/lib/settings.rb +22 -0
  16. data/lib/euston-daemons/event_processor_daemon/rake_task.rb +37 -0
  17. data/lib/euston-daemons/framework/basic_component.rb +33 -0
  18. data/lib/euston-daemons/framework/component_shutdown.rb +22 -0
  19. data/lib/euston-daemons/framework/daemon.rb +27 -0
  20. data/lib/euston-daemons/framework/queue.rb +71 -0
  21. data/lib/euston-daemons/message_buffer_daemon/config/environment.rb +26 -0
  22. data/lib/euston-daemons/message_buffer_daemon/lib/components/buffer_component.rb +74 -0
  23. data/lib/euston-daemons/message_buffer_daemon/lib/components/event_store_component.rb +51 -0
  24. data/lib/euston-daemons/message_buffer_daemon/lib/daemon.rb +48 -0
  25. data/lib/euston-daemons/message_buffer_daemon/lib/message_logger.rb +54 -0
  26. data/lib/euston-daemons/message_buffer_daemon/lib/publisher.rb +56 -0
  27. data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_buffer.rb +54 -0
  28. data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_log.rb +36 -0
  29. data/lib/euston-daemons/message_buffer_daemon/lib/settings.rb +14 -0
  30. data/lib/euston-daemons/message_buffer_daemon/lib/subscriber.rb +60 -0
  31. data/lib/euston-daemons/message_buffer_daemon/rake_task.rb +30 -0
  32. data/lib/euston-daemons/rake_task.rb +116 -0
  33. data/lib/euston-daemons/version.rb +5 -0
  34. metadata +235 -0
@@ -0,0 +1,26 @@
1
+ require_rel '../lib'
2
+
3
+ Safely::Strategy::Log.logger = EUSTON_LOG
4
+ Cqrs.uuid = Uuid
5
+ AMQP.settings.merge! ErbYaml.read(AMQP_CONFIG_PATH, EUSTON_ENV)
6
+ Euston::MessageBufferDaemon::Settings.configure ErbYaml.read(DAEMON_CONFIG_PATH, EUSTON_ENV)
7
+
8
+ hash = ErbYaml.read(MONGOID_CONFIG_PATH, EUSTON_ENV)
9
+
10
+ event_connection = Mongo::Connection.new hash[:host], hash[:port], :safe => hash[:safe] #, :logger => EUSTON_LOG
11
+ read_connection = Mongo::Connection.new hash[:host], hash[:port], :safe => hash[:safe] #, :logger => EUSTON_LOG
12
+
13
+ event_database = hash[:event_store_database]
14
+
15
+ Cqrs::RabbitMq.event_store_mongodb = Mongo::DB.new event_database, event_connection
16
+ Cqrs::RabbitMq.read_model_mongodb = Mongo::DB.new hash[:read_model_database], read_connection
17
+
18
+ EventStore::Persistence::Mongodb::Config.instance.logger = EUSTON_LOG
19
+ EventStore::Persistence::Mongodb::Config.instance.database = event_database
20
+
21
+ Cqrs::RabbitMq.event_store = EventStore::Persistence::Mongodb::MongoPersistenceFactory.build
22
+ Cqrs::RabbitMq.event_store.init
23
+
24
+ Cqrs::Repository.event_store = EventStore::OptimisticEventStore.new Cqrs::RabbitMq.event_store
25
+
26
+
@@ -0,0 +1,74 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ class BufferComponent
4
+ include Euston::Daemons::ComponentShutdown
5
+
6
+ class << self
7
+ def command_component pub_channel, sub_channel
8
+ self.new Publisher.commands_buffer(pub_channel), Subscriber.commands_buffer(sub_channel)
9
+ end
10
+
11
+ def event_component pub_channel, sub_channel
12
+ self.new Publisher.events_buffer(pub_channel), Subscriber.events_buffer(sub_channel)
13
+ end
14
+ end
15
+
16
+ attr_reader :pub_thread, :sub_thread
17
+ attr_writer :wait_time, :subscribe_timeout
18
+
19
+ def initialize pub_buffer, sub_buffer
20
+ @pub_buffer = pub_buffer
21
+ @sub_buffer = sub_buffer
22
+ end
23
+
24
+ def wait_time
25
+ @wait_time ||= 0.2
26
+ end
27
+
28
+ def subscribe_timeout
29
+ @subscribe_timeout ||= 2000
30
+ end
31
+
32
+ def thread_state
33
+ {:publish => @pub_thread.status, :subscribe => @sub_thread.status}
34
+ end
35
+
36
+ def stop
37
+ @pub_thread[:stop], @sub_thread[:stop] = true, true
38
+ @pub_buffer.disconnect
39
+ @sub_buffer.disconnect
40
+ end
41
+
42
+ def start
43
+ @pub_thread = Thread.new do
44
+ until Thread.current[:stop] do
45
+ begin
46
+ @msg_dispatched_count = 0
47
+ begin
48
+ @msg_dispatched_count = @pub_buffer.dispatch_due_messages
49
+ check_exception_and_shutdown
50
+ end until Thread.current[:stop] || @msg_dispatched_count.zero?
51
+ rescue => e
52
+ Thread.current[:exception] = e
53
+ end
54
+ check_exception_and_shutdown
55
+ sleep self.wait_time
56
+ end
57
+ end
58
+
59
+ @sub_thread = Thread.new do
60
+ until Thread.current[:stop] do
61
+ begin
62
+ @sub_buffer.dequeue_buffered_messages(self.subscribe_timeout)
63
+ check_exception_and_shutdown
64
+ rescue => e
65
+ Thread.current[:exception] = e
66
+ end
67
+ check_exception_and_shutdown
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,51 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ class EventStoreComponent
4
+ include Euston::Daemons::ComponentShutdown
5
+
6
+ attr_reader :cli_thread
7
+ attr_writer :wait_time
8
+
9
+ def initialize channel
10
+ @channel = channel
11
+ end
12
+
13
+ def wait_time
14
+ @wait_time ||= 0.2
15
+ end
16
+
17
+ def start
18
+ @cli_thread = Thread.new do
19
+ @event_buffer = Euston::MessageBufferDaemon::ReadModel::MessageBuffer.events
20
+ @event_store = Cqrs::RabbitMq.event_store
21
+ until Thread.current[:stop] do
22
+ begin
23
+ loop do
24
+ commits = @event_store.get_undispatched_commits
25
+ break if commits.empty?
26
+ commits.each do |commit|
27
+ commit.events.each { |event| @event_buffer.buffer_new_message event.to_hash }
28
+ @event_store.mark_commit_as_dispatched commit
29
+ end
30
+ end
31
+ rescue => e
32
+ Thread.current[:exception] = e
33
+ end
34
+ check_exception_and_shutdown
35
+ sleep self.wait_time
36
+ end
37
+ end
38
+ end
39
+
40
+ def thread_state
41
+ @cli_thread.status
42
+ end
43
+
44
+ def stop
45
+ @cli_thread[:stop] = true
46
+ @channel.disconnect
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,48 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ class Daemon
4
+ include Euston::Daemon
5
+
6
+ attr_reader :clients, :queue
7
+
8
+ def initialize()
9
+ @queue = Queue.new
10
+
11
+ @clients = {
12
+ :commands_logger => Euston::Daemons::BasicComponent.new(MessageLogger.commands_logger AMQP::Channel.new),
13
+ :events_logger => Euston::Daemons::BasicComponent.new(MessageLogger.events_logger AMQP::Channel.new),
14
+ :command_buffer_handler => BufferComponent.command_component(AMQP::Channel.new, AMQP::Channel.new),
15
+ :event_buffer_handler => BufferComponent.event_component(AMQP::Channel.new, AMQP::Channel.new),
16
+ :event_store_dispatcher => EventStoreComponent.new(AMQP::Channel.new)
17
+ }
18
+ end
19
+
20
+ def run
21
+ Thread.abort_on_exception = true
22
+
23
+ @clients.each do |name, component|
24
+ EUSTON_LOG.debug("Starting component: #{name}")
25
+ component.daemon = self
26
+ component.start
27
+ sleep 0.350
28
+ end
29
+
30
+ @clients.each do |name, component|
31
+ EUSTON_LOG.debug("Thread state of #{name}: #{component.thread_state.inspect}")
32
+ end
33
+
34
+ EUSTON_LOG.debug "Components started"
35
+
36
+ @queue.pop #<-------- stops here until interrupted
37
+
38
+ @clients.each do |name, component|
39
+ EUSTON_LOG.debug "Stopping component: #{name}"
40
+ component.stop
41
+ end
42
+
43
+ report_shutdown_reasons
44
+ EUSTON_LOG.debug "Components stopped"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ module Euston
2
+ class MessageLogger
3
+ class << self
4
+ def commands_logger channel
5
+ self.new channel, :commands
6
+ end
7
+
8
+ def events_logger channel
9
+ self.new channel, :events
10
+ end
11
+ end
12
+
13
+ include Cqrs::RabbitMq::Exchanges
14
+ include Cqrs::RabbitMq::Queues
15
+
16
+ def initialize channel, exchange_name
17
+ @channel = channel
18
+ @exchange = get_exchange channel, exchange_name
19
+ @read_model = Euston::MessageBufferDaemon::ReadModel::MessageLog.send exchange_name
20
+
21
+ @queue = get_queue channel, "#{exchange_name}_log"
22
+ @queue.bind @exchange, :routing_key => "#{exchange_name}.#"
23
+
24
+ @queue.when(:message_decode_failed => method(:log_failure),
25
+ :message_failed => method(:message_failed),
26
+ :message_received => method(:write_message_to_log))
27
+ end
28
+
29
+ def message_failed(message, error, header)
30
+ log_failure message, error
31
+ header.ack
32
+ end
33
+
34
+ def disconnect
35
+ @channel.disconnect
36
+ end
37
+
38
+ def start
39
+ @queue.safe_subscribe
40
+ end
41
+
42
+ def log_failure message, error
43
+ text = "A log queue subscription failed. [Error] #{error.message} [Payload] #{message}"
44
+ err = Cqrs::RabbitMq::MessageDecodeFailedError.new text
45
+ err.set_backtrace error.backtrace
46
+
47
+ Safely.report! err
48
+ end
49
+
50
+ def write_message_to_log message
51
+ @read_model.log_new_message message
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,56 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ class Publisher
4
+ class << self
5
+ def commands_buffer channel
6
+ self.new channel, :commands
7
+ end
8
+
9
+ def events_buffer channel
10
+ self.new channel, :events
11
+ end
12
+ end
13
+
14
+ include Cqrs::RabbitMq::Exchanges
15
+
16
+ def initialize channel, exchange_name
17
+ @read_model = Euston::MessageBufferDaemon::ReadModel::MessageBuffer.send exchange_name
18
+
19
+ @channel = channel
20
+ @exchange = get_exchange @channel, exchange_name
21
+ end
22
+
23
+ def disconnect
24
+ @channel.disconnect
25
+ end
26
+
27
+ def dispatch_one_due_message
28
+ if (message = @read_model.find_next_message)
29
+ @read_model.set_next_attempt message
30
+ protected_publish(message)
31
+ end
32
+ end
33
+
34
+ def dispatch_due_messages
35
+ dispatched = 0
36
+ @read_model.find_due_messages.each do |message|
37
+ @read_model.set_next_attempt message
38
+ break unless protected_publish(message)
39
+ dispatched += 1
40
+ end
41
+ dispatched
42
+ end
43
+
44
+ def protected_publish(message)
45
+ ret = true
46
+ begin
47
+ @exchange.publish message['json'], default_publish_options.merge(:routing_key => "#{@exchange.name}.#{message['type']}")
48
+ rescue NativeException => e
49
+ Thread.current[:exception] = e
50
+ ret = false
51
+ end
52
+ ret
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,54 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ module ReadModel
4
+ class MessageBuffer
5
+ class << self
6
+ def commands
7
+ self.new "buffered_commands", Cqrs::RabbitMq.read_model_mongodb
8
+ end
9
+
10
+ def events
11
+ self.new "buffered_events", Cqrs::RabbitMq.event_store_mongodb
12
+ end
13
+ end
14
+
15
+ def initialize name, mongodb
16
+ mongodb.create_collection name unless mongodb.collection_names.include? name
17
+
18
+ @collection = mongodb.collection name
19
+ @collection.ensure_index [ ['next_attempt', Mongo::ASCENDING] ], :unique => false, :name => "#{name}_next_attempt_index"
20
+ end
21
+
22
+ def buffer_new_message message
23
+ @collection.save({ 'doc_id' => message[:headers][:id],
24
+ 'type' => message[:headers][:type],
25
+ 'next_attempt' => (Time.now.to_f - 1.0),
26
+ 'json' => ActiveSupport::JSON.encode(message) }, :safe => { :fsync => true })
27
+ end
28
+
29
+ def find_next_message
30
+ query = { 'next_attempt' => { '$lte' => Time.now.to_f } }
31
+ @collection.find_one(query)
32
+ end
33
+
34
+ def find_due_messages
35
+ query = { 'next_attempt' => { '$lte' => Time.now.to_f }}
36
+ @collection.find(query)
37
+ end
38
+
39
+ def get_by_id id
40
+ @collection.find_one({ 'doc_id' => id }) #doc or nil
41
+ end
42
+
43
+ def set_next_attempt message
44
+ @collection.update({ 'doc_id' => message['doc_id'] },
45
+ { '$set' => { 'next_attempt' => Time.now.to_f + 10 } }, :safe => { :fsync => true })
46
+ end
47
+
48
+ def remove_published_message id
49
+ @collection.remove({ 'doc_id' => id }, :safe => { :fsync => true })
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ module ReadModel
4
+ class MessageLog
5
+ class << self
6
+ def commands
7
+ self.new "command_log", Cqrs::RabbitMq.read_model_mongodb
8
+ end
9
+
10
+ def events
11
+ self.new "event_log", Cqrs::RabbitMq.event_store_mongodb
12
+ end
13
+ end
14
+
15
+ def initialize name, mongodb
16
+ mongodb.create_collection name unless mongodb.collection_names.include? name
17
+
18
+ @collection = mongodb.collection name
19
+ @collection.ensure_index [ ['timestamp', Mongo::ASCENDING] ], :unique => false, :name => "#{name}_timestamp_index"
20
+ end
21
+
22
+ def find_all
23
+ @collection.find({}, { :sort => [ 'timestamp', Mongo::DESCENDING ] })
24
+ end
25
+
26
+ def log_new_message message
27
+ @collection.save({ '_id' => message[:headers][:id],
28
+ 'type' => message[:headers][:type],
29
+ 'version' => message[:headers][:version],
30
+ 'timestamp' => Time.now.to_f,
31
+ 'json' => ActiveSupport::JSON.encode(message) }, :safe => { :fsync => true })
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ module Settings
4
+ def self.configure(cfg=nil)
5
+ @config ||= {}
6
+ @config.merge!(cfg) if cfg && cfg.is_a?(Hash)
7
+ end
8
+
9
+ def self.debug
10
+ @config[:debug]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,60 @@
1
+ module Euston
2
+ module MessageBufferDaemon
3
+ class Subscriber
4
+ class << self
5
+ def commands_buffer channel
6
+ self.new channel, :commands
7
+ end
8
+
9
+ def events_buffer channel
10
+ self.new channel, :events
11
+ end
12
+ end
13
+
14
+ include Cqrs::RabbitMq::Exchanges
15
+ include Cqrs::RabbitMq::Queues
16
+ include Hollywood
17
+
18
+ def initialize channel, exchange_name
19
+ @read_model = Euston::MessageBufferDaemon::ReadModel::MessageBuffer.send exchange_name
20
+
21
+ @channel = channel
22
+ @channel.prefetch(1)
23
+ @exchange = get_exchange @channel, exchange_name
24
+ @queue = get_queue @channel, "#{exchange_name}_buffer"
25
+ @queue.bind @exchange, :routing_key => "#{exchange_name}.#"
26
+
27
+ @queue.when(:message_decode_failed => method(:log_failure),
28
+ :message_failed => method(:message_failed),
29
+ :message_received => method(:remove_message_from_buffer))
30
+
31
+ @consumer = @queue.consumer
32
+ end
33
+
34
+ def disconnect
35
+ @channel.disconnect
36
+ end
37
+
38
+ def dequeue_buffered_messages(timeout) #will block until message or timeout
39
+ @queue.safe_subscribe_with_timeout(@consumer, timeout)
40
+ end
41
+
42
+ def log_failure message, error
43
+ text = "A buffer queue subscription failed. [Error] #{error.message} [Payload] #{message}"
44
+ err = Cqrs::RabbitMq::MessageDecodeFailedError.new text
45
+ err.set_backtrace error.backtrace
46
+
47
+ Safely.report! err
48
+ end
49
+
50
+ def message_failed(message, error, header)
51
+ header.ack!
52
+ log_failure message, error
53
+ end
54
+
55
+ def remove_message_from_buffer message
56
+ @read_model.remove_published_message message[:headers][:id]
57
+ end
58
+ end
59
+ end
60
+ end