euston-daemons 1.1.0-java → 1.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/euston-daemons.gemspec +27 -52
- data/lib/euston-daemons/euston/daemon.rb +7 -1
- data/lib/euston-daemons/euston/daemon_component.rb +9 -49
- data/lib/euston-daemons/euston/daemon_component_host.rb +66 -0
- data/lib/euston-daemons/euston/daemon_environment.rb +10 -5
- data/lib/euston-daemons/euston/exceptions.rb +9 -0
- data/lib/euston-daemons/euston/stopwatch.rb +15 -0
- data/lib/euston-daemons/pipeline/config/environment.rb +74 -0
- data/lib/euston-daemons/pipeline/lib/command_logger/component.rb +54 -0
- data/lib/euston-daemons/pipeline/lib/command_logger/log.rb +31 -0
- data/lib/euston-daemons/pipeline/lib/command_processor/component.rb +50 -0
- data/lib/euston-daemons/pipeline/lib/command_processor/default_handlers/retry_failed_message.rb +34 -0
- data/lib/euston-daemons/pipeline/lib/command_processor/failed_message.rb +36 -0
- data/lib/euston-daemons/pipeline/lib/daemon.rb +77 -0
- data/lib/euston-daemons/pipeline/lib/event_processor/component.rb +67 -0
- data/lib/euston-daemons/pipeline/lib/event_processor/default_handlers/message_failure.rb +30 -0
- data/lib/euston-daemons/pipeline/lib/event_store_dispatcher/component.rb +68 -0
- data/lib/euston-daemons/pipeline/lib/message_buffer/buffer.rb +85 -0
- data/lib/euston-daemons/pipeline/lib/message_buffer/component.rb +59 -0
- data/lib/euston-daemons/pipeline/lib/snapshotter/component.rb +48 -0
- data/lib/euston-daemons/pipeline/rake_task.rb +45 -0
- data/lib/euston-daemons/rake_task.rb +40 -19
- data/lib/euston-daemons/rake_tasks.rb +1 -3
- data/lib/euston-daemons/version.rb +1 -1
- data/lib/euston-daemons.rb +3 -1
- data/spec/daemons/{command_handler_spec.rb → command_processor_spec.rb} +3 -3
- data/spec/daemons/{event_handler_spec.rb → event_processor_spec.rb} +2 -2
- data/spec/daemons/{command_buffer_publisher_spec.rb → message_buffer_spec.rb} +19 -23
- data/spec/daemons/{snapshot_client_spec.rb → snapshotter_spec.rb} +3 -5
- data/spec/spec_helper.rb +23 -9
- data/spec/support/filters.rb +4 -5
- metadata +73 -88
- data/lib/euston-daemons/command_processor_daemon/config/environment.rb +0 -34
- data/lib/euston-daemons/command_processor_daemon/lib/clients/command_handler.rb +0 -37
- data/lib/euston-daemons/command_processor_daemon/lib/command_handlers/retry_failed_message.rb +0 -35
- data/lib/euston-daemons/command_processor_daemon/lib/daemon.rb +0 -22
- data/lib/euston-daemons/command_processor_daemon/lib/mongo_models/failed_message.rb +0 -34
- data/lib/euston-daemons/command_processor_daemon/rake_task.rb +0 -36
- data/lib/euston-daemons/euston/daemon_client.rb +0 -24
- data/lib/euston-daemons/event_processor_daemon/config/environment.rb +0 -36
- data/lib/euston-daemons/event_processor_daemon/lib/clients/event_handler.rb +0 -42
- data/lib/euston-daemons/event_processor_daemon/lib/daemon.rb +0 -24
- data/lib/euston-daemons/event_processor_daemon/lib/event_handlers/message_failure.rb +0 -27
- data/lib/euston-daemons/event_processor_daemon/rake_task.rb +0 -39
- data/lib/euston-daemons/message_buffer_daemon/config/environment.rb +0 -42
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/command_buffer_cleanup.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/command_buffer_publisher.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/command_logger.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/euston_exchange_accessors.rb +0 -15
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/event_buffer_cleanup.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/event_buffer_publisher.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/event_logger.rb +0 -9
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/event_store_dispatcher.rb +0 -25
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/message_buffer_cleanup.rb +0 -52
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/message_buffer_publisher.rb +0 -37
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/message_logger.rb +0 -45
- data/lib/euston-daemons/message_buffer_daemon/lib/clients/mongo_model_accessors.rb +0 -21
- data/lib/euston-daemons/message_buffer_daemon/lib/daemon.rb +0 -17
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/command_buffer.rb +0 -11
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/command_log.rb +0 -11
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/event_buffer.rb +0 -11
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/event_log.rb +0 -11
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/message_buffer.rb +0 -57
- data/lib/euston-daemons/message_buffer_daemon/lib/mongo_models/message_log.rb +0 -29
- data/lib/euston-daemons/message_buffer_daemon/rake_task.rb +0 -32
- data/lib/euston-daemons/snapshot_daemon/lib/clients/snapshotter.rb +0 -43
- data/sample/Rakefile +0 -63
- data/sample/amqp_config.yml +0 -14
- data/sample/command_handlers.rb +0 -17
- data/sample/command_processor_daemon_config.yml +0 -9
- data/sample/event_handlers.rb +0 -21
- data/sample/event_processor_daemon_config.yml +0 -8
- data/sample/message_buffer_daemon_config.yml +0 -8
- data/sample/mongoid_config.yml +0 -13
- data/sample/pids/.placeholder +0 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
class Daemon < Euston::Daemon
|
5
|
+
private
|
6
|
+
|
7
|
+
def env
|
8
|
+
DaemonEnvironment
|
9
|
+
end
|
10
|
+
|
11
|
+
def post_shutdown_cleanup
|
12
|
+
env.amqp_connection.close
|
13
|
+
end
|
14
|
+
|
15
|
+
def pre_registration_setup
|
16
|
+
channel = env.amqp_connection.create_channel
|
17
|
+
|
18
|
+
begin
|
19
|
+
handler_finder = RabbitMq::HandlerFinder.new [AggregateRoot, Euston::CommandHandler], @log
|
20
|
+
handler_finder.namespaces.push Pipeline::CommandProcessor::DefaultHandlers, *DaemonEnvironment.command_handler_namespaces
|
21
|
+
@command_handler_references = handler_finder.find
|
22
|
+
|
23
|
+
binder = RabbitMq::CommandHandlerBinder.new channel, @command_handler_references, @log
|
24
|
+
binder.ensure_bindings_exist
|
25
|
+
|
26
|
+
handler_finder = RabbitMq::HandlerFinder.new [Euston::EventHandler], @log
|
27
|
+
handler_finder.namespaces.push Pipeline::EventProcessor::DefaultHandlers, *DaemonEnvironment.event_handler_namespaces
|
28
|
+
@event_handler_references = handler_finder.find
|
29
|
+
|
30
|
+
binder = RabbitMq::EventHandlerBinder.new channel, @event_handler_references, @log
|
31
|
+
binder.ensure_bindings_exist
|
32
|
+
rescue NativeException => e
|
33
|
+
@log.error "Caught the following native exception:\n\n#{e}\n\nDid you forget to create your rabbitmq vhost and user for this environment?"
|
34
|
+
raise e
|
35
|
+
ensure
|
36
|
+
channel.close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def register_components
|
41
|
+
(1..env.command_loggers).each do |i|
|
42
|
+
component = CommandLogger::Component.new env.amqp_connection.create_channel, i, @log
|
43
|
+
register_component "command_logger_#{i}".to_sym, component
|
44
|
+
end
|
45
|
+
|
46
|
+
(1..env.command_processors).each do |i|
|
47
|
+
component = CommandProcessor::Component.new env.amqp_connection.create_channel, @command_handler_references, i, @log
|
48
|
+
register_component "command_processor_#{i}".to_sym, component
|
49
|
+
end
|
50
|
+
|
51
|
+
(1..env.event_store_dispatchers).each do |i|
|
52
|
+
component = EventStoreDispatcher::Component.new env.amqp_connection.create_channel, "#{Mac.addr.to_s} #{i}", @log
|
53
|
+
component = register_component "event_store_dispatcher_#{i}", component
|
54
|
+
component.wait_time = env.event_store_dispatcher_wait_time
|
55
|
+
end
|
56
|
+
|
57
|
+
(1..env.message_buffers).each do |i|
|
58
|
+
component = MessageBuffer::Component.new env.amqp_connection.create_channel, "#{Mac.addr.to_s} #{i}", @log
|
59
|
+
component = register_component "message_buffer_#{i}", component
|
60
|
+
component.wait_time = env.message_buffer_wait_time
|
61
|
+
end
|
62
|
+
|
63
|
+
(1..env.snapshotters).each do |i|
|
64
|
+
component = Snapshotter::Component.new Euston::Repository.event_store, env.snapshot_threshold, i, @log
|
65
|
+
component = register_component "snapshotter_#{i}", component
|
66
|
+
component.wait_time = env.snapshotter_wait_time
|
67
|
+
end
|
68
|
+
|
69
|
+
(1..env.event_processors).each do |i|
|
70
|
+
component = EventProcessor::Component.new env.amqp_connection.create_channel, @event_handler_references, i, @log
|
71
|
+
register_component "event_processor_#{i}".to_sym, component
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module EventProcessor
|
5
|
+
class Component < Euston::DaemonComponent
|
6
|
+
include Euston::EventHandlerPrivateMethodNames
|
7
|
+
|
8
|
+
def initialize channel, references, id = 1, logger = Euston::NullLogger.instance
|
9
|
+
@channel = channel
|
10
|
+
@channel.prefetch = 1
|
11
|
+
@id = id
|
12
|
+
@references = references
|
13
|
+
@log = logger
|
14
|
+
@process_method = method(:process_message)
|
15
|
+
@stopwatch = Stopwatch.new.when(:finished => method(:log_elapsed_time))
|
16
|
+
|
17
|
+
@subscriptions = @references.map do |r|
|
18
|
+
{ :reference => r,
|
19
|
+
:subscription => RabbitMq::RetryingSubscription.new(@channel, r.name.to_s.underscore, @log)
|
20
|
+
.when(:message_received => @process_method) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def next_iteration
|
27
|
+
@subscriptions.each do |s|
|
28
|
+
@reference = s[:reference]
|
29
|
+
s[:subscription].get
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def process_message message
|
34
|
+
@stopwatch.time do
|
35
|
+
event_headers = EventHeaders.from_hash message[:headers]
|
36
|
+
event_body = message[:body]
|
37
|
+
|
38
|
+
handler_is_aggregate_root = @reference.handler.include? Euston::AggregateRoot
|
39
|
+
|
40
|
+
if handler_is_aggregate_root
|
41
|
+
id_getter_method = self.class.id_from_event_method_name event_headers.type, event_headers.version
|
42
|
+
id = @reference.handler.send id_getter_method, event_body
|
43
|
+
|
44
|
+
raise "Unable to extract the aggregate root id from an #{event_headers.type} event. Did you forget to add an { :id => :field_name } argument to your subscribes block?" if id.nil?
|
45
|
+
|
46
|
+
handler_instance = Euston::Repository.find @reference.handler, id
|
47
|
+
handler_instance.log = @log
|
48
|
+
handler_instance.consume_event_subscription event_headers.freeze, event_body.freeze
|
49
|
+
|
50
|
+
Euston::Repository.save handler_instance
|
51
|
+
else
|
52
|
+
handler_method = self.class.event_handler_method_name event_headers.type, event_headers.version
|
53
|
+
handler_instance = @reference.handler.new
|
54
|
+
handler_instance.log = @log if handler_instance.respond_to? :log=
|
55
|
+
handler_instance.send handler_method, event_headers.freeze, event_body.freeze
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def log_elapsed_time elapsed_time
|
61
|
+
@log.debug "Event handler client #{@reference.name.to_s.underscore} processed a message in #{elapsed_time} sec(s)"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module EventProcessor
|
5
|
+
module DefaultHandlers
|
6
|
+
class MessageFailure
|
7
|
+
include Euston::EventHandler
|
8
|
+
|
9
|
+
subscribes :message_failed, 1 do |headers, event|
|
10
|
+
headers = event[:message][:headers].dup
|
11
|
+
failure = { :message_id => headers.delete(:id),
|
12
|
+
:type => headers.delete(:type),
|
13
|
+
:version => headers.delete(:version),
|
14
|
+
:message_timestamp => headers.delete(:timestamp),
|
15
|
+
:routing_key => event[:routing_key],
|
16
|
+
:body => event[:message][:body],
|
17
|
+
:headers => headers,
|
18
|
+
:error => event[:error],
|
19
|
+
:backtrace => event[:backtrace],
|
20
|
+
:failure_timestamp => Time.now.to_f }
|
21
|
+
|
22
|
+
failed_messages = CommandProcessor::FailedMessage.new DaemonEnvironment.event_store_mongodb
|
23
|
+
failed_messages.log_failure failure
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module EventStoreDispatcher
|
5
|
+
class Component < DaemonComponent
|
6
|
+
extend RabbitMq::Exchanges
|
7
|
+
|
8
|
+
def initialize channel, id = 1, log = Euston::NullLogger.instance
|
9
|
+
@channel = channel
|
10
|
+
@channel.tx_select
|
11
|
+
@id = id
|
12
|
+
@log = log
|
13
|
+
@event_store = DaemonEnvironment.event_store
|
14
|
+
@stopwatch = Stopwatch.new.when(:finished => method(:log_elapsed_time))
|
15
|
+
@commands_exchange = self.class.get_exchange channel, :commands
|
16
|
+
@events_exchange = self.class.get_exchange channel, :events
|
17
|
+
@buffer = MessageBuffer::Buffer.new DaemonEnvironment.event_store_mongodb
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def log_elapsed_time elapsed_time
|
23
|
+
@log.debug "Event store dispatcher #{@id} dispatched #{@commits_dispatched} commit(s) in #{elapsed_time} sec(s)" if @commits_dispatched > 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def next_iteration
|
27
|
+
@commits_dispatched = 0
|
28
|
+
|
29
|
+
@stopwatch.time do
|
30
|
+
begin
|
31
|
+
@event_store.take_ownership_of_undispatched_commits @id
|
32
|
+
@commits = @event_store.get_undispatched_commits @id
|
33
|
+
|
34
|
+
begin
|
35
|
+
@commits.each do |commit|
|
36
|
+
commit.events.each do |event|
|
37
|
+
hash = event.to_hash
|
38
|
+
@events_exchange.publish ActiveSupport::JSON.encode(hash), self.class.default_publish_options.merge(:routing_key => "events.#{hash[:headers][:type]}")
|
39
|
+
end
|
40
|
+
|
41
|
+
commit.commands.each do |command|
|
42
|
+
hash = command.to_hash
|
43
|
+
|
44
|
+
if hash[:headers][:dispatch_at].nil?
|
45
|
+
@commands_exchange.publish ActiveSupport::JSON.encode(hash), self.class.default_publish_options.merge(:routing_key => "commands.#{hash[:headers][:type]}")
|
46
|
+
else
|
47
|
+
@buffer.enqueue :commands, hash, hash[:headers][:dispatch_at]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@channel.tx_commit
|
53
|
+
rescue StandardError => e
|
54
|
+
@channel.tx_rollback
|
55
|
+
raise e
|
56
|
+
end
|
57
|
+
|
58
|
+
@event_store.mark_commits_as_dispatched @commits
|
59
|
+
|
60
|
+
@commits_dispatched += @commits.size
|
61
|
+
end until stopped || @commits.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module MessageBuffer
|
5
|
+
class Buffer
|
6
|
+
def initialize mongodb
|
7
|
+
name = 'message_buffer'
|
8
|
+
mongodb.create_collection name unless mongodb.collection_names.include? name
|
9
|
+
|
10
|
+
@name = name
|
11
|
+
@collection = mongodb.collection name
|
12
|
+
|
13
|
+
@collection.ensure_index [ ['message_id', Mongo::ASCENDING] ],
|
14
|
+
:unique => false,
|
15
|
+
:name => "#{name}_message_id_index"
|
16
|
+
|
17
|
+
@collection.ensure_index [ ['component_id', Mongo::ASCENDING],
|
18
|
+
['dispatch_at', Mongo::ASCENDING] ],
|
19
|
+
:unique => false,
|
20
|
+
:name => "#{name}_component_id_dispatch_at_index"
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
def delete_dispatched_messages component_id
|
26
|
+
@collection.remove({ 'component_id' => component_id }, :multi => true)
|
27
|
+
end
|
28
|
+
|
29
|
+
def enqueue exchange, message, dispatch_at = nil
|
30
|
+
messages = message
|
31
|
+
messages = [{ :hash => message, :dispatch_at => dispatch_at }] unless messages.is_a? Array
|
32
|
+
|
33
|
+
messages = messages.map do |m|
|
34
|
+
message_is_well_formed = m.is_a?(Hash) && m.has_key?(:hash) && m.has_key?(:dispatch_at)
|
35
|
+
m = { :hash => m, :dispatch_at => dispatch_at } unless message_is_well_formed
|
36
|
+
map_to_document exchange, m
|
37
|
+
end
|
38
|
+
|
39
|
+
@collection.insert(messages) unless messages.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_dispatchable_messages component_id
|
43
|
+
query = { 'component_id' => component_id }
|
44
|
+
fields = ['exchange', 'type', 'json']
|
45
|
+
sort = [ 'dispatch_at', Mongo::ASCENDING ]
|
46
|
+
|
47
|
+
@collection.find query, :fields => fields, :sort => sort
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_by_id id
|
51
|
+
@collection.find_one 'message_id' => id
|
52
|
+
end
|
53
|
+
|
54
|
+
def take_ownership_of_dispatchable_messages component_id
|
55
|
+
new_messages_eligible_for_dispatch = { 'component_id' => '',
|
56
|
+
'dispatch_at' => { '$lte' => Time.now.to_f } }
|
57
|
+
|
58
|
+
messages_stuck_in_other_components = { 'component_id' => { '$ne' => '' },
|
59
|
+
'dispatch_at' => { '$lte' => Time.now.to_f - 60 } }
|
60
|
+
|
61
|
+
query = { '$or' => [ new_messages_eligible_for_dispatch, messages_stuck_in_other_components ] }
|
62
|
+
doc = { '$set' => { 'component_id' => component_id } }
|
63
|
+
|
64
|
+
@collection.update query, doc, :multi => true
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def map_to_document exchange, message
|
70
|
+
hash = message[:hash]
|
71
|
+
dispatch_at = (message[:dispatch_at] || Time.now.to_f).to_f
|
72
|
+
|
73
|
+
{ 'message_id' => hash[:headers][:id],
|
74
|
+
'exchange' => exchange,
|
75
|
+
'type' => hash[:headers][:type],
|
76
|
+
'component_id' => '',
|
77
|
+
'dispatch_at' => dispatch_at,
|
78
|
+
'dispatch_at_for_humans' => Time.at(dispatch_at),
|
79
|
+
'json' => ActiveSupport::JSON.encode(hash) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module MessageBuffer
|
5
|
+
class Component < Euston::DaemonComponent
|
6
|
+
extend RabbitMq::Exchanges
|
7
|
+
|
8
|
+
def initialize channel, id = 1, logger = Euston::NullLogger.instance
|
9
|
+
@channel = channel
|
10
|
+
@channel.tx_select
|
11
|
+
@id = "message_buffer #{id}"
|
12
|
+
@log = logger
|
13
|
+
@buffer = Buffer.new DaemonEnvironment.event_store_mongodb
|
14
|
+
@stopwatch = Stopwatch.new.when(:finished => method(:log_elapsed_time))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def dispatch_due_messages
|
20
|
+
@dispatched_count = 0
|
21
|
+
|
22
|
+
@stopwatch.time do
|
23
|
+
@buffer.take_ownership_of_dispatchable_messages @id
|
24
|
+
|
25
|
+
begin
|
26
|
+
@buffer.find_dispatchable_messages(@id).each do |message|
|
27
|
+
exchange = self.class.get_exchange @channel, message['exchange']
|
28
|
+
exchange.publish message['json'], self.class.default_publish_options.merge(:routing_key => "#{exchange.name}.#{message['type']}")
|
29
|
+
@dispatched_count += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
@channel.tx_commit
|
33
|
+
rescue StandardError => e
|
34
|
+
@channel.tx_rollback
|
35
|
+
raise e
|
36
|
+
end
|
37
|
+
|
38
|
+
@buffer.delete_dispatched_messages @id
|
39
|
+
end
|
40
|
+
|
41
|
+
@dispatched_count
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_elapsed_time elapsed_time
|
45
|
+
@log.debug "Message buffer #{@id} dispatched #{@dispatched_count} message(s) in #{elapsed_time} sec(s)" if @dispatched_count > 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def next_iteration
|
49
|
+
@messages_dispatched = 0
|
50
|
+
|
51
|
+
begin
|
52
|
+
@messages_dispatched = dispatch_due_messages
|
53
|
+
end until stopped || @messages_dispatched.zero?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module Pipeline
|
4
|
+
module Snapshotter
|
5
|
+
class Component < Euston::DaemonComponent
|
6
|
+
def initialize event_store, threshold, id = 1, logger = Euston::NullLogger.instance
|
7
|
+
@event_store = event_store
|
8
|
+
@threshold = threshold
|
9
|
+
@id = id
|
10
|
+
@log = logger
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def next_iteration
|
16
|
+
begin
|
17
|
+
stream_heads = @event_store.get_streams_to_snapshot @threshold
|
18
|
+
@log.debug "Found #{stream_heads.length} stream(s) eligible for snapshotting (threshold is #{@threshold})" if stream_heads.any?
|
19
|
+
|
20
|
+
stream_heads.each do |stream_head|
|
21
|
+
pair = @event_store.get_snapshot_stream_pair stream_head.stream_id
|
22
|
+
|
23
|
+
loader = RabbitMq::ConstantLoader.new
|
24
|
+
loader.when(:hit => ->(klass) { take_snapshot klass, pair },
|
25
|
+
:miss => ->(type) { Safely.report! "Snapshotter was unable to find a class: #{type}" })
|
26
|
+
|
27
|
+
loader.load pair.stream.committed_headers[:aggregate_type]
|
28
|
+
end
|
29
|
+
end until stopped || stream_heads.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def take_snapshot klass, pair
|
33
|
+
instance = klass.hydrate pair.stream, pair.snapshot
|
34
|
+
snapshot = instance.take_snapshot
|
35
|
+
snapshot = EventStore::Snapshot.new pair.stream.stream_id,
|
36
|
+
pair.stream.stream_revision,
|
37
|
+
snapshot[:payload],
|
38
|
+
:version => snapshot[:version]
|
39
|
+
|
40
|
+
@log.debug "Writing snapshot: #{snapshot.inspect}"
|
41
|
+
|
42
|
+
@event_store.add_snapshot snapshot
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Euston
|
2
|
+
class PipelineRakeTask < Euston::Daemons::RakeTask
|
3
|
+
attr_accessor :amqp_config_path,
|
4
|
+
:command_handler_namespaces,
|
5
|
+
:daemon_config_path,
|
6
|
+
:event_handler_namespaces,
|
7
|
+
:mongo_config_path
|
8
|
+
|
9
|
+
def initialize environment
|
10
|
+
super(environment, :pipeline_daemon)
|
11
|
+
end
|
12
|
+
|
13
|
+
def before_creating_task
|
14
|
+
@daemon_path = File.expand_path(File.dirname __FILE__) + File::SEPARATOR
|
15
|
+
@daemon_class = 'Euston::Daemons::Pipeline::Daemon'
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_daemon_config_file
|
19
|
+
@data[:daemon_config] = ErbYaml.read @daemon_config_path, @data[:environment]
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_settings
|
23
|
+
@logger.debug "AMQP config path: #{@amqp_config_path}"
|
24
|
+
@data[:amqp_config_path] = @amqp_config_path
|
25
|
+
|
26
|
+
@logger.debug "Command handler namespaces: #{@command_handler_namespaces}"
|
27
|
+
@data[:command_handler_namespaces] = @command_handler_namespaces
|
28
|
+
|
29
|
+
@logger.debug "Daemon config path: #{@daemon_config_path}"
|
30
|
+
|
31
|
+
@logger.debug "Event handler namespaces: #{@event_handler_namespaces}"
|
32
|
+
@data[:event_handler_namespaces] = @event_handler_namespaces
|
33
|
+
|
34
|
+
@logger.debug "Mongo config path: #{@mongo_config_path}"
|
35
|
+
@data[:mongo_config_path] = @mongo_config_path
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_environment
|
39
|
+
@logger.debug "Loading environment"
|
40
|
+
require 'euston-daemons/pipeline/config/environment'
|
41
|
+
|
42
|
+
Euston::Daemons::Pipeline::DaemonEnvironment.new(@data).setup
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -15,11 +15,8 @@ module Euston
|
|
15
15
|
# Daemon class. Must be supplied as a string.
|
16
16
|
attr_accessor :daemon_class
|
17
17
|
|
18
|
-
#
|
19
|
-
attr_accessor :
|
20
|
-
|
21
|
-
# # Path to write the daemon pid file to
|
22
|
-
attr_accessor :pid_path
|
18
|
+
# Callable. Receives the daemon environment object to allow the user to perform other related config operations.
|
19
|
+
attr_accessor :post_setup_callback
|
23
20
|
|
24
21
|
# Use verbose output. If this is set to true, the task will print the
|
25
22
|
# executed command to stdout.
|
@@ -40,11 +37,13 @@ module Euston
|
|
40
37
|
desc("Run a Euston daemon") unless ::Rake.application.last_comment
|
41
38
|
task name do
|
42
39
|
RakeFileUtils.send(:verbose, verbose) do
|
40
|
+
load_daemon_config_file
|
43
41
|
initialize_logger
|
42
|
+
initialize_settings
|
44
43
|
write_pid_file
|
45
44
|
log_startup
|
46
|
-
|
47
|
-
|
45
|
+
env = load_environment
|
46
|
+
post_setup_callback.call env unless post_setup_callback.nil?
|
48
47
|
launch_and_wait_for_exit
|
49
48
|
log_shutdown
|
50
49
|
remove_pid_file
|
@@ -54,16 +53,19 @@ module Euston
|
|
54
53
|
|
55
54
|
private
|
56
55
|
|
57
|
-
def validate_environment environment
|
58
|
-
environment = environment.to_s.downcase.to_sym
|
59
|
-
environments = [:development, :test, :staging, :production]
|
60
|
-
environment = :development unless environments.include? environment
|
61
|
-
environment
|
62
|
-
end
|
63
|
-
|
64
56
|
def initialize_logger
|
65
|
-
|
66
|
-
|
57
|
+
config = @data[:daemon_config]
|
58
|
+
log_path = config[:log_path]
|
59
|
+
|
60
|
+
raise "Required log path does not exist: #{log_path}" unless Dir.exist? log_path
|
61
|
+
|
62
|
+
@data[:logger] = @logger = Logger.new(File.join log_path, "#{@name}.#{@environment}.log")
|
63
|
+
|
64
|
+
begin
|
65
|
+
@logger.level = Logger.const_get config[:log_level].upcase.to_sym
|
66
|
+
rescue
|
67
|
+
@logger.level = Logger::DEBUG
|
68
|
+
end
|
67
69
|
end
|
68
70
|
|
69
71
|
def initialize_settings
|
@@ -81,6 +83,14 @@ module Euston
|
|
81
83
|
ns.new(@data).run
|
82
84
|
end
|
83
85
|
|
86
|
+
def load_daemon_config_file
|
87
|
+
# virtual
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_environment
|
91
|
+
# virtual
|
92
|
+
end
|
93
|
+
|
84
94
|
def log_shutdown
|
85
95
|
print_log_banner "Daemon shut down: #{@daemon_class}"
|
86
96
|
end
|
@@ -105,16 +115,27 @@ module Euston
|
|
105
115
|
File.delete @pid_file rescue Errno::ENOENT
|
106
116
|
end
|
107
117
|
|
118
|
+
def validate_environment environment
|
119
|
+
environment = environment.to_s.downcase.to_sym
|
120
|
+
environments = [:development, :test, :staging, :production]
|
121
|
+
environment = :development unless environments.include? environment
|
122
|
+
environment
|
123
|
+
end
|
124
|
+
|
108
125
|
def write_pid_file
|
109
|
-
|
126
|
+
pid_path = @data[:daemon_config][:pid_path]
|
127
|
+
|
128
|
+
raise "Required pid path does not exist: #{pid_path}" unless Dir.exist? pid_path
|
129
|
+
|
130
|
+
@pid_file = File.join pid_path, "#{@name}.#{@environment}.pid"
|
110
131
|
|
111
132
|
if defined? Java
|
112
|
-
@pid =
|
133
|
+
@pid = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split('@').first
|
113
134
|
else
|
114
135
|
@pid = File.readlink("/proc/self")
|
115
136
|
end
|
116
137
|
|
117
|
-
@logger.
|
138
|
+
@logger.error "Writing pid #{@pid} to #{@pid_file}"
|
118
139
|
|
119
140
|
File.open(@pid_file, 'w') { |f| f.puts @pid }
|
120
141
|
end
|
@@ -2,6 +2,4 @@ require 'euston-daemons'
|
|
2
2
|
|
3
3
|
require 'rake/tasklib'
|
4
4
|
require 'euston-daemons/rake_task'
|
5
|
-
require 'euston-daemons/
|
6
|
-
require 'euston-daemons/event_processor_daemon/rake_task'
|
7
|
-
require 'euston-daemons/message_buffer_daemon/rake_task'
|
5
|
+
require 'euston-daemons/pipeline/rake_task'
|
data/lib/euston-daemons.rb
CHANGED
@@ -10,7 +10,9 @@ if RUBY_PLATFORM.to_s == 'java'
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
require 'euston-daemons/euston/stopwatch'
|
14
|
+
require 'euston-daemons/euston/exceptions'
|
13
15
|
require 'euston-daemons/euston/daemon_environment'
|
14
|
-
require 'euston-daemons/euston/daemon_client'
|
15
16
|
require 'euston-daemons/euston/daemon_component'
|
17
|
+
require 'euston-daemons/euston/daemon_component_host'
|
16
18
|
require 'euston-daemons/euston/daemon'
|
@@ -1,10 +1,10 @@
|
|
1
|
-
describe 'command handler
|
2
|
-
require 'euston-daemons/
|
1
|
+
describe 'command handler component', :purge_event_store, :purge_rabbitmq do
|
2
|
+
require 'euston-daemons/pipeline/lib/command_processor/component'
|
3
3
|
|
4
4
|
let(:logger) { Euston::NullLogger.instance }
|
5
5
|
let(:handlers) { [] }
|
6
6
|
let(:subscription) { StubRetryingSubscription.new }
|
7
|
-
let(:client) { Euston::
|
7
|
+
let(:client) { Euston::Daemons::Pipeline::CommandProcessor::Component.new @channel, handlers, 1, logger }
|
8
8
|
let(:aggregate) { Euston::Daemons::SampleModel::Counter2.new }
|
9
9
|
let(:saved_aggregates) { [] }
|
10
10
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
describe 'event handler client', :purge_event_store, :purge_rabbitmq do
|
2
|
-
require 'euston-daemons/
|
2
|
+
require 'euston-daemons/pipeline/lib/event_processor/component'
|
3
3
|
|
4
4
|
let(:logger) { Euston::NullLogger.instance }
|
5
5
|
let(:subscription) { StubRetryingSubscription.new }
|
6
|
-
let(:client) { Euston::
|
6
|
+
let(:client) { Euston::Daemons::Pipeline::EventProcessor::Component.new @channel, reference, 1, logger }
|
7
7
|
let(:aggregate) { Euston::Daemons::SampleModel::Counter2.new }
|
8
8
|
let(:saved_aggregates) { [] }
|
9
9
|
|