sequent 4.0.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +33 -26
  3. data/lib/notices.rb +2 -0
  4. data/lib/sequent/application_record.rb +2 -0
  5. data/lib/sequent/configuration.rb +24 -31
  6. data/lib/sequent/core/aggregate_repository.rb +48 -13
  7. data/lib/sequent/core/aggregate_root.rb +36 -7
  8. data/lib/sequent/core/aggregate_roots.rb +24 -0
  9. data/lib/sequent/core/aggregate_snapshotter.rb +8 -5
  10. data/lib/sequent/core/base_command_handler.rb +4 -2
  11. data/lib/sequent/core/command.rb +17 -9
  12. data/lib/sequent/core/command_record.rb +8 -3
  13. data/lib/sequent/core/command_service.rb +18 -18
  14. data/lib/sequent/core/core.rb +2 -0
  15. data/lib/sequent/core/current_event.rb +2 -0
  16. data/lib/sequent/core/event.rb +16 -11
  17. data/lib/sequent/core/event_publisher.rb +16 -15
  18. data/lib/sequent/core/event_record.rb +7 -7
  19. data/lib/sequent/core/event_store.rb +89 -51
  20. data/lib/sequent/core/ext/ext.rb +9 -1
  21. data/lib/sequent/core/helpers/array_with_type.rb +4 -1
  22. data/lib/sequent/core/helpers/association_validator.rb +9 -7
  23. data/lib/sequent/core/helpers/attribute_support.rb +45 -28
  24. data/lib/sequent/core/helpers/autoset_attributes.rb +4 -4
  25. data/lib/sequent/core/helpers/boolean_validator.rb +6 -1
  26. data/lib/sequent/core/helpers/copyable.rb +2 -2
  27. data/lib/sequent/core/helpers/date_time_validator.rb +4 -1
  28. data/lib/sequent/core/helpers/date_validator.rb +6 -1
  29. data/lib/sequent/core/helpers/default_validators.rb +12 -10
  30. data/lib/sequent/core/helpers/equal_support.rb +8 -6
  31. data/lib/sequent/core/helpers/helpers.rb +2 -0
  32. data/lib/sequent/core/helpers/mergable.rb +6 -5
  33. data/lib/sequent/core/helpers/message_handler.rb +3 -1
  34. data/lib/sequent/core/helpers/param_support.rb +19 -15
  35. data/lib/sequent/core/helpers/secret.rb +14 -12
  36. data/lib/sequent/core/helpers/string_support.rb +5 -4
  37. data/lib/sequent/core/helpers/string_to_value_parsers.rb +7 -2
  38. data/lib/sequent/core/helpers/string_validator.rb +6 -1
  39. data/lib/sequent/core/helpers/type_conversion_support.rb +5 -3
  40. data/lib/sequent/core/helpers/uuid_helper.rb +5 -2
  41. data/lib/sequent/core/helpers/value_validators.rb +23 -9
  42. data/lib/sequent/core/persistors/active_record_persistor.rb +19 -9
  43. data/lib/sequent/core/persistors/persistor.rb +16 -14
  44. data/lib/sequent/core/persistors/persistors.rb +2 -0
  45. data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +70 -47
  46. data/lib/sequent/core/projector.rb +53 -22
  47. data/lib/sequent/core/random_uuid_generator.rb +2 -0
  48. data/lib/sequent/core/sequent_oj.rb +2 -0
  49. data/lib/sequent/core/stream_record.rb +9 -3
  50. data/lib/sequent/core/transactions/active_record_transaction_provider.rb +30 -9
  51. data/lib/sequent/core/transactions/no_transactions.rb +2 -1
  52. data/lib/sequent/core/transactions/read_only_active_record_transaction_provider.rb +46 -0
  53. data/lib/sequent/core/transactions/transactions.rb +3 -0
  54. data/lib/sequent/core/value_object.rb +8 -10
  55. data/lib/sequent/core/workflow.rb +35 -5
  56. data/lib/sequent/generator/aggregate.rb +16 -10
  57. data/lib/sequent/generator/command.rb +26 -19
  58. data/lib/sequent/generator/event.rb +19 -17
  59. data/lib/sequent/generator/generator.rb +2 -0
  60. data/lib/sequent/generator/project.rb +9 -0
  61. data/lib/sequent/generator/template_project/Gemfile +1 -1
  62. data/lib/sequent/generator/template_project/ruby-version +1 -0
  63. data/lib/sequent/generator.rb +2 -0
  64. data/lib/sequent/migrations/executor.rb +22 -13
  65. data/lib/sequent/migrations/functions.rb +5 -6
  66. data/lib/sequent/migrations/migrate_events.rb +12 -9
  67. data/lib/sequent/migrations/migrations.rb +2 -1
  68. data/lib/sequent/migrations/planner.rb +33 -23
  69. data/lib/sequent/migrations/projectors.rb +4 -3
  70. data/lib/sequent/migrations/sql.rb +2 -0
  71. data/lib/sequent/migrations/view_schema.rb +84 -45
  72. data/lib/sequent/rake/migration_tasks.rb +58 -22
  73. data/lib/sequent/rake/tasks.rb +5 -2
  74. data/lib/sequent/sequent.rb +2 -0
  75. data/lib/sequent/support/database.rb +30 -15
  76. data/lib/sequent/support/view_projection.rb +6 -3
  77. data/lib/sequent/support/view_schema.rb +2 -0
  78. data/lib/sequent/support.rb +2 -0
  79. data/lib/sequent/test/command_handler_helpers.rb +35 -17
  80. data/lib/sequent/test/event_handler_helpers.rb +10 -4
  81. data/lib/sequent/test/event_stream_helpers.rb +7 -3
  82. data/lib/sequent/test/time_comparison.rb +12 -5
  83. data/lib/sequent/test.rb +2 -0
  84. data/lib/sequent/util/dry_run.rb +28 -20
  85. data/lib/sequent/util/printer.rb +6 -5
  86. data/lib/sequent/util/skip_if_already_processing.rb +3 -1
  87. data/lib/sequent/util/timer.rb +2 -0
  88. data/lib/sequent/util/util.rb +2 -0
  89. data/lib/sequent.rb +2 -0
  90. data/lib/version.rb +3 -1
  91. 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
- self.table_name == other.table_name && version == other.version
40
+ table_name == other.table_name && version == other.version
39
41
  end
40
42
 
41
43
  def hash
42
- self.table_name.hash + (version&.hash || 0)
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 '[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.'
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) to run after the migrations run. E.g. close resources
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, &after_migration_block)
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
- begin
52
- Class.const_get("Database::MigrateToVersion#{upgrade_to_version}")
53
- rescue NameError
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,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Migrations
3
-
4
5
  end
5
6
  end
6
7
 
@@ -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.class == ReplayTable }
8
+ migrations.select { |m| m.instance_of?(ReplayTable) }
7
9
  end
8
10
 
9
11
  def alter_tables
10
- migrations.select { |m| m.class == AlterTable }
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
- .yield_self(&method(:group_identical_migrations))
47
- .yield_self(&method(:select_redundant_migrations))
48
- .yield_self(&method(:remove_redundancy))
49
- .values
50
- .flatten
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
- .each_with_index
68
- .select { |migration, _index| migration.class == AlterTable }
69
- .select { |migration, index| migrations
70
- .slice((index + 1)..-1)
71
- .find { |m| m.class == ReplayTable && m.record_class == migration.record_class }
72
- }.map(&:first)
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({}) { |memo, (key, ms)|
79
+ grouped_migrations.reduce({}) do |memo, (key, ms)|
77
80
  memo[key] = ms
78
- .yield_self(&method(:order_by_version_desc))
79
- .slice(1..-1)
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, _migrations)|
99
- fail "Declared migrations for version #{version} must be an Array. For example: {'3' => [FooProjector]}" unless _migrations.is_a?(Array)
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] = _migrations.flat_map do |migration|
106
+ memo[version] = ms.flat_map do |migration|
102
107
  if migration.is_a?(AlterTable)
103
- alter_table_sql_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{migration.table_name}_#{version}.sql"
104
- fail "Missing file #{alter_table_sql_file_name} to apply for version #{version}" unless File.exist?(alter_table_sql_file_name)
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 "Define your own Sequent::Migrations::List class that extends this class and implements this method"
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 "Define your own Sequent::Migrations::List class that extends this class and implements this method"
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_relative '../application_record'
2
4
 
3
5
  module Sequent
@@ -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` and specify in `Sequent.configuration.migrations_class_name`
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 (like TABLE names, INDEX names, PRIMARY KEYS)
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
- statements = sql_file_to_statements("#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.sql") { |raw_sql| raw_sql.remove('%SUFFIX%') }
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(%Q{CREATE SCHEMA IF NOT EXISTS #{view_schema}})
131
+ exec_sql(%(CREATE SCHEMA IF NOT EXISTS #{view_schema}))
125
132
  in_view_schema do
126
- exec_sql(%Q{CREATE TABLE IF NOT EXISTS #{Versions.table_name} (version integer NOT NULL, CONSTRAINT version_pk PRIMARY KEY(version))})
127
- exec_sql(%Q{CREATE TABLE IF NOT EXISTS #{ReplayedIds.table_name} (event_id bigint NOT NULL, CONSTRAINT event_id_pk PRIMARY KEY(event_id))})
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
- replay!(Sequent.configuration.offline_replay_persistor_class.new, exclude_ids: true, group_exponent: 1) if plan.projectors.any?
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
- fail ArgumentError.new("new_version [#{new_version}] must be greater or equal to current_version [#{current_version}]") if new_version < current_version
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 "Start replaying events"
258
+ logger.info 'Start replaying events'
239
259
 
240
- time("#{16 ** group_exponent} groups replayed") do
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 ** group_exponent
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(groups, in_processes: Sequent.configuration.number_of_replay_processes) do |aggregate_prefixes, index|
250
- begin
251
- @connected ||= establish_connection
252
- time("Group (#{aggregate_prefixes.first}-#{aggregate_prefixes.last}) #{index + 1}/#{number_of_groups} replayed") do
253
- replay_events(aggregate_prefixes, event_types, exclude_ids, replay_persistor, &insert_ids)
254
- end
255
- nil
256
- rescue => e
257
- logger.error "Replaying failed for ids: ^#{aggregate_prefixes.first} - #{aggregate_prefixes.last}"
258
- logger.error "+++++++++++++++ ERROR +++++++++++++++"
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 ** LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE).to_a.map { |i| i.to_s(16) } # first x digits of hex
297
- all_prefixes = all_prefixes.map { |s| s.length == 3 ? s : "#{"0" * (3 - s.length)}#{s}" }
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
- fail "Can not have more groups #{number_of_groups} than number of prefixes #{all_prefixes.length}" if number_of_groups > all_prefixes.length
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) do
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
- "select table_name from information_schema.tables where table_schema = '#{Sequent.configuration.view_schema_name}' and table_name LIKE '%_#{old_version}'"
318
- ).flat_map { |row| row.values }
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
- exec_sql("insert into #{ReplayedIds.table_name} (event_id) values #{ids.map { |id| "(#{id})" }.join(',')}") unless ids.empty?
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 { |projector_class| projector_class.new(projector_class.replay_persistor || replay_persistor) }
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.new("aggregate_prefixes is mandatory") unless aggregate_prefixes.present?
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("substring(aggregate_id::varchar from 1 for #{LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE}) in (?)", aggregate_prefixes)
353
- event_stream = event_stream.where("NOT EXISTS (SELECT 1 FROM #{ReplayedIds.table_name} WHERE event_id = event_records.id)") if exclude_already_replayed
354
- event_stream = event_stream.where("event_records.created_at > ?", 1.day.ago) if exclude_already_replayed
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