Skip to content

Releases: RailsEventStore/rails_event_store

v2.3.0

09 Sep 13:36
Compare
Choose a tag to compare

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 in RubyEventStore::InMemoryRepository and raise RubyEventStore::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 with RubyEventStore::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 and global_position queries [#1053]
  • Change: Remove redundant position order in queries [a3fc054]
  • Remove: Obsolete pgcrypto extension no longer required in Postgres migration [b435933]

AggregateRoot

  • Change: Repository instrumentation now pass through every custom method [7328c4f, #561]

RubyEventStore::RSpec

  • no changes

RubyEventStore::Browser

  • Change: UX — treat empty results separately from server errors [d4cc95e, #625]

v2.2.0

09 Apr 11:32
Compare
Choose a tag to compare

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 or event_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 to apply() matcher. [#1033]

  • Add: Support for exactly(n), times, time, once, strict modifiers to publish() matcher. [#1033]

  • Add: Support for in_streams([stream1, stream2]) modifier to have_published() and publish() matchers. [#1033, e4fbf2f]

  • Add: have_published() and have_applied() now work without arguments too, just as publish and apply — they check if any events has been published or applied. [#1033]

RubyEventStore::Browser

  • no changes

v2.1.0

07 Jan 15:47
Compare
Choose a tag to compare

⚠️ If you're somehow trying to migrate from RES 1.x to this version, please migrate to v2.0.0 first ⚠️

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

05 Jan 10:53
Compare
Choose a tag to compare

RubyEventStore

  • no changes

RailsEventStore

  • no changes

RailsEventStoreActiveRecord

  • no changes

AggregateRoot

  • Fix: Add Ruby 3.0 compatibility [#962, #963]

RubyEventStore::RSpec

  • no changes

RubyEventStore::Browser

  • no changes

v1.3.1

05 Jan 10:52
Compare
Choose a tag to compare

RubyEventStore

  • no changes

RailsEventStore

  • no changes

RailsEventStoreActiveRecord

  • no changes

AggregateRoot

  • Fix: Add Ruby 3.0 compatibility [#962, #963]

BoundedContext

  • no changes

RailsEventStore::RSpec

  • no changes

RubyEventStore::Browser

  • no changes

RubyEventStore::ROM

  • no changes

v2.0.0

30 Dec 15:09
Compare
Choose a tag to compare

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)

    Related: https://www.youtube.com/watch?v=xzekp1RuZbM

  • Performance: Reduce memory usage of InMemoryRepository, approximately twice. [#766]

  • Change: RubyEventStore::SerializedRecord now carries timestamp and valid_at fields. [#674, #765]

  • Change: RubyEventStore::Mappers::Transformation::Item is no more and has been replaced with RubyEventStore::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 with RailsEventStore::AfterCommitAsyncDispatcher as a new default in composed dispatcher. [00812f0]

    Rationale:

  • 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 carries timestamp and valid_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 and valid_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.withwhich makes AsyncHandler 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 database

    You 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 of created_at on commit — independently of RES setting timestamp when calling append or publish and persisting within serialized metadata.

    If that drift is problematic to you, consider migrating the timestamp from metadata to created_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...

Read more

v1.3.0

15 Dec 00:31
Compare
Choose a tag to compare

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

03 Nov 15:15
Compare
Choose a tag to compare

RailsEventStore

  • no changes

RubyEventStore

  • no changes

RailsEventStoreActiveRecord

  • no changes

AggregateRoot

  • no changes

RailsEventStore::RSpec

  • no changes

BoundedContext

  • no changes

RubyEventStore::Browser

  • Fix: Browser too strict on requiring correlation_id presence since v1.2.0 [#835, a9526a0]

RubyEventStore::ROM

  • no changes

v1.2.1

02 Nov 15:39
Compare
Choose a tag to compare

RailsEventStore

  • Fix: RailsEventStore::CorrelatedHandlerdemanded presence of correlation_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

30 Oct 12:19
Compare
Choose a tag to compare

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 has correlation_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