rails_event_store_active_record 2.1.0 → 2.4.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/README.md +1 -1
- data/lib/rails_event_store_active_record/batch_enumerator.rb +3 -4
- data/lib/rails_event_store_active_record/event.rb +3 -3
- data/lib/rails_event_store_active_record/event_repository.rb +61 -37
- data/lib/rails_event_store_active_record/event_repository_reader.rb +83 -44
- data/lib/rails_event_store_active_record/generators/migration_generator.rb +10 -8
- data/lib/rails_event_store_active_record/generators/templates/create_event_store_events_template.rb +30 -26
- data/lib/rails_event_store_active_record/index_violation_detector.rb +15 -19
- data/lib/rails_event_store_active_record/pg_linearized_event_repository.rb +1 -8
- data/lib/rails_event_store_active_record/version.rb +1 -1
- data/lib/rails_event_store_active_record/with_abstract_base_class.rb +11 -11
- data/lib/rails_event_store_active_record.rb +10 -10
- metadata +9 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b146f907eef70a988c0949381dbf897a4f93bd7aa9e1ccf8346559c2656feb80
|
4
|
+
data.tar.gz: 46b6f4ce8d62b7386d72a1219a375e012bd3d15b33b16fe552cb8adee9f4f6e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0be237c7518c776a5ee19e8625f39966a20f255b0076839695957df0aa337c181bc7c85351afb2a1fbffdd6b0fd75fa50217482c30d1aa82437b242a32552e8d
|
7
|
+
data.tar.gz: 32aa1160ba4de4081357319e25012330249b6e946d0e0b558452fe41e7a30aa25e70d65faf9897ba6b7afac841c6b2ea6011117a892a272179da58b38a96bbb4
|
data/README.md
CHANGED
@@ -4,4 +4,4 @@ Persistent event repository implementation for RubyEventStore based on ActiveRec
|
|
4
4
|
|
5
5
|
Includes repository implementation with linearized writes to achieve log-like properties of streams on top of SQL database engine.
|
6
6
|
|
7
|
-
Find out more at [https://railseventstore.org](https://railseventstore.org/)
|
7
|
+
Find out more at [https://railseventstore.org](https://railseventstore.org/)
|
@@ -3,9 +3,9 @@
|
|
3
3
|
module RailsEventStoreActiveRecord
|
4
4
|
class BatchEnumerator
|
5
5
|
def initialize(batch_size, total_limit, reader)
|
6
|
-
@batch_size
|
6
|
+
@batch_size = batch_size
|
7
7
|
@total_limit = total_limit
|
8
|
-
@reader
|
8
|
+
@reader = reader
|
9
9
|
end
|
10
10
|
|
11
11
|
def each
|
@@ -13,7 +13,7 @@ module RailsEventStoreActiveRecord
|
|
13
13
|
offset_id = nil
|
14
14
|
|
15
15
|
0.step(total_limit - 1, batch_size) do |batch_offset|
|
16
|
-
batch_limit
|
16
|
+
batch_limit = [batch_size, total_limit - batch_offset].min
|
17
17
|
results, offset_id = reader.call(offset_id, batch_limit)
|
18
18
|
|
19
19
|
break if results.empty?
|
@@ -34,4 +34,3 @@ module RailsEventStoreActiveRecord
|
|
34
34
|
attr_reader :batch_size, :total_limit, :reader
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_record"
|
4
4
|
|
5
5
|
module RailsEventStoreActiveRecord
|
6
6
|
class Event < ::ActiveRecord::Base
|
7
7
|
self.primary_key = :id
|
8
|
-
self.table_name =
|
8
|
+
self.table_name = "event_store_events"
|
9
9
|
end
|
10
10
|
|
11
11
|
class EventInStream < ::ActiveRecord::Base
|
12
12
|
self.primary_key = :id
|
13
|
-
self.table_name =
|
13
|
+
self.table_name = "event_store_events_in_streams"
|
14
14
|
belongs_to :event, primary_key: :event_id
|
15
15
|
end
|
16
16
|
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require 'activerecord-import'
|
3
|
+
require "active_support/core_ext/array"
|
5
4
|
|
6
5
|
module RailsEventStoreActiveRecord
|
7
6
|
class EventRepository
|
8
7
|
POSITION_SHIFT = 1
|
9
8
|
|
10
9
|
def initialize(model_factory: WithDefaultModels.new, serializer:)
|
11
|
-
@serializer
|
10
|
+
@serializer = serializer
|
12
11
|
|
13
12
|
@event_klass, @stream_klass = model_factory.call
|
14
13
|
@repo_reader = EventRepositoryReader.new(@event_klass, @stream_klass, serializer)
|
@@ -16,15 +15,13 @@ module RailsEventStoreActiveRecord
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def append_to_stream(records, stream, expected_version)
|
19
|
-
hashes
|
18
|
+
hashes = []
|
20
19
|
event_ids = []
|
21
20
|
records.each do |record|
|
22
|
-
hashes
|
21
|
+
hashes << insert_hash(record, record.serialize(serializer))
|
23
22
|
event_ids << record.event_id
|
24
23
|
end
|
25
|
-
add_to_stream(event_ids, stream, expected_version)
|
26
|
-
@event_klass.import(hashes)
|
27
|
-
end
|
24
|
+
add_to_stream(event_ids, stream, expected_version) { @event_klass.insert_all!(hashes) }
|
28
25
|
end
|
29
26
|
|
30
27
|
def link_to_stream(event_ids, stream, expected_version)
|
@@ -55,39 +52,61 @@ module RailsEventStoreActiveRecord
|
|
55
52
|
end
|
56
53
|
|
57
54
|
def update_messages(records)
|
58
|
-
hashes
|
55
|
+
hashes = records.map { |record| upsert_hash(record, record.serialize(serializer)) }
|
59
56
|
for_update = records.map(&:event_id)
|
60
57
|
start_transaction do
|
61
|
-
existing =
|
62
|
-
|
63
|
-
|
64
|
-
|
58
|
+
existing =
|
59
|
+
@event_klass
|
60
|
+
.where(event_id: for_update)
|
61
|
+
.pluck(:event_id, :id, :created_at)
|
62
|
+
.reduce({}) { |acc, (event_id, id, created_at)| acc.merge(event_id => [id, created_at]) }
|
63
|
+
(for_update - existing.keys).each { |id| raise RubyEventStore::EventNotFound.new(id) }
|
64
|
+
hashes.each do |h|
|
65
|
+
h[:id] = existing.fetch(h.fetch(:event_id)).at(0)
|
66
|
+
h[:created_at] = existing.fetch(h.fetch(:event_id)).at(1)
|
67
|
+
end
|
68
|
+
@event_klass.upsert_all(hashes)
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
72
|
def streams_of(event_id)
|
69
|
-
@
|
70
|
-
|
71
|
-
|
73
|
+
@repo_reader.streams_of(event_id)
|
74
|
+
end
|
75
|
+
|
76
|
+
def position_in_stream(event_id, stream)
|
77
|
+
@repo_reader.position_in_stream(event_id, stream)
|
78
|
+
end
|
79
|
+
|
80
|
+
def global_position(event_id)
|
81
|
+
@repo_reader.global_position(event_id)
|
82
|
+
end
|
83
|
+
|
84
|
+
def event_in_stream?(event_id, stream)
|
85
|
+
@repo_reader.event_in_stream?(event_id, stream)
|
72
86
|
end
|
73
87
|
|
74
88
|
private
|
89
|
+
|
75
90
|
attr_reader :serializer
|
76
91
|
|
77
92
|
def add_to_stream(event_ids, stream, expected_version)
|
78
|
-
last_stream_version = ->(stream_)
|
93
|
+
last_stream_version = ->(stream_) do
|
94
|
+
@stream_klass.where(stream: stream_.name).order("position DESC").first.try(:position)
|
95
|
+
end
|
79
96
|
resolved_version = expected_version.resolve_for(stream, last_stream_version)
|
80
97
|
|
81
98
|
start_transaction do
|
82
99
|
yield if block_given?
|
83
|
-
in_stream =
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
100
|
+
in_stream =
|
101
|
+
event_ids.map.with_index do |event_id, index|
|
102
|
+
{
|
103
|
+
stream: stream.name,
|
104
|
+
position: compute_position(resolved_version, index),
|
105
|
+
event_id: event_id,
|
106
|
+
created_at: Time.now.utc
|
107
|
+
}
|
108
|
+
end
|
109
|
+
@stream_klass.insert_all!(in_stream) unless stream.global?
|
91
110
|
end
|
92
111
|
self
|
93
112
|
rescue ActiveRecord::RecordNotUnique => e
|
@@ -95,30 +114,36 @@ module RailsEventStoreActiveRecord
|
|
95
114
|
end
|
96
115
|
|
97
116
|
def raise_error(e)
|
98
|
-
if detect_index_violated(e.message)
|
99
|
-
raise RubyEventStore::EventDuplicatedInStream
|
100
|
-
end
|
117
|
+
raise RubyEventStore::EventDuplicatedInStream if detect_index_violated(e.message)
|
101
118
|
raise RubyEventStore::WrongExpectedEventVersion
|
102
119
|
end
|
103
120
|
|
104
121
|
def compute_position(resolved_version, index)
|
105
|
-
unless resolved_version.nil?
|
106
|
-
resolved_version + index + POSITION_SHIFT
|
107
|
-
end
|
122
|
+
resolved_version + index + POSITION_SHIFT unless resolved_version.nil?
|
108
123
|
end
|
109
124
|
|
110
125
|
def detect_index_violated(message)
|
111
126
|
@index_violation_detector.detect(message)
|
112
127
|
end
|
113
128
|
|
114
|
-
def
|
129
|
+
def insert_hash(record, serialized_record)
|
115
130
|
{
|
116
|
-
event_id:
|
117
|
-
data:
|
118
|
-
metadata:
|
131
|
+
event_id: serialized_record.event_id,
|
132
|
+
data: serialized_record.data,
|
133
|
+
metadata: serialized_record.metadata,
|
119
134
|
event_type: serialized_record.event_type,
|
120
135
|
created_at: record.timestamp,
|
121
|
-
valid_at:
|
136
|
+
valid_at: optimize_timestamp(record.valid_at, record.timestamp)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
def upsert_hash(record, serialized_record)
|
141
|
+
{
|
142
|
+
event_id: serialized_record.event_id,
|
143
|
+
data: serialized_record.data,
|
144
|
+
metadata: serialized_record.metadata,
|
145
|
+
event_type: serialized_record.event_type,
|
146
|
+
valid_at: optimize_timestamp(record.valid_at, record.timestamp)
|
122
147
|
}
|
123
148
|
end
|
124
149
|
|
@@ -130,5 +155,4 @@ module RailsEventStoreActiveRecord
|
|
130
155
|
@event_klass.transaction(requires_new: true, &block)
|
131
156
|
end
|
132
157
|
end
|
133
|
-
|
134
158
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module RailsEventStoreActiveRecord
|
4
4
|
class EventRepositoryReader
|
5
|
-
|
6
5
|
def initialize(event_klass, stream_klass, serializer)
|
7
6
|
@event_klass = event_klass
|
8
7
|
@stream_klass = stream_klass
|
@@ -14,20 +13,14 @@ module RailsEventStoreActiveRecord
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def last_stream_event(stream)
|
17
|
-
record_ = @stream_klass.where(stream: stream.name).order(
|
16
|
+
record_ = @stream_klass.where(stream: stream.name).order("position DESC, id DESC").first
|
18
17
|
record(record_) if record_
|
19
18
|
end
|
20
19
|
|
21
20
|
def read(spec)
|
22
21
|
stream = read_scope(spec)
|
23
|
-
|
24
22
|
if spec.batched?
|
25
|
-
|
26
|
-
search_in = spec.stream.global? ? @event_klass.table_name : @stream_klass.table_name
|
27
|
-
records = offset_id.nil? ? stream.limit(limit) : stream.where(start_offset_condition(spec, offset_id, search_in)).limit(limit)
|
28
|
-
[records.map(&method(:record)), records.last]
|
29
|
-
end
|
30
|
-
BatchEnumerator.new(spec.batch_size, spec.limit, batch_reader).each
|
23
|
+
spec.time_sort_by ? offset_limit_batch_reader(spec, stream) : monotonic_id_batch_reader(spec, stream)
|
31
24
|
elsif spec.first?
|
32
25
|
record_ = stream.first
|
33
26
|
record(record_) if record_
|
@@ -43,35 +36,75 @@ module RailsEventStoreActiveRecord
|
|
43
36
|
read_scope(spec).count
|
44
37
|
end
|
45
38
|
|
39
|
+
def streams_of(event_id)
|
40
|
+
@stream_klass.where(event_id: event_id).pluck(:stream).map { |name| RubyEventStore::Stream.new(name) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def position_in_stream(event_id, stream)
|
44
|
+
record = @stream_klass.select("position").where(stream: stream.name).find_by(event_id: event_id)
|
45
|
+
raise RubyEventStore::EventNotFoundInStream if record.nil?
|
46
|
+
record.position
|
47
|
+
end
|
48
|
+
|
49
|
+
def global_position(event_id)
|
50
|
+
record = @event_klass.select("id").find_by(event_id: event_id)
|
51
|
+
raise RubyEventStore::EventNotFound.new(event_id) if record.nil?
|
52
|
+
record.id - 1
|
53
|
+
end
|
54
|
+
|
55
|
+
def event_in_stream?(event_id, stream)
|
56
|
+
@stream_klass.where(event_id: event_id, stream: stream.name).exists?
|
57
|
+
end
|
58
|
+
|
46
59
|
private
|
60
|
+
|
47
61
|
attr_reader :serializer
|
48
62
|
|
63
|
+
def offset_limit_batch_reader(spec, stream)
|
64
|
+
batch_reader = ->(offset, limit) { stream.offset(offset).limit(limit).map(&method(:record)) }
|
65
|
+
RubyEventStore::BatchEnumerator.new(spec.batch_size, spec.limit, batch_reader).each
|
66
|
+
end
|
67
|
+
|
68
|
+
def monotonic_id_batch_reader(spec, stream)
|
69
|
+
batch_reader = ->(offset_id, limit) do
|
70
|
+
search_in = spec.stream.global? ? @event_klass.table_name : @stream_klass.table_name
|
71
|
+
records =
|
72
|
+
if offset_id.nil?
|
73
|
+
stream.limit(limit)
|
74
|
+
else
|
75
|
+
stream.where(start_offset_condition(spec, offset_id, search_in)).limit(limit)
|
76
|
+
end
|
77
|
+
[records.map(&method(:record)), records.last]
|
78
|
+
end
|
79
|
+
BatchEnumerator.new(spec.batch_size, spec.limit, batch_reader).each
|
80
|
+
end
|
81
|
+
|
49
82
|
def read_scope(spec)
|
50
83
|
if spec.stream.global?
|
51
84
|
stream = @event_klass
|
52
|
-
stream = stream.where(event_id: spec.with_ids)
|
53
|
-
stream = stream.where(event_type: spec.with_types)
|
85
|
+
stream = stream.where(event_id: spec.with_ids) if spec.with_ids?
|
86
|
+
stream = stream.where(event_type: spec.with_types) if spec.with_types?
|
54
87
|
stream = ordered(stream, spec)
|
55
|
-
stream = stream.limit(spec.limit)
|
56
|
-
stream = stream.where(start_condition_in_global_stream(spec))
|
57
|
-
stream = stream.where(stop_condition_in_global_stream(spec))
|
58
|
-
stream = stream.where(older_than_condition(spec))
|
88
|
+
stream = stream.limit(spec.limit) if spec.limit?
|
89
|
+
stream = stream.where(start_condition_in_global_stream(spec)) if spec.start
|
90
|
+
stream = stream.where(stop_condition_in_global_stream(spec)) if spec.stop
|
91
|
+
stream = stream.where(older_than_condition(spec)) if spec.older_than
|
59
92
|
stream = stream.where(older_than_or_equal_condition(spec)) if spec.older_than_or_equal
|
60
|
-
stream = stream.where(newer_than_condition(spec))
|
93
|
+
stream = stream.where(newer_than_condition(spec)) if spec.newer_than
|
61
94
|
stream = stream.where(newer_than_or_equal_condition(spec)) if spec.newer_than_or_equal
|
62
95
|
stream.order(id: order(spec))
|
63
96
|
else
|
64
97
|
stream = @stream_klass.preload(:event).where(stream: spec.stream.name)
|
65
|
-
stream = stream.where(event_id: spec.with_ids)
|
66
|
-
stream = stream.where(@event_klass.table_name => {event_type: spec.with_types}) if spec.with_types?
|
98
|
+
stream = stream.where(event_id: spec.with_ids) if spec.with_ids?
|
99
|
+
stream = stream.where(@event_klass.table_name => { event_type: spec.with_types }) if spec.with_types?
|
67
100
|
stream = ordered(stream.joins(:event), spec)
|
68
|
-
stream = stream.order(
|
69
|
-
stream = stream.limit(spec.limit)
|
70
|
-
stream = stream.where(start_condition(spec))
|
71
|
-
stream = stream.where(stop_condition(spec))
|
72
|
-
stream = stream.where(older_than_condition(spec))
|
101
|
+
stream = stream.order(id: order(spec))
|
102
|
+
stream = stream.limit(spec.limit) if spec.limit?
|
103
|
+
stream = stream.where(start_condition(spec)) if spec.start
|
104
|
+
stream = stream.where(stop_condition(spec)) if spec.stop
|
105
|
+
stream = stream.where(older_than_condition(spec)) if spec.older_than
|
73
106
|
stream = stream.where(older_than_or_equal_condition(spec)) if spec.older_than_or_equal
|
74
|
-
stream = stream.where(newer_than_condition(spec))
|
107
|
+
stream = stream.where(newer_than_condition(spec)) if spec.newer_than
|
75
108
|
stream = stream.where(newer_than_or_equal_condition(spec)) if spec.newer_than_or_equal
|
76
109
|
stream
|
77
110
|
end
|
@@ -89,37 +122,41 @@ module RailsEventStoreActiveRecord
|
|
89
122
|
end
|
90
123
|
|
91
124
|
def start_offset_condition(specification, record_id, search_in)
|
92
|
-
condition = "#{search_in}.id #{specification.forward? ?
|
125
|
+
condition = "#{search_in}.id #{specification.forward? ? ">" : "<"} ?"
|
93
126
|
[condition, record_id]
|
94
127
|
end
|
95
128
|
|
96
129
|
def stop_offset_condition(specification, record_id, search_in)
|
97
|
-
condition = "#{search_in}.id #{specification.forward? ?
|
130
|
+
condition = "#{search_in}.id #{specification.forward? ? "<" : ">"} ?"
|
98
131
|
[condition, record_id]
|
99
132
|
end
|
100
133
|
|
101
134
|
def start_condition(specification)
|
102
|
-
start_offset_condition(
|
135
|
+
start_offset_condition(
|
136
|
+
specification,
|
103
137
|
@stream_klass.find_by!(event_id: specification.start, stream: specification.stream.name),
|
104
|
-
@stream_klass.table_name
|
138
|
+
@stream_klass.table_name
|
139
|
+
)
|
105
140
|
end
|
106
141
|
|
107
142
|
def stop_condition(specification)
|
108
|
-
stop_offset_condition(
|
143
|
+
stop_offset_condition(
|
144
|
+
specification,
|
109
145
|
@stream_klass.find_by!(event_id: specification.stop, stream: specification.stream.name),
|
110
|
-
@stream_klass.table_name
|
146
|
+
@stream_klass.table_name
|
147
|
+
)
|
111
148
|
end
|
112
149
|
|
113
150
|
def start_condition_in_global_stream(specification)
|
114
|
-
start_offset_condition(
|
151
|
+
start_offset_condition(
|
152
|
+
specification,
|
115
153
|
@event_klass.find_by!(event_id: specification.start),
|
116
|
-
@event_klass.table_name
|
154
|
+
@event_klass.table_name
|
155
|
+
)
|
117
156
|
end
|
118
157
|
|
119
158
|
def stop_condition_in_global_stream(specification)
|
120
|
-
stop_offset_condition(specification,
|
121
|
-
@event_klass.find_by!(event_id: specification.stop),
|
122
|
-
@event_klass.table_name)
|
159
|
+
stop_offset_condition(specification, @event_klass.find_by!(event_id: specification.stop), @event_klass.table_name)
|
123
160
|
end
|
124
161
|
|
125
162
|
def older_than_condition(specification)
|
@@ -139,20 +176,22 @@ module RailsEventStoreActiveRecord
|
|
139
176
|
end
|
140
177
|
|
141
178
|
def order(spec)
|
142
|
-
spec.forward? ?
|
179
|
+
spec.forward? ? "ASC" : "DESC"
|
143
180
|
end
|
144
181
|
|
145
182
|
def record(record)
|
146
183
|
record = record.event if @stream_klass === record
|
147
184
|
|
148
|
-
RubyEventStore::SerializedRecord
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
185
|
+
RubyEventStore::SerializedRecord
|
186
|
+
.new(
|
187
|
+
event_id: record.event_id,
|
188
|
+
metadata: record.metadata,
|
189
|
+
data: record.data,
|
190
|
+
event_type: record.event_type,
|
191
|
+
timestamp: record.created_at.iso8601(RubyEventStore::TIMESTAMP_PRECISION),
|
192
|
+
valid_at: (record.valid_at || record.created_at).iso8601(RubyEventStore::TIMESTAMP_PRECISION)
|
193
|
+
)
|
194
|
+
.deserialize(serializer)
|
156
195
|
end
|
157
196
|
end
|
158
197
|
|
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
begin
|
4
|
-
require
|
4
|
+
require "rails/generators"
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
8
|
module RailsEventStoreActiveRecord
|
9
9
|
class MigrationGenerator < Rails::Generators::Base
|
10
|
-
class Error < Thor::Error
|
10
|
+
class Error < Thor::Error
|
11
|
+
end
|
11
12
|
|
12
|
-
DATA_TYPES = %w
|
13
|
+
DATA_TYPES = %w[binary json jsonb].freeze
|
13
14
|
|
14
|
-
source_root File.expand_path(File.join(File.dirname(__FILE__),
|
15
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), "../generators/templates"))
|
15
16
|
class_option(
|
16
17
|
:data_type,
|
17
18
|
type: :string,
|
18
|
-
default:
|
19
|
-
desc:
|
19
|
+
default: "binary",
|
20
|
+
desc:
|
21
|
+
"Configure the data type for `data` and `meta data` fields in Postgres migration (options: #{DATA_TYPES.join("/")})"
|
20
22
|
)
|
21
23
|
|
22
24
|
def initialize(*args)
|
@@ -34,7 +36,7 @@ module RailsEventStoreActiveRecord
|
|
34
36
|
private
|
35
37
|
|
36
38
|
def data_type
|
37
|
-
options.fetch(
|
39
|
+
options.fetch("data_type")
|
38
40
|
end
|
39
41
|
|
40
42
|
def migration_version
|
@@ -45,4 +47,4 @@ module RailsEventStoreActiveRecord
|
|
45
47
|
Time.now.strftime("%Y%m%d%H%M%S")
|
46
48
|
end
|
47
49
|
end
|
48
|
-
end if defined?(Rails::Generators::Base)
|
50
|
+
end if defined?(Rails::Generators::Base)
|
data/lib/rails_event_store_active_record/generators/templates/create_event_store_events_template.rb
CHANGED
@@ -2,38 +2,42 @@
|
|
2
2
|
|
3
3
|
class CreateEventStoreEvents < ActiveRecord::Migration<%= migration_version %>
|
4
4
|
def change
|
5
|
-
postgres =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
if postgres
|
13
|
-
t.references :event, null: false, type: :uuid
|
14
|
-
else
|
15
|
-
t.references :event, null: false, type: :string, limit: 36
|
16
|
-
end
|
17
|
-
if postgres
|
5
|
+
postgres =
|
6
|
+
ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
7
|
+
if postgres
|
8
|
+
create_table(:event_store_events_in_streams, id: :bigserial, force: false) do |t|
|
9
|
+
t.string :stream, null: false
|
10
|
+
t.integer :position, null: true
|
11
|
+
t.references :event, null: false, type: :uuid
|
18
12
|
t.datetime :created_at, null: false
|
19
|
-
else
|
20
|
-
t.datetime :created_at, null: false, precision: 6
|
21
13
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
|
14
|
+
add_index :event_store_events_in_streams, [:stream, :position], unique: true
|
15
|
+
add_index :event_store_events_in_streams, [:created_at]
|
16
|
+
add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
|
26
17
|
|
27
|
-
|
28
|
-
create_table(:event_store_events, force: false) do |t|
|
18
|
+
create_table(:event_store_events, id: :bigserial, force: false) do |t|
|
29
19
|
t.references :event, null: false, type: :uuid
|
30
20
|
t.string :event_type, null: false
|
31
21
|
t.<%= data_type %> :metadata
|
32
|
-
t.<%= data_type %> :data,
|
22
|
+
t.<%= data_type %> :data, null: false
|
33
23
|
t.datetime :created_at, null: false
|
34
24
|
t.datetime :valid_at, null: true
|
35
25
|
end
|
26
|
+
add_index :event_store_events, :event_id, unique: true
|
27
|
+
add_index :event_store_events, :created_at
|
28
|
+
add_index :event_store_events, :valid_at
|
29
|
+
add_index :event_store_events, :event_type
|
36
30
|
else
|
31
|
+
create_table(:event_store_events_in_streams, force: false) do |t|
|
32
|
+
t.string :stream, null: false
|
33
|
+
t.integer :position, null: true
|
34
|
+
t.references :event, null: false, type: :string, limit: 36
|
35
|
+
t.datetime :created_at, null: false, precision: 6
|
36
|
+
end
|
37
|
+
add_index :event_store_events_in_streams, [:stream, :position], unique: true
|
38
|
+
add_index :event_store_events_in_streams, [:created_at]
|
39
|
+
add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
|
40
|
+
|
37
41
|
create_table(:event_store_events, force: false) do |t|
|
38
42
|
t.references :event, null: false, type: :string, limit: 36
|
39
43
|
t.string :event_type, null: false
|
@@ -42,10 +46,10 @@ class CreateEventStoreEvents < ActiveRecord::Migration<%= migration_version %>
|
|
42
46
|
t.datetime :created_at, null: false, precision: 6
|
43
47
|
t.datetime :valid_at, null: true, precision: 6
|
44
48
|
end
|
49
|
+
add_index :event_store_events, :event_id, unique: true
|
50
|
+
add_index :event_store_events, :created_at
|
51
|
+
add_index :event_store_events, :valid_at
|
52
|
+
add_index :event_store_events, :event_type
|
45
53
|
end
|
46
|
-
add_index :event_store_events, :event_id, unique: true
|
47
|
-
add_index :event_store_events, :created_at
|
48
|
-
add_index :event_store_events, :valid_at
|
49
|
-
add_index :event_store_events, :event_type
|
50
54
|
end
|
51
55
|
end
|
@@ -2,41 +2,37 @@
|
|
2
2
|
|
3
3
|
module RailsEventStoreActiveRecord
|
4
4
|
class IndexViolationDetector
|
5
|
-
|
6
5
|
def initialize(event_store_events, event_store_events_in_streams)
|
7
|
-
@postgres_pkey_error
|
6
|
+
@postgres_pkey_error = "Key (event_id)".freeze
|
8
7
|
@postgres_index_error = "Key (stream, event_id)".freeze
|
9
|
-
@mysql5_pkey_error
|
10
|
-
@mysql8_pkey_error
|
11
|
-
@mysql5_index_error
|
12
|
-
@mysql8_index_error
|
13
|
-
|
14
|
-
|
8
|
+
@mysql5_pkey_error = "for key 'index_#{event_store_events}_on_event_id'".freeze
|
9
|
+
@mysql8_pkey_error = "for key '#{event_store_events}.index_#{event_store_events}_on_event_id'".freeze
|
10
|
+
@mysql5_index_error = "for key 'index_#{event_store_events_in_streams}_on_stream_and_event_id'".freeze
|
11
|
+
@mysql8_index_error =
|
12
|
+
"for key '#{event_store_events_in_streams}.index_#{event_store_events_in_streams}_on_stream_and_event_id'"
|
13
|
+
.freeze
|
14
|
+
@sqlite3_pkey_error = "constraint failed: #{event_store_events}.event_id".freeze
|
15
|
+
@sqlite3_index_error =
|
16
|
+
"constraint failed: #{event_store_events_in_streams}.stream, #{event_store_events_in_streams}.event_id".freeze
|
15
17
|
end
|
16
18
|
|
17
19
|
def detect(message)
|
18
|
-
detect_postgres(message) ||
|
19
|
-
detect_mysql(message) ||
|
20
|
-
detect_sqlite(message)
|
20
|
+
detect_postgres(message) || detect_mysql(message) || detect_sqlite(message)
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def detect_postgres(message)
|
26
|
-
message.include?(@postgres_pkey_error) ||
|
27
|
-
message.include?(@postgres_index_error)
|
26
|
+
message.include?(@postgres_pkey_error) || message.include?(@postgres_index_error)
|
28
27
|
end
|
29
28
|
|
30
29
|
def detect_mysql(message)
|
31
|
-
message.include?(@mysql5_pkey_error)
|
32
|
-
message.include?(@
|
33
|
-
message.include?(@mysql5_index_error) ||
|
34
|
-
message.include?(@mysql8_index_error)
|
30
|
+
message.include?(@mysql5_pkey_error) || message.include?(@mysql8_pkey_error) ||
|
31
|
+
message.include?(@mysql5_index_error) || message.include?(@mysql8_index_error)
|
35
32
|
end
|
36
33
|
|
37
34
|
def detect_sqlite(message)
|
38
|
-
message.include?(@sqlite3_pkey_error) ||
|
39
|
-
message.include?(@sqlite3_index_error)
|
35
|
+
message.include?(@sqlite3_pkey_error) || message.include?(@sqlite3_index_error)
|
40
36
|
end
|
41
37
|
end
|
42
38
|
|
@@ -1,20 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'activerecord-import'
|
4
|
-
|
5
3
|
module RailsEventStoreActiveRecord
|
6
4
|
class PgLinearizedEventRepository < EventRepository
|
7
|
-
|
8
5
|
def start_transaction(&proc)
|
9
6
|
ActiveRecord::Base.transaction(requires_new: true) do
|
10
|
-
ActiveRecord::Base
|
11
|
-
.connection
|
12
|
-
.execute("SELECT pg_advisory_xact_lock(1845240511599988039) as l")
|
13
|
-
.each{}
|
7
|
+
ActiveRecord::Base.connection.execute("SELECT pg_advisory_xact_lock(1845240511599988039) as l").each {}
|
14
8
|
|
15
9
|
proc.call
|
16
10
|
end
|
17
11
|
end
|
18
|
-
|
19
12
|
end
|
20
13
|
end
|
@@ -3,34 +3,34 @@
|
|
3
3
|
module RailsEventStoreActiveRecord
|
4
4
|
class WithAbstractBaseClass
|
5
5
|
def initialize(base_klass)
|
6
|
-
|
7
|
-
"#{base_klass} must be an abstract class that inherits from ActiveRecord::Base"
|
8
|
-
|
6
|
+
unless base_klass < ActiveRecord::Base && base_klass.abstract_class?
|
7
|
+
raise ArgumentError.new("#{base_klass} must be an abstract class that inherits from ActiveRecord::Base")
|
8
|
+
end
|
9
9
|
@base_klass = base_klass
|
10
10
|
end
|
11
11
|
|
12
12
|
def call(instance_id: SecureRandom.hex)
|
13
|
-
[
|
14
|
-
build_event_klass(instance_id),
|
15
|
-
build_stream_klass(instance_id),
|
16
|
-
]
|
13
|
+
[build_event_klass(instance_id), build_stream_klass(instance_id)]
|
17
14
|
end
|
18
15
|
|
19
16
|
private
|
17
|
+
|
20
18
|
def build_event_klass(instance_id)
|
21
|
-
Object.const_set(
|
19
|
+
Object.const_set(
|
20
|
+
"Event_#{instance_id}",
|
22
21
|
Class.new(@base_klass) do
|
23
22
|
self.primary_key = :id
|
24
|
-
self.table_name
|
23
|
+
self.table_name = "event_store_events"
|
25
24
|
end
|
26
25
|
)
|
27
26
|
end
|
28
27
|
|
29
28
|
def build_stream_klass(instance_id)
|
30
|
-
Object.const_set(
|
29
|
+
Object.const_set(
|
30
|
+
"EventInStream_#{instance_id}",
|
31
31
|
Class.new(@base_klass) do
|
32
32
|
self.primary_key = :id
|
33
|
-
self.table_name =
|
33
|
+
self.table_name = "event_store_events_in_streams"
|
34
34
|
belongs_to :event, primary_key: :event_id, class_name: "Event_#{instance_id}"
|
35
35
|
end
|
36
36
|
)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
require_relative "rails_event_store_active_record/generators/migration_generator"
|
4
|
+
require_relative "rails_event_store_active_record/event"
|
5
|
+
require_relative "rails_event_store_active_record/with_default_models"
|
6
|
+
require_relative "rails_event_store_active_record/with_abstract_base_class"
|
7
|
+
require_relative "rails_event_store_active_record/event_repository"
|
8
|
+
require_relative "rails_event_store_active_record/batch_enumerator"
|
9
|
+
require_relative "rails_event_store_active_record/event_repository_reader"
|
10
|
+
require_relative "rails_event_store_active_record/index_violation_detector"
|
11
|
+
require_relative "rails_event_store_active_record/pg_linearized_event_repository"
|
12
|
+
require_relative "rails_event_store_active_record/version"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_event_store_active_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkency
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby_event_store
|
@@ -16,42 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.4.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.4.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '6.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: activerecord-import
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 1.0.2
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0.2
|
40
|
+
version: '6.0'
|
55
41
|
description: |
|
56
42
|
Persistent event repository implementation for RubyEventStore based on ActiveRecord. Ships with database schema
|
57
43
|
and migrations suitable for PostgreSQL, MySQL ans SQLite database engines.
|
@@ -85,6 +71,7 @@ metadata:
|
|
85
71
|
changelog_uri: https://github.com/RailsEventStore/rails_event_store/releases
|
86
72
|
source_code_uri: https://github.com/RailsEventStore/rails_event_store
|
87
73
|
bug_tracker_uri: https://github.com/RailsEventStore/rails_event_store/issues
|
74
|
+
rubygems_mfa_required: 'true'
|
88
75
|
post_install_message:
|
89
76
|
rdoc_options: []
|
90
77
|
require_paths:
|
@@ -93,14 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
80
|
requirements:
|
94
81
|
- - ">="
|
95
82
|
- !ruby/object:Gem::Version
|
96
|
-
version: '2.
|
83
|
+
version: '2.7'
|
97
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
85
|
requirements:
|
99
86
|
- - ">="
|
100
87
|
- !ruby/object:Gem::Version
|
101
88
|
version: '0'
|
102
89
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
90
|
+
rubygems_version: 3.3.7
|
104
91
|
signing_key:
|
105
92
|
specification_version: 4
|
106
93
|
summary: Persistent event repository implementation for RubyEventStore based on ActiveRecord
|