pg_eventstore 2.0.0 → 3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +186 -20
- data/README.md +25 -15
- data/db/migrations/10_setup_pg_cron.rb +1 -23
- data/db/migrations/15_create_streams_global_index.sql +17 -0
- data/db/migrations/16_populate_streams_global_index.rb +150 -0
- data/db/migrations/17_create_events_global_index.sql +62 -0
- data/db/migrations/18_populate_events_global_index.rb +124 -0
- data/db/migrations/19_populate_events_global_index_p2.rb +25 -0
- data/db/migrations/20_migrate_subscriptions_current_position.sql +19 -0
- data/db/migrations/21_create_partitions_stats.sql +2 -0
- data/db/migrations/22_drop_streams_sql_view.sql +2 -0
- data/db/migrations/23_adjust_partitions_indexes.sql +5 -0
- data/db/migrations/24_drop_legacy_events_indexes.sql +3 -0
- data/db/migrations/25_add_parent_partitions_info_to_partitions.sql +16 -0
- data/db/migrations/26_change_events_stream_revision_type.rb +35 -0
- data/db/migrations/27_drop_events_horizon.sql +3 -0
- data/db/migrations/28_create_maintenance_tasks.sql +8 -0
- data/db/migrations/29_create_event_markers.sql +35 -0
- data/db/migrations/30_improve_partitions_search_in_admin_ui.rb +24 -0
- data/db/migrations/31_drop_events_id_default_value.sql +1 -0
- data/db/migrations/32_create_parittions_triggers.sql +101 -0
- data/docs/AGENTS.fragment.md +696 -0
- data/docs/admin_ui.md +3 -1
- data/docs/appending_events.md +110 -49
- data/docs/configuration.md +69 -36
- data/docs/event_tracing.md +55 -0
- data/docs/events_and_streams.md +54 -30
- data/docs/how_it_works.md +57 -16
- data/docs/linking_events.md +2 -1
- data/docs/maintenance.md +14 -5
- data/docs/multiple_commands.md +17 -8
- data/docs/reading_events.md +208 -43
- data/docs/reading_streams.md +50 -0
- data/docs/replication.md +99 -0
- data/docs/subscriptions.md +165 -35
- data/docs/writing_middleware.md +25 -8
- data/ext/pg_eventstore_ext/extconf.rb +5 -0
- data/ext/pg_eventstore_ext/pg_eventstore_ext.c +74 -0
- data/lib/pg_eventstore/async_runner.rb +63 -0
- data/lib/pg_eventstore/basic_config.rb +15 -0
- data/lib/pg_eventstore/chunks/chunk.rb +31 -0
- data/lib/pg_eventstore/chunks/read_api_events_index_chunk.rb +83 -0
- data/lib/pg_eventstore/chunks/replica_events_index_chunk.rb +35 -0
- data/lib/pg_eventstore/chunks/repository.rb +73 -0
- data/lib/pg_eventstore/chunks/subscription_checkpoint_chunk.rb +48 -0
- data/lib/pg_eventstore/chunks/subscription_events_index_chunk.rb +99 -0
- data/lib/pg_eventstore/chunks.rb +14 -0
- data/lib/pg_eventstore/client.rb +163 -50
- data/lib/pg_eventstore/commands/append.rb +62 -43
- data/lib/pg_eventstore/commands/delete_event.rb +7 -8
- data/lib/pg_eventstore/commands/delete_stream.rb +3 -3
- data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +9 -7
- data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
- data/lib/pg_eventstore/commands/link_to.rb +5 -5
- data/lib/pg_eventstore/commands/read.rb +23 -5
- data/lib/pg_eventstore/commands/read_grouped.rb +46 -0
- data/lib/pg_eventstore/commands/read_streams.rb +18 -0
- data/lib/pg_eventstore/commands/read_streams_paginated.rb +56 -0
- data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +4 -4
- data/lib/pg_eventstore/commands/revision_check/current_revision.rb +51 -0
- data/lib/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rb +46 -0
- data/lib/pg_eventstore/commands/revision_check/expected_revision.rb +135 -0
- data/lib/pg_eventstore/commands/revision_check/stream_revision_check.rb +98 -0
- data/lib/pg_eventstore/commands/revision_check/stream_revision_comparison.rb +36 -0
- data/lib/pg_eventstore/commands/stream_revision.rb +14 -0
- data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +4 -4
- data/lib/pg_eventstore/commands.rb +4 -2
- data/lib/pg_eventstore/config.rb +30 -9
- data/lib/pg_eventstore/connection.rb +21 -2
- data/lib/pg_eventstore/errors.rb +136 -10
- data/lib/pg_eventstore/event.rb +34 -2
- data/lib/pg_eventstore/event_deserializer.rb +5 -4
- data/lib/pg_eventstore/event_global_index.rb +118 -0
- data/lib/pg_eventstore/event_marker.rb +16 -0
- data/lib/pg_eventstore/event_marker_index.rb +25 -0
- data/lib/pg_eventstore/event_serializer.rb +6 -0
- data/lib/pg_eventstore/extensions/acts_as_configurable.rb +88 -0
- data/lib/pg_eventstore/extensions/options_defaults.rb +25 -0
- data/lib/pg_eventstore/extensions/options_extension.rb +2 -2
- data/lib/pg_eventstore/feature_marker.rb +18 -0
- data/lib/pg_eventstore/maintenance.rb +11 -6
- data/lib/pg_eventstore/middleware/event_tracing.rb +53 -0
- data/lib/pg_eventstore/partition.rb +6 -0
- data/lib/pg_eventstore/pg_connection.rb +70 -14
- data/lib/pg_eventstore/queries/event_marker_queries.rb +74 -0
- data/lib/pg_eventstore/queries/event_queries.rb +7 -93
- data/lib/pg_eventstore/queries/events_global_index_queries.rb +131 -0
- data/lib/pg_eventstore/queries/index_filtering_queries.rb +179 -0
- data/lib/pg_eventstore/queries/links_resolver.rb +4 -4
- data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
- data/lib/pg_eventstore/queries/partition_queries.rb +45 -103
- data/lib/pg_eventstore/queries/replica_queries.rb +110 -0
- data/lib/pg_eventstore/queries/streams_global_index_queries.rb +163 -0
- data/lib/pg_eventstore/queries/transaction_queries.rb +52 -10
- data/lib/pg_eventstore/queries.rb +29 -1
- data/lib/pg_eventstore/query_builders/basic_filtering.rb +38 -5
- data/lib/pg_eventstore/query_builders/event_markers_filtering.rb +47 -0
- data/lib/pg_eventstore/query_builders/event_markers_index_filtering.rb +275 -0
- data/lib/pg_eventstore/query_builders/event_subscription_positions_filtering.rb +37 -0
- data/lib/pg_eventstore/query_builders/events_filtering.rb +15 -164
- data/lib/pg_eventstore/query_builders/events_global_index_filtering.rb +227 -0
- data/lib/pg_eventstore/query_builders/filters/collection.rb +324 -0
- data/lib/pg_eventstore/query_builders/filters/event_type_filter.rb +37 -0
- data/lib/pg_eventstore/query_builders/filters/filter_row.rb +54 -0
- data/lib/pg_eventstore/query_builders/filters/marker_filter.rb +20 -0
- data/lib/pg_eventstore/query_builders/filters/marker_filter_row.rb +49 -0
- data/lib/pg_eventstore/query_builders/filters/stream_filter.rb +48 -0
- data/lib/pg_eventstore/query_builders/index_based_events_filtering.rb +260 -0
- data/lib/pg_eventstore/query_builders/partitions_filtering.rb +175 -51
- data/lib/pg_eventstore/query_builders/read_cursor/stream_cursor.rb +122 -0
- data/lib/pg_eventstore/query_builders/streams_global_index_filtering.rb +83 -0
- data/lib/pg_eventstore/query_builders/subscription_events_filtering.rb +79 -0
- data/lib/pg_eventstore/query_strategy/async.rb +64 -0
- data/lib/pg_eventstore/query_strategy/foreground.rb +25 -0
- data/lib/pg_eventstore/query_strategy.rb +19 -0
- data/lib/pg_eventstore/raw_event.rb +46 -0
- data/lib/pg_eventstore/rspec/test_helpers.rb +21 -8
- data/lib/pg_eventstore/sql_builder.rb +87 -8
- data/lib/pg_eventstore/stream.rb +13 -16
- data/lib/pg_eventstore/stream_global_index.rb +28 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +5 -17
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rb +36 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +12 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +21 -7
- data/lib/pg_eventstore/subscriptions/events_processor.rb +20 -18
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/multiple.rb +63 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/replica.rb +68 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/single.rb +62 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer.rb +44 -0
- data/lib/pg_eventstore/subscriptions/events_subscription_position_worker.rb +68 -0
- data/lib/pg_eventstore/subscriptions/queries/event_subscription_position_queries.rb +177 -0
- data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +78 -54
- data/lib/pg_eventstore/subscriptions/replica_subscription_handler.rb +140 -0
- data/lib/pg_eventstore/subscriptions/replica_subscription_runner.rb +9 -0
- data/lib/pg_eventstore/subscriptions/subscription.rb +2 -1
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/collection.rb +36 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/index_read_strategy.rb +82 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/replication_strategy.rb +75 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategy.rb +31 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +24 -0
- data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +4 -2
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -9
- data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +9 -18
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +74 -14
- data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
- data/lib/pg_eventstore/tasks/setup.rake +2 -2
- data/lib/pg_eventstore/utils.rb +31 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +100 -16
- data/lib/pg_eventstore/web/paginator/base_collection.rb +5 -1
- data/lib/pg_eventstore/web/paginator/event_types_collection.rb +14 -4
- data/lib/pg_eventstore/web/paginator/events_collection.rb +29 -33
- data/lib/pg_eventstore/web/paginator/helpers.rb +16 -9
- data/lib/pg_eventstore/web/paginator/markers_collection.rb +48 -0
- data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +13 -2
- data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +21 -11
- data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +18 -4
- data/lib/pg_eventstore/web/paginator/streams_collection.rb +95 -0
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +49 -22
- data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/with_state/subscriptions.rb +1 -1
- data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -9
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +18 -5
- data/lib/pg_eventstore/web/views/home/partials/events.erb +38 -1
- data/lib/pg_eventstore/web/views/home/partials/markers_filter.erb +18 -0
- data/lib/pg_eventstore/web/views/layouts/application.erb +6 -0
- data/lib/pg_eventstore/web/views/streams/index.erb +110 -0
- data/lib/pg_eventstore/web/views/streams/partials/streams.erb +11 -0
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +4 -1
- data/lib/pg_eventstore/web.rb +2 -0
- data/lib/pg_eventstore.rb +23 -60
- data/pg_eventstore.gemspec +6 -4
- data/sig/interfaces/_raw_events_handler.rbs +3 -0
- data/sig/interfaces/subscription_handler.rbs +1 -1
- data/sig/pg/result.rbs +9 -0
- data/sig/pg_eventstore/async_runner.rbs +13 -0
- data/sig/pg_eventstore/basic_config.rbs +9 -0
- data/sig/pg_eventstore/chunks/chunk.rbs +13 -0
- data/sig/pg_eventstore/chunks/read_api_events_index_chunk.rbs +30 -0
- data/sig/pg_eventstore/chunks/replica_events_index_chunk.rbs +11 -0
- data/sig/pg_eventstore/chunks/repository.rbs +22 -0
- data/sig/pg_eventstore/chunks/subscription_checkpoint_chunk.rbs +18 -0
- data/sig/pg_eventstore/chunks/subscription_events_index_chunk.rbs +37 -0
- data/sig/pg_eventstore/client.rbs +40 -42
- data/sig/pg_eventstore/commands/append.rbs +11 -25
- data/sig/pg_eventstore/commands/event_modifiers/prepare_link_event.rbs +9 -12
- data/sig/pg_eventstore/commands/event_modifiers/prepare_regular_event.rbs +5 -4
- data/sig/pg_eventstore/commands/link_to.rbs +6 -11
- data/sig/pg_eventstore/commands/read.rbs +2 -5
- data/sig/pg_eventstore/commands/read_grouped.rbs +7 -0
- data/sig/pg_eventstore/commands/read_streams.rbs +7 -0
- data/sig/pg_eventstore/commands/read_streams_paginated.rbs +16 -0
- data/sig/pg_eventstore/commands/regular_stream_read_paginated.rbs +9 -17
- data/sig/pg_eventstore/commands/revision_check/current_revision.rbs +25 -0
- data/sig/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rbs +14 -0
- data/sig/pg_eventstore/commands/revision_check/expected_revision.rbs +67 -0
- data/sig/pg_eventstore/commands/revision_check/stream_revision_check.rbs +20 -0
- data/sig/pg_eventstore/commands/revision_check/stream_revision_comparison.rbs +9 -0
- data/sig/pg_eventstore/commands/stream_revision.rbs +7 -0
- data/sig/pg_eventstore/commands/system_stream_read_paginated.rbs +9 -14
- data/sig/pg_eventstore/config.rbs +15 -6
- data/sig/pg_eventstore/connection.rbs +11 -18
- data/sig/pg_eventstore/errors.rbs +64 -34
- data/sig/pg_eventstore/event.rbs +16 -2
- data/sig/pg_eventstore/event_deserializer.rbs +5 -10
- data/sig/pg_eventstore/event_global_index.rbs +60 -0
- data/sig/pg_eventstore/event_marker.rbs +9 -0
- data/sig/pg_eventstore/event_marker_index.rbs +12 -0
- data/sig/pg_eventstore/extensions/acts_as_configurable.rbs +29 -0
- data/sig/pg_eventstore/extensions/options_defaults.rbs +7 -0
- data/sig/pg_eventstore/feature_marker.rbs +10 -0
- data/sig/pg_eventstore/maintenance.rbs +12 -8
- data/sig/pg_eventstore/middleware/event_trace.rbs +14 -0
- data/sig/pg_eventstore/partition.rbs +2 -4
- data/sig/pg_eventstore/pg_connection.rbs +8 -0
- data/sig/pg_eventstore/queries/event_marker_queries.rbs +21 -0
- data/sig/pg_eventstore/queries/event_queries.rbs +4 -36
- data/sig/pg_eventstore/queries/events_global_index_queries.rbs +37 -0
- data/sig/pg_eventstore/queries/index_filtering_queries.rbs +50 -0
- data/sig/pg_eventstore/queries/links_resolver.rbs +5 -9
- data/sig/pg_eventstore/queries/maintenance_queries.rbs +12 -9
- data/sig/pg_eventstore/queries/partition_queries.rbs +32 -67
- data/sig/pg_eventstore/queries/replica_queries.rbs +31 -0
- data/sig/pg_eventstore/queries/streams_global_index_queries.rbs +39 -0
- data/sig/pg_eventstore/queries/transaction_queries.rbs +2 -0
- data/sig/pg_eventstore/queries.rbs +11 -21
- data/sig/pg_eventstore/query_builders/basic_filtering.rbs +10 -3
- data/sig/pg_eventstore/query_builders/event_markers_filtering.rbs +17 -0
- data/sig/pg_eventstore/query_builders/event_markers_index_filtering.rbs +69 -0
- data/sig/pg_eventstore/query_builders/event_subscription_positions_filtering.rbs +15 -0
- data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +9 -51
- data/sig/pg_eventstore/query_builders/events_global_index_filtering.rbs +68 -0
- data/sig/pg_eventstore/query_builders/filters/collection.rbs +84 -0
- data/sig/pg_eventstore/query_builders/filters/event_type_filter.rbs +19 -0
- data/sig/pg_eventstore/query_builders/filters/filter_row.rbs +21 -0
- data/sig/pg_eventstore/query_builders/filters/marker_filter.rbs +13 -0
- data/sig/pg_eventstore/query_builders/filters/marker_filter_row.rbs +19 -0
- data/sig/pg_eventstore/query_builders/filters/stream_filter.rbs +24 -0
- data/sig/pg_eventstore/query_builders/index_based_events_filtering.rbs +60 -0
- data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +29 -10
- data/sig/pg_eventstore/query_builders/read_cursor/stream_cursor.rbs +47 -0
- data/sig/pg_eventstore/query_builders/streams_global_index_filtering.rbs +27 -0
- data/sig/pg_eventstore/query_builders/subscription_events_filtering.rbs +26 -0
- data/sig/pg_eventstore/query_strategy/async.rbs +17 -0
- data/sig/pg_eventstore/query_strategy/foreground.rbs +11 -0
- data/sig/pg_eventstore/query_strategy.rbs +7 -0
- data/sig/pg_eventstore/raw_event.rbs +20 -0
- data/sig/pg_eventstore/sql_builder.rbs +32 -10
- data/sig/pg_eventstore/stream.rbs +10 -18
- data/sig/pg_eventstore/stream_global_index.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +5 -4
- data/sig/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rbs +9 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +17 -12
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +17 -6
- data/sig/pg_eventstore/subscriptions/event_subscription_position_queries.rbs +30 -0
- data/sig/pg_eventstore/subscriptions/events_processor.rbs +12 -5
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/multiple.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/replica.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/single.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/events_subscription_position_worker.rbs +28 -0
- data/sig/pg_eventstore/subscriptions/queries/subscription_queries.rbs +7 -39
- data/sig/pg_eventstore/subscriptions/replica_subscription_handler.rbs +33 -0
- data/sig/pg_eventstore/subscriptions/replica_subscription_runner.rbs +4 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/collection.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/index_read_strategy.rbs +23 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/replication_strategy.rbs +23 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +13 -10
- data/sig/pg_eventstore/subscriptions/subscription_handler_performance.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +13 -2
- data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +1 -8
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +4 -6
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +29 -43
- data/sig/pg_eventstore/synchronized_array.rbs +4 -0
- data/sig/pg_eventstore/utils.rbs +10 -11
- data/sig/pg_eventstore/web/application.rbs +13 -3
- data/sig/pg_eventstore/web/paginator/base_collection.rbs +2 -0
- data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/events_collection.rbs +11 -11
- data/sig/pg_eventstore/web/paginator/helpers.rbs +6 -15
- data/sig/pg_eventstore/web/paginator/markers_collection.rbs +13 -0
- data/sig/pg_eventstore/web/paginator/stream_contexts_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/stream_names_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/streams_collection.rbs +30 -0
- data/sig/pg_eventstore.rbs +8 -26
- metadata +175 -20
- data/Dockerfile +0 -3
- data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +0 -69
- data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +0 -31
- data/lib/pg_eventstore/subscriptions/queries/subscription_service_queries.rb +0 -78
- data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +0 -15
- data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +0 -16
- data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +0 -8
- data/sig/pg_eventstore/subscriptions/queries/subscription_service_queries.rbs +0 -19
- data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
- /data/sig/interfaces/{_raw_event_handler.rbs → raw_event_handler.rbs} +0 -0
data/docs/how_it_works.md
CHANGED
|
@@ -2,13 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Database architecture
|
|
4
4
|
|
|
5
|
-
The database is designed specifically for
|
|
5
|
+
The database is designed specifically for Event Sourcing alongside Domain-Driven Design. Internally, it uses a
|
|
6
|
+
Snowflake-based schema which consists of multiple tables:
|
|
7
|
+
- `events_global_index` is the primary event index fact table
|
|
8
|
+
- `event_markers_index` is a marker-specific factless bridge/index table, and is used as the primary access path for
|
|
9
|
+
marker-based event queries
|
|
10
|
+
- `streams_global_index` is normalized stream-level dimension/reference table for `events_global_index`
|
|
11
|
+
- `partitions` is a dictionary table for physical event partitions
|
|
12
|
+
- `events` is a table that holds all information about an event - its payload, metadata, markers, etc
|
|
13
|
+
|
|
14
|
+
`events` table is additionally partitioned in next way:
|
|
6
15
|
|
|
7
16
|
- For each `Stream#context` there is a subpartition of `events` table. Those tables have `contexts_` prefix.
|
|
8
17
|
- For each `Stream#stream_name` there is a subpartition of `contexts_` table. Those tables have `stream_names_` prefix.
|
|
9
18
|
- For each `Event#type` there is a subpartition of `stream_names_` table. Those tables have `event_types_` prefix.
|
|
10
19
|
|
|
11
|
-
To implement partitions - Declarative Partitioning is used.
|
|
20
|
+
To implement partitions - Declarative Partitioning feature is used. While the implementation scales well with the number
|
|
21
|
+
of partitions - it is recommended your application pre-defines all combinations of contexts, stream names and event
|
|
22
|
+
types. More about PostgreSQL partitions is [here](https://www.postgresql.org/docs/current/ddl-partitioning.html).
|
|
12
23
|
|
|
13
24
|
So, let's say you want to publish next event:
|
|
14
25
|
|
|
@@ -20,11 +31,15 @@ PgEventstore.client.append_to_stream(stream, event)
|
|
|
20
31
|
|
|
21
32
|
To actually create `events` record next partitions will be created:
|
|
22
33
|
|
|
23
|
-
- `contexts_81820a` table which is a subpartition of `events` table. It is needed to handle all events which comes to
|
|
24
|
-
|
|
25
|
-
- `
|
|
34
|
+
- `contexts_81820a` table which is a subpartition of `events` table. It is needed to handle all events which comes to
|
|
35
|
+
`"SomeCtx"` context
|
|
36
|
+
- `stream_names_ecb803` table which is a subpartition of `contexts_81820a` table. It is needed to handle all events
|
|
37
|
+
which comes to `"SomeStream"` stream name of `"SomeCtx"` context
|
|
38
|
+
- `event_types_aeadd5` table which is a subpartition of `stream_names_ecb803` table. It is needed to handle all events
|
|
39
|
+
which have `"SomethingChanged"` event type of `"SomeStream"` stream name of `"SomeCtx"` context
|
|
26
40
|
|
|
27
|
-
You can check all partitions and associated with them contexts, stream names and event types by querying
|
|
41
|
+
You can check all partitions and associated with them contexts, stream names and event types by querying special service
|
|
42
|
+
table called `partitions`. Example(based on the sample above):
|
|
28
43
|
|
|
29
44
|
```ruby
|
|
30
45
|
PgEventstore.connection.with do |conn|
|
|
@@ -38,26 +53,47 @@ end.to_a
|
|
|
38
53
|
|
|
39
54
|
### PostgreSQL settings
|
|
40
55
|
|
|
41
|
-
The more partitions you have, the more locks are required for operations that affect multiple partitions.
|
|
56
|
+
The more partitions you have, the more locks are required for operations that affect multiple partitions. It mainly
|
|
57
|
+
concerns the case when you involve many different event types when using `Client#multiple`. It may lead to the next
|
|
58
|
+
error:
|
|
42
59
|
|
|
43
60
|
```
|
|
44
61
|
ERROR: out of shared memory (PG::OutOfMemory)
|
|
45
|
-
HINT: You might need to increase
|
|
62
|
+
HINT: You might need to increase max_pred_locks_per_transaction.
|
|
46
63
|
```
|
|
47
64
|
|
|
48
|
-
PostgreSQL suggests to increase the `
|
|
65
|
+
PostgreSQL suggests to increase the `max_pred_locks_per_transaction`(the description of it
|
|
66
|
+
is [here](https://www.postgresql.org/docs/current/runtime-config-locks.html)). The default value is `64`. In case you
|
|
67
|
+
have several thousands of partitions - you may want to set it to `128` or even to `256`.
|
|
68
|
+
|
|
69
|
+
You may also face similar error which refers to `max_locks_per_transaction` setting:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
PG::OutOfMemory: ERROR: out of shared memory (PG::OutOfMemory)
|
|
73
|
+
HINT: You might need to increase "max_locks_per_transaction".
|
|
74
|
+
```
|
|
49
75
|
|
|
50
|
-
|
|
76
|
+
The reason of it to appear is the same - too many objects(partition tables, indexes, etc) are involved in a single
|
|
77
|
+
transaction.
|
|
51
78
|
|
|
52
79
|
## Appending events and multiple commands
|
|
53
80
|
|
|
54
|
-
You may want to get familiar with [Appending events](appending_events.md) and [multiple commands](multiple_commands.md)
|
|
81
|
+
You may want to get familiar with [Appending events](appending_events.md) and [multiple commands](multiple_commands.md)
|
|
82
|
+
first.
|
|
55
83
|
|
|
56
|
-
`pg_eventstore` internally uses `Serializable`
|
|
84
|
+
`pg_eventstore` internally uses `Serializable` and `Repeatable read` transaction isolation levels depending on the API
|
|
85
|
+
method(more about different transaction isolation levels in PostgreSQL is [here](https://www.postgresql.org/docs/current/transaction-iso.html)). On practice this means that any
|
|
86
|
+
transaction may fail with serialization error, and the common approach is to restart this transaction. For ruby this
|
|
87
|
+
means re-execution of the block of code. Which is why there is a warning regarding potential code re-execution when
|
|
88
|
+
using `#multiple`. However, current implementation allows to limit 99% of retries to the manipulations with one stream.
|
|
89
|
+
For example, when two parallel processes changing the same stream. If different streams are being changed at the same
|
|
90
|
+
time - it is less likely it would perform retry.
|
|
57
91
|
|
|
58
92
|
Examples:
|
|
59
93
|
|
|
60
|
-
- if "process 1" and "process 2" perform the append command at the same time - one of the append commands will be
|
|
94
|
+
- if "process 1" and "process 2" perform the append command at the same time - one of the append commands will be
|
|
95
|
+
retried:
|
|
96
|
+
|
|
61
97
|
```ruby
|
|
62
98
|
# process 1
|
|
63
99
|
stream = PgEventstore::Stream.new(context: 'MyCtx', stream_name: 'MyStream', stream_id: '1')
|
|
@@ -70,7 +106,8 @@ event = PgEventstore::Event.new(type: 'SomethingElseChanged', data: { baz: :bar
|
|
|
70
106
|
PgEventstore.client.append_to_stream(stream, event)
|
|
71
107
|
```
|
|
72
108
|
|
|
73
|
-
- if "process 1" performs multiple commands at the same time "process 2" performs append command which involves the same
|
|
109
|
+
- if "process 1" performs multiple commands at the same time "process 2" performs append command which involves the same
|
|
110
|
+
stream from "process 1" - either block of `#multiple` or `#append_to_stream` will be retried:
|
|
74
111
|
|
|
75
112
|
```ruby
|
|
76
113
|
# process 1
|
|
@@ -88,6 +125,10 @@ event = PgEventstore::Event.new(type: 'SomethingChanged', data: { foo: :bar })
|
|
|
88
125
|
PgEventstore.client.append_to_stream(stream2, event)
|
|
89
126
|
```
|
|
90
127
|
|
|
91
|
-
Retries also concern your potential implementation of [middlewares](writing_middleware.md). For example,
|
|
128
|
+
Retries also concern your potential implementation of [middlewares](writing_middleware.md). For example,
|
|
129
|
+
`YourAwesomeMiddleware#serialize` can be executed several times when appending an event. This is especially important
|
|
130
|
+
when you involve your microservices here - they can receive the same payload several times.
|
|
92
131
|
|
|
93
|
-
Conclusion. When developing using `pg_eventstore` - always keep in mind that some parts of your implementation can be
|
|
132
|
+
Conclusion. When developing using `pg_eventstore` - always keep in mind that some parts of your implementation can be
|
|
133
|
+
executed several times before successfully publishing an event, or event when reading events(`#deserializa` middleware
|
|
134
|
+
method) if you perform reading withing `#multiple` block. Thus, make sure your middlewares are idempotent.
|
data/docs/linking_events.md
CHANGED
|
@@ -54,6 +54,7 @@ Example:
|
|
|
54
54
|
|
|
55
55
|
```ruby
|
|
56
56
|
require 'securerandom'
|
|
57
|
+
|
|
57
58
|
class SomethingHappened < PgEventstore::Event
|
|
58
59
|
end
|
|
59
60
|
|
|
@@ -61,7 +62,7 @@ event1 = SomethingHappened.new
|
|
|
61
62
|
event2 = SomethingHappened.new
|
|
62
63
|
|
|
63
64
|
events_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeStream', stream_id: '1')
|
|
64
|
-
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: SecureRandom.
|
|
65
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: SecureRandom.uuid_v7)
|
|
65
66
|
|
|
66
67
|
event1, event2 = PgEventstore.client.append_to_stream(events_stream, [event1, event2])
|
|
67
68
|
|
data/docs/maintenance.md
CHANGED
|
@@ -9,14 +9,16 @@ stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyStream', st
|
|
|
9
9
|
PgEventstore.client.append_to_stream(stream, PgEventstore::Event.new)
|
|
10
10
|
PgEventstore.maintenance.delete_stream(stream) # => true
|
|
11
11
|
```
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
deletes all events in the given stream. If the given stream does not exist - `false` is returned:
|
|
13
14
|
|
|
14
15
|
```ruby
|
|
15
16
|
stream = PgEventstore::Stream.new(context: 'NonExistingCtx', stream_name: 'NonExistingStream', stream_id: '1')
|
|
16
17
|
PgEventstore.maintenance.delete_stream(stream) # => false
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
**Please note that this operation does not automatically delete links to deleted events - you have to do delete them
|
|
20
|
+
**Please note that this operation does not automatically delete links to deleted events - you have to do delete them
|
|
21
|
+
separately.**
|
|
20
22
|
|
|
21
23
|
## Delete an event
|
|
22
24
|
|
|
@@ -27,6 +29,7 @@ PgEventstore.client.append_to_stream(stream, PgEventstore::Event.new)
|
|
|
27
29
|
event = PgEventstore.client.read(stream, options: { max_count: 1 }).first
|
|
28
30
|
PgEventstore.maintenance.delete_event(event) # => true
|
|
29
31
|
```
|
|
32
|
+
|
|
30
33
|
deletes the given event. If the given event does not exist - `false` is returned:
|
|
31
34
|
|
|
32
35
|
```ruby
|
|
@@ -38,14 +41,20 @@ PgEventstore.maintenance.delete_event(event) # => true
|
|
|
38
41
|
PgEventstore.maintenance.delete_event(event) # => false
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
**Please note that this operation does not automatically delete links to the deleted event - you have to do delete them
|
|
44
|
+
**Please note that this operation does not automatically delete links to the deleted event - you have to do delete them
|
|
45
|
+
separately.**
|
|
42
46
|
|
|
43
47
|
### Deleting an event in a large stream
|
|
44
48
|
|
|
45
|
-
There can be a situation when you would like to delete an event in a large stream. If an event is in the position where
|
|
49
|
+
There can be a situation when you would like to delete an event in a large stream. If an event is in the position where
|
|
50
|
+
there are 1000+ events after it in a stream - a `PgEventstore::TooManyRecordsToLockError` error will be raised. This is
|
|
51
|
+
because in addition to removing the event, we need to adjust the rest of the stream by updating the `#stream_revision`
|
|
52
|
+
of all events that come after this event. To overcome this limitation - you can provide `force: true` flag:
|
|
46
53
|
|
|
47
54
|
```ruby
|
|
48
55
|
PgEventstore.maintenance.delete_event(event, force: true)
|
|
49
56
|
```
|
|
50
57
|
|
|
51
|
-
Because the update operation can lock thousands of events - `#delete_event` can be slow and can slow down other
|
|
58
|
+
Because the update operation can lock thousands of events - `#delete_event` can be slow and can slow down other
|
|
59
|
+
processes which insert events in the same stream at the same moment. **Thus, it is recommended to avoid
|
|
60
|
+
using `#delete_event` in your business logic.**
|
data/docs/multiple_commands.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# Multiple commands
|
|
2
2
|
|
|
3
|
-
`pg_eventstore` implements the `#multiple` method to allow you to make several different commands atomic. Internally it
|
|
3
|
+
`pg_eventstore` implements the `#multiple` method to allow you to make several different commands atomic. Internally it
|
|
4
|
+
executes the given block within PostgreSQL transaction. Example:
|
|
4
5
|
|
|
5
6
|
```ruby
|
|
6
7
|
PgEventstore.client.multiple do
|
|
7
8
|
unless PgEventstore.client.read(stream3, options: { max_count: 1, direction: 'Backwards' }).last&.type == 'Removed'
|
|
8
9
|
PgEventstore.client.append_to_stream(stream1, event1)
|
|
9
10
|
PgEventstore.client.append_to_stream(stream2, event2)
|
|
10
|
-
end
|
|
11
|
+
end
|
|
11
12
|
end
|
|
12
13
|
```
|
|
13
14
|
|
|
14
|
-
Optionally, you can provide `read_only: true` argument to run the transaction in read-only mode. This, however, will
|
|
15
|
+
Optionally, you can provide `read_only: true` argument to run the transaction in read-only mode. This, however, will
|
|
16
|
+
raise `PG::ReadOnlySqlTransaction` exception if any mutating query is executed within the block. Example:
|
|
15
17
|
|
|
16
18
|
```ruby
|
|
17
19
|
# Good
|
|
@@ -25,10 +27,15 @@ PgEventstore.client.multiple(read_only: true) do
|
|
|
25
27
|
end
|
|
26
28
|
```
|
|
27
29
|
|
|
30
|
+
All commands inside a `multiple` block either all succeed or all fail. This allows you to easily implement complex
|
|
31
|
+
business rules. However, it comes with a price of performance. The more you put in a single block, the higher the chance
|
|
32
|
+
it will have conflicts with other commands run in parallel, increasing overall time to complete. **Because of this
|
|
33
|
+
performance implications, do not put more events than needed in a `multple` block.** You may still want to use it though
|
|
34
|
+
as it could simplify your implementation.
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
**Please take into account that due to concurrency of parallel commands, a block of code may be re-run several times
|
|
37
|
+
before succeeding.** So, if you put any piece of code besides `pg_evenstore`'s commands - make sure it is ready for
|
|
38
|
+
re-runs. A good and a bad examples:
|
|
32
39
|
|
|
33
40
|
## Bad
|
|
34
41
|
|
|
@@ -37,7 +44,7 @@ PgEventstore.client.multiple do
|
|
|
37
44
|
old_email = PgEventstore.client.read(user_stream, options: { filter: { event_types: ['UserEmailChanged'] }, max_count: 1, direction: 'Backwards' }).first&.data&.dig('email')
|
|
38
45
|
# Email hasn't changed - prevent publishing unnecessary changes
|
|
39
46
|
next if old_email == user.email
|
|
40
|
-
|
|
47
|
+
|
|
41
48
|
PgEventstore.client.append_to_stream(user_stream, UserEmailChanged.new(data: { email: user.email }))
|
|
42
49
|
# This is the mistake. UserMailer.notify_email_changed may be triggered several times
|
|
43
50
|
UserMailer.notify_email_changed(user.id, old_email: old_email, new_email: user.email).deliver_later
|
|
@@ -62,4 +69,6 @@ UserMailer.notify_email_changed(user.id, old_email: old_email, new_email: user.e
|
|
|
62
69
|
|
|
63
70
|
## Side effect of internal implementation
|
|
64
71
|
|
|
65
|
-
Please note that when publishing an event with a type as part of a `multiple` block that does not yet exist in the
|
|
72
|
+
Please note that when publishing an event with a type as part of a `multiple` block that does not yet exist in the
|
|
73
|
+
database, the block will run twice as the first attempt to publish will always fail due to the way `append_to_stream` is
|
|
74
|
+
implemented. Consider this when writing expectations in your tests for example.
|
data/docs/reading_events.md
CHANGED
|
@@ -12,7 +12,9 @@ PgEventstore.client.read(stream)
|
|
|
12
12
|
|
|
13
13
|
### max_count
|
|
14
14
|
|
|
15
|
-
You can provide the `:max_count` option. This option determines how many records to return in a response. Default is
|
|
15
|
+
You can provide the `:max_count` option. This option determines how many records to return in a response. Default is
|
|
16
|
+
`1000` and it can be changed with the `:max_count` configuration setting (see [**"Configuration"**](configuration.md)
|
|
17
|
+
chapter):
|
|
16
18
|
|
|
17
19
|
```ruby
|
|
18
20
|
stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
|
@@ -21,7 +23,8 @@ PgEventstore.client.read(stream, options: { max_count: 100 })
|
|
|
21
23
|
|
|
22
24
|
### resolve_link_tos
|
|
23
25
|
|
|
24
|
-
When reading streams with projected events (links to other events) you can chose to resolve those links by setting
|
|
26
|
+
When reading streams with projected events (links to other events) you can chose to resolve those links by setting
|
|
27
|
+
`resolve_link_tos` to `true`, returning the original event instead of the "link" event.
|
|
25
28
|
|
|
26
29
|
```ruby
|
|
27
30
|
stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
|
@@ -39,7 +42,8 @@ PgEventstore.client.read(stream, options: { from_revision: 2 })
|
|
|
39
42
|
|
|
40
43
|
### direction
|
|
41
44
|
|
|
42
|
-
As well as being able to read a stream forwards you can also go backwards. This can be achieved by providing the
|
|
45
|
+
As well as being able to read a stream forwards you can also go backwards. This can be achieved by providing the
|
|
46
|
+
`:direction` option:
|
|
43
47
|
|
|
44
48
|
```ruby
|
|
45
49
|
stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
|
@@ -62,7 +66,8 @@ end
|
|
|
62
66
|
|
|
63
67
|
## Reading from the "all" stream
|
|
64
68
|
|
|
65
|
-
"all" stream definition means that you don't scope your events when reading them from the database. To get the "all"
|
|
69
|
+
"all" stream definition means that you don't scope your events when reading them from the database. To get the "all"
|
|
70
|
+
`PgEventstore::Stream` instance you have to call the `all_stream` method:
|
|
66
71
|
|
|
67
72
|
```ruby
|
|
68
73
|
PgEventstore::Stream.all_stream
|
|
@@ -74,24 +79,18 @@ Now you can use it to read from the "all" stream:
|
|
|
74
79
|
PgEventstore.client.read(PgEventstore::Stream.all_stream)
|
|
75
80
|
```
|
|
76
81
|
|
|
77
|
-
You can read from a specific position of the "all" stream. This is very similar to reading from a specific revision of a
|
|
82
|
+
You can read from a specific position of the "all" stream. This is very similar to reading from a specific revision of a
|
|
83
|
+
specific stream, but instead of the `:from_revision` option you have to provide the `:from_position` option:
|
|
78
84
|
|
|
79
85
|
```ruby
|
|
80
86
|
PgEventstore.client.read(PgEventstore::Stream.all_stream, options: { from_position: 9023, direction: 'Backwards' })
|
|
81
87
|
```
|
|
82
88
|
|
|
83
|
-
## Reading from "$streams" system stream
|
|
84
|
-
|
|
85
|
-
`"$streams"` is a special stream which consists of events with `stream_revision == 0`. This allows you to effectively query all streams. Example:
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
stream = PgEventstore::Stream.system_stream("$streams")
|
|
89
|
-
PgEventstore.client.read(stream).map(&:stream) # => array of unique streams
|
|
90
|
-
```
|
|
91
|
-
|
|
92
89
|
## Middlewares
|
|
93
90
|
|
|
94
|
-
If you would like to skip some of your registered middlewares from processing events after they being read from a
|
|
91
|
+
If you would like to skip some of your registered middlewares from processing events after they being read from a
|
|
92
|
+
stream - you should use the `:middlewares` argument which allows you to override the list of middlewares you would like
|
|
93
|
+
to use.
|
|
95
94
|
|
|
96
95
|
Let's say you have these registered middlewares:
|
|
97
96
|
|
|
@@ -101,7 +100,8 @@ PgEventstore.configure do |config|
|
|
|
101
100
|
end
|
|
102
101
|
```
|
|
103
102
|
|
|
104
|
-
And you want to skip `FooMiddleware` and `BazMiddleware`. You simply have to provide an array of corresponding
|
|
103
|
+
And you want to skip `FooMiddleware` and `BazMiddleware`. You simply have to provide an array of corresponding
|
|
104
|
+
middleware keys you would like to use:
|
|
105
105
|
|
|
106
106
|
```ruby
|
|
107
107
|
PgEventstore.client.read(PgEventstore::Stream.all_stream, middlewares: %i[bar])
|
|
@@ -111,78 +111,239 @@ See [Writing middleware](writing_middleware.md) chapter for info about what is m
|
|
|
111
111
|
|
|
112
112
|
## Filtering
|
|
113
113
|
|
|
114
|
-
When reading events, you can additionally filter the result. Available attributes for filtering depend on the type of
|
|
114
|
+
When reading events, you can additionally filter the result. Available attributes for filtering depend on the type of
|
|
115
|
+
stream you are reading from. Reading from the "all" stream supports filters by stream attributes, event types and event
|
|
116
|
+
markers. Reading from a specific stream supports filters by event types and event markers only.
|
|
115
117
|
|
|
116
118
|
### Specific stream filtering
|
|
117
119
|
|
|
118
|
-
Filtering events by their types
|
|
120
|
+
#### Filtering events by their types
|
|
119
121
|
|
|
122
|
+
Read `Foo` and `Bar` events from the given stream:
|
|
120
123
|
```ruby
|
|
121
|
-
stream = PgEventstore::Stream.new(context: '
|
|
124
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
122
125
|
PgEventstore.client.read(stream, options: { filter: { event_types: %w[Foo Bar] } })
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
Also, you can provide event type values using `{ type: 'MyType' }` syntax. Example:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
132
|
+
PgEventstore.client.read(stream, options: { filter: { event_types: [{ type: 'Foo' }, { type: 'Bar' }] } })
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Filtering events by markers
|
|
136
|
+
|
|
137
|
+
Read events, marked as either `'foo'` **or** `'bar'`:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
141
|
+
PgEventstore.client.read(stream, options: { filter: { event_types: [{ markers: %w[foo bar] }] } })
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
##### Limitations
|
|
145
|
+
|
|
146
|
+
Please note - there is no functional to filter events that are marked with both `'foo'` **and** `'bar'` markers. If you
|
|
147
|
+
need a combination of both - you have to create another event marker - for example `'foobar'` - and additionally mark
|
|
148
|
+
events using it that have both - `'foo'` and `'bar'` markers. Example:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
event1 = PgEventstore::Event.new(markers: ['foo'])
|
|
152
|
+
event2 = PgEventstore::Event.new(markers: ['bar'])
|
|
153
|
+
event3 = PgEventstore::Event.new(markers: %w[foo bar foobar])
|
|
154
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '3')
|
|
155
|
+
PgEventstore.client.append_to_stream(stream, [event1, event2, event3])
|
|
156
|
+
# Returns all three events
|
|
157
|
+
PgEventstore.client.read(stream, options: { filter: { event_types: [{ markers: %w[foo bar] }] } })
|
|
158
|
+
# Returns only the third event
|
|
159
|
+
PgEventstore.client.read(stream, options: { filter: { event_types: [{ markers: ['foobar'] }] } })
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Filtering events by event types with markers
|
|
163
|
+
|
|
164
|
+
You can provide per event type markers list. The effect of this is that each event type is additionally restricted with
|
|
165
|
+
the set of markers. In next example events with `'Foo'` type is only returned when they are additionally marked with
|
|
166
|
+
`'foo'` or `'bar'` markers:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
170
|
+
PgEventstore.client.read(stream, options: { filter: { event_types: [{ type: 'Foo', markers: %w[foo bar] }] } })
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
You can also combine event types filter, event type with markers filter and markers filter. Next query returns all
|
|
174
|
+
events that are either:
|
|
175
|
+
- marked with `'baz'`
|
|
176
|
+
- or have type `'Foo'` that are marked with either `'foo'` or `'bar'`
|
|
177
|
+
- or have type `'Bar'`
|
|
178
|
+
```ruby
|
|
179
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
180
|
+
PgEventstore.client.read(
|
|
181
|
+
stream,
|
|
182
|
+
options: {
|
|
183
|
+
filter: {
|
|
184
|
+
event_types: [
|
|
185
|
+
{ markers: ['baz'] },
|
|
186
|
+
{ type: 'Foo', markers: %w[foo bar] },
|
|
187
|
+
{ type: 'Bar' }
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
125
194
|
### "all" stream filtering
|
|
126
195
|
|
|
127
|
-
|
|
196
|
+
#### Limitations
|
|
197
|
+
|
|
198
|
+
There is a limitation on a set of stream attributes that can be used when filtering an "all" stream result.
|
|
199
|
+
Available combinations:
|
|
128
200
|
|
|
129
201
|
- `:context`
|
|
130
202
|
- `:context` and `:stream_name`
|
|
131
203
|
- `:context`, `:stream_name` and `:stream_id`
|
|
132
204
|
|
|
133
|
-
|
|
205
|
+
So this is not allowed(filter is simply ignored):
|
|
134
206
|
|
|
207
|
+
```ruby
|
|
208
|
+
PgEventstore.client.read(
|
|
209
|
+
PgEventstore::Stream.all_stream,
|
|
210
|
+
options: { filter: { streams: [{ stream_name: 'FooCtx' }] } }
|
|
211
|
+
) # => Array of events like if there is no filter at all
|
|
212
|
+
PgEventstore.client.read(
|
|
213
|
+
PgEventstore::Stream.all_stream,
|
|
214
|
+
options: { filter: { streams: [{ stream_name: 'FooCtx', stream_id: '1' }] } }
|
|
215
|
+
) # => Array of events like if there is no filter at all
|
|
216
|
+
PgEventstore.client.read(
|
|
217
|
+
PgEventstore::Stream.all_stream,
|
|
218
|
+
options: { filter: { streams: [{ stream_id: '1' }] } }
|
|
219
|
+
) # => Array of events like if there is no filter at all
|
|
220
|
+
```
|
|
135
221
|
|
|
136
|
-
|
|
222
|
+
but this is allowed:
|
|
137
223
|
|
|
138
224
|
```ruby
|
|
139
|
-
PgEventstore.client.read(
|
|
225
|
+
PgEventstore.client.read(
|
|
226
|
+
PgEventstore::Stream.all_stream,
|
|
227
|
+
options: { filter: { streams: [{ context: 'FooCtx' }] } }
|
|
228
|
+
)
|
|
229
|
+
PgEventstore.client.read(
|
|
230
|
+
PgEventstore::Stream.all_stream,
|
|
231
|
+
options: { filter: { streams: [{ context: 'FooCtx', stream_name: 'Foo' }] } }
|
|
232
|
+
)
|
|
233
|
+
PgEventstore.client.read(
|
|
234
|
+
PgEventstore::Stream.all_stream,
|
|
235
|
+
options: { filter: { streams: [{ context: 'FooCtx', stream_name: 'Foo', stream_id: '1' }] } }
|
|
236
|
+
)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Also, you can't filter by markers only without a specific event type with only `:context` or `:context` and
|
|
240
|
+
`:stream_name`. So, this is not allowed:
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
PgEventstore.client.read(
|
|
244
|
+
PgEventstore::Stream.all_stream,
|
|
245
|
+
options: { filter: { streams: [{ context: 'FooCtx' }], event_types: [{ markers: ['foo'] }] } }
|
|
246
|
+
) # => exception
|
|
247
|
+
PgEventstore.client.read(
|
|
248
|
+
PgEventstore::Stream.all_stream,
|
|
249
|
+
options: { filter: { streams: [{ context: 'FooCtx', stream_name: 'Foo' }], event_types: [{ markers: ['foo'] }] } }
|
|
250
|
+
) # => exception
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
but this is allowed:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
PgEventstore.client.read(
|
|
257
|
+
PgEventstore::Stream.all_stream,
|
|
258
|
+
options: { filter: { streams: [{ context: 'FooCtx' }], event_types: [{ markers: ['foo'], type: 'Foo' }] } }
|
|
259
|
+
) # => Array of events
|
|
140
260
|
```
|
|
141
261
|
|
|
142
|
-
Filtering events by
|
|
262
|
+
#### Filtering events by type and/or markers
|
|
263
|
+
|
|
264
|
+
Please refer to the documentation of [Specific stream filtering](reading_events.md#reading-from-a-specific-stream) for
|
|
265
|
+
more examples how to filter by event types and/or markers. The only thing which changes is stream argument. To filter
|
|
266
|
+
"all" stream - you have to provide `PgEventstore::Stream.all_stream` instead of specific stream. Example:
|
|
143
267
|
|
|
144
268
|
```ruby
|
|
145
|
-
PgEventstore.client.read(PgEventstore::Stream.all_stream, options: { filter: {
|
|
269
|
+
PgEventstore.client.read(PgEventstore::Stream.all_stream, options: { filter: { event_types: %w[Foo Bar] } })
|
|
146
270
|
```
|
|
147
271
|
|
|
148
|
-
Filtering events by context
|
|
272
|
+
#### Filtering events by context
|
|
149
273
|
|
|
150
274
|
```ruby
|
|
151
|
-
PgEventstore.client.read(
|
|
275
|
+
PgEventstore.client.read(
|
|
276
|
+
PgEventstore::Stream.all_stream,
|
|
277
|
+
options: { filter: { streams: [{ context: 'MyAwesomeContext' }] } }
|
|
278
|
+
)
|
|
152
279
|
```
|
|
153
280
|
|
|
154
|
-
Filtering events by
|
|
281
|
+
#### Filtering events by context and name
|
|
155
282
|
|
|
156
283
|
```ruby
|
|
157
|
-
PgEventstore.client.read(
|
|
284
|
+
PgEventstore.client.read(
|
|
285
|
+
PgEventstore::Stream.all_stream,
|
|
286
|
+
options: { filter: { streams: [{ context: 'MyAwesomeContext', stream_name: 'User' }] } }
|
|
287
|
+
)
|
|
158
288
|
```
|
|
159
289
|
|
|
160
|
-
|
|
290
|
+
#### Filtering events by stream context, stream name and stream id
|
|
161
291
|
|
|
162
292
|
```ruby
|
|
163
|
-
PgEventstore.client.read(
|
|
293
|
+
PgEventstore.client.read(
|
|
294
|
+
PgEventstore::Stream.all_stream,
|
|
295
|
+
options: {
|
|
296
|
+
filter: {
|
|
297
|
+
streams: [{ context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae' }]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
)
|
|
164
301
|
```
|
|
165
302
|
|
|
166
|
-
You can
|
|
303
|
+
You can provide several sets of stream's attributes. The result will be a union of events that match those criteria. For
|
|
304
|
+
example, next query will return all events that belong to streams with `AnotherContext` context and all events that
|
|
305
|
+
belong to streams with `MyAwesomeContext` context and `User` stream name:
|
|
167
306
|
|
|
168
307
|
```ruby
|
|
169
|
-
PgEventstore.client.read(
|
|
308
|
+
PgEventstore.client.read(
|
|
309
|
+
PgEventstore::Stream.all_stream,
|
|
310
|
+
options: {
|
|
311
|
+
filter: {
|
|
312
|
+
streams: [{ context: 'AnotherContext' }, { context: 'MyAwesomeContext', stream_name: 'User' }]
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
)
|
|
170
316
|
```
|
|
171
317
|
|
|
172
|
-
|
|
318
|
+
#### Filtering events by stream attributes, event types and/or markers
|
|
319
|
+
|
|
320
|
+
Please refer to the documentation of [Specific stream filtering](reading_events.md#reading-from-a-specific-stream) for
|
|
321
|
+
more examples how to filter by event types and/or markers.
|
|
322
|
+
|
|
323
|
+
You can also mix filtering by stream attributes, event types and markers. The result is union of combinations of
|
|
324
|
+
each stream filter and event types filter. For example, next query returns events with type either `Foo`, marked
|
|
325
|
+
by `'foo'` marker, or `Bar` and belong to streams from `FooCtx` or `BarCtx` contexts:
|
|
173
326
|
|
|
174
|
-
When reading from `"$streams"` same rules apply as when reading from "all" stream. For example, read all streams which have `context == "MyAwesomeContext"` and start from events with event type either `"Foo"` or `"Bar"`:
|
|
175
327
|
```ruby
|
|
176
|
-
PgEventstore.client.read(
|
|
328
|
+
PgEventstore.client.read(
|
|
329
|
+
PgEventstore::Stream.all_stream,
|
|
330
|
+
options: {
|
|
331
|
+
filter: {
|
|
332
|
+
streams: [{ context: 'FooCtx' }, { context: 'BarCtx' }],
|
|
333
|
+
event_types: [{ type: 'Foo', markers: ['foo'] }, { type: 'Bar' }]
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
)
|
|
177
337
|
```
|
|
178
338
|
|
|
179
339
|
## Pagination
|
|
180
340
|
|
|
181
|
-
You can use `#read_paginated` to iterate over all (filtered) events. It yields each batch of records that was found
|
|
341
|
+
You can use `#read_paginated` to iterate over all (filtered) events. It yields each batch of records that was found
|
|
342
|
+
according to the filter options:
|
|
182
343
|
|
|
183
344
|
```ruby
|
|
184
345
|
# Read from the specific stream
|
|
185
|
-
stream = PgEventstore::Stream.new(context: '
|
|
346
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
186
347
|
PgEventstore.client.read_paginated(stream).each do |events|
|
|
187
348
|
events.each do |event|
|
|
188
349
|
# iterate through events
|
|
@@ -201,7 +362,7 @@ Options are the same as for `#read` method. Several examples:
|
|
|
201
362
|
|
|
202
363
|
```ruby
|
|
203
364
|
# Read "Foo" events only from the specific stream
|
|
204
|
-
stream = PgEventstore::Stream.new(context: '
|
|
365
|
+
stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|
|
205
366
|
PgEventstore.client.read_paginated(stream, options: { filter: { event_types: ['Foo'] } }).each do |events|
|
|
206
367
|
events.each do |event|
|
|
207
368
|
# iterate through events
|
|
@@ -223,7 +384,7 @@ PgEventstore.client.read_paginated(PgEventstore::Stream.all_stream, options: { m
|
|
|
223
384
|
end
|
|
224
385
|
|
|
225
386
|
# Reading from projection stream
|
|
226
|
-
projection_stream = PgEventstore::Stream.new(context: '
|
|
387
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'FooProjection', stream_id: '1')
|
|
227
388
|
PgEventstore.client.read_paginated(projection_stream, options: { resolve_link_tos: true }).each do |events|
|
|
228
389
|
events.each do |event|
|
|
229
390
|
# iterate through events
|
|
@@ -246,7 +407,8 @@ PgEventstore.client.append_to_stream(stream, [event1, event2, event3])
|
|
|
246
407
|
PgEventstore.client.read_grouped(stream) # => returns event1 and event3
|
|
247
408
|
```
|
|
248
409
|
|
|
249
|
-
API is very similar to the API of `#read`, but it ignores `:max_count` options as the result is always returns a set of
|
|
410
|
+
API is very similar to the API of `#read`, but it ignores `:max_count` options as the result is always returns a set of
|
|
411
|
+
event types in your stream.
|
|
250
412
|
|
|
251
413
|
Reading most recent events:
|
|
252
414
|
|
|
@@ -307,11 +469,14 @@ PgEventstore.client.read_grouped(PgEventstore::Stream.all_stream, options: { dir
|
|
|
307
469
|
|
|
308
470
|
### Event types list lookup
|
|
309
471
|
|
|
310
|
-
If you do not provide event types filter - event types list will be determined based on the rest of arguments(a stream
|
|
472
|
+
If you do not provide event types filter - event types list will be determined based on the rest of arguments(a stream
|
|
473
|
+
argument or a stream filters option).
|
|
311
474
|
|
|
312
475
|
### Multiple events of same type in the result
|
|
313
476
|
|
|
314
|
-
If same event type appear in different streams(different by `#context` and `#stream_name`) - those events will appear in
|
|
477
|
+
If same event type appear in different streams(different by `#context` and `#stream_name`) - those events will appear in
|
|
478
|
+
the result. This is because even though `Event#type` value may be the same - its meaning may have different meaning in
|
|
479
|
+
different `context`/`stream_name` couple. Example:
|
|
315
480
|
|
|
316
481
|
```ruby
|
|
317
482
|
stream1 = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
|