rails_event_store_active_record 2.1.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|