pg_eventstore 1.13.4 → 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 +11 -2
- data/CHANGELOG.md +195 -16
- data/README.md +33 -10
- data/db/migrations/10_setup_pg_cron.rb +1 -0
- data/db/migrations/11_add_events_link_global_position.sql +1 -0
- data/db/migrations/12_migrate_legacy_links.rb +83 -0
- data/db/migrations/13_remove_events_link_id.sql +6 -0
- data/db/migrations/14_remove_ids_events_id_index.sql +1 -0
- 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/db/migrations/9_create_events_horizon.sql +21 -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 +30 -6
- 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/callbacks.rb +7 -5
- 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/cli/try_to_delete_subscriptions_set.rb +2 -2
- data/lib/pg_eventstore/client.rb +167 -52
- 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 +11 -6
- data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
- data/lib/pg_eventstore/commands/link_to.rb +9 -9
- data/lib/pg_eventstore/commands/multiple.rb +2 -2
- 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 +22 -37
- data/lib/pg_eventstore/errors.rb +137 -11
- data/lib/pg_eventstore/event.rb +39 -7
- 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 +42 -13
- 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 +9 -95
- 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 +10 -7
- data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
- data/lib/pg_eventstore/queries/partition_queries.rb +46 -104
- 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 +58 -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 +14 -17
- data/lib/pg_eventstore/stream_global_index.rb +28 -0
- data/lib/pg_eventstore/subscriptions/basic_runner.rb +4 -4
- 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 +13 -1
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +19 -14
- data/lib/pg_eventstore/subscriptions/events_processor.rb +21 -19
- 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_command_queries.rb +5 -5
- data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +80 -56
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +2 -2
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_queries.rb +1 -1
- 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 +9 -7
- 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 +26 -2
- data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +5 -5
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -22
- data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +10 -11
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +84 -29
- data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
- data/lib/pg_eventstore/tasks/setup.rake +32 -33
- data/lib/pg_eventstore/utils.rb +39 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +104 -20
- 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 +19 -12
- 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/helpers.rb +2 -2
- 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 +26 -63
- 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 +41 -43
- 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/multiple.rbs +1 -1
- 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 +20 -6
- 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/extensions/options_extension.rbs +9 -1
- 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 +6 -38
- 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 +7 -11
- 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 +4 -2
- 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 +16 -8
- 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 +18 -50
- 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 -5
- data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +5 -5
- 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 +18 -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 +181 -21
- 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/service_queries.rb +0 -73
- data/lib/pg_eventstore/subscriptions/subscription_position_evaluation.rb +0 -195
- 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/service_queries.rbs +0 -15
- data/sig/pg_eventstore/subscriptions/subscription_position_evaluation.rbs +0 -53
- data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
- /data/sig/interfaces/{_raw_event_handler.rbs → raw_event_handler.rbs} +0 -0
|
@@ -9,8 +9,9 @@ module PgEventstore
|
|
|
9
9
|
private :connection
|
|
10
10
|
|
|
11
11
|
# @param connection [PgEventstore::Connection]
|
|
12
|
-
def initialize(connection)
|
|
12
|
+
def initialize(connection, query_strategy = QueryStrategy::Foreground.new(connection))
|
|
13
13
|
@connection = connection
|
|
14
|
+
@query_strategy = query_strategy
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
# @param stream [PgEventstore::Stream]
|
|
@@ -28,24 +29,18 @@ module PgEventstore
|
|
|
28
29
|
INSERT INTO partitions (#{attributes.keys.join(', ')})
|
|
29
30
|
VALUES (#{Utils.positional_vars(attributes.values)}) RETURNING *
|
|
30
31
|
SQL
|
|
31
|
-
|
|
32
|
+
connection.with do |conn|
|
|
32
33
|
conn.exec_params(partition_sql, [*attributes.values])
|
|
33
34
|
end.to_a.first
|
|
34
|
-
connection.with do |conn|
|
|
35
|
-
conn.exec(<<~SQL)
|
|
36
|
-
CREATE TABLE #{attributes[:table_name]} PARTITION OF events
|
|
37
|
-
FOR VALUES IN('#{conn.escape_string(stream.context)}') PARTITION BY LIST (stream_name)
|
|
38
|
-
SQL
|
|
39
|
-
end
|
|
40
|
-
partition
|
|
41
35
|
end
|
|
42
36
|
|
|
43
37
|
# @param stream [PgEventstore::Stream]
|
|
44
|
-
# @param
|
|
38
|
+
# @param context_partition_id [Integer]
|
|
45
39
|
# @return [Hash] partition attributes
|
|
46
|
-
def create_stream_name_partition(stream,
|
|
40
|
+
def create_stream_name_partition(stream, context_partition_id)
|
|
47
41
|
attributes = {
|
|
48
|
-
context: stream.context, stream_name: stream.stream_name, table_name: stream_name_partition_name(stream)
|
|
42
|
+
context: stream.context, stream_name: stream.stream_name, table_name: stream_name_partition_name(stream),
|
|
43
|
+
parent_context_partition_id: context_partition_id
|
|
49
44
|
}
|
|
50
45
|
|
|
51
46
|
loop do
|
|
@@ -58,26 +53,22 @@ module PgEventstore
|
|
|
58
53
|
INSERT INTO partitions (#{attributes.keys.join(', ')})
|
|
59
54
|
VALUES (#{Utils.positional_vars(attributes.values)}) RETURNING *
|
|
60
55
|
SQL
|
|
61
|
-
|
|
56
|
+
connection.with do |conn|
|
|
62
57
|
conn.exec_params(partition_sql, [*attributes.values])
|
|
63
58
|
end.to_a.first
|
|
64
|
-
connection.with do |conn|
|
|
65
|
-
conn.exec(<<~SQL)
|
|
66
|
-
CREATE TABLE #{attributes[:table_name]} PARTITION OF #{context_partition_name}
|
|
67
|
-
FOR VALUES IN('#{conn.escape_string(stream.stream_name)}') PARTITION BY LIST (type)
|
|
68
|
-
SQL
|
|
69
|
-
end
|
|
70
|
-
partition
|
|
71
59
|
end
|
|
72
60
|
|
|
73
61
|
# @param stream [PgEventstore::Stream]
|
|
74
62
|
# @param event_type [String]
|
|
75
|
-
# @param
|
|
63
|
+
# @param context_partition_id [Integer]
|
|
64
|
+
# @param stream_name_partition_id [Integer]
|
|
76
65
|
# @return [Hash] partition attributes
|
|
77
|
-
def create_event_type_partition(stream, event_type,
|
|
66
|
+
def create_event_type_partition(stream, event_type, context_partition_id,
|
|
67
|
+
stream_name_partition_id)
|
|
78
68
|
attributes = {
|
|
79
|
-
context: stream.context, stream_name: stream.stream_name, event_type
|
|
80
|
-
table_name: event_type_partition_name(stream, event_type)
|
|
69
|
+
context: stream.context, stream_name: stream.stream_name, event_type:,
|
|
70
|
+
table_name: event_type_partition_name(stream, event_type), parent_context_partition_id: context_partition_id,
|
|
71
|
+
parent_stream_name_partition_id: stream_name_partition_id
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
loop do
|
|
@@ -90,16 +81,9 @@ module PgEventstore
|
|
|
90
81
|
INSERT INTO partitions (#{attributes.keys.join(', ')})
|
|
91
82
|
VALUES (#{Utils.positional_vars(attributes.values)}) RETURNING *
|
|
92
83
|
SQL
|
|
93
|
-
|
|
84
|
+
connection.with do |conn|
|
|
94
85
|
conn.exec_params(partition_sql, [*attributes.values])
|
|
95
86
|
end.to_a.first
|
|
96
|
-
connection.with do |conn|
|
|
97
|
-
conn.exec(<<~SQL)
|
|
98
|
-
CREATE TABLE #{attributes[:table_name]} PARTITION OF #{stream_name_partition_name}
|
|
99
|
-
FOR VALUES IN('#{conn.escape_string(event_type)}')
|
|
100
|
-
SQL
|
|
101
|
-
end
|
|
102
|
-
partition
|
|
103
87
|
end
|
|
104
88
|
|
|
105
89
|
# @param stream [PgEventstore::Stream]
|
|
@@ -117,9 +101,15 @@ module PgEventstore
|
|
|
117
101
|
|
|
118
102
|
context_partition = context_partition(stream) || create_context_partition(stream)
|
|
119
103
|
stream_name_partition =
|
|
120
|
-
stream_name_partition(stream) ||
|
|
104
|
+
stream_name_partition(stream) ||
|
|
105
|
+
create_stream_name_partition(stream, context_partition['id'])
|
|
121
106
|
|
|
122
|
-
create_event_type_partition(
|
|
107
|
+
create_event_type_partition(
|
|
108
|
+
stream,
|
|
109
|
+
event_type,
|
|
110
|
+
context_partition['id'],
|
|
111
|
+
stream_name_partition['id']
|
|
112
|
+
)
|
|
123
113
|
end
|
|
124
114
|
|
|
125
115
|
# @param stream [PgEventstore::Stream]
|
|
@@ -171,47 +161,15 @@ module PgEventstore
|
|
|
171
161
|
# @param ids [Array<Integer>]
|
|
172
162
|
# @return [Array<Hash>]
|
|
173
163
|
def find_by_ids(ids)
|
|
174
|
-
|
|
175
|
-
conn.exec_params('select * from partitions where id = ANY($1::bigint[])', [ids])
|
|
176
|
-
end.to_a
|
|
164
|
+
@query_strategy.exec_params('select * from partitions where id = ANY($1::bigint[])', [ids]).to_a
|
|
177
165
|
end
|
|
178
166
|
|
|
179
|
-
# @param
|
|
180
|
-
# @param event_filters [Array<String>]
|
|
167
|
+
# @param filters_collection [PgEventstore::QueryBuilders::Filters::Collection]
|
|
181
168
|
# @param scope [Symbol] what kind of partition we want to receive. Available options are :event_type, :context,
|
|
182
169
|
# :stream_name and :auto. In :auto mode the scope will be calculated based on stream_filters and event_filters.
|
|
183
170
|
# @return [Array<PgEventstore::Partition>]
|
|
184
|
-
def partitions(
|
|
185
|
-
|
|
186
|
-
sql_builder =
|
|
187
|
-
if event_filters.any?
|
|
188
|
-
# When event type filters are present - they apply constraints to any stream filter. Thus, we can't look up
|
|
189
|
-
# partitions by stream attributes separately.
|
|
190
|
-
filter = QueryBuilders::PartitionsFiltering.new
|
|
191
|
-
stream_filters.each { |attrs| filter.add_stream_attrs(**attrs) }
|
|
192
|
-
filter.add_event_types(event_filters)
|
|
193
|
-
set_partitions_scope(filter, stream_filters, event_filters, scope)
|
|
194
|
-
else
|
|
195
|
-
# When event type filters are absent - we can look up partitions by context and context/stream_name
|
|
196
|
-
# separately, thus potentially producing one-to-one mapping of filter-to-partition with :auto scope. For
|
|
197
|
-
# example, let's say we have stream attributes filter like
|
|
198
|
-
# [{ context: 'FooCtx', stream_name: 'Bar'}, { context: 'BarCtx' }], then we would be able to look up
|
|
199
|
-
# partitions by the exact match, returning only two of them according to the provided filters - stream
|
|
200
|
-
# partition for first filter and context partition for second filter.
|
|
201
|
-
builders = stream_filters.map do |attrs|
|
|
202
|
-
filter = QueryBuilders::PartitionsFiltering.new
|
|
203
|
-
filter.add_stream_attrs(**attrs)
|
|
204
|
-
set_partitions_scope(filter, [attrs], event_filters, scope)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
sql_builder = SQLBuilder.union_builders(builders) if builders.any?
|
|
208
|
-
sql_builder ||
|
|
209
|
-
begin
|
|
210
|
-
builder = QueryBuilders::PartitionsFiltering.new
|
|
211
|
-
set_partitions_scope(builder, stream_filters, event_filters, scope)
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
171
|
+
def partitions(filters_collection, scope: :event_type)
|
|
172
|
+
sql_builder = QueryBuilders::PartitionsFiltering.assemble_sql_builder(filters_collection, scope:)
|
|
215
173
|
connection.with do |conn|
|
|
216
174
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
217
175
|
end.map(&method(:deserialize))
|
|
@@ -236,45 +194,29 @@ module PgEventstore
|
|
|
236
194
|
"event_types_#{Digest::MD5.hexdigest("#{stream.context}-#{stream.stream_name}-#{event_type}")[0..5]}"
|
|
237
195
|
end
|
|
238
196
|
|
|
239
|
-
|
|
197
|
+
# @param partition [PgEventstore::Partition]
|
|
198
|
+
# @return [void]
|
|
199
|
+
def detach_event_type_partition(partition)
|
|
200
|
+
parent = deserialize(find_by_ids([partition.parent_stream_name_partition_id]).first)
|
|
201
|
+
connection.with do |conn|
|
|
202
|
+
conn.exec("alter table #{parent.table_name} detach partition #{partition.table_name}")
|
|
203
|
+
end
|
|
204
|
+
end
|
|
240
205
|
|
|
241
|
-
# @param
|
|
242
|
-
# @
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
partitions_filter.with_event_types
|
|
250
|
-
when :stream_name
|
|
251
|
-
filter = QueryBuilders::PartitionsFiltering.new
|
|
252
|
-
filter.without_event_types
|
|
253
|
-
filter.with_stream_names
|
|
254
|
-
builder = filter.to_sql_builder
|
|
255
|
-
builder.where(
|
|
256
|
-
'(context, stream_name) in ?',
|
|
257
|
-
partitions_filter.to_sql_builder.unselect.select('context, stream_name').group('context, stream_name')
|
|
206
|
+
# @param partition [PgEventstore::Partition]
|
|
207
|
+
# @return [void]
|
|
208
|
+
def attach_event_type_partition(partition)
|
|
209
|
+
parent = deserialize(find_by_ids([partition.parent_stream_name_partition_id]).first)
|
|
210
|
+
connection.with do |conn|
|
|
211
|
+
event_type = conn.escape_string(partition.event_type)
|
|
212
|
+
conn.exec(
|
|
213
|
+
"alter table #{parent.table_name} attach partition #{partition.table_name} for values in ('#{event_type}')"
|
|
258
214
|
)
|
|
259
|
-
when :context
|
|
260
|
-
filter = QueryBuilders::PartitionsFiltering.new
|
|
261
|
-
filter.without_event_types
|
|
262
|
-
filter.without_stream_names
|
|
263
|
-
builder = filter.to_sql_builder
|
|
264
|
-
builder.where('context in ?', partitions_filter.to_sql_builder.unselect.select('context').group('context'))
|
|
265
|
-
when :auto
|
|
266
|
-
if event_filters.any?
|
|
267
|
-
set_partitions_scope(partitions_filter, stream_filters, event_filters, :event_type)
|
|
268
|
-
elsif stream_filters.any? { _1[:stream_name] }
|
|
269
|
-
set_partitions_scope(partitions_filter, stream_filters, event_filters, :stream_name)
|
|
270
|
-
else
|
|
271
|
-
set_partitions_scope(partitions_filter, stream_filters, event_filters, :context)
|
|
272
|
-
end
|
|
273
|
-
else
|
|
274
|
-
raise NotImplementedError, "Don't know how to handle #{scope.inspect} scope!"
|
|
275
215
|
end
|
|
276
216
|
end
|
|
277
217
|
|
|
218
|
+
private
|
|
219
|
+
|
|
278
220
|
# @param attrs [Hash]
|
|
279
221
|
# @return [PgEventstore::Partition]
|
|
280
222
|
def deserialize(attrs)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgEventstore
|
|
4
|
+
# @!visibility private
|
|
5
|
+
class ReplicaQueries
|
|
6
|
+
MAX_PARTITIONS_TO_RESOLVE_PER_CALL = 100
|
|
7
|
+
|
|
8
|
+
# @!attribute connection
|
|
9
|
+
# @return [PgEventstore::Connection]
|
|
10
|
+
attr_reader :connection
|
|
11
|
+
private :connection
|
|
12
|
+
|
|
13
|
+
# @param connection [PgEventstore::Connection]
|
|
14
|
+
# @param query_strategy [PgEventstore::QueryStrategy]
|
|
15
|
+
def initialize(connection, query_strategy)
|
|
16
|
+
@connection = connection
|
|
17
|
+
@query_strategy = query_strategy
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex::SubscriptionRepr>]
|
|
21
|
+
# @return [Array<Integer>]
|
|
22
|
+
def load_subscription_positions(indexes)
|
|
23
|
+
params = [indexes.first.subscription_position, indexes.last.subscription_position]
|
|
24
|
+
exists = @query_strategy.exec_params(<<~SQL, params).first&.[]('one')
|
|
25
|
+
select 1 as one from event_subscription_positions where subscription_position between $1 and $2 limit 1
|
|
26
|
+
SQL
|
|
27
|
+
return [] unless exists
|
|
28
|
+
|
|
29
|
+
@query_strategy.exec_params(<<~SQL, [indexes.map(&:subscription_position)]).map { _1['subscription_position'] }
|
|
30
|
+
select subscription_position from event_subscription_positions where subscription_position = any($1::bigint[])
|
|
31
|
+
SQL
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex::SubscriptionRepr>]
|
|
35
|
+
# @return [Array<PgEventstore::RawEvent>]
|
|
36
|
+
def load_events(indexes)
|
|
37
|
+
indexes = indexes.dup
|
|
38
|
+
raw_events = []
|
|
39
|
+
loop do
|
|
40
|
+
range = Utils.range_to_slice(indexes.map(&:event_type_partition_id), MAX_PARTITIONS_TO_RESOLVE_PER_CALL)
|
|
41
|
+
indexes_to_resolve = indexes.slice!(range)
|
|
42
|
+
break if indexes_to_resolve.empty?
|
|
43
|
+
|
|
44
|
+
raw_events.push(*events_global_index_queries.resolve_indexes(indexes_to_resolve, resolve_link_tos: false))
|
|
45
|
+
end
|
|
46
|
+
raw_events.map { RawEvent.new(**_1.transform_keys(&:to_sym)) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex::SubscriptionRepr>]
|
|
50
|
+
# @return [Array<PgEventstore::EventGlobalIndex>]
|
|
51
|
+
def load_events_global_index(indexes)
|
|
52
|
+
result = @query_strategy.exec_params(<<~SQL, [indexes.map(&:global_position)])
|
|
53
|
+
select * from events_global_index where global_position = any($1::bigint[])
|
|
54
|
+
SQL
|
|
55
|
+
result.map { EventGlobalIndex.new(**_1.transform_keys(&:to_sym)) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex>]
|
|
59
|
+
# @return [Array<PgEventstore::StreamGlobalIndex>]
|
|
60
|
+
def load_streams_global_index(indexes)
|
|
61
|
+
result = @query_strategy.exec_params(<<~SQL, [indexes.map(&:streams_global_index_id)])
|
|
62
|
+
select * from streams_global_index where id = any($1::bigint[])
|
|
63
|
+
SQL
|
|
64
|
+
stream_revisions_map = indexes.group_by(&:streams_global_index_id).to_h do |stream_id, events_idx|
|
|
65
|
+
[stream_id, events_idx.max_by(&:stream_revision).stream_revision]
|
|
66
|
+
end
|
|
67
|
+
result.map do |attrs|
|
|
68
|
+
attrs = attrs.transform_keys(&:to_sym)
|
|
69
|
+
attrs[:stream_revision] = stream_revisions_map[attrs[:id]]
|
|
70
|
+
StreamGlobalIndex.new(**attrs)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex::SubscriptionRepr>]
|
|
75
|
+
# @return [Array<PgEventstore::EventMarkerIndex>]
|
|
76
|
+
def load_event_markers_index(indexes)
|
|
77
|
+
result = @query_strategy.exec_params(<<~SQL, [indexes.map(&:global_position)])
|
|
78
|
+
select * from event_markers_index where global_position = any($1::bigint[])
|
|
79
|
+
SQL
|
|
80
|
+
result.map { EventMarkerIndex.new(**_1.transform_keys(&:to_sym)) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @param indexes [Array<PgEventstore::EventMarkerIndex>]
|
|
84
|
+
# @return [Array<PgEventstore::EventMarker>]
|
|
85
|
+
def load_markers(indexes)
|
|
86
|
+
result = @query_strategy.exec_params(<<~SQL, [indexes.map(&:marker_id).uniq])
|
|
87
|
+
select * from event_markers where id = any($1::bigint[])
|
|
88
|
+
SQL
|
|
89
|
+
result.map { EventMarker.new(**_1.transform_keys(&:to_sym)) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @param indexes [Array<PgEventstore::EventGlobalIndex>]
|
|
93
|
+
# @return [Array<PgEventstore::Partition>]
|
|
94
|
+
def load_partitions(indexes)
|
|
95
|
+
ids = indexes.map(&:context_partition_id).uniq + indexes.map(&:stream_name_partition_id).uniq +
|
|
96
|
+
indexes.map(&:event_type_partition_id).uniq
|
|
97
|
+
result = @query_strategy.exec_params(<<~SQL, [ids])
|
|
98
|
+
select * from partitions where id = any($1::bigint[]) order by id
|
|
99
|
+
SQL
|
|
100
|
+
result.map { Partition.new(**_1.transform_keys(&:to_sym)) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# @return [PgEventstore::EventsGlobalIndexQueries]
|
|
106
|
+
def events_global_index_queries
|
|
107
|
+
EventsGlobalIndexQueries.new(connection, @query_strategy)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgEventstore
|
|
4
|
+
# @!visibility private
|
|
5
|
+
class StreamsGlobalIndexQueries
|
|
6
|
+
# @!attribute connection
|
|
7
|
+
# @return [PgEventstore::Connection]
|
|
8
|
+
attr_reader :connection
|
|
9
|
+
private :connection
|
|
10
|
+
|
|
11
|
+
# @param connection [PgEventstore::Connection]
|
|
12
|
+
# @param query_strategy [PgEventstore::QueryStrategy]
|
|
13
|
+
def initialize(connection, query_strategy)
|
|
14
|
+
@connection = connection
|
|
15
|
+
@query_strategy = query_strategy
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param stream [PgEventstore::Stream]
|
|
19
|
+
# @return [PgEventstore::StreamGlobalIndex]
|
|
20
|
+
def find_or_create_by(stream)
|
|
21
|
+
transaction_queries.transaction do
|
|
22
|
+
find_by(stream) || create(stream)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param stream [PgEventstore::Stream]
|
|
27
|
+
# @return [PgEventstore::StreamGlobalIndex, nil]
|
|
28
|
+
def find_by(stream)
|
|
29
|
+
idx_partitions_filter = QueryBuilders::Filters::Collection.from_options({ filter: { streams: [stream.to_hash] } })
|
|
30
|
+
filtering = QueryBuilders::StreamsGlobalIndexFiltering.new
|
|
31
|
+
idx_partitions_filter.collection.each(&filtering.method(:add_filter_row))
|
|
32
|
+
deserialize_one(@query_strategy.exec_params(*filtering.to_exec_params))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @param stream [PgEventstore::Stream]
|
|
36
|
+
# @return [PgEventstore::StreamGlobalIndex]
|
|
37
|
+
def find_by!(stream)
|
|
38
|
+
find_by(stream) ||
|
|
39
|
+
raise(RecordNotFound.new(QueryBuilders::StreamsGlobalIndexFiltering::PRIMARY_TABLE_NAME, stream))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param id [Integer]
|
|
43
|
+
# @param attrs [Hash]
|
|
44
|
+
# @return [void]
|
|
45
|
+
def update(id, **attrs)
|
|
46
|
+
attrs_sql = attrs.keys.map.with_index(2) do |attr, index|
|
|
47
|
+
"#{attr} = $#{index}"
|
|
48
|
+
end.join(', ')
|
|
49
|
+
@query_strategy.exec_params(<<~SQL, [id, *attrs.values])
|
|
50
|
+
UPDATE streams_global_index SET #{attrs_sql} WHERE id = $1
|
|
51
|
+
SQL
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param stream [PgEventstore::Stream]
|
|
55
|
+
# @return [PgEventstore::StreamGlobalIndex]
|
|
56
|
+
def create(stream)
|
|
57
|
+
filter_collection = QueryBuilders::Filters::Collection.from_options(
|
|
58
|
+
{ filter: { streams: [{ context: stream.context, stream_name: stream.stream_name }] } }
|
|
59
|
+
)
|
|
60
|
+
partitions_sql_builder = QueryBuilders::PartitionsFiltering.assemble_sql_builder(
|
|
61
|
+
filter_collection,
|
|
62
|
+
scope: :stream_name
|
|
63
|
+
)
|
|
64
|
+
partitions_sql_builder.unselect.select('id')
|
|
65
|
+
sql, positional_args = partitions_sql_builder.to_exec_params
|
|
66
|
+
stream_revision = Stream::NON_EXISTING_STREAM_REVISION
|
|
67
|
+
starting_position = StreamGlobalIndex::INITIAL_STARTING_POSITION
|
|
68
|
+
stream_id = PG::Connection.escape(stream.stream_id)
|
|
69
|
+
value = "((#{sql}), '#{stream_id}', #{stream_revision}, #{starting_position})"
|
|
70
|
+
|
|
71
|
+
res = @query_strategy.exec_params(<<~SQL, positional_args)
|
|
72
|
+
INSERT INTO streams_global_index ("partition_id", "stream_id", "stream_revision", "starting_position")
|
|
73
|
+
VALUES #{value} RETURNING *
|
|
74
|
+
SQL
|
|
75
|
+
deserialize_one(res)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param id [Integer]
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
def delete(id)
|
|
81
|
+
res = @query_strategy.exec_params('delete from streams_global_index where id = $1', [id])
|
|
82
|
+
res.cmd_tuples == 1
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param stream [PgEventstore::Stream]
|
|
86
|
+
# @return [Boolean]
|
|
87
|
+
def stream_exists?(stream)
|
|
88
|
+
idx_partitions_filter = QueryBuilders::Filters::Collection.from_options({ filter: { streams: [stream.to_hash] } })
|
|
89
|
+
filtering = QueryBuilders::StreamsGlobalIndexFiltering.new
|
|
90
|
+
idx_partitions_filter.collection.each(&filtering.method(:add_filter_row))
|
|
91
|
+
builder = filtering.to_sql_builder.unselect.select('1 as exists')
|
|
92
|
+
@query_strategy.exec_params(*builder.to_exec_params).ntuples == 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @param stream [PgEventstore::Stream]
|
|
96
|
+
# @return [Integer, nil]
|
|
97
|
+
def stream_revision(stream)
|
|
98
|
+
idx_partitions_filter = QueryBuilders::Filters::Collection.from_options({ filter: { streams: [stream.to_hash] } })
|
|
99
|
+
filtering = QueryBuilders::StreamsGlobalIndexFiltering.new
|
|
100
|
+
idx_partitions_filter.collection.each(&filtering.method(:add_filter_row))
|
|
101
|
+
builder = filtering.to_sql_builder.unselect.select('stream_revision')
|
|
102
|
+
@query_strategy.exec_params(*builder.to_exec_params).first&.dig('stream_revision')
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @param options [Hash]
|
|
106
|
+
# @option options [Integer, nil] :from_position
|
|
107
|
+
# @option options [Integer, nil] :max_count
|
|
108
|
+
# @option options [String, Symbol, nil] :direction
|
|
109
|
+
# @return [Array<PgEventstore::StreamGlobalIndex>]
|
|
110
|
+
def streams_global_index(options)
|
|
111
|
+
sql_builder = QueryBuilders::StreamsGlobalIndexFiltering.sql_builder_for_basic_pagination(options)
|
|
112
|
+
deserialize_many(@query_strategy.exec_params(*sql_builder.to_exec_params))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @param indexes [Array<PgEventstore::StreamGlobalIndex>]
|
|
116
|
+
# @return [Array<PgEventstore::Stream>]
|
|
117
|
+
def resolve_indexes(indexes)
|
|
118
|
+
return [] if indexes.empty?
|
|
119
|
+
|
|
120
|
+
partition_ids = indexes.map(&:partition_id).uniq
|
|
121
|
+
partitions = partition_queries.find_by_ids(partition_ids).to_h { [_1['id'], _1] }
|
|
122
|
+
indexes.map do |stream_global_index|
|
|
123
|
+
partition = partitions[stream_global_index.partition_id]
|
|
124
|
+
Stream.new(
|
|
125
|
+
context: partition['context'],
|
|
126
|
+
stream_name: partition['stream_name'],
|
|
127
|
+
stream_id: stream_global_index.stream_id,
|
|
128
|
+
stream_revision: stream_global_index.stream_revision,
|
|
129
|
+
starting_position: stream_global_index.starting_position
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
# @return [PgEventstore::TransactionQueries]
|
|
137
|
+
def transaction_queries
|
|
138
|
+
TransactionQueries.new(connection)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @param pg_result [PG::Result]
|
|
142
|
+
# @return [PgEventstore::StreamGlobalIndex, nil]
|
|
143
|
+
def deserialize_one(pg_result)
|
|
144
|
+
first_result = pg_result.first
|
|
145
|
+
return unless first_result
|
|
146
|
+
|
|
147
|
+
StreamGlobalIndex.new(**first_result.transform_keys(&:to_sym))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @param pg_result [PG::Result]
|
|
151
|
+
# @return [Array<PgEventstore::StreamGlobalIndex>]
|
|
152
|
+
def deserialize_many(pg_result)
|
|
153
|
+
pg_result.map do |attrs|
|
|
154
|
+
StreamGlobalIndex.new(**attrs.transform_keys(&:to_sym))
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @return [PgEventstore::PartitionQueries]
|
|
159
|
+
def partition_queries
|
|
160
|
+
PartitionQueries.new(connection)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -11,6 +11,8 @@ module PgEventstore
|
|
|
11
11
|
}.tap do |h|
|
|
12
12
|
h.default = h[:serializable]
|
|
13
13
|
end.freeze
|
|
14
|
+
# @return [Integer]
|
|
15
|
+
MAX_NUMBER_OF_UNIQUE_CONSTRAINTS_ERROR_RETRIES = 5
|
|
14
16
|
|
|
15
17
|
# @!attribute connection
|
|
16
18
|
# @return [PgEventstore::Connection]
|
|
@@ -23,39 +25,85 @@ module PgEventstore
|
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# @param level [Symbol] transaction isolation level
|
|
28
|
+
# @param read_only [Boolean] whether transaction is read-only
|
|
26
29
|
# @return [void]
|
|
27
|
-
def transaction(level = :serializable, &blk)
|
|
30
|
+
def transaction(level = :serializable, read_only: false, &blk)
|
|
28
31
|
connection.with do |conn|
|
|
29
32
|
# We are inside a transaction already - no need to start another one
|
|
30
33
|
next yield if [PG::PQTRANS_ACTIVE, PG::PQTRANS_INTRANS].include?(conn.transaction_status)
|
|
31
34
|
|
|
32
|
-
pg_transaction(ISOLATION_LEVELS[level], conn, &blk)
|
|
35
|
+
pg_transaction(ISOLATION_LEVELS[level], read_only, conn, &blk)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
private
|
|
37
40
|
|
|
38
41
|
# @param level [String] PostgreSQL transaction isolation level
|
|
42
|
+
# @param read_only [Boolean]
|
|
39
43
|
# @param pg_connection [PG::Connection]
|
|
40
44
|
# @return [void]
|
|
41
|
-
def pg_transaction(level, pg_connection, &
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
def pg_transaction(level, read_only, pg_connection, &)
|
|
46
|
+
retries_count = 0
|
|
47
|
+
begin
|
|
48
|
+
_transaction(pg_connection) do
|
|
49
|
+
if read_only
|
|
50
|
+
pg_connection.exec("SET TRANSACTION ISOLATION LEVEL #{level} READ ONLY")
|
|
51
|
+
else
|
|
52
|
+
pg_connection.exec("SET TRANSACTION ISOLATION LEVEL #{level}")
|
|
53
|
+
end
|
|
54
|
+
yield
|
|
55
|
+
end
|
|
56
|
+
rescue PG::TRSerializationFailure, PG::TRDeadlockDetected
|
|
57
|
+
retry
|
|
58
|
+
rescue PG::UniqueViolation
|
|
59
|
+
retries_count += 1
|
|
60
|
+
raise if retries_count >= MAX_NUMBER_OF_UNIQUE_CONSTRAINTS_ERROR_RETRIES
|
|
61
|
+
|
|
62
|
+
retry
|
|
45
63
|
end
|
|
46
|
-
rescue PG::TRSerializationFailure, PG::TRDeadlockDetected
|
|
47
|
-
retry
|
|
48
64
|
rescue MissingPartitions => error
|
|
49
65
|
error.event_types.each do |event_type|
|
|
50
66
|
transaction do
|
|
51
67
|
partition_queries.create_partitions(error.stream, event_type)
|
|
52
68
|
end
|
|
53
|
-
rescue PG::UniqueViolation
|
|
54
|
-
retry
|
|
55
69
|
end
|
|
56
70
|
retry
|
|
57
71
|
end
|
|
58
72
|
|
|
73
|
+
# TODO: next pg gem release (presumably v1.6.4) should already include the fix of PG::Connection#transaction.
|
|
74
|
+
# Changes are already in master branch
|
|
75
|
+
# https://github.com/ged/ruby-pg/commit/c3d2aabd0a19d20bb1f2b2fa2e5b30f03b043aff
|
|
76
|
+
# Once it is released - change pg gem requirement in gemspec to '>= 1.6.4' and remove this implementation.
|
|
77
|
+
# @param conn [PG::Connection]
|
|
78
|
+
# rubocop:disable Lint/RescueException
|
|
79
|
+
def _transaction(conn)
|
|
80
|
+
rollback = false
|
|
81
|
+
conn.exec('BEGIN')
|
|
82
|
+
yield
|
|
83
|
+
rescue PG::RollbackTransaction
|
|
84
|
+
rollback = true
|
|
85
|
+
perform_rollback(conn)
|
|
86
|
+
rescue Exception
|
|
87
|
+
rollback = true
|
|
88
|
+
perform_rollback(conn)
|
|
89
|
+
raise
|
|
90
|
+
ensure
|
|
91
|
+
unless rollback
|
|
92
|
+
if Thread.current.status == 'aborting'
|
|
93
|
+
perform_rollback(conn)
|
|
94
|
+
else
|
|
95
|
+
conn.exec('COMMIT')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
# rubocop:enable Lint/RescueException
|
|
100
|
+
|
|
101
|
+
def perform_rollback(conn)
|
|
102
|
+
conn.cancel if conn.transaction_status == PG::PQTRANS_ACTIVE
|
|
103
|
+
conn.block
|
|
104
|
+
conn.exec('ROLLBACK')
|
|
105
|
+
end
|
|
106
|
+
|
|
59
107
|
# @return [PgEventstore::PartitionQueries]
|
|
60
108
|
def partition_queries
|
|
61
109
|
PartitionQueries.new(connection)
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'sql_builder'
|
|
4
|
+
require_relative 'query_builders/filters/collection'
|
|
5
|
+
require_relative 'query_builders/read_cursor/stream_cursor'
|
|
4
6
|
require_relative 'query_builders/basic_filtering'
|
|
7
|
+
require_relative 'query_builders/subscription_events_filtering'
|
|
5
8
|
require_relative 'query_builders/events_filtering'
|
|
6
9
|
require_relative 'query_builders/partitions_filtering'
|
|
10
|
+
require_relative 'query_builders/events_global_index_filtering'
|
|
11
|
+
require_relative 'query_builders/streams_global_index_filtering'
|
|
12
|
+
require_relative 'query_builders/event_subscription_positions_filtering'
|
|
13
|
+
require_relative 'query_builders/event_markers_filtering'
|
|
14
|
+
require_relative 'query_builders/event_markers_index_filtering'
|
|
15
|
+
require_relative 'query_builders/index_based_events_filtering'
|
|
7
16
|
require_relative 'queries/transaction_queries'
|
|
8
17
|
require_relative 'queries/event_queries'
|
|
9
18
|
require_relative 'queries/partition_queries'
|
|
10
19
|
require_relative 'queries/links_resolver'
|
|
11
20
|
require_relative 'queries/maintenance_queries'
|
|
21
|
+
require_relative 'queries/events_global_index_queries'
|
|
22
|
+
require_relative 'queries/streams_global_index_queries'
|
|
23
|
+
require_relative 'queries/event_marker_queries'
|
|
24
|
+
require_relative 'queries/index_filtering_queries'
|
|
25
|
+
require_relative 'queries/replica_queries'
|
|
12
26
|
|
|
13
27
|
module PgEventstore
|
|
14
28
|
# @!visibility private
|
|
@@ -24,9 +38,23 @@ module PgEventstore
|
|
|
24
38
|
# @!attribute transactions
|
|
25
39
|
# @return [PgEventstore::TransactionQueries, nil]
|
|
26
40
|
attribute(:transactions)
|
|
27
|
-
|
|
28
41
|
# @!attribute maintenance
|
|
29
42
|
# @return [PgEventstore::MaintenanceQueries, nil]
|
|
30
43
|
attribute(:maintenance)
|
|
44
|
+
# @!attribute maintenance
|
|
45
|
+
# @return [PgEventstore::EventsGlobalIndexQueries, nil]
|
|
46
|
+
attribute(:events_global_index)
|
|
47
|
+
# @!attribute maintenance
|
|
48
|
+
# @return [PgEventstore::StreamsGlobalIndexQueries, nil]
|
|
49
|
+
attribute(:streams_global_index)
|
|
50
|
+
# @!attribute event_subscription_positions
|
|
51
|
+
# @return [PgEventstore::EventSubscriptionPositionQueries, nil]
|
|
52
|
+
attribute(:event_subscription_positions)
|
|
53
|
+
# @!attribute event_subscription_positions
|
|
54
|
+
# @return [PgEventstore::EventMarkerQueries, nil]
|
|
55
|
+
attribute(:event_markers)
|
|
56
|
+
# @!attribute event_subscription_positions
|
|
57
|
+
# @return [PgEventstore::IndexFilteringQueries, nil]
|
|
58
|
+
attribute(:index_filtering)
|
|
31
59
|
end
|
|
32
60
|
end
|