rails_event_store_active_record 1.3.1 → 2.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +1 -0
  3. data/Gemfile +5 -10
  4. data/Gemfile.lock +245 -0
  5. data/Gemfile.rails_5_0 +13 -0
  6. data/Gemfile.rails_5_0.lock +217 -0
  7. data/Gemfile.rails_5_1 +13 -0
  8. data/Gemfile.rails_5_1.lock +217 -0
  9. data/Gemfile.rails_5_2 +13 -0
  10. data/Gemfile.rails_5_2.lock +225 -0
  11. data/Gemfile.rails_6_0 +13 -0
  12. data/Gemfile.rails_6_0.lock +241 -0
  13. data/Makefile +8 -15
  14. data/lib/rails_event_store_active_record/event.rb +1 -1
  15. data/lib/rails_event_store_active_record/event_repository.rb +45 -43
  16. data/lib/rails_event_store_active_record/event_repository_reader.rb +101 -37
  17. data/lib/rails_event_store_active_record/generators/{limit_for_event_id_generator.rb → add_valid_at_generator.rb} +2 -2
  18. data/lib/rails_event_store_active_record/generators/{index_by_event_type_generator.rb → created_at_precision_generator.rb} +2 -2
  19. data/lib/rails_event_store_active_record/generators/{binary_data_and_metadata_generator.rb → no_global_stream_entries_generator.rb} +2 -4
  20. data/lib/rails_event_store_active_record/generators/templates/add_valid_at_template.rb +14 -0
  21. data/lib/rails_event_store_active_record/generators/templates/create_event_store_events_template.rb +15 -9
  22. data/lib/rails_event_store_active_record/generators/templates/created_at_precision_template.rb +45 -0
  23. data/lib/rails_event_store_active_record/generators/templates/no_global_stream_entries_template.rb +59 -0
  24. data/lib/rails_event_store_active_record/index_violation_detector.rb +33 -18
  25. data/lib/rails_event_store_active_record/version.rb +1 -1
  26. data/lib/rails_event_store_active_record/with_abstract_base_class.rb +39 -0
  27. data/lib/rails_event_store_active_record/with_default_models.rb +9 -0
  28. data/lib/rails_event_store_active_record.rb +5 -3
  29. data/rails_event_store_active_record.gemspec +1 -3
  30. metadata +23 -13
  31. data/bin/console +0 -14
  32. data/bin/setup +0 -7
  33. data/lib/rails_event_store_active_record/generators/templates/binary_data_and_metadata_template.rb +0 -35
  34. data/lib/rails_event_store_active_record/generators/templates/index_by_event_type_template.rb +0 -7
  35. data/lib/rails_event_store_active_record/generators/templates/limit_for_event_id_template.rb +0 -8
data/Makefile CHANGED
@@ -1,9 +1,15 @@
1
1
  GEM_VERSION = $(shell cat ../RES_VERSION)
2
2
  GEM_NAME = rails_event_store_active_record
3
3
  REQUIRE = $(GEM_NAME)
4
- IGNORE = RailsEventStoreActiveRecord::IndexViolationDetector\#detect \
4
+ IGNORE = RailsEventStoreActiveRecord::IndexViolationDetector\#initialize \
5
+ RailsEventStoreActiveRecord::IndexViolationDetector\#detect \
6
+ RailsEventStoreActiveRecord::IndexViolationDetector\#detect_mysql \
7
+ RailsEventStoreActiveRecord::IndexViolationDetector\#detect_postgres \
5
8
  RailsEventStoreActiveRecord::PgLinearizedEventRepository* \
6
- RailsEventStoreActiveRecord::EventRepository\#update_messages
9
+ RailsEventStoreActiveRecord::EventRepository\#update_messages \
10
+ RailsEventStoreActiveRecord::WithAbstractBaseClass\#build_event_klass \
11
+ RailsEventStoreActiveRecord::WithAbstractBaseClass\#build_stream_klass
12
+
7
13
  SUBJECT ?= RailsEventStoreActiveRecord*
8
14
  DATABASE_URL ?= sqlite3::memory:
9
15
 
@@ -13,16 +19,3 @@ include ../support/make/mutant.mk
13
19
  include ../support/make/gem.mk
14
20
  include ../support/make/help.mk
15
21
 
16
- install: install-gemfiles
17
-
18
- remove-lock: remove-lockfiles
19
-
20
- install-gemfiles:
21
- @BUNDLE_GEMFILE=../support/bundler/Gemfile.0_33_0 bundle check || BUNDLE_GEMFILE=../support/bundler/Gemfile.0_33_0 bundle install
22
- @BUNDLE_GEMFILE=../support/bundler/Gemfile.0_34_0 bundle check || BUNDLE_GEMFILE=../support/bundler/Gemfile.0_34_0 bundle install
23
- @BUNDLE_GEMFILE=../support/bundler/Gemfile.0_35_0 bundle check || BUNDLE_GEMFILE=../support/bundler/Gemfile.0_35_0 bundle install
24
-
25
- remove-lockfiles:
26
- -rm ../support/bundler/Gemfile.0_33_0.lock
27
- -rm ../support/bundler/Gemfile.0_34_0.lock
28
- -rm ../support/bundler/Gemfile.0_35_0.lock
@@ -11,6 +11,6 @@ module RailsEventStoreActiveRecord
11
11
  class EventInStream < ::ActiveRecord::Base
12
12
  self.primary_key = :id
13
13
  self.table_name = 'event_store_events_in_streams'
14
- belongs_to :event
14
+ belongs_to :event, primary_key: :event_id
15
15
  end
16
16
  end
@@ -6,33 +6,37 @@ require 'activerecord-import'
6
6
  module RailsEventStoreActiveRecord
7
7
  class EventRepository
8
8
  POSITION_SHIFT = 1
9
- SERIALIZED_GLOBAL_STREAM_NAME = "all".freeze
10
9
 
11
- def initialize
12
- @repo_reader = EventRepositoryReader.new
10
+ def initialize(model_factory: WithDefaultModels.new, serializer:)
11
+ @serializer = serializer
12
+
13
+ @event_klass, @stream_klass = model_factory.call
14
+ @repo_reader = EventRepositoryReader.new(@event_klass, @stream_klass, serializer)
15
+ @index_violation_detector = IndexViolationDetector.new(@event_klass.table_name, @stream_klass.table_name)
13
16
  end
14
17
 
15
- def append_to_stream(events, stream, expected_version)
16
- records, event_ids = [], []
17
- Array(events).each do |event|
18
- records << build_event_hash(event)
19
- event_ids << event.event_id
18
+ def append_to_stream(records, stream, expected_version)
19
+ hashes = []
20
+ event_ids = []
21
+ Array(records).each do |record|
22
+ hashes << import_hash(record, record.serialize(serializer))
23
+ event_ids << record.event_id
20
24
  end
21
- add_to_stream(event_ids, stream, expected_version, true) do
22
- Event.import(records)
25
+ add_to_stream(event_ids, stream, expected_version) do
26
+ @event_klass.import(hashes)
23
27
  end
24
28
  end
25
29
 
26
30
  def link_to_stream(event_ids, stream, expected_version)
27
31
  event_ids = Array(event_ids)
28
- (event_ids - Event.where(id: event_ids).pluck(:id)).each do |id|
32
+ (event_ids - @event_klass.where(event_id: event_ids).pluck(:event_id)).each do |id|
29
33
  raise RubyEventStore::EventNotFound.new(id)
30
34
  end
31
- add_to_stream(event_ids, stream, expected_version, nil)
35
+ add_to_stream(event_ids, stream, expected_version)
32
36
  end
33
37
 
34
38
  def delete_stream(stream)
35
- EventInStream.where(stream: stream.name).delete_all
39
+ @stream_klass.where(stream: stream.name).delete_all
36
40
  end
37
41
 
38
42
  def has_event?(event_id)
@@ -51,49 +55,41 @@ module RailsEventStoreActiveRecord
51
55
  @repo_reader.count(specification)
52
56
  end
53
57
 
54
- def update_messages(messages)
55
- hashes = messages.map(&:to_h)
56
- hashes.each{|h| h[:id] = h.delete(:event_id) }
57
- for_update = messages.map(&:event_id)
58
+ def update_messages(records)
59
+ hashes = Array(records).map{|record| import_hash(record, record.serialize(serializer)) }
60
+ for_update = records.map(&:event_id)
58
61
  start_transaction do
59
- existing = Event.where(id: for_update).pluck(:id)
60
- (for_update - existing).each{|id| raise RubyEventStore::EventNotFound.new(id) }
61
- Event.import(hashes, on_duplicate_key_update: [:data, :metadata, :event_type])
62
+ existing = @event_klass.where(event_id: for_update).pluck(:event_id, :id).to_h
63
+ (for_update - existing.keys).each{|id| raise RubyEventStore::EventNotFound.new(id) }
64
+ hashes.each { |h| h[:id] = existing.fetch(h.fetch(:event_id)) }
65
+ @event_klass.import(hashes, on_duplicate_key_update: [:data, :metadata, :event_type, :valid_at])
62
66
  end
63
67
  end
64
68
 
65
69
  def streams_of(event_id)
66
- EventInStream.where(event_id: event_id)
67
- .where.not(stream: SERIALIZED_GLOBAL_STREAM_NAME)
70
+ @stream_klass.where(event_id: event_id)
68
71
  .pluck(:stream)
69
72
  .map{|name| RubyEventStore::Stream.new(name)}
70
73
  end
71
74
 
72
75
  private
76
+ attr_reader :serializer
73
77
 
74
- def add_to_stream(event_ids, stream, expected_version, include_global)
75
- last_stream_version = ->(stream_) { EventInStream.where(stream: stream_.name).order("position DESC").first.try(:position) }
78
+ def add_to_stream(event_ids, stream, expected_version)
79
+ last_stream_version = ->(stream_) { @stream_klass.where(stream: stream_.name).order("position DESC").first.try(:position) }
76
80
  resolved_version = expected_version.resolve_for(stream, last_stream_version)
77
81
 
78
82
  start_transaction do
79
83
  yield if block_given?
80
- in_stream = event_ids.flat_map.with_index do |event_id, index|
81
- position = compute_position(resolved_version, index)
82
- collection = []
83
- collection.unshift({
84
- stream: SERIALIZED_GLOBAL_STREAM_NAME,
85
- position: nil,
86
- event_id: event_id,
87
- }) if include_global
88
- collection.unshift({
84
+ in_stream = event_ids.map.with_index do |event_id, index|
85
+ {
89
86
  stream: stream.name,
90
- position: position,
91
- event_id: event_id
92
- }) unless stream.global?
93
- collection
87
+ position: compute_position(resolved_version, index),
88
+ event_id: event_id,
89
+ }
94
90
  end
95
91
  fill_ids(in_stream)
96
- EventInStream.import(in_stream)
92
+ @stream_klass.import(in_stream) unless stream.global?
97
93
  end
98
94
  self
99
95
  rescue ActiveRecord::RecordNotUnique => e
@@ -114,24 +110,30 @@ module RailsEventStoreActiveRecord
114
110
  end
115
111
 
116
112
  def detect_index_violated(message)
117
- IndexViolationDetector.new.detect(message)
113
+ @index_violation_detector.detect(message)
118
114
  end
119
115
 
120
- def build_event_hash(serialized_record)
116
+ def import_hash(record, serialized_record)
121
117
  {
122
- id: serialized_record.event_id,
118
+ event_id: serialized_record.event_id,
123
119
  data: serialized_record.data,
124
120
  metadata: serialized_record.metadata,
125
- event_type: serialized_record.event_type
121
+ event_type: serialized_record.event_type,
122
+ created_at: record.timestamp,
123
+ valid_at: optimize_timestamp(record.valid_at, record.timestamp),
126
124
  }
127
125
  end
128
126
 
127
+ def optimize_timestamp(valid_at, created_at)
128
+ valid_at unless valid_at.eql?(created_at)
129
+ end
130
+
129
131
  # Overwritten in a sub-class
130
132
  def fill_ids(_in_stream)
131
133
  end
132
134
 
133
135
  def start_transaction(&block)
134
- ActiveRecord::Base.transaction(requires_new: true, &block)
136
+ @event_klass.transaction(requires_new: true, &block)
135
137
  end
136
138
  end
137
139
 
@@ -3,92 +3,156 @@
3
3
  module RailsEventStoreActiveRecord
4
4
  class EventRepositoryReader
5
5
 
6
+ def initialize(event_klass, stream_klass, serializer)
7
+ @event_klass = event_klass
8
+ @stream_klass = stream_klass
9
+ @serializer = serializer
10
+ end
11
+
6
12
  def has_event?(event_id)
7
- Event.exists?(id: event_id)
13
+ @event_klass.exists?(event_id: event_id)
8
14
  end
9
15
 
10
16
  def last_stream_event(stream)
11
- record = EventInStream.where(stream: stream.name).order('position DESC, id DESC').first
12
- record && build_serialized_record(record)
17
+ record_ = @stream_klass.where(stream: stream.name).order('position DESC, id DESC').first
18
+ record(record_) if record_
13
19
  end
14
20
 
15
21
  def read(spec)
16
- raise RubyEventStore::ReservedInternalName if spec.stream.name.eql?(EventRepository::SERIALIZED_GLOBAL_STREAM_NAME)
17
-
18
22
  stream = read_scope(spec)
19
23
 
20
24
  if spec.batched?
21
25
  batch_reader = ->(offset_id, limit) do
22
- records = offset_id.nil? ? stream.limit(limit) : stream.where(start_offset_condition(spec, offset_id)).limit(limit)
23
- [records.map(&method(:build_serialized_record)), records.last]
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]
24
29
  end
25
30
  BatchEnumerator.new(spec.batch_size, spec.limit, batch_reader).each
26
31
  elsif spec.first?
27
- record = stream.first
28
- build_serialized_record(record) if record
32
+ record_ = stream.first
33
+ record(record_) if record_
29
34
  elsif spec.last?
30
- record = stream.last
31
- build_serialized_record(record) if record
35
+ record_ = stream.last
36
+ record(record_) if record_
32
37
  else
33
- stream.map(&method(:build_serialized_record)).each
38
+ stream.map(&method(:record)).each
34
39
  end
35
40
  end
36
41
 
37
42
  def count(spec)
38
- raise RubyEventStore::ReservedInternalName if spec.stream.name.eql?(EventRepository::SERIALIZED_GLOBAL_STREAM_NAME)
39
-
40
43
  read_scope(spec).count
41
44
  end
42
45
 
43
46
  private
47
+ attr_reader :serializer
44
48
 
45
49
  def read_scope(spec)
46
- stream = EventInStream.preload(:event).where(stream: normalize_stream_name(spec))
47
- stream = stream.where(event_id: spec.with_ids) if spec.with_ids?
48
- stream = stream.joins(:event).where(event_store_events: {event_type: spec.with_types}) if spec.with_types?
49
- stream = stream.order(position: order(spec)) unless spec.stream.global?
50
- stream = stream.limit(spec.limit) if spec.limit?
51
- stream = stream.where(start_condition(spec)) if spec.start
52
- stream = stream.where(stop_condition(spec)) if spec.stop
53
- stream = stream.order(id: order(spec))
54
- stream
50
+ if spec.stream.global?
51
+ stream = @event_klass
52
+ stream = stream.where(event_id: spec.with_ids) if spec.with_ids?
53
+ stream = stream.where(event_type: spec.with_types) if spec.with_types?
54
+ stream = ordered(stream, spec)
55
+ stream = stream.limit(spec.limit) if spec.limit?
56
+ stream = stream.where(start_condition_in_global_stream(spec)) if spec.start
57
+ stream = stream.where(stop_condition_in_global_stream(spec)) if spec.stop
58
+ stream = stream.where(older_than_condition(spec)) if spec.older_than
59
+ stream = stream.where(older_than_or_equal_condition(spec)) if spec.older_than_or_equal
60
+ stream = stream.where(newer_than_condition(spec)) if spec.newer_than
61
+ stream = stream.where(newer_than_or_equal_condition(spec)) if spec.newer_than_or_equal
62
+ stream.order(id: order(spec))
63
+ else
64
+ stream = @stream_klass.preload(:event).where(stream: spec.stream.name)
65
+ stream = stream.where(event_id: spec.with_ids) if spec.with_ids?
66
+ stream = stream.where(@event_klass.table_name => {event_type: spec.with_types}) if spec.with_types?
67
+ stream = ordered(stream.joins(:event), spec)
68
+ stream = stream.order(position: order(spec), id: order(spec))
69
+ stream = stream.limit(spec.limit) if spec.limit?
70
+ stream = stream.where(start_condition(spec)) if spec.start
71
+ stream = stream.where(stop_condition(spec)) if spec.stop
72
+ stream = stream.where(older_than_condition(spec)) if spec.older_than
73
+ stream = stream.where(older_than_or_equal_condition(spec)) if spec.older_than_or_equal
74
+ stream = stream.where(newer_than_condition(spec)) if spec.newer_than
75
+ stream = stream.where(newer_than_or_equal_condition(spec)) if spec.newer_than_or_equal
76
+ stream
77
+ end
55
78
  end
56
79
 
57
- def normalize_stream_name(specification)
58
- specification.stream.global? ? EventRepository::SERIALIZED_GLOBAL_STREAM_NAME : specification.stream.name
80
+ def ordered(stream, spec)
81
+ case spec.time_sort_by
82
+ when :as_at
83
+ stream.order("#{@event_klass.table_name}.created_at #{order(spec)}")
84
+ when :as_of
85
+ stream.order("#{@event_klass.table_name}.valid_at #{order(spec)}")
86
+ else
87
+ stream
88
+ end
59
89
  end
60
90
 
61
- def start_offset_condition(specification, record_id)
62
- condition = specification.forward? ? 'event_store_events_in_streams.id > ?' : 'event_store_events_in_streams.id < ?'
91
+ def start_offset_condition(specification, record_id, search_in)
92
+ condition = "#{search_in}.id #{specification.forward? ? '>' : '<'} ?"
63
93
  [condition, record_id]
64
94
  end
65
95
 
66
- def stop_offset_condition(specification, record_id)
67
- condition = specification.forward? ? 'event_store_events_in_streams.id < ?' : 'event_store_events_in_streams.id > ?'
96
+ def stop_offset_condition(specification, record_id, search_in)
97
+ condition = "#{search_in}.id #{specification.forward? ? '<' : '>'} ?"
68
98
  [condition, record_id]
69
99
  end
70
100
 
71
101
  def start_condition(specification)
72
102
  start_offset_condition(specification,
73
- EventInStream.find_by!(event_id: specification.start, stream: normalize_stream_name(specification)))
103
+ @stream_klass.find_by!(event_id: specification.start, stream: specification.stream.name),
104
+ @stream_klass.table_name)
74
105
  end
75
106
 
76
107
  def stop_condition(specification)
77
108
  stop_offset_condition(specification,
78
- EventInStream.find_by!(event_id: specification.stop, stream: normalize_stream_name(specification)))
109
+ @stream_klass.find_by!(event_id: specification.stop, stream: specification.stream.name),
110
+ @stream_klass.table_name)
111
+ end
112
+
113
+ def start_condition_in_global_stream(specification)
114
+ start_offset_condition(specification,
115
+ @event_klass.find_by!(event_id: specification.start),
116
+ @event_klass.table_name)
117
+ end
118
+
119
+ 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)
123
+ end
124
+
125
+ def older_than_condition(specification)
126
+ ["#{@event_klass.table_name}.created_at < ?", specification.older_than]
127
+ end
128
+
129
+ def older_than_or_equal_condition(specification)
130
+ ["#{@event_klass.table_name}.created_at <= ?", specification.older_than_or_equal]
131
+ end
132
+
133
+ def newer_than_condition(specification)
134
+ ["#{@event_klass.table_name}.created_at > ?", specification.newer_than]
135
+ end
136
+
137
+ def newer_than_or_equal_condition(specification)
138
+ ["#{@event_klass.table_name}.created_at >= ?", specification.newer_than_or_equal]
79
139
  end
80
140
 
81
141
  def order(spec)
82
142
  spec.forward? ? 'ASC' : 'DESC'
83
143
  end
84
144
 
85
- def build_serialized_record(record)
145
+ def record(record)
146
+ record = record.event if @stream_klass === record
147
+
86
148
  RubyEventStore::SerializedRecord.new(
87
- event_id: record.event.id,
88
- metadata: record.event.metadata,
89
- data: record.event.data,
90
- event_type: record.event.event_type
91
- )
149
+ event_id: record.event_id,
150
+ metadata: record.metadata,
151
+ data: record.data,
152
+ event_type: record.event_type,
153
+ timestamp: record.created_at.iso8601(RubyEventStore::TIMESTAMP_PRECISION),
154
+ valid_at: (record.valid_at || record.created_at).iso8601(RubyEventStore::TIMESTAMP_PRECISION),
155
+ ).deserialize(serializer)
92
156
  end
93
157
  end
94
158
 
@@ -6,11 +6,11 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  module RailsEventStoreActiveRecord
9
- class LimitForEventIdGenerator < Rails::Generators::Base
9
+ class AddValidAtGenerator < Rails::Generators::Base
10
10
  source_root File.expand_path(File.join(File.dirname(__FILE__), '../generators/templates'))
11
11
 
12
12
  def create_migration
13
- template "limit_for_event_id_template.rb", "db/migrate/#{timestamp}_limit_for_event_id.rb"
13
+ template "add_valid_at_template.rb", "db/migrate/#{timestamp}_add_valid_at.rb"
14
14
  end
15
15
 
16
16
  private
@@ -6,11 +6,11 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  module RailsEventStoreActiveRecord
9
- class IndexByEventTypeGenerator < Rails::Generators::Base
9
+ class CreatedAtPrecisionGenerator < Rails::Generators::Base
10
10
  source_root File.expand_path(File.join(File.dirname(__FILE__), '../generators/templates'))
11
11
 
12
12
  def create_migration
13
- template "index_by_event_type_template.rb", "db/migrate/#{timestamp}_index_by_event_type.rb"
13
+ template "created_at_precision_template.rb", "db/migrate/#{timestamp}_created_at_precision.rb"
14
14
  end
15
15
 
16
16
  private
@@ -1,16 +1,14 @@
1
- # frozen_string_literal: true
2
-
3
1
  begin
4
2
  require 'rails/generators'
5
3
  rescue LoadError
6
4
  end
7
5
 
8
6
  module RailsEventStoreActiveRecord
9
- class BinaryDataAndMetadataGenerator < Rails::Generators::Base
7
+ class NoGlobalStreamEntriesGenerator < Rails::Generators::Base
10
8
  source_root File.expand_path(File.join(File.dirname(__FILE__), '../generators/templates'))
11
9
 
12
10
  def create_migration
13
- template "binary_data_and_metadata_template.rb", "db/migrate/#{timestamp}_binary_data_and_metadata.rb"
11
+ template "no_global_stream_entries_template.rb", "db/migrate/#{timestamp}_no_global_stream_entries.rb"
14
12
  end
15
13
 
16
14
  private
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddValidAt < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ case ActiveRecord::Base.connection.adapter_name
6
+ when "PostgreSQL"
7
+ add_column :event_store_events, :valid_at, :datetime, null: true
8
+ else
9
+ add_column :event_store_events, :valid_at, :datetime, precision: 6, null: true
10
+ end
11
+
12
+ add_index :event_store_events, :valid_at
13
+ end
14
+ end
@@ -4,7 +4,7 @@ class CreateEventStoreEvents < ActiveRecord::Migration<%= migration_version %>
4
4
  def change
5
5
  postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
6
6
  sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite"
7
- rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0")
7
+
8
8
  enable_extension "pgcrypto" if postgres
9
9
  create_table(:event_store_events_in_streams, force: false) do |t|
10
10
  t.string :stream, null: false
@@ -14,32 +14,38 @@ class CreateEventStoreEvents < ActiveRecord::Migration<%= migration_version %>
14
14
  else
15
15
  t.references :event, null: false, type: :string, limit: 36
16
16
  end
17
- t.datetime :created_at, null: false
17
+ if postgres
18
+ t.datetime :created_at, null: false
19
+ else
20
+ t.datetime :created_at, null: false, precision: 6
21
+ end
18
22
  end
19
23
  add_index :event_store_events_in_streams, [:stream, :position], unique: true
20
24
  add_index :event_store_events_in_streams, [:created_at]
21
25
  add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
22
26
 
23
27
  if postgres
24
- create_table(:event_store_events, id: :uuid, default: 'gen_random_uuid()', force: false) do |t|
28
+ create_table(:event_store_events, force: false) do |t|
29
+ t.references :event, null: false, type: :uuid
25
30
  t.string :event_type, null: false
26
31
  t.<%= data_type %> :metadata
27
32
  t.<%= data_type %> :data, null: false
28
33
  t.datetime :created_at, null: false
34
+ t.datetime :valid_at, null: true
29
35
  end
30
36
  else
31
- create_table(:event_store_events, id: false, force: false) do |t|
32
- t.string :id, limit: 36, primary_key: true, null: false
37
+ create_table(:event_store_events, force: false) do |t|
38
+ t.references :event, null: false, type: :string, limit: 36
33
39
  t.string :event_type, null: false
34
40
  t.binary :metadata
35
41
  t.binary :data, null: false
36
- t.datetime :created_at, null: false
37
- end
38
- if sqlite && rails_42
39
- add_index :event_store_events, :id, unique: true
42
+ t.datetime :created_at, null: false, precision: 6
43
+ t.datetime :valid_at, null: true, precision: 6
40
44
  end
41
45
  end
46
+ add_index :event_store_events, :event_id, unique: true
42
47
  add_index :event_store_events, :created_at
48
+ add_index :event_store_events, :valid_at
43
49
  add_index :event_store_events, :event_type
44
50
  end
45
51
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatedAtPrecision < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ case ActiveRecord::Base.connection.adapter_name
6
+ when "SQLite"
7
+ rename_table :event_store_events, :old_event_store_events
8
+ create_table(:event_store_events, id: false, force: false) do |t|
9
+ t.string :id, limit: 36, primary_key: true, null: false
10
+ t.string :event_type, null: false
11
+ t.binary :metadata
12
+ t.binary :data, null: false
13
+ t.datetime :created_at, null: false, precision: 6
14
+ end
15
+ add_index :event_store_events, :created_at
16
+ add_index :event_store_events, :event_type
17
+ execute <<-SQL
18
+ INSERT INTO event_store_events(id, event_type, metadata, data, created_at)
19
+ SELECT id, event_type, metadata, data, created_at FROM old_event_store_events;
20
+ SQL
21
+ drop_table :old_event_store_events
22
+
23
+ rename_table :event_store_events_in_streams, :old_event_store_events_in_streams
24
+ create_table(:event_store_events_in_streams, force: false) do |t|
25
+ t.string :stream, null: false
26
+ t.integer :position, null: true
27
+ t.references :event, null: false, type: :string, limit: 36
28
+ t.datetime :created_at, null: false, precision: 6
29
+ end
30
+ add_index :event_store_events_in_streams, [:stream, :position], unique: true
31
+ add_index :event_store_events_in_streams, [:created_at]
32
+ add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
33
+
34
+ execute <<-SQL
35
+ INSERT INTO event_store_events_in_streams(id, stream, position, event_id, created_at)
36
+ SELECT id, stream, position, event_id, created_at FROM old_event_store_events_in_streams;
37
+ SQL
38
+ drop_table :old_event_store_events_in_streams
39
+ when "PostgreSQL"
40
+ else
41
+ change_column :event_store_events, :created_at, :datetime, precision: 6
42
+ change_column :event_store_events_in_streams, :created_at, :datetime, precision: 6
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,59 @@
1
+ class NoGlobalStreamEntries < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ case ActiveRecord::Base.connection.adapter_name
4
+ when "SQLite"
5
+ rename_table :event_store_events, :old_event_store_events
6
+ create_table(:event_store_events, force: false) do |t|
7
+ t.references :event, null: false, type: :string, limit: 36
8
+ t.string :event_type, null: false
9
+ t.binary :metadata
10
+ t.binary :data, null: false
11
+ t.datetime :created_at, precision: 6, null: false
12
+ t.datetime :valid_at, precision: 6, null: true
13
+ end
14
+ add_index :event_store_events, :event_id, unique: true
15
+ add_index :event_store_events, :created_at
16
+ add_index :event_store_events, :valid_at
17
+ add_index :event_store_events, :event_type
18
+
19
+ execute <<-SQL
20
+ INSERT INTO event_store_events(event_id, event_type, metadata, data, created_at)
21
+ SELECT id, event_type, metadata, data, created_at FROM old_event_store_events;
22
+ SQL
23
+ drop_table :old_event_store_events
24
+ when "PostgreSQL"
25
+ rename_column :event_store_events, :id, :event_id
26
+ change_column_default :event_store_events, :event_id, nil
27
+ add_column :event_store_events, :id, :serial
28
+
29
+ execute <<~SQL
30
+ UPDATE event_store_events
31
+ SET id = event_store_events_in_streams.id
32
+ FROM event_store_events_in_streams
33
+ WHERE event_store_events.event_id = event_store_events_in_streams.event_id AND event_store_events_in_streams.stream = 'all';
34
+ UPDATE event_store_events
35
+ SET id = ese.new_id
36
+ FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM event_store_events) ese
37
+ WHERE event_store_events.id = ese.id;
38
+ SELECT setval(pg_get_serial_sequence('event_store_events', 'id'), max(id)) FROM event_store_events;
39
+ ALTER TABLE event_store_events DROP CONSTRAINT event_store_events_pkey;
40
+ ALTER TABLE event_store_events ADD PRIMARY KEY (id);
41
+ SQL
42
+ add_index :event_store_events, :event_id, unique: true
43
+ else
44
+ rename_column :event_store_events, :id, :event_id
45
+ add_column :event_store_events, :id, :integer
46
+
47
+ execute <<-SQL
48
+ UPDATE event_store_events
49
+ INNER JOIN event_store_events_in_streams ON (event_store_events.event_id = event_store_events_in_streams.event_id)
50
+ SET event_store_events.id = event_store_events_in_streams.id
51
+ WHERE event_store_events_in_streams.stream = 'all';
52
+ SQL
53
+ execute "SET @row_number = 0"
54
+ execute "UPDATE event_store_events SET event_store_events.id = (@row_number:=@row_number + 1) ORDER BY id"
55
+ execute "ALTER TABLE event_store_events DROP PRIMARY KEY, ADD PRIMARY KEY (id), MODIFY id INT AUTO_INCREMENT"
56
+ add_index :event_store_events, :event_id, unique: true
57
+ end
58
+ end
59
+ end