euston-daemons 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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