sequent 6.0.1 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|