sequent 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sequent +65 -0
- data/db/sequent_schema.rb +9 -8
- data/lib/sequent.rb +3 -0
- data/lib/sequent/configuration.rb +67 -0
- data/lib/sequent/core/aggregate_repository.rb +1 -1
- data/lib/sequent/core/core.rb +2 -2
- data/lib/sequent/core/event_store.rb +2 -2
- data/lib/sequent/core/helpers/attribute_support.rb +3 -0
- data/lib/sequent/core/helpers/self_applier.rb +1 -1
- data/lib/sequent/core/{record_sessions/active_record_session.rb → persistors/active_record_persistor.rb} +21 -19
- data/lib/sequent/core/persistors/persistor.rb +84 -0
- data/lib/sequent/core/persistors/persistors.rb +3 -0
- data/lib/sequent/core/{record_sessions/replay_events_session.rb → persistors/replay_optimized_postgres_persistor.rb} +16 -7
- data/lib/sequent/core/projector.rb +96 -0
- data/lib/sequent/generator.rb +2 -0
- data/lib/sequent/generator/aggregate.rb +71 -0
- data/lib/sequent/generator/project.rb +61 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate.rb +4 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/commands.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/events.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate.rb +9 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate_command_handler.rb +5 -0
- data/lib/sequent/generator/template_project/Gemfile +11 -0
- data/lib/sequent/generator/template_project/Gemfile.lock +72 -0
- data/lib/sequent/generator/template_project/Rakefile +12 -0
- data/lib/sequent/generator/template_project/app/projectors/post_projector.rb +22 -0
- data/lib/sequent/generator/template_project/app/records/post_record.rb +2 -0
- data/lib/sequent/generator/template_project/config/initializers/sequent.rb +13 -0
- data/lib/sequent/generator/template_project/db/database.yml +17 -0
- data/lib/sequent/generator/template_project/db/migrations.rb +17 -0
- data/lib/sequent/generator/template_project/db/sequent_schema.rb +51 -0
- data/lib/sequent/generator/template_project/db/tables/post_records.sql +10 -0
- data/lib/sequent/generator/template_project/lib/post.rb +4 -0
- data/lib/sequent/generator/template_project/lib/post/commands.rb +4 -0
- data/lib/sequent/generator/template_project/lib/post/events.rb +14 -0
- data/lib/sequent/generator/template_project/lib/post/post.rb +24 -0
- data/lib/sequent/generator/template_project/lib/post/post_command_handler.rb +5 -0
- data/lib/sequent/generator/template_project/my_app.rb +11 -0
- data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +32 -0
- data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +20 -0
- data/lib/sequent/generator/template_project/spec/spec_helper.rb +29 -0
- data/lib/sequent/migrations/migrate_events.rb +1 -0
- data/lib/sequent/migrations/migrations.rb +2 -0
- data/lib/sequent/migrations/projectors.rb +18 -0
- data/lib/sequent/migrations/view_schema.rb +364 -0
- data/lib/sequent/rake/migration_tasks.rb +109 -0
- data/lib/sequent/rake/tasks.rb +16 -0
- data/lib/sequent/sequent.rb +53 -13
- data/lib/sequent/support/database.rb +53 -8
- data/lib/sequent/util/printer.rb +16 -0
- data/lib/sequent/util/timer.rb +14 -0
- data/lib/sequent/util/util.rb +2 -0
- data/lib/version.rb +1 -1
- metadata +67 -14
- data/lib/sequent/core/base_event_handler.rb +0 -54
- data/lib/sequent/core/record_sessions/record_sessions.rb +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee34fdc286ef0c550e0ccd50b64f1e726457c3e58e656797e84cb36bd9e36aa0
|
4
|
+
data.tar.gz: 77546ab25b533fc77ce08cfee7252df089362d533b52d2c09e885298e197c737
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91d3d4c9e946d75488adf4e3b86fb20c99c90f4c324d3d29f276518357f5ec8ba2db12707f48ecf0a577eb15582dce38c01fd1a6885501faea1e1a384c967ccf
|
7
|
+
data.tar.gz: 007c283a04b967879769152a4e0b2e5f84b4571f64de751621e5337d75b39e17473be44f8a61358e3fcbfe8e73d9bec64c3d83b7d8d0823ebdf2e5740cb3f22c
|
data/bin/sequent
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/sequent/generator'
|
3
|
+
|
4
|
+
def new_project(args)
|
5
|
+
name, _ = args
|
6
|
+
abort('Please specify a directory name. i.e. `sequent new myapp`') if name.empty?
|
7
|
+
|
8
|
+
Sequent::Generator::Project.new(name).execute
|
9
|
+
puts <<~NEXTSTEPS
|
10
|
+
|
11
|
+
Success!
|
12
|
+
|
13
|
+
Your brand spanking new sequent app is waiting for you in:
|
14
|
+
#{File.expand_path(name, __dir__)}
|
15
|
+
|
16
|
+
To finish setting up your app:
|
17
|
+
cd #{name}
|
18
|
+
bundle install
|
19
|
+
bundle exec rake sequent:db:create
|
20
|
+
bundle exec rake sequent:migrate:online
|
21
|
+
bundle exec rake sequent:migrate:offline
|
22
|
+
|
23
|
+
Run the example specs:
|
24
|
+
RACK_ENV=test bundle exec rake sequent:db:create
|
25
|
+
bundle exec rspec spec
|
26
|
+
|
27
|
+
To generate new aggregates use:
|
28
|
+
sequent generate <aggregate_name>. e.g. sequent generate address
|
29
|
+
|
30
|
+
For more information see:
|
31
|
+
sequent.io
|
32
|
+
|
33
|
+
Happy coding!
|
34
|
+
|
35
|
+
NEXTSTEPS
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate(args)
|
39
|
+
entity, _ = args
|
40
|
+
abort('Please specify a command. i.e. `sequent g aggregate user`') if entity.empty?
|
41
|
+
|
42
|
+
case entity
|
43
|
+
when 'aggregate'
|
44
|
+
_, aggregate_name = args
|
45
|
+
abort('Please specify a aggregate name. i.e. `sequent g aggregate user`') if !aggregate_name || aggregate_name.empty?
|
46
|
+
|
47
|
+
Sequent::Generator::Aggregate.new(aggregate_name).execute
|
48
|
+
puts "#{aggregate_name} aggregate has been generated"
|
49
|
+
else
|
50
|
+
abort("Unknown argument #{entity} for `generate`. Try `sequent g aggregate user`")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
command = ARGV[0].to_s.strip
|
55
|
+
abort('Please specify a command. i.e. `sequent new myapp`') if command.empty?
|
56
|
+
args = ARGV[1..10].map(&:to_s).map(&:strip)
|
57
|
+
|
58
|
+
case command
|
59
|
+
when 'new'
|
60
|
+
new_project(args)
|
61
|
+
when 'generate', 'g'
|
62
|
+
generate(args)
|
63
|
+
else
|
64
|
+
abort("Unknown command #{command}. Try `sequent new myapp`")
|
65
|
+
end
|
data/db/sequent_schema.rb
CHANGED
@@ -10,14 +10,6 @@ ActiveRecord::Schema.define do
|
|
10
10
|
t.integer "stream_record_id", :null => false
|
11
11
|
end
|
12
12
|
|
13
|
-
create_table "command_records", :force => true do |t|
|
14
|
-
t.string "user_id"
|
15
|
-
t.string "aggregate_id"
|
16
|
-
t.string "command_type", :null => false
|
17
|
-
t.text "command_json", :null => false
|
18
|
-
t.datetime "created_at", :null => false
|
19
|
-
end
|
20
|
-
|
21
13
|
execute %Q{
|
22
14
|
CREATE UNIQUE INDEX unique_event_per_aggregate ON event_records (
|
23
15
|
aggregate_id,
|
@@ -28,10 +20,19 @@ CREATE UNIQUE INDEX unique_event_per_aggregate ON event_records (
|
|
28
20
|
execute %Q{
|
29
21
|
CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DESC) WHERE event_type = 'Sequent::Core::SnapshotEvent'
|
30
22
|
}
|
23
|
+
|
31
24
|
add_index "event_records", ["command_record_id"], :name => "index_event_records_on_command_record_id"
|
32
25
|
add_index "event_records", ["event_type"], :name => "index_event_records_on_event_type"
|
33
26
|
add_index "event_records", ["created_at"], :name => "index_event_records_on_created_at"
|
34
27
|
|
28
|
+
create_table "command_records", :force => true do |t|
|
29
|
+
t.string "user_id"
|
30
|
+
t.string "aggregate_id"
|
31
|
+
t.string "command_type", :null => false
|
32
|
+
t.text "command_json", :null => false
|
33
|
+
t.datetime "created_at", :null => false
|
34
|
+
end
|
35
|
+
|
35
36
|
create_table "stream_records", :force => true do |t|
|
36
37
|
t.datetime "created_at", :null => false
|
37
38
|
t.string "aggregate_type", :null => false
|
data/lib/sequent.rb
CHANGED
@@ -2,9 +2,28 @@ require_relative 'core/event_store'
|
|
2
2
|
require_relative 'core/command_service'
|
3
3
|
require_relative 'core/transactions/no_transactions'
|
4
4
|
require_relative 'core/aggregate_repository'
|
5
|
+
require_relative 'core/persistors/active_record_persistor'
|
6
|
+
require 'logger'
|
5
7
|
|
6
8
|
module Sequent
|
7
9
|
class Configuration
|
10
|
+
|
11
|
+
DEFAULT_VERSIONS_TABLE_NAME = 'sequent_versions'
|
12
|
+
DEFAULT_REPLAYED_IDS_TABLE_NAME = 'sequent_replayed_ids'
|
13
|
+
|
14
|
+
DEFAULT_MIGRATION_SQL_FILES_DIRECTORY = 'db/tables'
|
15
|
+
DEFAULT_DATABASE_CONFIG_DIRECTORY = 'db'
|
16
|
+
|
17
|
+
DEFAULT_VIEW_SCHEMA_NAME = 'view_schema'
|
18
|
+
DEFAULT_EVENT_STORE_SCHEMA_NAME= 'sequent_schema'
|
19
|
+
|
20
|
+
MIGRATIONS_CLASS_NAME = 'Sequent::Migrations::Projectors'
|
21
|
+
|
22
|
+
DEFAULT_NUMBER_OF_REPLAY_PROCESSES = 4
|
23
|
+
|
24
|
+
DEFAULT_OFFLINE_REPLAY_PERSISTOR_CLASS = Sequent::Core::Persistors::ActiveRecordPersistor
|
25
|
+
DEFAULT_ONLINE_REPLAY_PERSISTOR_CLASS = Sequent::Core::Persistors::ActiveRecordPersistor
|
26
|
+
|
8
27
|
attr_accessor :aggregate_repository
|
9
28
|
|
10
29
|
attr_accessor :event_store,
|
@@ -24,6 +43,20 @@ module Sequent
|
|
24
43
|
|
25
44
|
attr_accessor :disable_event_handlers
|
26
45
|
|
46
|
+
attr_accessor :logger
|
47
|
+
|
48
|
+
attr_accessor :migration_sql_files_directory,
|
49
|
+
:view_schema_name,
|
50
|
+
:offline_replay_persistor_class,
|
51
|
+
:online_replay_persistor_class,
|
52
|
+
:number_of_replay_processes,
|
53
|
+
:database_config_directory,
|
54
|
+
:event_store_schema_name
|
55
|
+
|
56
|
+
attr_reader :migrations_class_name,
|
57
|
+
:versions_table_name,
|
58
|
+
:replayed_ids_table_name
|
59
|
+
|
27
60
|
def self.instance
|
28
61
|
@instance ||= new
|
29
62
|
end
|
@@ -51,6 +84,40 @@ module Sequent
|
|
51
84
|
self.uuid_generator = Sequent::Core::RandomUuidGenerator
|
52
85
|
self.event_publisher = Sequent::Core::EventPublisher.new
|
53
86
|
self.disable_event_handlers = false
|
87
|
+
self.versions_table_name = DEFAULT_VERSIONS_TABLE_NAME
|
88
|
+
self.replayed_ids_table_name = DEFAULT_REPLAYED_IDS_TABLE_NAME
|
89
|
+
self.migration_sql_files_directory = DEFAULT_MIGRATION_SQL_FILES_DIRECTORY
|
90
|
+
self.view_schema_name = DEFAULT_VIEW_SCHEMA_NAME
|
91
|
+
self.event_store_schema_name = DEFAULT_EVENT_STORE_SCHEMA_NAME
|
92
|
+
self.migrations_class_name = MIGRATIONS_CLASS_NAME
|
93
|
+
self.number_of_replay_processes = DEFAULT_NUMBER_OF_REPLAY_PROCESSES
|
94
|
+
|
95
|
+
self.offline_replay_persistor_class = DEFAULT_OFFLINE_REPLAY_PERSISTOR_CLASS
|
96
|
+
self.online_replay_persistor_class = DEFAULT_ONLINE_REPLAY_PERSISTOR_CLASS
|
97
|
+
self.database_config_directory = DEFAULT_DATABASE_CONFIG_DIRECTORY
|
98
|
+
|
99
|
+
self.logger = Logger.new(STDOUT).tap {|l| l.level = Logger::INFO }
|
100
|
+
end
|
101
|
+
|
102
|
+
def replayed_ids_table_name=(table_name)
|
103
|
+
fail ArgumentError.new('table_name can not be nil') unless table_name
|
104
|
+
|
105
|
+
@replayed_ids_table_name = table_name
|
106
|
+
Sequent::Migrations::ViewSchema::ReplayedIds.table_name = table_name
|
107
|
+
end
|
108
|
+
|
109
|
+
def versions_table_name=(table_name)
|
110
|
+
fail ArgumentError.new('table_name can not be nil') unless table_name
|
111
|
+
|
112
|
+
@versions_table_name = table_name
|
113
|
+
Sequent::Migrations::ViewSchema::Versions.table_name = table_name
|
54
114
|
end
|
115
|
+
|
116
|
+
def migrations_class_name=(class_name)
|
117
|
+
migration_class = Class.const_get(class_name)
|
118
|
+
fail ArgumentError.new("#{migration_class} must extend Sequent::Migrations::Projectors") unless migration_class <= Sequent::Migrations::Projectors
|
119
|
+
@migrations_class_name = class_name
|
120
|
+
end
|
121
|
+
|
55
122
|
end
|
56
123
|
end
|
@@ -10,7 +10,7 @@ module Sequent
|
|
10
10
|
# queried for uncommitted events. After persisting these events
|
11
11
|
# the uncommitted events are cleared from the aggregate.
|
12
12
|
#
|
13
|
-
# The repository
|
13
|
+
# The repository keeps track of the Unit-Of-Work per thread,
|
14
14
|
# so can be shared between threads.
|
15
15
|
class AggregateRepository
|
16
16
|
# Key used in thread local
|
data/lib/sequent/core/core.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'sequent_oj'
|
2
2
|
require_relative 'helpers/helpers'
|
3
|
-
require_relative '
|
3
|
+
require_relative 'persistors/persistors'
|
4
4
|
require_relative 'transactions/transactions'
|
5
5
|
require_relative 'event'
|
6
6
|
require_relative 'aggregate_repository'
|
@@ -9,7 +9,7 @@ require_relative 'base_command_handler'
|
|
9
9
|
require_relative 'command'
|
10
10
|
require_relative 'command_service'
|
11
11
|
require_relative 'value_object'
|
12
|
-
require_relative '
|
12
|
+
require_relative 'projector'
|
13
13
|
require_relative 'event_store'
|
14
14
|
require_relative 'event_record'
|
15
15
|
require_relative 'command_record'
|
@@ -119,9 +119,9 @@ ORDER BY sequence_number ASC, (CASE event_type WHEN #{quote Sequent.configuratio
|
|
119
119
|
|
120
120
|
PRINT_PROGRESS = lambda do |progress, done, _|
|
121
121
|
if done
|
122
|
-
|
122
|
+
Sequent.logger.debug "Done replaying #{progress} events"
|
123
123
|
else
|
124
|
-
|
124
|
+
Sequent.logger.debug "Replayed #{progress} events"
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
@@ -2,6 +2,9 @@ require 'active_support'
|
|
2
2
|
require_relative '../ext/ext'
|
3
3
|
require_relative 'array_with_type'
|
4
4
|
require_relative 'default_validators'
|
5
|
+
require_relative 'type_conversion_support'
|
6
|
+
require_relative 'date_time_validator'
|
7
|
+
require_relative 'association_validator'
|
5
8
|
|
6
9
|
module Sequent
|
7
10
|
module Core
|
@@ -1,15 +1,21 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require_relative './persistor'
|
2
3
|
|
3
4
|
module Sequent
|
4
5
|
module Core
|
5
|
-
module
|
6
|
+
module Persistors
|
6
7
|
|
7
8
|
#
|
8
|
-
#
|
9
|
+
# The ActiveRecordPersistor uses ActiveRecord to update the projection
|
9
10
|
#
|
10
|
-
#
|
11
|
+
# This is the default persistor in Sequent, but when your event store
|
12
|
+
# is growing it is not the best choice as persistor while rebuilding
|
13
|
+
# a projection when migrating to a new version of that projection.
|
11
14
|
#
|
12
|
-
|
15
|
+
# Please see the +ReplayOptimizedPostgresPersistor+ as alternative
|
16
|
+
# for migrating projections to a new version.
|
17
|
+
class ActiveRecordPersistor
|
18
|
+
include Persistor
|
13
19
|
|
14
20
|
def update_record(record_class, event, where_clause = {aggregate_id: event.aggregate_id}, options = {}, &block)
|
15
21
|
record = record_class.unscoped.where(where_clause).first
|
@@ -23,10 +29,6 @@ module Sequent
|
|
23
29
|
record.save!
|
24
30
|
end
|
25
31
|
|
26
|
-
def execute(statement)
|
27
|
-
ActiveRecord::Base.connection.execute(statement)
|
28
|
-
end
|
29
|
-
|
30
32
|
def create_record(record_class, values)
|
31
33
|
record = new_record(record_class, values)
|
32
34
|
yield record if block_given?
|
@@ -46,7 +48,7 @@ module Sequent
|
|
46
48
|
insert_manager.to_sql
|
47
49
|
end.join(";")
|
48
50
|
|
49
|
-
|
51
|
+
execute_sql(query)
|
50
52
|
end
|
51
53
|
|
52
54
|
def create_or_update_record(record_class, values, created_at = Time.now)
|
@@ -101,6 +103,14 @@ module Sequent
|
|
101
103
|
record_class.unscoped.where(where_clause).last
|
102
104
|
end
|
103
105
|
|
106
|
+
def execute_sql(statement)
|
107
|
+
ActiveRecord::Base.connection.execute(statement)
|
108
|
+
end
|
109
|
+
|
110
|
+
def commit
|
111
|
+
# noop
|
112
|
+
end
|
113
|
+
|
104
114
|
private
|
105
115
|
|
106
116
|
def new_record(record_class, values)
|
@@ -108,19 +118,11 @@ module Sequent
|
|
108
118
|
end
|
109
119
|
|
110
120
|
def new_insert_manager
|
111
|
-
|
112
|
-
Arel::InsertManager.new(ActiveRecord::Base)
|
113
|
-
else
|
114
|
-
Arel::InsertManager.new
|
115
|
-
end
|
121
|
+
Arel::InsertManager.new
|
116
122
|
end
|
117
123
|
|
118
124
|
def convert_to_values(key, table, value)
|
119
|
-
|
120
|
-
[table[key], value]
|
121
|
-
else
|
122
|
-
[table[key], table.type_cast_for_database(key, value)]
|
123
|
-
end
|
125
|
+
[table[key], table.type_cast_for_database(key, value)]
|
124
126
|
end
|
125
127
|
end
|
126
128
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Sequent
|
2
|
+
module Core
|
3
|
+
module Persistors
|
4
|
+
# Defines the methods that can be implemented by the specific +Persistors+
|
5
|
+
#
|
6
|
+
# See
|
7
|
+
# - +ActiveRecordPersistor+
|
8
|
+
# - +ReplayOptimizedPostgresPersistor+
|
9
|
+
module Persistor
|
10
|
+
# Updates the view state
|
11
|
+
def update_record
|
12
|
+
fail "Method not supported in this persistor"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a single record in the view state
|
16
|
+
def create_record
|
17
|
+
fail "Method not supported in this persistor"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates multiple records at once in the view state
|
21
|
+
def create_records
|
22
|
+
fail "Method not supported in this persistor"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates or updates a record in the view state.
|
26
|
+
def create_or_update_record
|
27
|
+
fail "Method not supported in this persistor"
|
28
|
+
end
|
29
|
+
# Gets a record from the view state, fails if it not exists
|
30
|
+
def get_record!
|
31
|
+
fail "Method not supported in this persistor"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Gets a record from the view state, returns +nil+ if it not exists
|
35
|
+
def get_record
|
36
|
+
fail "Method not supported in this persistor"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Deletes all records given a where
|
40
|
+
def delete_all_records
|
41
|
+
fail "Method not supported in this persistor"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Updates all record given a where and an update clause
|
45
|
+
def update_all_records
|
46
|
+
fail "Method not supported in this persistor"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Decide for yourself what to do with the records
|
50
|
+
# @deprecated
|
51
|
+
def do_with_records
|
52
|
+
fail "Method not supported in this persistor"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Decide for yourself what to do with a single record
|
56
|
+
# @deprecated
|
57
|
+
def do_with_record
|
58
|
+
fail "Method not supported in this persistor"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Delete a single record
|
62
|
+
# @deprecated
|
63
|
+
def delete_record
|
64
|
+
fail "Method not supported in this persistor"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Find records given a where
|
68
|
+
def find_records
|
69
|
+
fail "Method not supported in this persistor"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the last record given a where
|
73
|
+
def last_record
|
74
|
+
fail "Method not supported in this persistor"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Hook to implement for instance the persistor batches statements
|
78
|
+
def commit
|
79
|
+
fail "Method not supported in this persistor"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,14 +1,22 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'active_record'
|
3
3
|
require 'csv'
|
4
|
+
require_relative './persistor'
|
4
5
|
|
5
6
|
module Sequent
|
6
7
|
module Core
|
7
|
-
module
|
8
|
+
module Persistors
|
8
9
|
#
|
9
|
-
#
|
10
|
+
# The ReplayOptimizedPostgresPersistor is optimized for bulk loading records in a Postgres database.
|
10
11
|
#
|
11
|
-
#
|
12
|
+
# Depending on the amount of records it uses CSV import, otherwise statements are batched
|
13
|
+
# using normal sql.
|
14
|
+
#
|
15
|
+
# Rebuilding the view state (or projection) of an aggregate typically consists
|
16
|
+
# of an initial insert and then many updates and maybe a delete. With a normal Persistor (like ActiveRecordPersistor)
|
17
|
+
# each action is executed to the database. This persitor creates an inmemory store first and finally flushes
|
18
|
+
# the in memory store to the database. This can significantly reduces the amount of queries to the database.
|
19
|
+
# E.g. 1 insert, 6 updates is only a single insert using this Persistor.
|
12
20
|
#
|
13
21
|
# After lot of experimenting this turned out to be the fastest way to to bulk inserts in the database.
|
14
22
|
# You can tweak the amount of records in the CSV via +insert_with_csv_size+ before
|
@@ -19,7 +27,7 @@ module Sequent
|
|
19
27
|
#
|
20
28
|
# Example:
|
21
29
|
#
|
22
|
-
# class
|
30
|
+
# class InvoiceProjector < Sequent::Core::Projector
|
23
31
|
# on RecipientMovedEvent do |event|
|
24
32
|
# update_all_records InvoiceRecord, recipient_id: event.recipient.aggregate_id do |record|
|
25
33
|
# record.recipient_street = record.recipient.street
|
@@ -31,11 +39,12 @@ module Sequent
|
|
31
39
|
#
|
32
40
|
# Example:
|
33
41
|
#
|
34
|
-
#
|
42
|
+
# ReplayOptimizedPostgresPersistor.new(
|
35
43
|
# 50,
|
36
44
|
# {InvoiceRecord => [[:recipient_id]]}
|
37
45
|
# )
|
38
|
-
class
|
46
|
+
class ReplayOptimizedPostgresPersistor
|
47
|
+
include Persistor
|
39
48
|
|
40
49
|
attr_reader :record_store
|
41
50
|
attr_accessor :insert_with_csv_size
|
@@ -186,7 +195,7 @@ module Sequent
|
|
186
195
|
end
|
187
196
|
EOD
|
188
197
|
eval("#{class_def}")
|
189
|
-
struct_class =
|
198
|
+
struct_class = ReplayOptimizedPostgresPersistor.const_get(struct_class_name)
|
190
199
|
self.class.struct_cache[struct_class_name] = struct_class
|
191
200
|
end
|
192
201
|
record = struct_class.new.set_values(values)
|