sequent 4.0.0 → 4.3.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 +33 -26
- data/lib/notices.rb +2 -0
- data/lib/sequent/application_record.rb +2 -0
- data/lib/sequent/configuration.rb +24 -31
- data/lib/sequent/core/aggregate_repository.rb +48 -13
- data/lib/sequent/core/aggregate_root.rb +36 -7
- data/lib/sequent/core/aggregate_roots.rb +24 -0
- data/lib/sequent/core/aggregate_snapshotter.rb +8 -5
- data/lib/sequent/core/base_command_handler.rb +4 -2
- data/lib/sequent/core/command.rb +17 -9
- data/lib/sequent/core/command_record.rb +8 -3
- data/lib/sequent/core/command_service.rb +18 -18
- data/lib/sequent/core/core.rb +2 -0
- data/lib/sequent/core/current_event.rb +2 -0
- data/lib/sequent/core/event.rb +16 -11
- data/lib/sequent/core/event_publisher.rb +16 -15
- data/lib/sequent/core/event_record.rb +7 -7
- data/lib/sequent/core/event_store.rb +89 -51
- data/lib/sequent/core/ext/ext.rb +9 -1
- data/lib/sequent/core/helpers/array_with_type.rb +4 -1
- data/lib/sequent/core/helpers/association_validator.rb +9 -7
- data/lib/sequent/core/helpers/attribute_support.rb +45 -28
- data/lib/sequent/core/helpers/autoset_attributes.rb +4 -4
- data/lib/sequent/core/helpers/boolean_validator.rb +6 -1
- data/lib/sequent/core/helpers/copyable.rb +2 -2
- data/lib/sequent/core/helpers/date_time_validator.rb +4 -1
- data/lib/sequent/core/helpers/date_validator.rb +6 -1
- data/lib/sequent/core/helpers/default_validators.rb +12 -10
- data/lib/sequent/core/helpers/equal_support.rb +8 -6
- data/lib/sequent/core/helpers/helpers.rb +2 -0
- data/lib/sequent/core/helpers/mergable.rb +6 -5
- data/lib/sequent/core/helpers/message_handler.rb +3 -1
- data/lib/sequent/core/helpers/param_support.rb +19 -15
- data/lib/sequent/core/helpers/secret.rb +14 -12
- data/lib/sequent/core/helpers/string_support.rb +5 -4
- data/lib/sequent/core/helpers/string_to_value_parsers.rb +7 -2
- data/lib/sequent/core/helpers/string_validator.rb +6 -1
- data/lib/sequent/core/helpers/type_conversion_support.rb +5 -3
- data/lib/sequent/core/helpers/uuid_helper.rb +5 -2
- data/lib/sequent/core/helpers/value_validators.rb +23 -9
- data/lib/sequent/core/persistors/active_record_persistor.rb +19 -9
- data/lib/sequent/core/persistors/persistor.rb +16 -14
- data/lib/sequent/core/persistors/persistors.rb +2 -0
- data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +70 -47
- data/lib/sequent/core/projector.rb +53 -22
- data/lib/sequent/core/random_uuid_generator.rb +2 -0
- data/lib/sequent/core/sequent_oj.rb +2 -0
- data/lib/sequent/core/stream_record.rb +9 -3
- data/lib/sequent/core/transactions/active_record_transaction_provider.rb +30 -9
- data/lib/sequent/core/transactions/no_transactions.rb +2 -1
- data/lib/sequent/core/transactions/read_only_active_record_transaction_provider.rb +46 -0
- data/lib/sequent/core/transactions/transactions.rb +3 -0
- data/lib/sequent/core/value_object.rb +8 -10
- data/lib/sequent/core/workflow.rb +35 -5
- data/lib/sequent/generator/aggregate.rb +16 -10
- data/lib/sequent/generator/command.rb +26 -19
- data/lib/sequent/generator/event.rb +19 -17
- data/lib/sequent/generator/generator.rb +2 -0
- data/lib/sequent/generator/project.rb +9 -0
- data/lib/sequent/generator/template_project/Gemfile +1 -1
- data/lib/sequent/generator/template_project/ruby-version +1 -0
- data/lib/sequent/generator.rb +2 -0
- data/lib/sequent/migrations/executor.rb +22 -13
- data/lib/sequent/migrations/functions.rb +5 -6
- data/lib/sequent/migrations/migrate_events.rb +12 -9
- data/lib/sequent/migrations/migrations.rb +2 -1
- data/lib/sequent/migrations/planner.rb +33 -23
- data/lib/sequent/migrations/projectors.rb +4 -3
- data/lib/sequent/migrations/sql.rb +2 -0
- data/lib/sequent/migrations/view_schema.rb +84 -45
- data/lib/sequent/rake/migration_tasks.rb +58 -22
- data/lib/sequent/rake/tasks.rb +5 -2
- data/lib/sequent/sequent.rb +2 -0
- data/lib/sequent/support/database.rb +30 -15
- data/lib/sequent/support/view_projection.rb +6 -3
- data/lib/sequent/support/view_schema.rb +2 -0
- data/lib/sequent/support.rb +2 -0
- data/lib/sequent/test/command_handler_helpers.rb +35 -17
- data/lib/sequent/test/event_handler_helpers.rb +10 -4
- data/lib/sequent/test/event_stream_helpers.rb +7 -3
- data/lib/sequent/test/time_comparison.rb +12 -5
- data/lib/sequent/test.rb +2 -0
- data/lib/sequent/util/dry_run.rb +28 -20
- data/lib/sequent/util/printer.rb +6 -5
- data/lib/sequent/util/skip_if_already_processing.rb +3 -1
- data/lib/sequent/util/timer.rb +2 -0
- data/lib/sequent/util/util.rb +2 -0
- data/lib/sequent.rb +2 -0
- data/lib/version.rb +3 -1
- metadata +84 -67
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Migrations
|
|
3
5
|
class Migration
|
|
4
|
-
|
|
5
6
|
module ClassMethods
|
|
6
7
|
def create(record_class, version)
|
|
7
8
|
migration = new(record_class)
|
|
@@ -11,6 +12,7 @@ module Sequent
|
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def self.inherited(child_class)
|
|
15
|
+
super
|
|
14
16
|
class << child_class
|
|
15
17
|
include ClassMethods
|
|
16
18
|
end
|
|
@@ -35,11 +37,11 @@ module Sequent
|
|
|
35
37
|
def ==(other)
|
|
36
38
|
return false unless other.class == self.class
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
table_name == other.table_name && version == other.version
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
def hash
|
|
42
|
-
|
|
44
|
+
table_name.hash + (version&.hash || 0)
|
|
43
45
|
end
|
|
44
46
|
end
|
|
45
47
|
|
|
@@ -48,7 +50,6 @@ module Sequent
|
|
|
48
50
|
class ReplayTable < Migration; end
|
|
49
51
|
|
|
50
52
|
module Functions
|
|
51
|
-
|
|
52
53
|
module ClassMethods
|
|
53
54
|
def alter_table(name)
|
|
54
55
|
AlterTable.new(name)
|
|
@@ -62,13 +63,11 @@ module Sequent
|
|
|
62
63
|
def all_projectors
|
|
63
64
|
Sequent::Core::Migratable.all
|
|
64
65
|
end
|
|
65
|
-
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def self.included(base)
|
|
69
69
|
base.extend(ClassMethods)
|
|
70
70
|
end
|
|
71
|
-
|
|
72
71
|
end
|
|
73
72
|
|
|
74
73
|
include Functions
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
##
|
|
2
4
|
# When you need to upgrade the event store based on information of the previous schema version
|
|
3
5
|
# this is the place you need to implement a migration.
|
|
@@ -20,11 +22,12 @@
|
|
|
20
22
|
module Sequent
|
|
21
23
|
module Migrations
|
|
22
24
|
class MigrateEvents
|
|
23
|
-
|
|
24
25
|
##
|
|
25
26
|
# @param env The string representing the current environment. E.g. "development", "production"
|
|
26
27
|
def initialize(env)
|
|
27
|
-
warn
|
|
28
|
+
warn <<~EOS
|
|
29
|
+
[DEPRECATED] Use of MigrateEvents is deprecated and will be removed from future version. Please use Sequent::Migrations::ViewSchema instead. See the changelog on how to update.
|
|
30
|
+
EOS
|
|
28
31
|
@env = env
|
|
29
32
|
end
|
|
30
33
|
|
|
@@ -32,9 +35,10 @@ module Sequent
|
|
|
32
35
|
#
|
|
33
36
|
# @param current_version The current version of the application. E.g. 10
|
|
34
37
|
# @param new_version The version to migrate to. E.g. 11
|
|
35
|
-
# @param &after_migration_block an optional block (with the current upgrade version as param)
|
|
38
|
+
# @param &after_migration_block an optional block (with the current upgrade version as param)
|
|
39
|
+
# to run after the migrations run. E.g. close resources
|
|
36
40
|
#
|
|
37
|
-
def execute_migrations(current_version, new_version
|
|
41
|
+
def execute_migrations(current_version, new_version)
|
|
38
42
|
migrations(current_version, new_version).each do |migration_class|
|
|
39
43
|
migration = migration_class.new(@env)
|
|
40
44
|
begin
|
|
@@ -47,12 +51,11 @@ module Sequent
|
|
|
47
51
|
|
|
48
52
|
def migrations(current_version, new_version)
|
|
49
53
|
return [] if current_version == 0
|
|
54
|
+
|
|
50
55
|
((current_version + 1)..new_version).map do |upgrade_to_version|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
nil
|
|
55
|
-
end
|
|
56
|
+
Class.const_get("Database::MigrateToVersion#{upgrade_to_version}")
|
|
57
|
+
rescue NameError
|
|
58
|
+
nil
|
|
56
59
|
end.compact
|
|
57
60
|
end
|
|
58
61
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Migrations
|
|
3
5
|
class Planner
|
|
4
6
|
Plan = Struct.new(:projectors, :migrations) do
|
|
5
7
|
def replay_tables
|
|
6
|
-
migrations.select { |m| m.
|
|
8
|
+
migrations.select { |m| m.instance_of?(ReplayTable) }
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
def alter_tables
|
|
10
|
-
migrations.select { |m| m.
|
|
12
|
+
migrations.select { |m| m.instance_of?(AlterTable) }
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def empty?
|
|
@@ -28,7 +30,7 @@ module Sequent
|
|
|
28
30
|
migrations.yield_self(&method(:select_projectors)),
|
|
29
31
|
migrations
|
|
30
32
|
.yield_self(&method(:create_migrations))
|
|
31
|
-
.yield_self(&method(:remove_redundant_migrations))
|
|
33
|
+
.yield_self(&method(:remove_redundant_migrations)),
|
|
32
34
|
)
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -43,11 +45,11 @@ module Sequent
|
|
|
43
45
|
|
|
44
46
|
def remove_redundant_migrations(migrations)
|
|
45
47
|
redundant_migrations = migrations
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
.yield_self(&method(:group_identical_migrations))
|
|
49
|
+
.yield_self(&method(:select_redundant_migrations))
|
|
50
|
+
.yield_self(&method(:remove_redundancy))
|
|
51
|
+
.values
|
|
52
|
+
.flatten
|
|
51
53
|
|
|
52
54
|
(migrations - redundant_migrations)
|
|
53
55
|
.yield_self(&method(:remove_alter_tables_before_replay_table))
|
|
@@ -64,21 +66,22 @@ module Sequent
|
|
|
64
66
|
|
|
65
67
|
def remove_alter_tables_before_replay_table(migrations)
|
|
66
68
|
migrations - migrations
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
.each_with_index
|
|
70
|
+
.select { |migration, _index| migration.instance_of?(AlterTable) }
|
|
71
|
+
.select do |migration, index|
|
|
72
|
+
migrations
|
|
73
|
+
.slice((index + 1)..-1)
|
|
74
|
+
.find { |m| m.instance_of?(ReplayTable) && m.record_class == migration.record_class }
|
|
75
|
+
end.map(&:first)
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
def remove_redundancy(grouped_migrations)
|
|
76
|
-
grouped_migrations.reduce({})
|
|
79
|
+
grouped_migrations.reduce({}) do |memo, (key, ms)|
|
|
77
80
|
memo[key] = ms
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
.yield_self(&method(:order_by_version_desc))
|
|
82
|
+
.slice(1..-1)
|
|
80
83
|
memo
|
|
81
|
-
|
|
84
|
+
end
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
def order_by_version_desc(migrations)
|
|
@@ -95,13 +98,20 @@ module Sequent
|
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
def map_to_migrations(migrations)
|
|
98
|
-
migrations.reduce({}) do |memo, (version,
|
|
99
|
-
|
|
101
|
+
migrations.reduce({}) do |memo, (version, ms)|
|
|
102
|
+
unless ms.is_a?(Array)
|
|
103
|
+
fail "Declared migrations for version #{version} must be an Array. For example: {'3' => [FooProjector]}"
|
|
104
|
+
end
|
|
100
105
|
|
|
101
|
-
memo[version] =
|
|
106
|
+
memo[version] = ms.flat_map do |migration|
|
|
102
107
|
if migration.is_a?(AlterTable)
|
|
103
|
-
alter_table_sql_file_name =
|
|
104
|
-
|
|
108
|
+
alter_table_sql_file_name = <<~EOS.chomp
|
|
109
|
+
#{Sequent.configuration.migration_sql_files_directory}/#{migration.table_name}_#{version}.sql
|
|
110
|
+
EOS
|
|
111
|
+
unless File.exist?(alter_table_sql_file_name)
|
|
112
|
+
fail "Missing file #{alter_table_sql_file_name} to apply for version #{version}"
|
|
113
|
+
end
|
|
114
|
+
|
|
105
115
|
migration.copy(version)
|
|
106
116
|
elsif migration < Sequent::Projector
|
|
107
117
|
migration.managed_tables.map { |table| ReplayTable.create(table, version) }
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'planner'
|
|
2
4
|
module Sequent
|
|
3
5
|
module Migrations
|
|
4
6
|
class Projectors
|
|
5
7
|
def self.versions
|
|
6
|
-
fail
|
|
8
|
+
fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
def self.version
|
|
10
|
-
fail
|
|
12
|
+
fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def self.migrations_between(old, new)
|
|
14
16
|
Planner.new(versions).plan(old, new)
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
|
-
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'parallel'
|
|
2
4
|
require 'postgresql_cursor'
|
|
3
5
|
|
|
@@ -24,11 +26,13 @@ module Sequent
|
|
|
24
26
|
# - AlterTable (For instance if you introduce a new column)
|
|
25
27
|
#
|
|
26
28
|
# To maintain your migrations you need to:
|
|
27
|
-
# 1. Create a class that extends `Sequent::Migrations::Projectors`
|
|
29
|
+
# 1. Create a class that extends `Sequent::Migrations::Projectors`
|
|
30
|
+
# and specify in `Sequent.configuration.migrations_class_name`
|
|
28
31
|
# 2. Define per version which migrations you want to execute
|
|
29
32
|
# See the definition of `Sequent::Migrations::Projectors.versions` and `Sequent::Migrations::Projectors.version`
|
|
30
33
|
# 3. Specify in Sequent where your sql files reside (Sequent.configuration.migration_sql_files_directory)
|
|
31
|
-
# 4. Ensure that you add %SUFFIX% to each name that needs to be unique in postgres
|
|
34
|
+
# 4. Ensure that you add %SUFFIX% to each name that needs to be unique in postgres
|
|
35
|
+
# (like TABLE names, INDEX names, PRIMARY KEYS)
|
|
32
36
|
# E.g. `create table foo%SUFFIX% (id serial NOT NULL, CONSTRAINT foo_pkey%SUFFIX% PRIMARY KEY (id))`
|
|
33
37
|
# 5. If you want to run an `alter_table` migration ensure that
|
|
34
38
|
# a sql file named `table_name_VERSION.sql` exists.
|
|
@@ -96,7 +100,10 @@ module Sequent
|
|
|
96
100
|
create_view_schema_if_not_exists
|
|
97
101
|
in_view_schema do
|
|
98
102
|
Sequent::Core::Migratable.all.flat_map(&:managed_tables).each do |table|
|
|
99
|
-
|
|
103
|
+
sql_file = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.sql"
|
|
104
|
+
statements = sql_file_to_statements(sql_file) do |raw_sql|
|
|
105
|
+
raw_sql.remove('%SUFFIX%')
|
|
106
|
+
end
|
|
100
107
|
statements.each { |statement| exec_sql(statement) }
|
|
101
108
|
|
|
102
109
|
indexes_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.indexes.sql"
|
|
@@ -121,10 +128,14 @@ module Sequent
|
|
|
121
128
|
#
|
|
122
129
|
# This method is mainly useful during an initial setup of the view schema
|
|
123
130
|
def create_view_schema_if_not_exists
|
|
124
|
-
exec_sql(%
|
|
131
|
+
exec_sql(%(CREATE SCHEMA IF NOT EXISTS #{view_schema}))
|
|
125
132
|
in_view_schema do
|
|
126
|
-
exec_sql(
|
|
127
|
-
|
|
133
|
+
exec_sql(<<~SQL.chomp)
|
|
134
|
+
CREATE TABLE IF NOT EXISTS #{Versions.table_name} (version integer NOT NULL, CONSTRAINT version_pk PRIMARY KEY(version))
|
|
135
|
+
SQL
|
|
136
|
+
exec_sql(<<~SQL.chomp)
|
|
137
|
+
CREATE TABLE IF NOT EXISTS #{ReplayedIds.table_name} (event_id bigint NOT NULL, CONSTRAINT event_id_pk PRIMARY KEY(event_id))
|
|
138
|
+
SQL
|
|
128
139
|
end
|
|
129
140
|
end
|
|
130
141
|
|
|
@@ -162,14 +173,14 @@ module Sequent
|
|
|
162
173
|
executor.execute_online(plan)
|
|
163
174
|
end
|
|
164
175
|
|
|
165
|
-
if plan.projectors.any?
|
|
166
|
-
replay!(Sequent.configuration.online_replay_persistor_class.new)
|
|
167
|
-
end
|
|
176
|
+
replay!(Sequent.configuration.online_replay_persistor_class.new) if plan.projectors.any?
|
|
168
177
|
|
|
169
178
|
in_view_schema do
|
|
170
179
|
executor.create_indexes_after_execute_online(plan)
|
|
171
180
|
end
|
|
181
|
+
# rubocop:disable Lint/RescueException
|
|
172
182
|
rescue Exception => e
|
|
183
|
+
# rubocop:enable Lint/RescueException
|
|
173
184
|
rollback_migration
|
|
174
185
|
raise e
|
|
175
186
|
end
|
|
@@ -201,7 +212,13 @@ module Sequent
|
|
|
201
212
|
executor.set_table_names_to_new_version(plan)
|
|
202
213
|
|
|
203
214
|
# 1 replay events not yet replayed
|
|
204
|
-
|
|
215
|
+
if plan.projectors.any?
|
|
216
|
+
replay!(
|
|
217
|
+
Sequent.configuration.offline_replay_persistor_class.new,
|
|
218
|
+
exclude_ids: true,
|
|
219
|
+
group_exponent: 1,
|
|
220
|
+
)
|
|
221
|
+
end
|
|
205
222
|
|
|
206
223
|
in_view_schema do
|
|
207
224
|
Sequent::ApplicationRecord.transaction do
|
|
@@ -215,50 +232,57 @@ module Sequent
|
|
|
215
232
|
truncate_replay_ids_table!
|
|
216
233
|
end
|
|
217
234
|
logger.info "Migrated to version #{Sequent.new_version}"
|
|
235
|
+
# rubocop:disable Lint/RescueException
|
|
218
236
|
rescue Exception => e
|
|
237
|
+
# rubocop:enable Lint/RescueException
|
|
219
238
|
rollback_migration
|
|
220
239
|
raise e
|
|
221
240
|
end
|
|
222
241
|
|
|
223
242
|
private
|
|
224
243
|
|
|
225
|
-
|
|
226
244
|
def ensure_version_correct!
|
|
227
245
|
create_view_schema_if_not_exists
|
|
228
246
|
new_version = Sequent.new_version
|
|
229
247
|
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
if new_version < current_version
|
|
249
|
+
fail ArgumentError,
|
|
250
|
+
"new_version [#{new_version}] must be greater or equal to current_version [#{current_version}]"
|
|
251
|
+
end
|
|
232
252
|
end
|
|
233
253
|
|
|
234
254
|
def replay!(replay_persistor, projectors: plan.projectors, exclude_ids: false, group_exponent: 3)
|
|
235
255
|
logger.info "group_exponent: #{group_exponent.inspect}"
|
|
236
256
|
|
|
237
257
|
with_sequent_config(replay_persistor, projectors) do
|
|
238
|
-
logger.info
|
|
258
|
+
logger.info 'Start replaying events'
|
|
239
259
|
|
|
240
|
-
time("#{16
|
|
260
|
+
time("#{16**group_exponent} groups replayed") do
|
|
241
261
|
event_types = projectors.flat_map { |projector| projector.message_mapping.keys }.uniq.map(&:name)
|
|
242
262
|
disconnect!
|
|
243
263
|
|
|
244
|
-
number_of_groups = 16
|
|
264
|
+
number_of_groups = 16**group_exponent
|
|
245
265
|
groups = groups_of_aggregate_id_prefixes(number_of_groups)
|
|
246
266
|
|
|
247
267
|
@connected = false
|
|
248
268
|
# using `map_with_index` because https://github.com/grosser/parallel/issues/175
|
|
249
|
-
result = Parallel.map_with_index(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
recursively_print(e)
|
|
260
|
-
raise Parallel::Kill # immediately kill all sub-processes
|
|
269
|
+
result = Parallel.map_with_index(
|
|
270
|
+
groups,
|
|
271
|
+
in_processes: Sequent.configuration.number_of_replay_processes,
|
|
272
|
+
) do |aggregate_prefixes, index|
|
|
273
|
+
@connected ||= establish_connection
|
|
274
|
+
msg = <<~EOS.chomp
|
|
275
|
+
Group (#{aggregate_prefixes.first}-#{aggregate_prefixes.last}) #{index + 1}/#{number_of_groups} replayed
|
|
276
|
+
EOS
|
|
277
|
+
time(msg) do
|
|
278
|
+
replay_events(aggregate_prefixes, event_types, exclude_ids, replay_persistor, &insert_ids)
|
|
261
279
|
end
|
|
280
|
+
nil
|
|
281
|
+
rescue StandardError => e
|
|
282
|
+
logger.error "Replaying failed for ids: ^#{aggregate_prefixes.first} - #{aggregate_prefixes.last}"
|
|
283
|
+
logger.error '+++++++++++++++ ERROR +++++++++++++++'
|
|
284
|
+
recursively_print(e)
|
|
285
|
+
raise Parallel::Kill # immediately kill all sub-processes
|
|
262
286
|
end
|
|
263
287
|
establish_connection
|
|
264
288
|
fail if result.nil?
|
|
@@ -270,7 +294,7 @@ module Sequent
|
|
|
270
294
|
Sequent.configuration.event_store.replay_events_from_cursor(
|
|
271
295
|
block_size: 1000,
|
|
272
296
|
get_events: -> { event_stream(aggregate_prefixes, event_types, exclude_already_replayed) },
|
|
273
|
-
on_progress: on_progress
|
|
297
|
+
on_progress: on_progress,
|
|
274
298
|
)
|
|
275
299
|
|
|
276
300
|
replay_persistor.commit
|
|
@@ -293,29 +317,31 @@ module Sequent
|
|
|
293
317
|
end
|
|
294
318
|
|
|
295
319
|
def groups_of_aggregate_id_prefixes(number_of_groups)
|
|
296
|
-
all_prefixes = (0...16
|
|
297
|
-
|
|
320
|
+
all_prefixes = (0...16**LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE).to_a.map do |i|
|
|
321
|
+
i.to_s(16)
|
|
322
|
+
end
|
|
323
|
+
all_prefixes = all_prefixes.map { |s| s.length == 3 ? s : "#{'0' * (3 - s.length)}#{s}" }
|
|
298
324
|
|
|
299
325
|
logger.info "Number of groups #{number_of_groups}"
|
|
300
326
|
|
|
301
327
|
logger.debug "Prefixes: #{all_prefixes.length}"
|
|
302
|
-
|
|
328
|
+
if number_of_groups > all_prefixes.length
|
|
329
|
+
fail "Can not have more groups #{number_of_groups} than number of prefixes #{all_prefixes.length}"
|
|
330
|
+
end
|
|
303
331
|
|
|
304
332
|
all_prefixes.each_slice(all_prefixes.length / number_of_groups).to_a
|
|
305
333
|
end
|
|
306
334
|
|
|
307
|
-
def in_view_schema
|
|
308
|
-
Sequent::Support::Database.with_schema_search_path(view_schema, db_config)
|
|
309
|
-
yield
|
|
310
|
-
end
|
|
335
|
+
def in_view_schema(&block)
|
|
336
|
+
Sequent::Support::Database.with_schema_search_path(view_schema, db_config, &block)
|
|
311
337
|
end
|
|
312
338
|
|
|
313
339
|
def drop_old_tables(new_version)
|
|
314
340
|
versions_to_check = (current_version - 10)..new_version
|
|
315
341
|
old_tables = versions_to_check.flat_map do |old_version|
|
|
316
|
-
exec_sql(
|
|
317
|
-
|
|
318
|
-
|
|
342
|
+
exec_sql(<<~SQL).flat_map(&:values)
|
|
343
|
+
select table_name from information_schema.tables where table_schema = '#{Sequent.configuration.view_schema_name}' and table_name LIKE '%_#{old_version}'
|
|
344
|
+
SQL
|
|
319
345
|
end
|
|
320
346
|
old_tables.each do |old_table|
|
|
321
347
|
exec_sql("DROP TABLE #{Sequent.configuration.view_schema_name}.#{old_table} CASCADE")
|
|
@@ -324,7 +350,13 @@ module Sequent
|
|
|
324
350
|
|
|
325
351
|
def insert_ids
|
|
326
352
|
->(progress, done, ids) do
|
|
327
|
-
|
|
353
|
+
unless ids.empty?
|
|
354
|
+
exec_sql(
|
|
355
|
+
"insert into #{ReplayedIds.table_name} (event_id) values #{ids.map do |id|
|
|
356
|
+
"(#{id})"
|
|
357
|
+
end.join(',')}",
|
|
358
|
+
)
|
|
359
|
+
end
|
|
328
360
|
Sequent::Core::EventStore::PRINT_PROGRESS[progress, done, ids] if progress > 0
|
|
329
361
|
end
|
|
330
362
|
end
|
|
@@ -334,7 +366,9 @@ module Sequent
|
|
|
334
366
|
|
|
335
367
|
config = Sequent.configuration.dup
|
|
336
368
|
|
|
337
|
-
replay_projectors = projectors.map
|
|
369
|
+
replay_projectors = projectors.map do |projector_class|
|
|
370
|
+
projector_class.new(projector_class.replay_persistor || replay_persistor)
|
|
371
|
+
end
|
|
338
372
|
config.transaction_provider = Sequent::Core::Transactions::NoTransactions.new
|
|
339
373
|
config.event_handlers = replay_projectors
|
|
340
374
|
|
|
@@ -346,12 +380,17 @@ module Sequent
|
|
|
346
380
|
end
|
|
347
381
|
|
|
348
382
|
def event_stream(aggregate_prefixes, event_types, exclude_already_replayed)
|
|
349
|
-
fail ArgumentError
|
|
383
|
+
fail ArgumentError, 'aggregate_prefixes is mandatory' unless aggregate_prefixes.present?
|
|
350
384
|
|
|
351
385
|
event_stream = Sequent.configuration.event_record_class.where(event_type: event_types)
|
|
352
|
-
event_stream = event_stream.where(
|
|
353
|
-
|
|
354
|
-
|
|
386
|
+
event_stream = event_stream.where(<<~SQL, aggregate_prefixes)
|
|
387
|
+
substring(aggregate_id::varchar from 1 for #{LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE}) in (?)
|
|
388
|
+
SQL
|
|
389
|
+
if exclude_already_replayed
|
|
390
|
+
event_stream = event_stream
|
|
391
|
+
.where("NOT EXISTS (SELECT 1 FROM #{ReplayedIds.table_name} WHERE event_id = event_records.id)")
|
|
392
|
+
end
|
|
393
|
+
event_stream = event_stream.where('event_records.created_at > ?', 1.day.ago) if exclude_already_replayed
|
|
355
394
|
event_stream.order('sequence_number ASC').select('id, event_type, event_json, sequence_number')
|
|
356
395
|
end
|
|
357
396
|
|