-
Database connection management has been removed from Sequent and must be provided by the hosting application. This is done automatically by Rails and can be done using the
sinatra-activerecordgem if you are using Sinatra.Otherwise you can add code like:
ActiveRecord::Base.configurations = YAML.load_file( 'db/database.yml', aliases: true, ) ActiveRecord::Base.establish_connection(Sequent.env&.to_sym)
The Sequent configuration options
database_config_directory,database_schema_directory,primary_database_key, andprimary_database_rolehave been removed. -
To prepare for rolling upgrades of Sequent based applications the snapshot of an aggregate can now be versioned. This means that whenever the snapshot format changes you should either delete all snapshot on deployment of the new version (and ensure no old code is running, which is a current requirement) or you should increment the snapshot version using
enable_snapshots version: 2. In this case the old version of the code will continue to use snapshot version 1 (the default version) while new code will only use snapshot version 2. -
Also in preperation of rolling upgrades the enabled projectors are now optionally tracked in the
projector_statestable. Use theenable_projector_statesconfiguration flag andActiveProjectorsEventPublisheras theevent_publisherenable this feature.Only projectors that are active will process events during normal operation. Events that are published while a newer projector is activating or active will fail with a
DifferentProjectorVersionIsActiveErrorerror. Event replay uses the normalEventPublisherwhich does not check for projector state, since replaying will write to temporary tables until replay is finalizedWhen an unknown projector is active all event processing will be blocked. This allows deploying a new version while the old version of the code is still running and processing events. Only after executing
Sequent#activate_current_configuration!will the new code start processing events, while the old code is blocked. At this moment traffic can be redirected from the old version to the new version.Activating the current configuration is part of the
migrate:offlinerake task, so the current deployment process (run online migrations, take old code offline, run offline migrations, start running the new code) will keep working as is.
- Bug: Fix resetting column information and table_name when using Single Table Inheritance.
- Improvement: Fail fast when passing a non managed_table to the Persistor
- Improvement: Log current event when replaying fails
-
The sequent gem now uses ActiveRecord migrations to handle the
sequent_schema. Theview_schemais still handled by sequent itself. You can install the sequent migrations in your project by runningrake sequent:install:migrationswhich will copy the necessary files to yourdb/migratedirectory.After upgrading Sequent you can run this task again to install any updates for the schema.
-
There is a new rake task
sequent:migrate:unique_keyswhich allows you to migrate aggregates when you change an aggregate's unique keys. The task loads each aggregate (optionally filtered by the type) from history, and updates the associated unique keys.
-
Revert type id assignment changes as it introduces potential deadlocks when new types are added. If you already aplied the changes in release 8.1.0 you will have to execute the following SQL when upgrading:
SET search_path TO sequent_schema; ALTER TABLE aggregate_types ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY; ALTER TABLE command_types ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY; ALTER TABLE event_types ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY; SELECT setval('aggregate_types_id_seq', COALESCE((SELECT MAX(id) + 1 FROM aggregate_types), 1), false); SELECT setval('command_types_id_seq', COALESCE((SELECT MAX(id) + 1 FROM command_types), 1), false); SELECT setval('event_types_id_seq', COALESCE((SELECT MAX(id) + 1 FROM event_types), 1), false);
-
Support specifying unique keys for aggregates that are checked when transaction is committed, reducing the need for having aggregates to ensure uniqueness or relying on projectors. See lib/sequent/core/helpers/unique_keys.rb for details.
This feature requires a new database, you can find an example migration at
db/migrate/20250108162754_aggregate_unique_keys.rb. -
CommandHandlerHelpershas been updated to:- Load aggregates using the
given_eventsso that unique keys can be tested. This may require changing your test to correctly set up the given events. - Run your test using the real or fake event store.
- Use RSpec matchers in
then_events. then_eventsnow only checks the events since the lastwhen_commandcall occurred.
- Load aggregates using the
-
You can now retrieve the current position of the event store using
EventStore#position_markand load all the events since this position usingEventStore#load_events_since_marked_position. This is used byCommandHandlerHelpersbut in the future might have other uses (such as tailing the event store as events are being committed). -
Use
max(id) + 1query to assign type ids to avoid exhausing the SMALLINT range. Sequences are not rolled back when a transaction is aborted so type ids may become exhausted. This is mainly a problem in development when running tests, but could occur in production if a transaction that adds a new type is buggy and gets rolled back multiple times.Re-apply the
sequent_pgsql.sqlfile to your Sequent schema to apply this fix.
- Add support for ActiveRecord 8. Thanks evsasse.
- Improvements for internal testing of Sequent.
- Fix Sequent CLI
- Check the upgrade guide for upgrading an existing application using Sequent < 8.
- Sequent now requires at least Ruby 3.2, ActiveRecord 7.1, and PostgreSQL 14.
- The internal database schema has been re-organized to support table
partitioning so the events of related aggregates can be stored
together. Partitioning is optional. You can override the
AggregateRoot#events_partition_keymethod to return the partition key of an aggregate that will be used by PostgreSQL to store the events in the correct partition. - Storage logic of Sequent is now done by PostgreSQL stored procedures. This reduces the number of round-trips necessary between the Ruby process and the PostgreSQL server and optimizes storage requirements.
AggregateRoot#take_snapshot!has been replaced withAggregateRoot#take_snapshotwhich returns the snapshot instead of adding it to the uncommitted events. Snapshots can be stored usingEventStore#store_snapshotsmethod.- The
stream_records,event_records, andcommand_recordsrelations are no longer tables but views on the underlyingaggregates,events, andcommandstables. These views take care of joining related tables using the correct partitioning key and type ids. The views are mainly for backwards compatibility with the*RecordRuby classes and can only be used for querying. For updates theEventStore(and higher-levelAggregateRepository) classes should be used. New APIs have been added so there is no longer a need to use ActiveRecord methods directly to manage the event store. - Events, commands, and snapshot are now serialized directly to a
JSONorJSONBcolumn. TheTEXTcolumn type is no longer supported. JSON size is minimized by extracting various repetitive fields (such asaggregate_id) into database columns. - Snapshots are now stored into a separate
snapshot_recordstable instead of being mixed in with events in theeventstable. This also makes it possible to store snapshots without an associated command record. Aggregates that need snapshots are tracked in a separateaggregates_that_need_snapshotsso that snapshots can be created more quickly by a background snapshotting process. - The
parent,originandchildrenmethods ofCommandRecordandEventRecordhave been replaced by more specific methods (origin_command,parent_event, etc) and will always return a record of the appropriate type. - Events that are updated or deleted (a rare occurrence) are archived
into the
saved_event_recordstable. - The
idcolumn ofaggregateshas been removed and the primary key is now theaggregate_idcolumn. - The
idcolumn ofeventshas been removed and the primary key are now theaggregate_idandsequence_numbercolumns.
- Many documentation improvements.
- Other small bug fixes.
Small bugfix for metadata tables migrations. See zilverline#418. Thanks evsasse.
BREAKING CHANGE:
- Replaying all events for the view schema (using
sequent:migrate:onlineandsequent:migrate:offline) now make use of the PostgreSQL committed transaction id (xact_id()) to track events that have already been replayed. The replayed ids table (specified by the removedSequent::configuration.replayed_ids_table_nameoption) is no longer used and can be dropped from your database. There is no activerecord migration provided for the event store to add thexact_idsince depending on the size of the event store you may want to take run this migration yourself. ReplaceSCHEMA_NAMEwith the name of the sequent schema:
BEGIN;
ALTER TABLE SCHEMA_NAME.event_records ADD COLUMN xact_id bigint;
COMMIT;
BEGIN;
# SET max_parallel_maintenance_workers = 8; # optionally set this depending on size of your event_records
# ALTER TABLE SCHEMA_NAME.event_records SET (parallel_workers = 8); # optionally set this depending on size of your event_records
CREATE INDEX event_records_xact_id_idx ON SCHEMA_NAME.event_records (xact_id) WHERE xact_id IS NOT NULL;
# ALTER TABLE SCHEMA_NAME.event_records RESET (parallel_workers); # optionally set this depending on size of your event_records
COMMIT;
ALTER TABLE SCHEMA_NAME.event_records ALTER COLUMN xact_id SET DEFAULT pg_current_xact_id()::text::bigint;
Next to this migration make sure you copy over the new sequent_schema.rb into your project so when you regenerate the database from scratch
in for instance your development environment you have the correct version.
Other notable changes:
- The
MessageDispatcherclass has been removed. - Instance-of routes in projectors and other message handlers now use an optimized lookup mechanism. These are the most common handlers (
on MyEvent do ... end). - Many optimizations were applied to the
ReplayOptimizedPostgresPersistor:- Multi-value indexes are no longer supported, each column is now individually indexed. When a where clauses references multiple indexed columns all applicable indexes are used. For backwards compatibility multi-column index definitions are automatically changed to single-column indexes (one for each colum in the multi-column definition).
- Default indexed columns can be specified when instantiating the
ReplayOptimizedPostgresPersistor. - Indexed values are now automatically frozen.
- Array matching and string/symbol matching in where-clauses now work for indexed columns as well.
- The internal struct classes are now generated differently and these classes are no longer associated with a Ruby constant so cannot be referenced from your code.
- Added possibility
enable_autoregistrationfor automatically registering all Command and EventHandlers - In a Rails app all code will be eager loaded when
enable_autoregistrationis set to true upon sequent initialization viaRails.autoloaders.main.eager_load(force: true). If other parts of your app (esp initializers) are dependent on code not being loaded yet you can ensure Sequent loads as last by renaming the initializer to e.g.zz_sequent.rbas Rails loads initializers in alphabetical order.
BREAKING CHANGES:
- Introduced
event_store_cache_event_typesas alternative for manually instantiating the EventStore yourself if you want to disable caching of event types. - Calling
Sequent.configuretwice will now create a new instance of the configuration instead of changing the current instance. This is done to better support Rails apps and the reload functionality during development.
- Drop support for ruby < 3
- Upgraded ActiveStar
- Add support for ruby 3.2
- Changed the default type of
aggregate_idinsequent_schematouuidsince Postgres support this for quite long. - Added support for applications using ActiveRecord multiple database connections feature
- Improved out-of-the-box Rails support by fixing various bugs and providing Rake task to ease integration. See for more details: https://www.sequent.io/docs/rails-sequent.html
- Introduce
SEQUENT_ENVinstead ofRACK_ENV.SEQUENT_ENVdefaults to the value ofRAILS_ENVorRACK_ENV. - Introduce
Sequent.configuration.time_precisionwhich defaults toActiveSupport::JSON::Encoding.time_precisionwhich is the precision "after seconds" to store time in json format when an event is serialized. - Custom command validations will now be translated according to the locale set by
Sequent.configuration.error_locale_resolver
BREAKING CHANGES:
- Since
DateTimeis deprecated in the Ruby std lib the standard attributecreated_atofEventandCommandis now aTime. This should not be a problem when serializing an deserializing but might be breaking if you rely on the fact it being aDateTimerather than aTime. - Renamed file of
Sequent::Test::WorkflowHelperstoworkflow_helpers. If you require this file manually you will need to update it's references - You now must "tag" specs using
Sequent::Test::WorkflowHelperswith the following metadataworkflows: trueto avoid collision with other specs - Bugfix:
rake sequent:migrate:onlinewill now callreset_column_informationwhen done. Otherwise a subsequentsequent:migrate:offlinecalled in the same memory space fails.
- Introduce several advanced features for
Sequent::Core::Helpers::MessageHandlers (ie.Sequent::AggregateRoot,Sequent::Projector,Sequent::WorkflowandSequent::CommandHandler), namely:- Message matching (see https://www.sequent.io/docs/concepts/advanced.html#message-matching)
- Load time options (see https://www.sequent.io/docs/concepts/advanced.html#message-handler-load-time-options)
on MyEvent, MyEventwill now raise an error stating the duplicate argumentsonwithout arguments will now raise an error- Declaring duplicate
attrswill now raise an error
- Add ability to to load aggregates up until a certain point in time (use with caution)
- Support for ActiveStar 7.x
- Support for Ruby 3.1
- Various documentation fixes
- Upgrade to latest ar 6.1.4.x version
- Various setup fixes when doing
sequent new myapp - Improve
dry_runfeature by using real event store
- Improve performance when running specs by not using
descendants - Support for ActiveRecord 6.1.x
- Fixed some rake tasks for snapshotting (Thanks HEROGWP)
- Improve
ReplayOptimizedPostgresPersistor::Index - Various improvements to the database config (to use database url and aliases) (Thanks Bér Kessels)
- Allow for event upcasting
- Add rubocop
- Changed default ruby to 3.0.0 release
- Added
database_schema_directoryconfiguration parameter to determine where to findsequent_schema.rbandsequent_migrations.rb. Currently it has the same default asdatabase_config_directory - Dropped support for ruby < 2.7
- Moved to Github Actions for CI
- Allow splitting of indices and table definition in sql files to speed up replaying projectors
- Changed default ruby to latest 2.6 release
- Added support for ActiveRecord 6.0
- Added documentation for using with Rails
- Added alter table capabilities to migrations. Useful for larger projections.
- Added
Sequent::Projector.manages_no_tables.
- Introduced
strict_check_attributes_on_apply_events. Sequent will fail when callingapplywith unknown attributes.
Introduces optional event_aggregate_id and event_sequence_number columns to the command_records table.
This enables keeping track of events causing commands in workflows.
To add these columns to an existing event store you can use this sql to add them:
Please note these sql statements use the uuid type for aggregate_id.
ALTER TABLE command_records ADD COLUMN event_aggregate_id uuid;
ALTER TABLE command_records ADD COLUMN event_sequence_number integer;
CREATE INDEX CONCURRENTLY index_command_records_on_event ON command_records(event_aggregate_id, event_sequence_number);
The most notable changes are:
- Added more documentation on https://www.sequent.io
- Added support for AR 5.2
- Added rake task to support installation on existing databases
The most notable changes are:
- Added extensive documentation for Sequent.
- Addition of more sophisticated way of replaying. See the documentation on how to configure.
- Dropped support for AR < 5.0
- Deprecated MigrateEvents as strategy for event migration
- Renamed Sequent::Core::BaseEventHandler to Sequent::Core::Projector
- Renamed Sequent::Core::Sessions::ActiveRecordSession to Sequent::Core::Persistors::ActiveRecordPersistor
- Renamed Sequent::Core::Sessions::ReplayEventsSession to Sequent::Core::Persistors::ReplayOptimzedPostgresPersistor
The most notable changes are:
To illustrate the difference see example below:
# command c1 results in event e1
on c1 do
apply e1
end
# workflow: event e1 results in new command c3
on e1 do
execute_commands(c3)
end
# main
execute_commands(c1, c2)Prior to version 1.1 the order is as follows:
c1c3c2e1
As you can see command c3 is executed before c2 although c2 was scheduled before c3.
As of version 1.1 the order will be:
c1c2c3e1
Commands and events are added to the queues as they occur and than handled in that order. If you have never had workflows scheduling new commands in the foreground nothing changes. If you have used workflows in the foreground the order will be different, so ensure your system still behaves correctly.
The Sequent::Core::EventStore::PublishEventError is renamed to Sequent::Core::EventPublisher::PublishEventError
Another possible breaking change is the way the config is setup. The sequent config now global, so some sequent classes in the config do not take parameters anymore. You will need to change your sequent config, and will have affect on your tests. Please see the docs and the example apps for more information.
Full list of changes:
- New: Adding
TakeSnapshotcommand to enable fine-grained support for snapshotting aggregates zilverline#92 - Improvement: Minimize differences between test environment and normal environment zilverline#93
- Bugfix: Fix query to load multiple aggregates of which one is snapshotted zilverline#94
- Improvement: Speed up event query zilverline#95
- New: Added
create_recordsmethod available toBaseEventHandlersto insert multiple records in one go zilverline#96 zilverline#100 - New: Added queue-based command and event publishing to ensure commands and events are handled in order they occurred zilverline#97
- Improvement: Update
ojto mimic ActiveSupport version 5. Thanks @respire! zilverline#98 - Bugfixes: Fix sequent for AR => 5 zilverline#99
- Improvement: Global sequent config zilverline#101