Releases: RailsEventStore/rails_event_store
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::Event
with additional metadata. -
Add: Check incorrect usage of
expected_version: :any
inRubyEventStore::InMemoryRepository
and raiseRubyEventStore::InMemoryRepository::UnsupportedVersionAnyUsage
[#1091]Disabled by default. Can be turned on by passing
ensure_supported_any_usage
like 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::Event
equality 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_stream
andglobal_position
queries [#1053] - Change: Remove redundant position order in queries [a3fc054]
- Remove: Obsolete
pgcrypto
extension 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::DomainEvent
transformation 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_at
orevent_store.read.as_of
introduced in v2.1.0, this is an important update for you. -
Remove: No longer needed
pgcrypto
Postgres extension dropped from initial migration. [e856ca4]
AggregateRoot
- no changes
RubyEventStore::RSpec
-
Add: An experimental formatter for
have_published
rspec 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
,once
modifiers toapply()
matcher. [#1033] -
Add: Support for
exactly(n)
,times
,time
,once
,strict
modifiers 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 aspublish
andapply
— 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_s
can be passed to APIs expecting event type —of_type
,subscribe
etc. 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
activerecord
version specified [afe6bbd]
AggregateRoot
- no changes
RubyEventStore::RSpec
- Remove: Wrapper for old
rails_event_store-rspec
gem 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
...
end
Bump 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::Serialization
is 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_at
time. 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::SerializedRecord
now carriestimestamp
andvalid_at
fields. [#674, #765] -
Change:
RubyEventStore::Mappers::Transformation::Item
is 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_stream
no 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::ImmediateAsyncDispatcher
withRailsEventStore::AfterCommitAsyncDispatcher
as a new default in composed dispatcher. [00812f0]Rationale:
- recommended for production use
- with no wrapping transaction it behaves like
RubyEventStore::ImmediateAsyncDispatcher
it 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::SerializedRecord
now carriestimestamp
andvalid_at
fields. [#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
timestamp
andvalid_at
are 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.with
which makesAsyncHandler
configurable. [#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_precision
Related: 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_at
column 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_at
on commit — independently of RES setting timestamp when callingappend
orpublish
and persisting within serialized metadata.If that drift is problematic to you, consider migrating the timestamp from
metadata
tocreated_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_stream
and 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
v1.2.2
RailsEventStore
- no changes
RubyEventStore
- no changes
RailsEventStoreActiveRecord
- no changes
AggregateRoot
- no changes
RailsEventStore::RSpec
- no changes
BoundedContext
- no changes
RubyEventStore::Browser
RubyEventStore::ROM
- no changes
v1.2.1
RailsEventStore
- Fix:
RailsEventStore::CorrelatedHandler
demanded presence ofcorrelation_id
in event metadata (since v1.2.0). This is an unwanted change when dealing with events scheduled on previous version [8e725fd]
RubyEventStore
- no changes
RailsEventStoreActiveRecord
- no changes
AggregateRoot
- no changes
RailsEventStore::RSpec
- no changes
BoundedContext
- no changes
RubyEventStore::Browser
- no changes
RubyEventStore::ROM
- no changes
v1.2.0
RubyEventStore
-
Change: Appending or publishing to stream now generates
correlation_id
in metadata when none such is already provided. [#832]Previously in case of missing
correlation_id
, second event in the chain had its value backfilled from causing event id. This was confusing at best. After this change, every event hascorrelation_id
.Was:
event A -> event B -> event C event_id: A event_id: B event_id: C correlation_id: nil correlation_id: A correlation_id: A causation_id: nil causation_id: A causation_id: B
Is now:
event A -> event B -> event C event_id: A event_id: B event_id: C correlation_id: X correlation_id: X correlation_id: X causation_id: nil causation_id: A causation_id: B
RailsEventStore
- no changes other than in RubyEventStore
RailsEventStoreActiveRecord
- no changes
AggregateRoot
- no changes
RailsEventStore::RSpec
- no changes
BoundedContext
- no changes
RubyEventStore::Browser
- no changes
RubyEventStore::ROM
- no changes