sequent 6.0.1 → 7.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/db/sequent_schema.rb +5 -0
- data/lib/sequent/configuration.rb +64 -13
- data/lib/sequent/core/aggregate_repository.rb +2 -2
- data/lib/sequent/core/aggregate_snapshotter.rb +4 -0
- data/lib/sequent/core/base_command_handler.rb +5 -0
- data/lib/sequent/core/core.rb +1 -1
- data/lib/sequent/core/event.rb +2 -2
- data/lib/sequent/core/event_record.rb +1 -0
- data/lib/sequent/core/event_store.rb +20 -16
- data/lib/sequent/core/helpers/attribute_support.rb +7 -7
- data/lib/sequent/core/helpers/message_handler.rb +10 -11
- data/lib/sequent/core/helpers/message_router.rb +13 -7
- data/lib/sequent/core/persistors/active_record_persistor.rb +4 -0
- data/lib/sequent/core/persistors/persistor.rb +5 -0
- data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +140 -133
- data/lib/sequent/core/projector.rb +4 -0
- data/lib/sequent/core/transactions/active_record_transaction_provider.rb +2 -1
- data/lib/sequent/core/workflow.rb +4 -0
- data/lib/sequent/dry_run/dry_run.rb +4 -0
- data/lib/sequent/dry_run/read_only_replay_optimized_postgres_persistor.rb +26 -0
- data/lib/sequent/dry_run/view_schema.rb +36 -0
- data/lib/sequent/generator/template_project/db/sequent_schema.rb +1 -0
- data/lib/sequent/migrations/errors.rb +12 -0
- data/lib/sequent/migrations/migrations.rb +0 -1
- data/lib/sequent/migrations/planner.rb +11 -7
- data/lib/sequent/migrations/versions.rb +82 -0
- data/lib/sequent/migrations/view_schema.rb +101 -58
- data/lib/sequent/rake/migration_tasks.rb +89 -6
- data/lib/sequent/sequent.rb +4 -11
- data/lib/sequent/support/database.rb +3 -11
- data/lib/sequent/support.rb +0 -2
- data/lib/sequent/util/util.rb +1 -0
- data/lib/sequent/util/web/clear_cache.rb +19 -0
- data/lib/sequent.rb +1 -0
- data/lib/version.rb +1 -1
- metadata +20 -30
- data/lib/sequent/core/helpers/message_dispatcher.rb +0 -20
- data/lib/sequent/migrations/migrate_events.rb +0 -67
- data/lib/sequent/support/view_projection.rb +0 -61
- data/lib/sequent/support/view_schema.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31a7eb29122b5706014155cacea33cc9064991eca03762d6c9eb0a300e4d3fe5
|
4
|
+
data.tar.gz: 4724c03d49b69fd01d111a53961936c3b1a6adba91bacb9af0abd97c4337d49a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c13866f01dde089ae6cd95ca17fbbfd594209751300f429b0ef4f3680d19f1a468bf2675b24536b6e09150f80471afdab5a663be79fd45692fe758c47dd8f26
|
7
|
+
data.tar.gz: 96d19ae22452eb86ed09fda426e6cb56aad6b47be5589004a8ba0e24623bd8316297d8b29829210fafb57e75b251797007bb9b610d27850809ce4b1c661adcde
|
data/db/sequent_schema.rb
CHANGED
@@ -8,8 +8,12 @@ ActiveRecord::Schema.define do
|
|
8
8
|
t.text "event_json", :null => false
|
9
9
|
t.integer "command_record_id", :null => false
|
10
10
|
t.integer "stream_record_id", :null => false
|
11
|
+
t.bigint "xact_id"
|
11
12
|
end
|
12
13
|
|
14
|
+
execute %Q{
|
15
|
+
ALTER TABLE event_records ALTER COLUMN xact_id SET DEFAULT pg_current_xact_id()::text::bigint
|
16
|
+
}
|
13
17
|
execute %Q{
|
14
18
|
CREATE UNIQUE INDEX unique_event_per_aggregate ON event_records (
|
15
19
|
aggregate_id,
|
@@ -24,6 +28,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
|
|
24
28
|
add_index "event_records", ["command_record_id"], :name => "index_event_records_on_command_record_id"
|
25
29
|
add_index "event_records", ["event_type"], :name => "index_event_records_on_event_type"
|
26
30
|
add_index "event_records", ["created_at"], :name => "index_event_records_on_created_at"
|
31
|
+
add_index "event_records", ["xact_id"], :name => "index_event_records_on_xact_id"
|
27
32
|
|
28
33
|
create_table "command_records", :force => true do |t|
|
29
34
|
t.string "user_id"
|
@@ -10,7 +10,6 @@ require 'logger'
|
|
10
10
|
module Sequent
|
11
11
|
class Configuration
|
12
12
|
DEFAULT_VERSIONS_TABLE_NAME = 'sequent_versions'
|
13
|
-
DEFAULT_REPLAYED_IDS_TABLE_NAME = 'sequent_replayed_ids'
|
14
13
|
|
15
14
|
DEFAULT_MIGRATION_SQL_FILES_DIRECTORY = 'db/tables'
|
16
15
|
DEFAULT_DATABASE_CONFIG_DIRECTORY = 'db'
|
@@ -36,6 +35,7 @@ module Sequent
|
|
36
35
|
|
37
36
|
attr_accessor :aggregate_repository,
|
38
37
|
:event_store,
|
38
|
+
:event_store_cache_event_types,
|
39
39
|
:command_service,
|
40
40
|
:event_record_class,
|
41
41
|
:stream_record_class,
|
@@ -63,20 +63,23 @@ module Sequent
|
|
63
63
|
:enable_multiple_database_support,
|
64
64
|
:primary_database_role,
|
65
65
|
:primary_database_key,
|
66
|
-
:time_precision
|
66
|
+
:time_precision,
|
67
|
+
:enable_autoregistration
|
67
68
|
|
68
69
|
attr_reader :migrations_class_name,
|
69
|
-
:versions_table_name
|
70
|
-
:replayed_ids_table_name
|
70
|
+
:versions_table_name
|
71
71
|
|
72
72
|
def self.instance
|
73
73
|
@instance ||= new
|
74
74
|
end
|
75
75
|
|
76
|
+
# Create a new instance of Configuration
|
76
77
|
def self.reset
|
77
78
|
@instance = new
|
78
79
|
end
|
79
80
|
|
81
|
+
# Restore the given Configuration
|
82
|
+
# @param configuration [Sequent::Configuration]
|
80
83
|
def self.restore(configuration)
|
81
84
|
@instance = configuration
|
82
85
|
end
|
@@ -88,6 +91,7 @@ module Sequent
|
|
88
91
|
self.command_middleware = Sequent::Core::Middleware::Chain.new
|
89
92
|
|
90
93
|
self.aggregate_repository = Sequent::Core::AggregateRepository.new
|
94
|
+
self.event_store_cache_event_types = true
|
91
95
|
self.event_store = Sequent::Core::EventStore.new
|
92
96
|
self.command_service = Sequent::Core::CommandService.new
|
93
97
|
self.event_record_class = Sequent::Core::EventRecord
|
@@ -98,7 +102,6 @@ module Sequent
|
|
98
102
|
self.event_publisher = Sequent::Core::EventPublisher.new
|
99
103
|
self.disable_event_handlers = false
|
100
104
|
self.versions_table_name = DEFAULT_VERSIONS_TABLE_NAME
|
101
|
-
self.replayed_ids_table_name = DEFAULT_REPLAYED_IDS_TABLE_NAME
|
102
105
|
self.migration_sql_files_directory = DEFAULT_MIGRATION_SQL_FILES_DIRECTORY
|
103
106
|
self.view_schema_name = DEFAULT_VIEW_SCHEMA_NAME
|
104
107
|
self.event_store_schema_name = DEFAULT_EVENT_STORE_SCHEMA_NAME
|
@@ -121,24 +124,19 @@ module Sequent
|
|
121
124
|
self.primary_database_key = :primary
|
122
125
|
|
123
126
|
self.time_precision = DEFAULT_TIME_PRECISION
|
127
|
+
|
128
|
+
self.enable_autoregistration = false
|
124
129
|
end
|
125
130
|
|
126
131
|
def can_use_multiple_databases?
|
127
132
|
enable_multiple_database_support && ActiveRecord.version > Gem::Version.new('6.1.0')
|
128
133
|
end
|
129
134
|
|
130
|
-
def replayed_ids_table_name=(table_name)
|
131
|
-
fail ArgumentError, 'table_name can not be nil' unless table_name
|
132
|
-
|
133
|
-
@replayed_ids_table_name = table_name
|
134
|
-
Sequent::Migrations::ViewSchema::ReplayedIds.table_name = table_name
|
135
|
-
end
|
136
|
-
|
137
135
|
def versions_table_name=(table_name)
|
138
136
|
fail ArgumentError, 'table_name can not be nil' unless table_name
|
139
137
|
|
140
138
|
@versions_table_name = table_name
|
141
|
-
Sequent::Migrations::
|
139
|
+
Sequent::Migrations::Versions.table_name = table_name
|
142
140
|
end
|
143
141
|
|
144
142
|
def migrations_class_name=(class_name)
|
@@ -149,5 +147,58 @@ module Sequent
|
|
149
147
|
|
150
148
|
@migrations_class_name = class_name
|
151
149
|
end
|
150
|
+
|
151
|
+
# @!visibility private
|
152
|
+
def autoregister!
|
153
|
+
return unless enable_autoregistration
|
154
|
+
|
155
|
+
# Only autoregister the AggregateSnapshotter if the autoregistration is enabled
|
156
|
+
Sequent::Core::AggregateSnapshotter.skip_autoregister = false
|
157
|
+
|
158
|
+
autoload_if_in_rails
|
159
|
+
|
160
|
+
self.class.instance.command_handlers ||= []
|
161
|
+
for_each_autoregisterable_descenant_of(Sequent::CommandHandler) do |command_handler_class|
|
162
|
+
Sequent.logger.debug("[Configuration] Autoregistering CommandHandler #{command_handler_class}")
|
163
|
+
self.class.instance.command_handlers << command_handler_class.new
|
164
|
+
end
|
165
|
+
|
166
|
+
self.class.instance.event_handlers ||= []
|
167
|
+
for_each_autoregisterable_descenant_of(Sequent::Projector) do |projector_class|
|
168
|
+
Sequent.logger.debug("[Configuration] Autoregistering Projector #{projector_class}")
|
169
|
+
self.class.instance.event_handlers << projector_class.new
|
170
|
+
end
|
171
|
+
|
172
|
+
for_each_autoregisterable_descenant_of(Sequent::Workflow) do |workflow_class|
|
173
|
+
Sequent.logger.debug("[Configuration] Autoregistering Workflow #{workflow_class}")
|
174
|
+
self.class.instance.event_handlers << workflow_class.new
|
175
|
+
end
|
176
|
+
|
177
|
+
self.class.instance.command_handlers.map(&:class).tally.each do |(clazz, count)|
|
178
|
+
if count > 1
|
179
|
+
fail "CommandHandler #{clazz} is registered #{count} times. A CommandHandler can only be registered once"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
self.class.instance.event_handlers.map(&:class).tally.each do |(clazz, count)|
|
184
|
+
if count > 1
|
185
|
+
fail "EventHandler #{clazz} is registered #{count} times. An EventHandler can only be registered once"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def autoload_if_in_rails
|
193
|
+
Rails.autoloaders.main.eager_load(force: true) if defined?(Rails) && Rails.respond_to?(:autoloaders)
|
194
|
+
end
|
195
|
+
|
196
|
+
def for_each_autoregisterable_descenant_of(clazz, &block)
|
197
|
+
clazz
|
198
|
+
.descendants
|
199
|
+
.reject(&:abstract_class)
|
200
|
+
.reject(&:skip_autoregister)
|
201
|
+
.each(&block)
|
202
|
+
end
|
152
203
|
end
|
153
204
|
end
|
@@ -79,8 +79,8 @@ module Sequent
|
|
79
79
|
.configuration
|
80
80
|
.event_store
|
81
81
|
.stream_events_for_aggregate(aggregate_id, load_until: load_until) do |event_stream|
|
82
|
-
|
83
|
-
|
82
|
+
aggregate.stream_from_history(event_stream)
|
83
|
+
end
|
84
84
|
|
85
85
|
if clazz
|
86
86
|
fail TypeError, "#{aggregate.class} is not a #{clazz}" unless aggregate.class <= clazz
|
@@ -15,6 +15,10 @@ module Sequent
|
|
15
15
|
end
|
16
16
|
|
17
17
|
class AggregateSnapshotter < BaseCommandHandler
|
18
|
+
# By default skip autoregistering this CommandHandler.
|
19
|
+
# The AggregateSnapshotter is only autoregistered if autoregistration is enabled.
|
20
|
+
self.skip_autoregister = true
|
21
|
+
|
18
22
|
on SnapshotCommand do |command|
|
19
23
|
aggregate_ids = Sequent.configuration.event_store.aggregates_that_need_snapshots(
|
20
24
|
@last_aggregate_id,
|
@@ -21,6 +21,11 @@ module Sequent
|
|
21
21
|
class BaseCommandHandler
|
22
22
|
include Sequent::Core::Helpers::UuidHelper
|
23
23
|
include Sequent::Core::Helpers::MessageHandler
|
24
|
+
extend ActiveSupport::DescendantsTracker
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_accessor :abstract_class, :skip_autoregister
|
28
|
+
end
|
24
29
|
|
25
30
|
protected
|
26
31
|
|
data/lib/sequent/core/core.rb
CHANGED
@@ -16,7 +16,7 @@ require_relative 'projector'
|
|
16
16
|
require_relative 'event_store'
|
17
17
|
require_relative 'event_record'
|
18
18
|
require_relative 'command_record'
|
19
|
-
require_relative 'aggregate_snapshotter'
|
20
19
|
require_relative 'workflow'
|
21
20
|
require_relative 'random_uuid_generator'
|
22
21
|
require_relative 'event_publisher'
|
22
|
+
require_relative 'aggregate_snapshotter'
|
data/lib/sequent/core/event.rb
CHANGED
@@ -29,8 +29,8 @@ module Sequent
|
|
29
29
|
.reject { |k| payload_variables.include?(k) }
|
30
30
|
.select { |k| self.class.types.keys.include?(to_attribute_name(k)) }
|
31
31
|
.each do |k|
|
32
|
-
|
33
|
-
|
32
|
+
result[k.to_s[1..-1].to_sym] = instance_variable_get(k)
|
33
|
+
end
|
34
34
|
result
|
35
35
|
end
|
36
36
|
|
@@ -35,14 +35,6 @@ module Sequent
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def initialize(cache_event_types: true)
|
39
|
-
@event_types = if cache_event_types
|
40
|
-
ThreadSafe::Cache.new
|
41
|
-
else
|
42
|
-
NoEventTypesCache.new
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
38
|
##
|
47
39
|
# Stores the events in the EventStore and publishes the events
|
48
40
|
# to the registered event_handlers.
|
@@ -114,13 +106,13 @@ module Sequent
|
|
114
106
|
events
|
115
107
|
.group_by(&:aggregate_id)
|
116
108
|
.map do |aggregate_id, es|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
109
|
+
[
|
110
|
+
streams.find do |stream_record|
|
111
|
+
stream_record.aggregate_id == aggregate_id
|
112
|
+
end.event_stream,
|
113
|
+
es,
|
114
|
+
]
|
115
|
+
end
|
124
116
|
end
|
125
117
|
|
126
118
|
def aggregate_query(aggregate_id)
|
@@ -217,6 +209,18 @@ module Sequent
|
|
217
209
|
|
218
210
|
private
|
219
211
|
|
212
|
+
def quote_table_name(table_name)
|
213
|
+
Sequent.configuration.event_record_class.connection.quote_table_name(table_name)
|
214
|
+
end
|
215
|
+
|
216
|
+
def event_types
|
217
|
+
@event_types = if Sequent.configuration.event_store_cache_event_types
|
218
|
+
ThreadSafe::Cache.new
|
219
|
+
else
|
220
|
+
NoEventTypesCache.new
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
220
224
|
def column_names
|
221
225
|
@column_names ||= Sequent
|
222
226
|
.configuration
|
@@ -238,7 +242,7 @@ module Sequent
|
|
238
242
|
end
|
239
243
|
|
240
244
|
def resolve_event_type(event_type)
|
241
|
-
|
245
|
+
event_types.fetch_or_store(event_type) { |k| Class.const_get(k) }
|
242
246
|
end
|
243
247
|
|
244
248
|
def publish_events(events)
|
@@ -79,8 +79,8 @@ module Sequent
|
|
79
79
|
super if defined?(super)
|
80
80
|
ensure_known_attributes(attrs)
|
81
81
|
#{@types.map do |attribute, _|
|
82
|
-
|
83
|
-
|
82
|
+
"@#{attribute} = attrs[:#{attribute}]"
|
83
|
+
end.join("\n ")}
|
84
84
|
self
|
85
85
|
end
|
86
86
|
EOS
|
@@ -89,8 +89,8 @@ EOS
|
|
89
89
|
def update_all_attributes_from_json(attrs)
|
90
90
|
super if defined?(super)
|
91
91
|
#{@types.map do |attribute, type|
|
92
|
-
|
93
|
-
|
92
|
+
"@#{attribute} = #{type}.deserialize_from_json(attrs['#{attribute}'])"
|
93
|
+
end.join("\n ")}
|
94
94
|
end
|
95
95
|
EOS
|
96
96
|
end
|
@@ -206,10 +206,10 @@ EOS
|
|
206
206
|
value
|
207
207
|
.select { |val| val.respond_to?(:validation_errors) }
|
208
208
|
.each_with_index do |val, index|
|
209
|
-
|
210
|
-
|
209
|
+
val.validation_errors.each do |k, v|
|
210
|
+
result["#{field[0]}_#{index}_#{k}".to_sym] = v
|
211
|
+
end
|
211
212
|
end
|
212
|
-
end
|
213
213
|
end
|
214
214
|
end
|
215
215
|
prefix ? HashWithIndifferentAccess[result.map { |k, v| ["#{prefix}_#{k}", v] }] : result
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require_relative 'message_handler_option_registry'
|
4
4
|
require_relative 'message_router'
|
5
|
-
require_relative 'message_dispatcher'
|
6
5
|
|
7
6
|
module Sequent
|
8
7
|
module Core
|
@@ -60,11 +59,7 @@ module Sequent
|
|
60
59
|
end
|
61
60
|
|
62
61
|
def message_mapping
|
63
|
-
message_router
|
64
|
-
.routes
|
65
|
-
.select { |matcher, _handlers| matcher.is_a?(MessageMatchers::InstanceOf) }
|
66
|
-
.map { |k, v| [k.expected_class, v] }
|
67
|
-
.to_h
|
62
|
+
message_router.instanceof_routes
|
68
63
|
end
|
69
64
|
|
70
65
|
def handles_message?(message)
|
@@ -106,13 +101,17 @@ module Sequent
|
|
106
101
|
end
|
107
102
|
|
108
103
|
def handle_message(message)
|
109
|
-
|
104
|
+
handlers = self.class.message_router.match_message(message)
|
105
|
+
dispatch_message(message, handlers) unless handlers.empty?
|
110
106
|
end
|
111
107
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
108
|
+
def dispatch_message(message, handlers)
|
109
|
+
handlers.each do |handler|
|
110
|
+
if Sequent.logger.debug?
|
111
|
+
Sequent.logger.debug("[MessageHandler] Handler #{self.class} handling #{message.class}")
|
112
|
+
end
|
113
|
+
instance_exec(message, &handler)
|
114
|
+
end
|
116
115
|
end
|
117
116
|
end
|
118
117
|
end
|
@@ -7,7 +7,7 @@ module Sequent
|
|
7
7
|
module Core
|
8
8
|
module Helpers
|
9
9
|
class MessageRouter
|
10
|
-
attr_reader :routes
|
10
|
+
attr_reader :routes, :instanceof_routes
|
11
11
|
|
12
12
|
def initialize
|
13
13
|
clear_routes
|
@@ -21,7 +21,11 @@ module Sequent
|
|
21
21
|
#
|
22
22
|
def register_matchers(*matchers, handler)
|
23
23
|
matchers.each do |matcher|
|
24
|
-
|
24
|
+
if matcher.is_a?(MessageMatchers::InstanceOf)
|
25
|
+
@instanceof_routes[matcher.expected_class] << handler
|
26
|
+
else
|
27
|
+
@routes[matcher] << handler
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
@@ -29,11 +33,12 @@ module Sequent
|
|
29
33
|
# Returns a set of handlers that match the given message, or an empty set when none match.
|
30
34
|
#
|
31
35
|
def match_message(message)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
result = Set.new
|
37
|
+
result.merge(@instanceof_routes[message.class])
|
38
|
+
@routes.each do |matcher, handlers|
|
39
|
+
result.merge(handlers) if matcher.matches_message?(message)
|
40
|
+
end
|
41
|
+
result
|
37
42
|
end
|
38
43
|
|
39
44
|
##
|
@@ -47,6 +52,7 @@ module Sequent
|
|
47
52
|
# Removes all routes from the router.
|
48
53
|
#
|
49
54
|
def clear_routes
|
55
|
+
@instanceof_routes = Hash.new { |h, k| h[k] = Set.new }
|
50
56
|
@routes = Hash.new { |h, k| h[k] = Set.new }
|
51
57
|
end
|
52
58
|
end
|
@@ -76,6 +76,11 @@ module Sequent
|
|
76
76
|
fail 'Method not supported in this persistor'
|
77
77
|
end
|
78
78
|
|
79
|
+
# Hook to implement for instance the persistor batches statements
|
80
|
+
def prepare
|
81
|
+
fail 'Method not supported in this persistor'
|
82
|
+
end
|
83
|
+
|
79
84
|
# Hook to implement for instance the persistor batches statements
|
80
85
|
def commit
|
81
86
|
fail 'Method not supported in this persistor'
|