sequent 3.5.0 → 4.0.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/lib/notices.rb +4 -0
- data/lib/sequent.rb +2 -0
- data/lib/sequent/configuration.rb +3 -0
- data/lib/sequent/core/aggregate_repository.rb +2 -1
- data/lib/sequent/core/command_record.rb +4 -1
- data/lib/sequent/core/command_service.rb +2 -0
- data/lib/sequent/core/event_store.rb +13 -2
- data/lib/sequent/core/transactions/active_record_transaction_provider.rb +5 -3
- 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/migrations/executor.rb +12 -0
- data/lib/sequent/migrations/view_schema.rb +10 -0
- data/lib/sequent/rake/migration_tasks.rb +1 -1
- data/lib/sequent/test/command_handler_helpers.rb +4 -0
- data/lib/sequent/util/skip_if_already_processing.rb +19 -5
- data/lib/version.rb +1 -1
- metadata +50 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3322a4b950e847a5818555b2718df70e2b81a8e398074309a0b58c2a8d332ce5
|
4
|
+
data.tar.gz: 6136697a8a9596e999fabf3814509428b06188a0ac577fa3443a80ec190cbdd7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8ce675ae0a16274630066192086fb8c46bb0306e9280fd4b8901500af4a7af56844c5d6c40cd0f45a87342c33a4faeb2eee48f0dc0fb05a0a09046a2dea7414
|
7
|
+
data.tar.gz: e750e54d72de1c8641a513839c675adeb32fbeaa3435528a8aca755a951a7572d696e9348b7594e104f044978a0e9780f8e080ff1ad5e4ea9b7cc203fac6fee2
|
data/lib/notices.rb
ADDED
data/lib/sequent.rb
CHANGED
@@ -13,6 +13,7 @@ module Sequent
|
|
13
13
|
|
14
14
|
DEFAULT_MIGRATION_SQL_FILES_DIRECTORY = 'db/tables'
|
15
15
|
DEFAULT_DATABASE_CONFIG_DIRECTORY = 'db'
|
16
|
+
DEFAULT_DATABASE_SCHEMA_DIRECTORY = 'db'
|
16
17
|
|
17
18
|
DEFAULT_VIEW_SCHEMA_NAME = 'view_schema'
|
18
19
|
DEFAULT_EVENT_STORE_SCHEMA_NAME= 'sequent_schema'
|
@@ -61,6 +62,7 @@ module Sequent
|
|
61
62
|
:online_replay_persistor_class,
|
62
63
|
:number_of_replay_processes,
|
63
64
|
:database_config_directory,
|
65
|
+
:database_schema_directory,
|
64
66
|
:event_store_schema_name
|
65
67
|
|
66
68
|
attr_accessor :strict_check_attributes_on_apply_events
|
@@ -109,6 +111,7 @@ module Sequent
|
|
109
111
|
self.offline_replay_persistor_class = DEFAULT_OFFLINE_REPLAY_PERSISTOR_CLASS
|
110
112
|
self.online_replay_persistor_class = DEFAULT_ONLINE_REPLAY_PERSISTOR_CLASS
|
111
113
|
self.database_config_directory = DEFAULT_DATABASE_CONFIG_DIRECTORY
|
114
|
+
self.database_schema_directory = DEFAULT_DATABASE_SCHEMA_DIRECTORY
|
112
115
|
self.strict_check_attributes_on_apply_events = DEFAULT_STRICT_CHECK_ATTRIBUTES_ON_APPLY_EVENTS
|
113
116
|
|
114
117
|
self.logger = Logger.new(STDOUT).tap {|l| l.level = Logger::INFO }
|
@@ -100,7 +100,8 @@ module Sequent
|
|
100
100
|
##
|
101
101
|
# Returns whether the event store has an aggregate with the given id
|
102
102
|
def contains_aggregate?(aggregate_id)
|
103
|
-
Sequent.configuration.event_store.stream_exists?(aggregate_id)
|
103
|
+
Sequent.configuration.event_store.stream_exists?(aggregate_id) &&
|
104
|
+
Sequent.configuration.event_store.events_exists?(aggregate_id)
|
104
105
|
end
|
105
106
|
|
106
107
|
# Gets all uncommitted_events from the 'registered' aggregates
|
@@ -42,7 +42,10 @@ module Sequent
|
|
42
42
|
validates_presence_of :command_type, :command_json
|
43
43
|
|
44
44
|
def parent
|
45
|
-
EventRecord
|
45
|
+
EventRecord
|
46
|
+
.where(aggregate_id: event_aggregate_id, sequence_number: event_sequence_number)
|
47
|
+
.where('event_type != ?', Sequent::Core::SnapshotEvent.name)
|
48
|
+
.first
|
46
49
|
end
|
47
50
|
|
48
51
|
def children
|
@@ -52,6 +52,7 @@ module Sequent
|
|
52
52
|
while(!command_queue.empty?) do
|
53
53
|
process_command(command_queue.pop)
|
54
54
|
end
|
55
|
+
Sequent::Util.done_processing(:command_service_process_commands)
|
55
56
|
end
|
56
57
|
ensure
|
57
58
|
command_queue.clear
|
@@ -103,6 +104,7 @@ module Sequent
|
|
103
104
|
|
104
105
|
# Raised when BaseCommand.valid? returns false
|
105
106
|
class CommandNotValid < ArgumentError
|
107
|
+
attr_reader :command
|
106
108
|
|
107
109
|
def initialize(command)
|
108
110
|
@command = command
|
@@ -88,6 +88,9 @@ ORDER BY sequence_number ASC, (CASE event_type WHEN #{quote Sequent.configuratio
|
|
88
88
|
Sequent.configuration.stream_record_class.exists?(aggregate_id: aggregate_id)
|
89
89
|
end
|
90
90
|
|
91
|
+
def events_exists?(aggregate_id)
|
92
|
+
Sequent.configuration.event_record_class.exists?(aggregate_id: aggregate_id)
|
93
|
+
end
|
91
94
|
##
|
92
95
|
# Replays all events in the event store to the registered event_handlers.
|
93
96
|
#
|
@@ -166,7 +169,15 @@ SELECT aggregate_id
|
|
166
169
|
private
|
167
170
|
|
168
171
|
def column_names
|
169
|
-
@column_names ||= Sequent
|
172
|
+
@column_names ||= Sequent
|
173
|
+
.configuration
|
174
|
+
.event_record_class
|
175
|
+
.column_names
|
176
|
+
.reject { |c| c == primary_key_event_records }
|
177
|
+
end
|
178
|
+
|
179
|
+
def primary_key_event_records
|
180
|
+
@primary_key_event_records ||= Sequent.configuration.event_record_class.primary_key
|
170
181
|
end
|
171
182
|
|
172
183
|
def deserialize_event(event_hash)
|
@@ -208,7 +219,7 @@ SELECT aggregate_id
|
|
208
219
|
.join(',')
|
209
220
|
columns = column_names.map { |c| connection.quote_column_name(c) }.join(',')
|
210
221
|
sql = %Q{insert into #{connection.quote_table_name(Sequent.configuration.event_record_class.table_name)} (#{columns}) values #{values}}
|
211
|
-
Sequent.configuration.event_record_class.connection.insert(sql)
|
222
|
+
Sequent.configuration.event_record_class.connection.insert(sql, nil, primary_key_event_records)
|
212
223
|
rescue ActiveRecord::RecordNotUnique
|
213
224
|
fail OptimisticLockingError.new
|
214
225
|
end
|
@@ -7,7 +7,9 @@ module Sequent
|
|
7
7
|
Sequent::ApplicationRecord.transaction(requires_new: true) do
|
8
8
|
yield
|
9
9
|
end
|
10
|
-
after_commit_queue.
|
10
|
+
while(!after_commit_queue.empty?) do
|
11
|
+
after_commit_queue.pop.call
|
12
|
+
end
|
11
13
|
ensure
|
12
14
|
clear_after_commit_queue
|
13
15
|
end
|
@@ -19,11 +21,11 @@ module Sequent
|
|
19
21
|
private
|
20
22
|
|
21
23
|
def after_commit_queue
|
22
|
-
Thread.current[:after_commit_queue] ||=
|
24
|
+
Thread.current[:after_commit_queue] ||= Queue.new
|
23
25
|
end
|
24
26
|
|
25
27
|
def clear_after_commit_queue
|
26
|
-
|
28
|
+
after_commit_queue.clear
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -21,6 +21,18 @@ module Sequent
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def create_indexes_after_execute_online(plan)
|
25
|
+
plan.replay_tables.each do |migration|
|
26
|
+
table = migration.record_class
|
27
|
+
original_table_name = table.table_name.gsub("_#{migration.version}", '')
|
28
|
+
indexes_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{original_table_name}.indexes.sql"
|
29
|
+
if File.exist?(indexes_file_name)
|
30
|
+
statements = sql_file_to_statements(indexes_file_name) { |raw_sql| raw_sql.gsub('%SUFFIX%', "_#{migration.version}") }
|
31
|
+
statements.each(&method(:exec_sql))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
24
36
|
def execute_offline(plan, current_version)
|
25
37
|
plan.replay_tables.each do |migration|
|
26
38
|
table = migration.record_class
|
@@ -98,6 +98,12 @@ module Sequent
|
|
98
98
|
Sequent::Core::Migratable.all.flat_map(&:managed_tables).each do |table|
|
99
99
|
statements = sql_file_to_statements("#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.sql") { |raw_sql| raw_sql.remove('%SUFFIX%') }
|
100
100
|
statements.each { |statement| exec_sql(statement) }
|
101
|
+
|
102
|
+
indexes_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.indexes.sql"
|
103
|
+
if File.exist?(indexes_file_name)
|
104
|
+
statements = sql_file_to_statements(indexes_file_name) { |raw_sql| raw_sql.remove('%SUFFIX%') }
|
105
|
+
statements.each(&method(:exec_sql))
|
106
|
+
end
|
101
107
|
end
|
102
108
|
end
|
103
109
|
end
|
@@ -159,6 +165,10 @@ module Sequent
|
|
159
165
|
if plan.projectors.any?
|
160
166
|
replay!(Sequent.configuration.online_replay_persistor_class.new)
|
161
167
|
end
|
168
|
+
|
169
|
+
in_view_schema do
|
170
|
+
executor.create_indexes_after_execute_online(plan)
|
171
|
+
end
|
162
172
|
rescue Exception => e
|
163
173
|
rollback_migration
|
164
174
|
raise e
|
@@ -55,7 +55,7 @@ module Sequent
|
|
55
55
|
|
56
56
|
def create_event_store(db_config)
|
57
57
|
event_store_schema = Sequent.configuration.event_store_schema_name
|
58
|
-
sequent_schema = File.join(Sequent.configuration.
|
58
|
+
sequent_schema = File.join(Sequent.configuration.database_schema_directory, "#{event_store_schema}.rb")
|
59
59
|
fail "File #{sequent_schema} does not exist. Check your Sequent configuration." unless File.exists?(sequent_schema)
|
60
60
|
|
61
61
|
Sequent::Support::Database.establish_connection(db_config)
|
@@ -1,15 +1,29 @@
|
|
1
1
|
module Sequent
|
2
2
|
module Util
|
3
|
-
|
4
|
-
|
3
|
+
##
|
4
|
+
# Returns if the current Thread is already processing work
|
5
|
+
# given the +processing_key+ otherwise
|
6
|
+
# it yields the given +&block+.
|
7
|
+
#
|
8
|
+
# Useful in a Queue and Processing strategy
|
9
|
+
def self.skip_if_already_processing(processing_key, &block)
|
10
|
+
return if Thread.current[processing_key]
|
5
11
|
|
6
12
|
begin
|
7
|
-
Thread.current[
|
13
|
+
Thread.current[processing_key] = true
|
8
14
|
|
9
|
-
|
15
|
+
yield
|
10
16
|
ensure
|
11
|
-
Thread.current[
|
17
|
+
Thread.current[processing_key] = nil
|
12
18
|
end
|
13
19
|
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Reset the given +processing_key+ for the current Thread.
|
23
|
+
#
|
24
|
+
# Usefull to make a block protected by +skip_if_already_processing+ reentrant.
|
25
|
+
def self.done_processing(processing_key)
|
26
|
+
Thread.current[processing_key] = nil
|
27
|
+
end
|
14
28
|
end
|
15
29
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Vonk
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
- Erik Rozendaal
|
10
10
|
- Mike van Diepen
|
11
11
|
- Stephan van Diepen
|
12
|
-
autorequire:
|
12
|
+
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2021-02-24 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: activerecord
|
@@ -21,9 +21,9 @@ dependencies:
|
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: '5.0'
|
24
|
-
- - "
|
24
|
+
- - "<"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 6.0.4
|
27
27
|
type: :runtime
|
28
28
|
prerelease: false
|
29
29
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,9 +31,9 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '5.0'
|
34
|
-
- - "
|
34
|
+
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
36
|
+
version: 6.0.4
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: activemodel
|
39
39
|
requirement: !ruby/object:Gem::Requirement
|
@@ -41,9 +41,9 @@ dependencies:
|
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '5.0'
|
44
|
-
- - "
|
44
|
+
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
46
|
+
version: 6.0.4
|
47
47
|
type: :runtime
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -51,23 +51,23 @@ dependencies:
|
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '5.0'
|
54
|
-
- - "
|
54
|
+
- - "<"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
56
|
+
version: 6.0.4
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: pg
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - "~>"
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: '1.
|
63
|
+
version: '1.2'
|
64
64
|
type: :runtime
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
68
|
- - "~>"
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version: '1.
|
70
|
+
version: '1.2'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: postgresql_cursor
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,14 +116,14 @@ dependencies:
|
|
116
116
|
requirements:
|
117
117
|
- - "~>"
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version: '1.
|
119
|
+
version: '1.20'
|
120
120
|
type: :runtime
|
121
121
|
prerelease: false
|
122
122
|
version_requirements: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
124
|
- - "~>"
|
125
125
|
- !ruby/object:Gem::Version
|
126
|
-
version: '1.
|
126
|
+
version: '1.20'
|
127
127
|
- !ruby/object:Gem::Dependency
|
128
128
|
name: bcrypt
|
129
129
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,16 +142,22 @@ dependencies:
|
|
142
142
|
name: parser
|
143
143
|
requirement: !ruby/object:Gem::Requirement
|
144
144
|
requirements:
|
145
|
-
- - "
|
145
|
+
- - ">="
|
146
146
|
- !ruby/object:Gem::Version
|
147
147
|
version: 2.6.5
|
148
|
+
- - "<="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '3'
|
148
151
|
type: :runtime
|
149
152
|
prerelease: false
|
150
153
|
version_requirements: !ruby/object:Gem::Requirement
|
151
154
|
requirements:
|
152
|
-
- - "
|
155
|
+
- - ">="
|
153
156
|
- !ruby/object:Gem::Version
|
154
157
|
version: 2.6.5
|
158
|
+
- - "<="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '3'
|
155
161
|
- !ruby/object:Gem::Dependency
|
156
162
|
name: i18n
|
157
163
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,20 +172,34 @@ dependencies:
|
|
166
172
|
- - ">="
|
167
173
|
- !ruby/object:Gem::Version
|
168
174
|
version: '0'
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: tzinfo
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "<="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 1.2.7
|
182
|
+
type: :runtime
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "<="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 1.2.7
|
169
189
|
- !ruby/object:Gem::Dependency
|
170
190
|
name: rspec
|
171
191
|
requirement: !ruby/object:Gem::Requirement
|
172
192
|
requirements:
|
173
193
|
- - "~>"
|
174
194
|
- !ruby/object:Gem::Version
|
175
|
-
version: '3.
|
195
|
+
version: '3.10'
|
176
196
|
type: :development
|
177
197
|
prerelease: false
|
178
198
|
version_requirements: !ruby/object:Gem::Requirement
|
179
199
|
requirements:
|
180
200
|
- - "~>"
|
181
201
|
- !ruby/object:Gem::Version
|
182
|
-
version: '3.
|
202
|
+
version: '3.10'
|
183
203
|
- !ruby/object:Gem::Dependency
|
184
204
|
name: timecop
|
185
205
|
requirement: !ruby/object:Gem::Requirement
|
@@ -200,14 +220,14 @@ dependencies:
|
|
200
220
|
requirements:
|
201
221
|
- - "~>"
|
202
222
|
- !ruby/object:Gem::Version
|
203
|
-
version: '3.
|
223
|
+
version: '3.10'
|
204
224
|
type: :development
|
205
225
|
prerelease: false
|
206
226
|
version_requirements: !ruby/object:Gem::Requirement
|
207
227
|
requirements:
|
208
228
|
- - "~>"
|
209
229
|
- !ruby/object:Gem::Version
|
210
|
-
version: '3.
|
230
|
+
version: '3.10'
|
211
231
|
- !ruby/object:Gem::Dependency
|
212
232
|
name: rspec-collection_matchers
|
213
233
|
requirement: !ruby/object:Gem::Requirement
|
@@ -242,28 +262,28 @@ dependencies:
|
|
242
262
|
requirements:
|
243
263
|
- - "~>"
|
244
264
|
- !ruby/object:Gem::Version
|
245
|
-
version: '0.
|
265
|
+
version: '0.13'
|
246
266
|
type: :development
|
247
267
|
prerelease: false
|
248
268
|
version_requirements: !ruby/object:Gem::Requirement
|
249
269
|
requirements:
|
250
270
|
- - "~>"
|
251
271
|
- !ruby/object:Gem::Version
|
252
|
-
version: '0.
|
272
|
+
version: '0.13'
|
253
273
|
- !ruby/object:Gem::Dependency
|
254
274
|
name: simplecov
|
255
275
|
requirement: !ruby/object:Gem::Requirement
|
256
276
|
requirements:
|
257
277
|
- - "~>"
|
258
278
|
- !ruby/object:Gem::Version
|
259
|
-
version: '0.
|
279
|
+
version: '0.21'
|
260
280
|
type: :development
|
261
281
|
prerelease: false
|
262
282
|
version_requirements: !ruby/object:Gem::Requirement
|
263
283
|
requirements:
|
264
284
|
- - "~>"
|
265
285
|
- !ruby/object:Gem::Version
|
266
|
-
version: '0.
|
286
|
+
version: '0.21'
|
267
287
|
description: Sequent is a CQRS and event sourcing framework for Ruby.
|
268
288
|
email:
|
269
289
|
- lars.vonk@gmail.com
|
@@ -278,6 +298,7 @@ extra_rdoc_files: []
|
|
278
298
|
files:
|
279
299
|
- bin/sequent
|
280
300
|
- db/sequent_schema.rb
|
301
|
+
- lib/notices.rb
|
281
302
|
- lib/sequent.rb
|
282
303
|
- lib/sequent/application_record.rb
|
283
304
|
- lib/sequent/configuration.rb
|
@@ -390,7 +411,7 @@ homepage: https://github.com/zilverline/sequent
|
|
390
411
|
licenses:
|
391
412
|
- MIT
|
392
413
|
metadata: {}
|
393
|
-
post_install_message:
|
414
|
+
post_install_message:
|
394
415
|
rdoc_options: []
|
395
416
|
require_paths:
|
396
417
|
- lib
|
@@ -398,15 +419,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
398
419
|
requirements:
|
399
420
|
- - ">="
|
400
421
|
- !ruby/object:Gem::Version
|
401
|
-
version: '2.
|
422
|
+
version: '2.7'
|
402
423
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
403
424
|
requirements:
|
404
425
|
- - ">="
|
405
426
|
- !ruby/object:Gem::Version
|
406
427
|
version: '0'
|
407
428
|
requirements: []
|
408
|
-
rubygems_version: 3.
|
409
|
-
signing_key:
|
429
|
+
rubygems_version: 3.2.3
|
430
|
+
signing_key:
|
410
431
|
specification_version: 4
|
411
432
|
summary: Event sourcing framework for Ruby
|
412
433
|
test_files: []
|