sequent 3.3.1 → 4.1.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 +31 -25
- data/lib/notices.rb +6 -0
- data/lib/sequent/application_record.rb +2 -0
- data/lib/sequent/configuration.rb +29 -29
- data/lib/sequent/core/aggregate_repository.rb +24 -14
- data/lib/sequent/core/aggregate_root.rb +16 -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 +30 -11
- data/lib/sequent/core/command_record.rb +12 -4
- data/lib/sequent/core/command_service.rb +41 -25
- 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 +20 -15
- data/lib/sequent/core/event_record.rb +7 -7
- data/lib/sequent/core/event_store.rb +75 -49
- 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 +64 -33
- 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 -4
- 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 +25 -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 +7 -9
- data/lib/sequent/core/transactions/no_transactions.rb +2 -1
- data/lib/sequent/core/transactions/transactions.rb +2 -0
- data/lib/sequent/core/value_object.rb +8 -10
- data/lib/sequent/core/workflow.rb +7 -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 +6 -0
- data/lib/sequent/generator/project.rb +3 -1
- data/lib/sequent/generator/template_project/Gemfile +1 -1
- data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +1 -1
- data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +1 -1
- data/lib/sequent/generator.rb +3 -4
- data/lib/sequent/migrations/executor.rb +30 -9
- 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 +93 -44
- data/lib/sequent/rake/migration_tasks.rb +59 -23
- data/lib/sequent/rake/tasks.rb +5 -2
- data/lib/sequent/sequent.rb +6 -1
- data/lib/sequent/support/database.rb +39 -17
- 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 +39 -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 +194 -0
- data/lib/sequent/util/printer.rb +6 -5
- data/lib/sequent/util/skip_if_already_processing.rb +21 -5
- data/lib/sequent/util/timer.rb +2 -0
- data/lib/sequent/util/util.rb +3 -0
- data/lib/sequent.rb +4 -0
- data/lib/version.rb +3 -1
- metadata +110 -59
@@ -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,8 +100,17 @@ 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) }
|
108
|
+
|
109
|
+
indexes_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.indexes.sql"
|
110
|
+
if File.exist?(indexes_file_name)
|
111
|
+
statements = sql_file_to_statements(indexes_file_name) { |raw_sql| raw_sql.remove('%SUFFIX%') }
|
112
|
+
statements.each(&method(:exec_sql))
|
113
|
+
end
|
101
114
|
end
|
102
115
|
end
|
103
116
|
end
|
@@ -115,10 +128,14 @@ module Sequent
|
|
115
128
|
#
|
116
129
|
# This method is mainly useful during an initial setup of the view schema
|
117
130
|
def create_view_schema_if_not_exists
|
118
|
-
exec_sql(%
|
131
|
+
exec_sql(%(CREATE SCHEMA IF NOT EXISTS #{view_schema}))
|
119
132
|
in_view_schema do
|
120
|
-
exec_sql(
|
121
|
-
|
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
|
122
139
|
end
|
123
140
|
end
|
124
141
|
|
@@ -156,10 +173,14 @@ module Sequent
|
|
156
173
|
executor.execute_online(plan)
|
157
174
|
end
|
158
175
|
|
159
|
-
if plan.projectors.any?
|
160
|
-
|
176
|
+
replay!(Sequent.configuration.online_replay_persistor_class.new) if plan.projectors.any?
|
177
|
+
|
178
|
+
in_view_schema do
|
179
|
+
executor.create_indexes_after_execute_online(plan)
|
161
180
|
end
|
181
|
+
# rubocop:disable Lint/RescueException
|
162
182
|
rescue Exception => e
|
183
|
+
# rubocop:enable Lint/RescueException
|
163
184
|
rollback_migration
|
164
185
|
raise e
|
165
186
|
end
|
@@ -191,7 +212,13 @@ module Sequent
|
|
191
212
|
executor.set_table_names_to_new_version(plan)
|
192
213
|
|
193
214
|
# 1 replay events not yet replayed
|
194
|
-
|
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
|
195
222
|
|
196
223
|
in_view_schema do
|
197
224
|
Sequent::ApplicationRecord.transaction do
|
@@ -205,50 +232,57 @@ module Sequent
|
|
205
232
|
truncate_replay_ids_table!
|
206
233
|
end
|
207
234
|
logger.info "Migrated to version #{Sequent.new_version}"
|
235
|
+
# rubocop:disable Lint/RescueException
|
208
236
|
rescue Exception => e
|
237
|
+
# rubocop:enable Lint/RescueException
|
209
238
|
rollback_migration
|
210
239
|
raise e
|
211
240
|
end
|
212
241
|
|
213
242
|
private
|
214
243
|
|
215
|
-
|
216
244
|
def ensure_version_correct!
|
217
245
|
create_view_schema_if_not_exists
|
218
246
|
new_version = Sequent.new_version
|
219
247
|
|
220
|
-
|
221
|
-
|
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
|
222
252
|
end
|
223
253
|
|
224
254
|
def replay!(replay_persistor, projectors: plan.projectors, exclude_ids: false, group_exponent: 3)
|
225
255
|
logger.info "group_exponent: #{group_exponent.inspect}"
|
226
256
|
|
227
257
|
with_sequent_config(replay_persistor, projectors) do
|
228
|
-
logger.info
|
258
|
+
logger.info 'Start replaying events'
|
229
259
|
|
230
|
-
time("#{16
|
260
|
+
time("#{16**group_exponent} groups replayed") do
|
231
261
|
event_types = projectors.flat_map { |projector| projector.message_mapping.keys }.uniq.map(&:name)
|
232
262
|
disconnect!
|
233
263
|
|
234
|
-
number_of_groups = 16
|
264
|
+
number_of_groups = 16**group_exponent
|
235
265
|
groups = groups_of_aggregate_id_prefixes(number_of_groups)
|
236
266
|
|
237
267
|
@connected = false
|
238
268
|
# using `map_with_index` because https://github.com/grosser/parallel/issues/175
|
239
|
-
result = Parallel.map_with_index(
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
recursively_print(e)
|
250
|
-
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)
|
251
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
|
252
286
|
end
|
253
287
|
establish_connection
|
254
288
|
fail if result.nil?
|
@@ -260,7 +294,7 @@ module Sequent
|
|
260
294
|
Sequent.configuration.event_store.replay_events_from_cursor(
|
261
295
|
block_size: 1000,
|
262
296
|
get_events: -> { event_stream(aggregate_prefixes, event_types, exclude_already_replayed) },
|
263
|
-
on_progress: on_progress
|
297
|
+
on_progress: on_progress,
|
264
298
|
)
|
265
299
|
|
266
300
|
replay_persistor.commit
|
@@ -283,29 +317,31 @@ module Sequent
|
|
283
317
|
end
|
284
318
|
|
285
319
|
def groups_of_aggregate_id_prefixes(number_of_groups)
|
286
|
-
all_prefixes = (0...16
|
287
|
-
|
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}" }
|
288
324
|
|
289
325
|
logger.info "Number of groups #{number_of_groups}"
|
290
326
|
|
291
327
|
logger.debug "Prefixes: #{all_prefixes.length}"
|
292
|
-
|
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
|
293
331
|
|
294
332
|
all_prefixes.each_slice(all_prefixes.length / number_of_groups).to_a
|
295
333
|
end
|
296
334
|
|
297
|
-
def in_view_schema
|
298
|
-
Sequent::Support::Database.with_schema_search_path(view_schema, db_config)
|
299
|
-
yield
|
300
|
-
end
|
335
|
+
def in_view_schema(&block)
|
336
|
+
Sequent::Support::Database.with_schema_search_path(view_schema, db_config, &block)
|
301
337
|
end
|
302
338
|
|
303
339
|
def drop_old_tables(new_version)
|
304
340
|
versions_to_check = (current_version - 10)..new_version
|
305
341
|
old_tables = versions_to_check.flat_map do |old_version|
|
306
|
-
exec_sql(
|
307
|
-
|
308
|
-
|
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
|
309
345
|
end
|
310
346
|
old_tables.each do |old_table|
|
311
347
|
exec_sql("DROP TABLE #{Sequent.configuration.view_schema_name}.#{old_table} CASCADE")
|
@@ -314,7 +350,13 @@ module Sequent
|
|
314
350
|
|
315
351
|
def insert_ids
|
316
352
|
->(progress, done, ids) do
|
317
|
-
|
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
|
318
360
|
Sequent::Core::EventStore::PRINT_PROGRESS[progress, done, ids] if progress > 0
|
319
361
|
end
|
320
362
|
end
|
@@ -324,7 +366,9 @@ module Sequent
|
|
324
366
|
|
325
367
|
config = Sequent.configuration.dup
|
326
368
|
|
327
|
-
replay_projectors = projectors.map
|
369
|
+
replay_projectors = projectors.map do |projector_class|
|
370
|
+
projector_class.new(projector_class.replay_persistor || replay_persistor)
|
371
|
+
end
|
328
372
|
config.transaction_provider = Sequent::Core::Transactions::NoTransactions.new
|
329
373
|
config.event_handlers = replay_projectors
|
330
374
|
|
@@ -336,12 +380,17 @@ module Sequent
|
|
336
380
|
end
|
337
381
|
|
338
382
|
def event_stream(aggregate_prefixes, event_types, exclude_already_replayed)
|
339
|
-
fail ArgumentError
|
383
|
+
fail ArgumentError, 'aggregate_prefixes is mandatory' unless aggregate_prefixes.present?
|
340
384
|
|
341
385
|
event_stream = Sequent.configuration.event_record_class.where(event_type: event_types)
|
342
|
-
event_stream = event_stream.where(
|
343
|
-
|
344
|
-
|
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
|
345
394
|
event_stream.order('sequence_number ASC').select('id, event_type, event_json, sequence_number')
|
346
395
|
end
|
347
396
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
require 'rake'
|
3
5
|
require 'rake/tasklib'
|
@@ -12,13 +14,14 @@ module Sequent
|
|
12
14
|
|
13
15
|
def register_tasks!
|
14
16
|
namespace :sequent do
|
15
|
-
desc
|
17
|
+
desc <<~EOS
|
18
|
+
Rake task that runs before all sequent rake tasks. Hook applications can use to for instance run other rake tasks.
|
19
|
+
EOS
|
16
20
|
task :init
|
17
21
|
|
18
22
|
namespace :db do
|
19
|
-
|
20
23
|
desc 'Creates the database and initializes the event_store schema for the current env'
|
21
|
-
task :
|
24
|
+
task create: ['sequent:init'] do
|
22
25
|
ensure_rack_env_set!
|
23
26
|
|
24
27
|
db_config = Sequent::Support::Database.read_config(@env)
|
@@ -31,14 +34,18 @@ module Sequent
|
|
31
34
|
task :drop, [:production] => ['sequent:init'] do |_t, args|
|
32
35
|
ensure_rack_env_set!
|
33
36
|
|
34
|
-
|
37
|
+
if @env == 'production' && args[:production] != 'yes_drop_production'
|
38
|
+
fail <<~OES
|
39
|
+
Wont drop db in production unless you whitelist the environment as follows: rake sequent:db:drop[yes_drop_production]
|
40
|
+
OES
|
41
|
+
end
|
35
42
|
|
36
43
|
db_config = Sequent::Support::Database.read_config(@env)
|
37
44
|
Sequent::Support::Database.drop!(db_config)
|
38
45
|
end
|
39
46
|
|
40
47
|
desc 'Creates the view schema for the current env'
|
41
|
-
task :
|
48
|
+
task create_view_schema: ['sequent:init'] do
|
42
49
|
ensure_rack_env_set!
|
43
50
|
|
44
51
|
db_config = Sequent::Support::Database.read_config(@env)
|
@@ -47,33 +54,41 @@ module Sequent
|
|
47
54
|
end
|
48
55
|
|
49
56
|
desc 'Creates the event_store schema for the current env'
|
50
|
-
task :
|
57
|
+
task create_event_store: ['sequent:init'] do
|
51
58
|
ensure_rack_env_set!
|
52
59
|
db_config = Sequent::Support::Database.read_config(@env)
|
53
60
|
create_event_store(db_config)
|
54
61
|
end
|
55
62
|
|
63
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
56
64
|
def create_event_store(db_config)
|
57
65
|
event_store_schema = Sequent.configuration.event_store_schema_name
|
58
|
-
sequent_schema = File.join(Sequent.configuration.
|
59
|
-
|
66
|
+
sequent_schema = File.join(Sequent.configuration.database_schema_directory, "#{event_store_schema}.rb")
|
67
|
+
unless File.exist?(sequent_schema)
|
68
|
+
fail "File #{sequent_schema} does not exist. Check your Sequent configuration."
|
69
|
+
end
|
60
70
|
|
61
71
|
Sequent::Support::Database.establish_connection(db_config)
|
62
|
-
|
72
|
+
if Sequent::Support::Database.schema_exists?(event_store_schema)
|
73
|
+
fail "Schema #{event_store_schema} already exists in the database"
|
74
|
+
end
|
63
75
|
|
64
76
|
Sequent::Support::Database.create_schema(event_store_schema)
|
65
77
|
Sequent::Support::Database.with_schema_search_path(event_store_schema, db_config, @env) do
|
66
78
|
load(sequent_schema)
|
67
79
|
end
|
68
80
|
end
|
81
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
69
82
|
end
|
70
83
|
|
71
84
|
namespace :migrate do
|
72
|
-
desc
|
85
|
+
desc <<~EOS
|
86
|
+
Rake task that runs before all migrate rake tasks. Hook applications can use to for instance run other rake tasks.
|
87
|
+
EOS
|
73
88
|
task :init
|
74
89
|
|
75
90
|
desc 'Prints the current version in the database'
|
76
|
-
task :
|
91
|
+
task current_version: ['sequent:init', :init] do
|
77
92
|
ensure_rack_env_set!
|
78
93
|
|
79
94
|
Sequent::Support::Database.connect!(@env)
|
@@ -81,8 +96,10 @@ module Sequent
|
|
81
96
|
puts "Current version in the database is: #{Sequent::Migrations::ViewSchema::Versions.maximum(:version)}"
|
82
97
|
end
|
83
98
|
|
84
|
-
desc
|
85
|
-
|
99
|
+
desc <<~EOS
|
100
|
+
Migrates the Projectors while the app is running. Call +sequent:migrate:offline+ after this successfully completed.
|
101
|
+
EOS
|
102
|
+
task online: ['sequent:init', :init] do
|
86
103
|
ensure_rack_env_set!
|
87
104
|
|
88
105
|
db_config = Sequent::Support::Database.read_config(@env)
|
@@ -91,8 +108,10 @@ module Sequent
|
|
91
108
|
view_schema.migrate_online
|
92
109
|
end
|
93
110
|
|
94
|
-
desc
|
95
|
-
|
111
|
+
desc <<~EOS
|
112
|
+
Migrates the events inserted while +online+ was running. It is expected +sequent:migrate:online+ ran first.
|
113
|
+
EOS
|
114
|
+
task offline: ['sequent:init', :init] do
|
96
115
|
ensure_rack_env_set!
|
97
116
|
|
98
117
|
db_config = Sequent::Support::Database.read_config(@env)
|
@@ -103,21 +122,35 @@ module Sequent
|
|
103
122
|
end
|
104
123
|
|
105
124
|
namespace :snapshots do
|
106
|
-
desc
|
125
|
+
desc <<~EOS
|
126
|
+
Rake task that runs before all snapshots rake tasks. Hook applications can use to for instance run other rake tasks.
|
127
|
+
EOS
|
107
128
|
task :init
|
108
129
|
|
109
|
-
task :set_snapshot_threshold, [
|
130
|
+
task :set_snapshot_threshold, %i[aggregate_type threshold] => ['sequent:init', :init] do |_t, args|
|
110
131
|
aggregate_type = args['aggregate_type']
|
111
132
|
threshold = args['threshold']
|
112
133
|
|
113
|
-
|
114
|
-
|
134
|
+
unless aggregate_type
|
135
|
+
fail ArgumentError,
|
136
|
+
'usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]'
|
137
|
+
end
|
138
|
+
unless threshold
|
139
|
+
fail ArgumentError,
|
140
|
+
'usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]'
|
141
|
+
end
|
115
142
|
|
116
|
-
execute
|
143
|
+
execute <<~EOS
|
144
|
+
UPDATE #{Sequent.configuration.stream_record_class} SET snapshot_threshold = #{threshold.to_i} WHERE aggregate_type = '#{aggregate_type}'
|
145
|
+
EOS
|
117
146
|
end
|
118
147
|
|
119
|
-
task :
|
120
|
-
result = Sequent::ApplicationRecord
|
148
|
+
task delete_all: ['sequent:init', :init] do
|
149
|
+
result = Sequent::ApplicationRecord
|
150
|
+
.connection
|
151
|
+
.execute(<<~EOS)
|
152
|
+
DELETE FROM #{Sequent.configuration.event_record_class.table_name} WHERE event_type = 'Sequent::Core::SnapshotEvent'
|
153
|
+
EOS
|
121
154
|
Sequent.logger.info "Deleted #{result.cmd_tuples} aggregate snapshots from the event store"
|
122
155
|
end
|
123
156
|
end
|
@@ -125,9 +158,12 @@ module Sequent
|
|
125
158
|
end
|
126
159
|
|
127
160
|
private
|
161
|
+
|
162
|
+
# rubocop:disable Naming/MemoizedInstanceVariableName
|
128
163
|
def ensure_rack_env_set!
|
129
|
-
@env ||= ENV['RACK_ENV'] || fail(
|
164
|
+
@env ||= ENV['RACK_ENV'] || fail('RACK_ENV not set')
|
130
165
|
end
|
166
|
+
# rubocop:enable Naming/MemoizedInstanceVariableName
|
131
167
|
end
|
132
168
|
end
|
133
169
|
end
|
data/lib/sequent/rake/tasks.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
require 'rake'
|
3
5
|
require 'rake/tasklib'
|
@@ -11,12 +13,13 @@ module Sequent
|
|
11
13
|
|
12
14
|
DEFAULT_OPTIONS = {
|
13
15
|
migrations_path: 'db/migrate',
|
14
|
-
event_store_schema: 'public'
|
15
|
-
}
|
16
|
+
event_store_schema: 'public',
|
17
|
+
}.freeze
|
16
18
|
|
17
19
|
attr_reader :options
|
18
20
|
|
19
21
|
def initialize(options)
|
22
|
+
super()
|
20
23
|
@options = DEFAULT_OPTIONS.merge(options)
|
21
24
|
end
|
22
25
|
|
data/lib/sequent/sequent.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'configuration'
|
2
4
|
require_relative 'core/event'
|
3
5
|
require_relative 'core/command'
|
@@ -6,7 +8,6 @@ require_relative 'core/aggregate_root'
|
|
6
8
|
require_relative 'core/projector'
|
7
9
|
require_relative 'core/workflow'
|
8
10
|
require_relative 'core/value_object'
|
9
|
-
require_relative 'generator'
|
10
11
|
require_relative 'migrations/migrations'
|
11
12
|
|
12
13
|
module Sequent
|
@@ -65,6 +66,10 @@ module Sequent
|
|
65
66
|
configuration.aggregate_repository
|
66
67
|
end
|
67
68
|
|
69
|
+
def self.dry_run(*commands)
|
70
|
+
Sequent::Util::DryRun.these_commands(commands)
|
71
|
+
end
|
72
|
+
|
68
73
|
# Shortcut classes for easy usage
|
69
74
|
Event = Sequent::Core::Event
|
70
75
|
Command = Sequent::Core::Command
|