euston-daemons 1.0.1
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 +4 -0
- data/Gemfile.lock +96 -0
- data/LICENSE +19 -0
- data/Rakefile +161 -0
- data/euston-daemons.gemspec +72 -0
- data/lib/euston-daemons/command_processor_daemon/config/environment.rb +17 -0
- data/lib/euston-daemons/command_processor_daemon/lib/components/command_handler_component.rb +55 -0
- data/lib/euston-daemons/command_processor_daemon/lib/daemon.rb +43 -0
- data/lib/euston-daemons/command_processor_daemon/lib/settings.rb +19 -0
- data/lib/euston-daemons/command_processor_daemon/rake_task.rb +31 -0
- data/lib/euston-daemons/event_processor_daemon/config/environment.rb +23 -0
- data/lib/euston-daemons/event_processor_daemon/lib/components/event_handler_component.rb +72 -0
- data/lib/euston-daemons/event_processor_daemon/lib/daemon.rb +72 -0
- data/lib/euston-daemons/event_processor_daemon/lib/settings.rb +22 -0
- data/lib/euston-daemons/event_processor_daemon/rake_task.rb +37 -0
- data/lib/euston-daemons/framework/basic_component.rb +33 -0
- data/lib/euston-daemons/framework/component_shutdown.rb +22 -0
- data/lib/euston-daemons/framework/daemon.rb +27 -0
- data/lib/euston-daemons/framework/queue.rb +71 -0
- data/lib/euston-daemons/message_buffer_daemon/config/environment.rb +30 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/components/buffer_component.rb +74 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/components/event_store_component.rb +51 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/daemon.rb +48 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/message_logger.rb +54 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/publisher.rb +56 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_buffer.rb +54 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_log.rb +36 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/settings.rb +14 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/subscriber.rb +60 -0
- data/lib/euston-daemons/message_buffer_daemon/rake_task.rb +30 -0
- data/lib/euston-daemons/rake_task.rb +116 -0
- data/lib/euston-daemons/version.rb +5 -0
- data/lib/euston-daemons.rb +17 -0
- metadata +201 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Euston
|
2
|
+
module EventProcessorDaemon
|
3
|
+
module Settings
|
4
|
+
def self.configure(cfg=nil)
|
5
|
+
@config ||= {}
|
6
|
+
@config.merge!(cfg) if cfg && cfg.is_a?(Hash)
|
7
|
+
end
|
8
|
+
def self.server_instances
|
9
|
+
@config[:server_instances] || 2
|
10
|
+
end
|
11
|
+
def self.bus_port_base
|
12
|
+
(@config[:zmq_base_port] || 8200).to_i
|
13
|
+
end
|
14
|
+
def self.mongo_db_name
|
15
|
+
@config[:mongo_db_name]
|
16
|
+
end
|
17
|
+
def self.debug
|
18
|
+
@config[:debug]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Euston
|
2
|
+
class EventProcessorRakeTask < Euston::Daemons::RakeTask
|
3
|
+
attr_accessor :amqp_config_path, :daemon_config_path, :event_handler_namespaces, :i18n_locales_path, :mongoid_config_path
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@event_handler_namespaces = []
|
7
|
+
super(:event_processor_daemon)
|
8
|
+
end
|
9
|
+
|
10
|
+
def before_creating_task
|
11
|
+
@daemon_path = File.expand_path(File.dirname __FILE__) + File::SEPARATOR
|
12
|
+
@daemon_class = 'Euston::EventProcessorDaemon::Daemon'
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_paths
|
16
|
+
EUSTON_LOG.debug "AMQP config path: #{@amqp_config_path}"
|
17
|
+
Object.const_set :AMQP_CONFIG_PATH, @amqp_config_path
|
18
|
+
|
19
|
+
EUSTON_LOG.debug "Daemon config path: #{@daemon_config_path}"
|
20
|
+
Object.const_set :DAEMON_CONFIG_PATH, @daemon_config_path
|
21
|
+
|
22
|
+
EUSTON_LOG.debug "Event handler namespaces: #{@event_handler_namespaces}"
|
23
|
+
Object.const_set :EVENT_HANDLER_NAMESPACES, @event_handler_namespaces
|
24
|
+
|
25
|
+
EUSTON_LOG.debug "i18n locales path: #{@i18n_locales_path}"
|
26
|
+
Object.const_set :I18N_LOCALES_PATH, @i18n_locales_path
|
27
|
+
|
28
|
+
EUSTON_LOG.debug "Mongoid config path: #{@mongoid_config_path}"
|
29
|
+
Object.const_set :MONGOID_CONFIG_PATH, @mongoid_config_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_environment
|
33
|
+
EUSTON_LOG.debug "Loading environment"
|
34
|
+
require_rel 'config/environment.rb'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
class BasicComponent
|
4
|
+
include Euston::Daemons::ComponentShutdown
|
5
|
+
|
6
|
+
attr_reader :client, :cli_thread
|
7
|
+
|
8
|
+
def initialize(hosted_object)
|
9
|
+
@client = hosted_object
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@cli_thread = Thread.new do
|
14
|
+
begin
|
15
|
+
@client.start
|
16
|
+
rescue => e
|
17
|
+
Thread.current[:exception] = e
|
18
|
+
end
|
19
|
+
check_exception_and_shutdown
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def thread_state
|
24
|
+
@cli_thread.status
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
@cli_thread[:stop] = true
|
29
|
+
@client.disconnect if @client.respond_to?(:disconnect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module ComponentShutdown
|
4
|
+
attr_writer :daemon
|
5
|
+
|
6
|
+
def check_exception_and_shutdown(errors=[])
|
7
|
+
if errors.compact.empty?
|
8
|
+
errors << Thread.current[:exception]
|
9
|
+
return if errors.compact.empty?
|
10
|
+
end
|
11
|
+
errors.compact.each do |e|
|
12
|
+
if e.respond_to?(:cause) #its a serious (Java?) exception
|
13
|
+
Thread.current[:stop] = true
|
14
|
+
@daemon.irrecoverable_signal_from_component! e
|
15
|
+
else
|
16
|
+
Safely.report! e
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemon
|
3
|
+
def errors
|
4
|
+
@errors ||= Queue.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def irrecoverable_signal_from_component!(e)
|
8
|
+
self.errors.push e
|
9
|
+
@queue.push "SHUTDOWN"
|
10
|
+
end
|
11
|
+
|
12
|
+
def report_shutdown_reasons
|
13
|
+
sleep(0.25)
|
14
|
+
reports = []
|
15
|
+
until self.errors.empty? do
|
16
|
+
e = self.errors.pop(true) rescue nil
|
17
|
+
next if e.nil?
|
18
|
+
case e
|
19
|
+
when NativeException
|
20
|
+
Safely.report!(e.cause)
|
21
|
+
else
|
22
|
+
Safely.report!(e)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class RabbitMQClient
|
2
|
+
class Queue
|
3
|
+
include Hollywood
|
4
|
+
|
5
|
+
attr_writer :timeout
|
6
|
+
|
7
|
+
def delivery_timeout
|
8
|
+
@timeout ||= 500
|
9
|
+
end
|
10
|
+
|
11
|
+
def safe_subscribe
|
12
|
+
_consumer = self.consumer
|
13
|
+
until Thread.current[:stop] do
|
14
|
+
safe_subscribe_with_timeout(_consumer, self.delivery_timeout)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def safe_handle_message(msg)
|
19
|
+
begin
|
20
|
+
message = JSON.parse_sym(msg.body)
|
21
|
+
begin
|
22
|
+
callback :message_received, message
|
23
|
+
msg.ack!
|
24
|
+
rescue Euston::EventStore::ConcurrencyError
|
25
|
+
msg.reject! true #requeue
|
26
|
+
rescue => e
|
27
|
+
callback :message_failed, message, e, msg
|
28
|
+
Safely.report! e
|
29
|
+
end
|
30
|
+
rescue => e
|
31
|
+
callback :message_decode_failed, msg.body, e
|
32
|
+
msg.ack!
|
33
|
+
Safely.report! e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def consumer(auto_ack=false)
|
38
|
+
consumer = QueueingConsumer.new(@channel)
|
39
|
+
@channel.basic_consume(@name, auto_ack, consumer)
|
40
|
+
consumer
|
41
|
+
end
|
42
|
+
|
43
|
+
def safe_subscribe_with_timeout(consumer, timeout=500)
|
44
|
+
loop do
|
45
|
+
delivery = nil
|
46
|
+
begin
|
47
|
+
delivery = consumer.next_delivery(timeout)
|
48
|
+
rescue NativeException => e
|
49
|
+
Thread.current[:exception] = e
|
50
|
+
break
|
51
|
+
end
|
52
|
+
break if delivery.nil?
|
53
|
+
remsg = ReactiveMessage.new(@channel, delivery, String.from_java_bytes(delivery.get_body))
|
54
|
+
safe_handle_message remsg
|
55
|
+
@channel.basic_ack(delivery.envelope.delivery_tag, false) if remsg.should_acknowledge?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ReactiveMessage
|
61
|
+
def reject(opts={})
|
62
|
+
self.reject!(opts.fetch(:requeue, true))
|
63
|
+
end
|
64
|
+
def ack(multiple=false)
|
65
|
+
self.ack!
|
66
|
+
end
|
67
|
+
def method()
|
68
|
+
self.envelope
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'jessica'
|
2
|
+
require 'euston'
|
3
|
+
require 'euston-eventstore'
|
4
|
+
require 'euston-rabbitmq'
|
5
|
+
|
6
|
+
require_rel '../lib'
|
7
|
+
|
8
|
+
Safely::Strategy::Log.logger = EUSTON_LOG
|
9
|
+
AMQP.settings.merge! ErbYaml.read(AMQP_CONFIG_PATH, EUSTON_ENV)
|
10
|
+
Euston::MessageBufferDaemon::Settings.configure ErbYaml.read(DAEMON_CONFIG_PATH, EUSTON_ENV)
|
11
|
+
|
12
|
+
hash = ErbYaml.read(MONGOID_CONFIG_PATH, EUSTON_ENV)
|
13
|
+
|
14
|
+
event_connection = Mongo::Connection.new hash[:host], hash[:port], :safe => hash[:safe] #, :logger => EUSTON_LOG
|
15
|
+
read_connection = Mongo::Connection.new hash[:host], hash[:port], :safe => hash[:safe] #, :logger => EUSTON_LOG
|
16
|
+
|
17
|
+
event_database = hash[:event_store_database]
|
18
|
+
|
19
|
+
Euston::RabbitMq.event_store_mongodb = Mongo::DB.new event_database, event_connection
|
20
|
+
Euston::RabbitMq.read_model_mongodb = Mongo::DB.new hash[:read_model_database], read_connection
|
21
|
+
|
22
|
+
Euston::EventStore::Persistence::Mongodb::Config.instance.logger = EUSTON_LOG
|
23
|
+
Euston::EventStore::Persistence::Mongodb::Config.instance.database = event_database
|
24
|
+
|
25
|
+
Euston::RabbitMq.event_store = Euston::EventStore::Persistence::Mongodb::MongoPersistenceFactory.build
|
26
|
+
Euston::RabbitMq.event_store.init
|
27
|
+
|
28
|
+
Euston::Repository.event_store = Euston::EventStore::OptimisticEventStore.new Euston::RabbitMq.event_store
|
29
|
+
|
30
|
+
|
@@ -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 = Euston::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 Euston::RabbitMq::Exchanges
|
14
|
+
include Euston::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 = Euston::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 Euston::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", Euston::RabbitMq.read_model_mongodb
|
8
|
+
end
|
9
|
+
|
10
|
+
def events
|
11
|
+
self.new "buffered_events", Euston::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", Euston::RabbitMq.read_model_mongodb
|
8
|
+
end
|
9
|
+
|
10
|
+
def events
|
11
|
+
self.new "event_log", Euston::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
|