sequent 3.3.1 → 4.1.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 +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
|