sequent 2.1.0 → 3.0.0
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.
- 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)
|