Releases: RailsEventStore/rails_event_store
v2.4.0
RubyEventStore
-
Add:
RubyEventStore::Client#event_in_stream?API which returns the position of given event in the stream [#1225] -
Add: Testing against and official support for Ruby 3.1
-
Fix: YAML support under Ruby 3.1 and Psych 4.0 [#1294]
Up until now regular YAML module shipped with Ruby was used as a default serializer. However, with release of Ruby 3.1 this module started to use Psych 4 instead of Psych 3. This, in turn, resulted in a breaking change when non-primitive values were serialized and deserializes, as since version 4 Psych does "safe loading" by default, which disallows deserializing objects such as BigDecimal or Time. As this breaking change also leaks into RES (old events would no longer be correctly deserialized afer upgrading to Ruby 3.1), a proxy serializer is introduced.
-
Remove: Testing against Ruby 2.6 and official support for this EOL release
-
Remove: Drop no longer needed
ruby2_keywordsdependency
RailsEventStore
-
Add:
RailsEventStore::AsyncHandler.withnow also takesevent_store_locator:keyword argument which overrides event store passed byevent_store:keyword argument if both are passed [#1239]The
event_store_locatorserves the same purpose except that it takes a callable, which returns a lazy-evaluated event store, instead of taking strictly-evaluated event store instance. Theevent_store:will be removed in next major release (3.0). -
Add: Testing against and official support for Rails 7.0
-
Add: Testing against and official support for Ruby 3.1
-
Remove: Testing against Rails 5.0, 5.1 and 5.2 and official support for this EOL releases
-
Remove: Testing against Ruby 2.6 and official support for this EOL release
RailsEventStoreActiveRecord
- Add: Testing against and official support for Rails 7.0
- Add: Testing against and official support for Ruby 3.1
- Remove: Testing against Rails 5.0, 5.1 and 5.2 and official support for this EOL releases
- Remove: Testing against Ruby 2.6 and official support for this EOL release
- Remove: Drop no longer needed
activerecord-importdependency. This functionality is now available natively in ActiveRecord [#1313]
AggregateRoot
- Add: Testing against and official support for Ruby 3.1
- Remove: Testing against Ruby 2.6 and official support for this EOL release
- Remove: Drop no longer needed
ruby2_keywordsdependency
RubyEventStore::RSpec
- Add: Testing against and official support for Ruby 3.1
- Remove: Testing against Ruby 2.6 and official support for this EOL release
RubyEventStore::Browser
v2.0.3
RubyEventStore
- no changes
RailsEventStore
- no changes
RailsEventStoreActiveRecord
- no changes
AggregateRoot
- no changes
RubyEventStore::RSpec
- no changes
RubyEventStore::Browser
- Fix: Release 2.0.2 was released without javascript packages, so whole browser does not work in that release.
v2.0.2
RubyEventStore
- Fix: Remove problematic
mutant.ymlsymlink from released gem [#1236]
RailsEventStore
- Fix: Remove problematic
mutant.ymlsymlink from released gem [#1236]
RailsEventStoreActiveRecord
- Fix: Remove problematic
mutant.ymlsymlink from released gem [#1236]
AggregateRoot
- Fix: Remove problematic
mutant.ymlsymlink from released gem [#1236]
RubyEventStore::RSpec
- Fix: Remove problematic
mutant.ymlsymlink from released gem [#1236]
RubyEventStore::Browser
- no changes
v2.3.0
RubyEventStore
-
Add:
position_in_stream(event_id, stream_name)API which returns the position of given event in stream [#1053] -
Add:
global_position(event_id)API which returns the global position of given event [#1053] -
Change: Make it possible to load events that no longer have a class representation in code [#1065]
Less frustrating experience for retrieving events that cannot be represented with a class in code:
- someone has deleted their class
- never was available in particular codebase accessing events (i.e. standalone event browser)
Represented as as a generic
RubyEventStore::Eventwith additional metadata. -
Add: Check incorrect usage of
expected_version: :anyinRubyEventStore::InMemoryRepositoryand raiseRubyEventStore::InMemoryRepository::UnsupportedVersionAnyUsage[#1091]Disabled by default. Can be turned on by passing
ensure_supported_any_usagelike in the following test snippet:specify 'publishing with specific position to stream with any position raise an error' do repository = InMemoryRepository.new(ensure_supported_any_usage: true) repository.append_to_stream([ event0 = SRecord.new, ], stream, version_any) expect do repository.append_to_stream([ event1 = SRecord.new, ], stream, version_auto) end.to raise_error(RubyEventStore::InMemoryRepository::UnsupportedVersionAnyUsage) end
-
Change: Shared specs intended for implementing custom event repositories are now parametrized [883723e]
-
Change: Do not conflate event class with event type. Extend
RubyEventStore::Eventequality check withRubyEventStore::Event#event_type[f7e7346]
RailsEventStore
- Change: Repository and Dispatcher instrumentations now pass through every custom method [c23a973, 2b83bab, #561]
RailsEventStoreActiveRecord
- Add: Support for
position_in_streamandglobal_positionqueries [#1053] - Change: Remove redundant position order in queries [a3fc054]
- Remove: Obsolete
pgcryptoextension no longer required in Postgres migration [b435933]
AggregateRoot
RubyEventStore::RSpec
- no changes
RubyEventStore::Browser
v2.2.0
From this release we require Ruby version to be at least 2.6 in all gems. Ruby 2.5 reached its end-of-life and stopped receiving security updates on last day of March 2021. [#1035]
RubyEventStore
-
Fix:
RubyEventStore::Mappers::Transformation::DomainEventtransformation should never change passed event. [#1002, 3ba0549] -
Fix: Custom event type resolver now plays well with pubsub. [#1034]
RailsEventStore
- no changes
RailsEventStoreActiveRecord
-
Fix: Batched reads with bi-temporal queries would return duplicate events. [#1037, #1040]
If you ever used
event_store.read.as_atorevent_store.read.as_ofintroduced in v2.1.0, this is an important update for you. -
Remove: No longer needed
pgcryptoPostgres extension dropped from initial migration. [e856ca4]
AggregateRoot
- no changes
RubyEventStore::RSpec
-
Add: An experimental formatter for
have_publishedrspec matcher. [#841]It guides the user step by step into what could be the reason that the matcher failed. It can be enabled with:
RubyEventStore::RSpec.default_formatter = RubyEventStore::RSpec::StepByStepFailureMessageFormatter.new
-
Add: Support for
exactly(n),times,time,oncemodifiers toapply()matcher. [#1033] -
Add: Support for
exactly(n),times,time,once,strictmodifiers topublish()matcher. [#1033] -
Add: Support for
in_streams([stream1, stream2])modifier tohave_published()andpublish()matchers. [#1033, e4fbf2f] -
Add:
have_published()andhave_applied()now work without arguments too, just aspublishandapply— they check if any events has been published or applied. [#1033]
RubyEventStore::Browser
- no changes
v2.1.0
RubyEventStore
-
Remove: Deprecated methods from previous release are gone [#946]
-
Add: Record upcasting as a way to on-the-fly transform previous event versions into newer ones when reading [#836]
Usage:
class Mapper < PipelineMapper
def initialize(upcast_map: {})
super(Pipeline.new(
Transformation::Upcast.new(upcast_map),
))
end
end
RubyEventStore::Client.new(
mapper: Mapper.new(upcast_map: {
'OldEventType' => lambda { |record|
Record.new(
event_type: 'NewEventType',
data: ...,
metadata: record.metadata,
timestamp: record.timestamp,
valid_at: record.valid_at,
event_id: record.event_id
)
}
}),
repository: ...
) -
Add: Introduce explicit event type resolver [#837]
Previously we'd always expect that a string with event type or anything responding to
to_scan be passed to APIs expecting event type —of_type,subscribeetc. That stays as a default for backward compatibility but can be customized so that:
class SomeEvent < MyEvent
self.event_type = "some.event"
end
client = RubyEventStore::Client.new(
repository: InMemoryRepository.new,
subscriptions: Subscriptions.new(event_type_resolver: ->(klass) { klass.event_type })
)
client.subscribe(lambda { |event| ... }, to: [SomeEvent])
# Previously this needed to be: [SomeEvent.event_type]RailsEventStore
- Remove: Deprecated methods from previous release are gone [#946]
RailsEventStoreActiveRecord
- Change: Minimal required
activerecordversion specified [afe6bbd]
AggregateRoot
- no changes
RubyEventStore::RSpec
- Remove: Wrapper for old
rails_event_store-rspecgem name is now gone [#946]
RubyEventStore::Browser
- no changes
v2.0.1
v1.3.1
v2.0.0
TL;DR upgrading from 1.3.0
I'm running on defaults and can deploy changes with downtime needed for required schema migrations
This is the most common and the simplest scenario of configuration, as seen below:
Rails.configuration.to_prepare do
Rails.configuration.event_store = RailsEventStore::Client.new
...
endBump version in Gemfile to 2.0.0:
gem 'rails_event_store', '~> 2.0.0'Add required migrations and run them:
rails g rails_event_store_active_record:created_at_precision
rails g rails_event_store_active_record:add_valid_at
rails g rails_event_store_active_record:no_global_stream_entries
rails db:migrate
I have some custom components: mapper, event, dispatcher, scheduler, repository
Refer to custom components guide for detailed changes. You'll need to have custom components in a working state before running migrations.
I cannot deploy changes with downtime needed for schema migrations
I feel you. Read on how to migrate large database tables for inspiration and feel free to modify migrations that we provide to suit your needs.
When in doubt, ask for support.
Upgrade verification checklist
- events appended by current version can be read by current version
- events appended by previous version can be read by current version
- events scheduled for handlers by current version can be picked by current version
- events scheduled for handlers by previous version can be picked by current version
👇 back to regular changelog👇
RubyEventStore
-
Deprecate: Serialization is now a responsibility of the repository and the scheduler. It is no longer performed in mappers. [#760]
Was:
event_store = RubyEventStore::Client.new( mapper: Mappers::Default.new(serializer: YAML), repository: RubyEventStore::InMemoryRepository.new, )
Is now:
event_store = RubyEventStore::Client.new( mapper: Mappers::Default.new, repository: RubyEventStore::InMemoryRepository.new(serializer: YAML), )
Also
RubyEventStore::Mappers::Transformation::Serializationis no longer in use and is effectively no-op. Please remove it from your pipeline if it is defined there. -
Add: Support filtering events by timestamp. [#764, #457]
Usage:
event_store.read.older_than(7.days.ago).to_a event_store.read.newer_than_or_equal(Time.utc(2020, 1, 1)).to_a event_store.read.newer_than(14.days.ago).older_than(7.days.ago).to_a event_store.read.between(14.days.ago...7.days.ago).to_a
-
Add: Support for Bi-Temporal Event Sourcing. Event not only has a timestamp of the time it was appended to the store, but also
valid_attime. Querying supports ordering events by creation or validity time. [#765]Usage:
event_store.read.stream("my-stream").as_at.to_a # ordered by time of appending (timestamp) event_store.read.stream("my-stream").as_of.to_a # ordered by validity time (valid_at)
-
Performance: Reduce memory usage of InMemoryRepository, approximately twice. [#766]
-
Change:
RubyEventStore::SerializedRecordnow carriestimestampandvalid_atfields. [#674, #765] -
Change:
RubyEventStore::Mappers::Transformation::Itemis no more and has been replaced withRubyEventStore::Record. Refer to existing transformations for sample usage [#760] -
Change: Protobuf support has been moved to separate, contrib gem [#871, #925]
If you depended on Protobuf before, you need this gem now in your
Gemfile:gem 'ruby_event_store-protobuf`
-
Add:
Client#subscribers_for(event_type)returns list of handlers subscribing for given event type. Useful in specs and diagnostics [#916] -
Change:
Projection.from_streamno longer takes splat arguments. Its API now takes singular stream name or array of stream names:Was:
RubyEventStore::Projection.from_stream('some_stream') RubyEventStore::Projection.from_stream('some_stream', 'other_stream')
Is now:
RubyEventStore::Projection.from_stream('some_stream') RubyEventStore::Projection.from_stream(%w[some_stream other_stream])
RailsEventStore
-
Change: Replace
RubyEventStore::ImmediateAsyncDispatcherwithRailsEventStore::AfterCommitAsyncDispatcheras a new default in composed dispatcher. [00812f0]Rationale:
- recommended for production use
- with no wrapping transaction it behaves like
RubyEventStore::ImmediateAsyncDispatcherit replaces - with a transaction wrapping scheduling, it avoids typical problems with visibility across different data stores (see https://blog.arkency.com/2015/10/run-it-in-background-job-after-commit/)
- there are no drawbacks coming from transactional tests anymore (since https://github.com/RailsEventStore/rails_event_store/releases/tag/v0.41.0)
-
Deprecate: Serialization is now a responsibility of the repository and the scheduler. It is no longer performed in mappers. [#760]
Was:
Rails.configuration.event_store = RailsEventStore::Client.new( mapper: RubyEventStore::Mappers::Default.new(serializer: YAML), repository: RailsEventStoreActiveRecord::EventRepository.new, dispatcher: RubyEventStore::ComposedDispatcher.new( RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: ActiveJobScheduler.new, RubyEventStore::Dispatcher.new ) )
Is now:
Rails.configuration.event_store = RailsEventStore::Client.new( mapper: RubyEventStore::Mappers::Default.new, repository: RailsEventStoreActiveRecord::EventRepository.new(serializer: YAML), dispatcher: RubyEventStore::ComposedDispatcher.new( RailsEventStore::AfterCommitAsyncDispatcher.new(scheduler: ActiveJobScheduler.new(serializer: YAML)), RubyEventStore::Dispatcher.new ) )
In fact both of these components facing external systems can use different serializers now! Serialization is performed lazily when needed and its product is reused. Using different serializers though has the cost of additional serialization in a different format.
-
Change:
RubyEventStore::SerializedRecordnow carriestimestampandvalid_atfields. [#674, #765]This affects async handlers in form of background jobs in the context of new deployment:
-
when a worker running already on a new code picks a job scheduled still in an old format from queue
Missing
timestampandvalid_atare polyfilled from event metadata in this RES release. -
when a worker running still on an old code picks a job scheduled already in a new format from queue
Additional keyword arguments (
timestamp,valid_at) will not be handled gracefully.
Retry those jobs which failed once the code is reloaded and you'll be good.
-
-
Add:
RailsEventStore::AsyncHandler.withwhich makesAsyncHandlerconfigurable. [#761]This allows providing particular event store instance (i.e. when needed for multiple databases support) or changing serialization format (to match one used by scheduler).
Usage:
class SomeHandler < ActiveJob::Base prepend RailsEventStore::AsyncHandler.with(event_store: json_event_store, serializer: JSON) # ... end
-
Remove: Dropped support for Rails 4.2. Most likely RailsEventStore will continue working as is on this Rails version. We're no longer tracking this [#849]
RailsEventStoreActiveRecord
-
Change: Increase timestamp precision on MySQL and SQLite. Adds fractional time component [#674]
⚠️ This requires migrating your databaseYou can skip it to maintain current timestamp precision (up to seconds). The following migration is provided merely as an example. Do not assume it is performed online in your database system. Consult your MySQL/PostgreSQL version documentation before running in production.
Usage:
rails g rails_event_store_active_record:created_at_precisionRelated: https://blog.arkency.com/how-to-migrate-large-database-tables-without-a-headache/
-
Change: Store timestamp only in a dedicated, indexed column making it independent of serializer. [#729, #627, #674]
This means timestamp is no longer present in serialized metadata within database table. Timestamp is still present in event object metadata.
This also means that historical data takes
created_atcolumn as a source of a timestamp. This can introduce a sub-second drift in timestamps. Until now it was the ActiveRecord that set the value ofcreated_aton commit — independently of RES setting timestamp when callingappendorpublishand persisting within serialized metadata.If that drift is problematic to you, consider migrating the timestamp from
metadatatocreated_at. If your serializer was YAML, this could be used to extract the timestamp in MySQL:SELECT STR_TO_DATE(SUBSTR(metadata, LOCATE(':timestamp: ', metadata) + 12, 31), '%Y-%m-%d %H:%i:%s.%f') FROM event_store_events; -
Add: Multiple databases support [#753, #740]
As a side-effect you can now pass AR model classes to be used by repository. Useful for e...
v1.3.0
RailsEventStore
- no changes
RubyEventStore
- Change: Warn about incorrect usage of
Projection.from_streamand normalize argument of such [#796, #847, 5694783]
RailsEventStoreActiveRecord
AggregateRoot
- no changes
RailsEventStore::RSpec
- no changes
BoundedContext
- no changes
RubyEventStore::Browser
- no changes
RubyEventStore::ROM
- no changes