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.
Files changed (303) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/CHANGELOG.md +186 -20
  4. data/README.md +25 -15
  5. data/db/migrations/10_setup_pg_cron.rb +1 -23
  6. data/db/migrations/15_create_streams_global_index.sql +17 -0
  7. data/db/migrations/16_populate_streams_global_index.rb +150 -0
  8. data/db/migrations/17_create_events_global_index.sql +62 -0
  9. data/db/migrations/18_populate_events_global_index.rb +124 -0
  10. data/db/migrations/19_populate_events_global_index_p2.rb +25 -0
  11. data/db/migrations/20_migrate_subscriptions_current_position.sql +19 -0
  12. data/db/migrations/21_create_partitions_stats.sql +2 -0
  13. data/db/migrations/22_drop_streams_sql_view.sql +2 -0
  14. data/db/migrations/23_adjust_partitions_indexes.sql +5 -0
  15. data/db/migrations/24_drop_legacy_events_indexes.sql +3 -0
  16. data/db/migrations/25_add_parent_partitions_info_to_partitions.sql +16 -0
  17. data/db/migrations/26_change_events_stream_revision_type.rb +35 -0
  18. data/db/migrations/27_drop_events_horizon.sql +3 -0
  19. data/db/migrations/28_create_maintenance_tasks.sql +8 -0
  20. data/db/migrations/29_create_event_markers.sql +35 -0
  21. data/db/migrations/30_improve_partitions_search_in_admin_ui.rb +24 -0
  22. data/db/migrations/31_drop_events_id_default_value.sql +1 -0
  23. data/db/migrations/32_create_parittions_triggers.sql +101 -0
  24. data/docs/AGENTS.fragment.md +696 -0
  25. data/docs/admin_ui.md +3 -1
  26. data/docs/appending_events.md +110 -49
  27. data/docs/configuration.md +69 -36
  28. data/docs/event_tracing.md +55 -0
  29. data/docs/events_and_streams.md +54 -30
  30. data/docs/how_it_works.md +57 -16
  31. data/docs/linking_events.md +2 -1
  32. data/docs/maintenance.md +14 -5
  33. data/docs/multiple_commands.md +17 -8
  34. data/docs/reading_events.md +208 -43
  35. data/docs/reading_streams.md +50 -0
  36. data/docs/replication.md +99 -0
  37. data/docs/subscriptions.md +165 -35
  38. data/docs/writing_middleware.md +25 -8
  39. data/ext/pg_eventstore_ext/extconf.rb +5 -0
  40. data/ext/pg_eventstore_ext/pg_eventstore_ext.c +74 -0
  41. data/lib/pg_eventstore/async_runner.rb +63 -0
  42. data/lib/pg_eventstore/basic_config.rb +15 -0
  43. data/lib/pg_eventstore/chunks/chunk.rb +31 -0
  44. data/lib/pg_eventstore/chunks/read_api_events_index_chunk.rb +83 -0
  45. data/lib/pg_eventstore/chunks/replica_events_index_chunk.rb +35 -0
  46. data/lib/pg_eventstore/chunks/repository.rb +73 -0
  47. data/lib/pg_eventstore/chunks/subscription_checkpoint_chunk.rb +48 -0
  48. data/lib/pg_eventstore/chunks/subscription_events_index_chunk.rb +99 -0
  49. data/lib/pg_eventstore/chunks.rb +14 -0
  50. data/lib/pg_eventstore/client.rb +163 -50
  51. data/lib/pg_eventstore/commands/append.rb +62 -43
  52. data/lib/pg_eventstore/commands/delete_event.rb +7 -8
  53. data/lib/pg_eventstore/commands/delete_stream.rb +3 -3
  54. data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +9 -7
  55. data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
  56. data/lib/pg_eventstore/commands/link_to.rb +5 -5
  57. data/lib/pg_eventstore/commands/read.rb +23 -5
  58. data/lib/pg_eventstore/commands/read_grouped.rb +46 -0
  59. data/lib/pg_eventstore/commands/read_streams.rb +18 -0
  60. data/lib/pg_eventstore/commands/read_streams_paginated.rb +56 -0
  61. data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +4 -4
  62. data/lib/pg_eventstore/commands/revision_check/current_revision.rb +51 -0
  63. data/lib/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rb +46 -0
  64. data/lib/pg_eventstore/commands/revision_check/expected_revision.rb +135 -0
  65. data/lib/pg_eventstore/commands/revision_check/stream_revision_check.rb +98 -0
  66. data/lib/pg_eventstore/commands/revision_check/stream_revision_comparison.rb +36 -0
  67. data/lib/pg_eventstore/commands/stream_revision.rb +14 -0
  68. data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +4 -4
  69. data/lib/pg_eventstore/commands.rb +4 -2
  70. data/lib/pg_eventstore/config.rb +30 -9
  71. data/lib/pg_eventstore/connection.rb +21 -2
  72. data/lib/pg_eventstore/errors.rb +136 -10
  73. data/lib/pg_eventstore/event.rb +34 -2
  74. data/lib/pg_eventstore/event_deserializer.rb +5 -4
  75. data/lib/pg_eventstore/event_global_index.rb +118 -0
  76. data/lib/pg_eventstore/event_marker.rb +16 -0
  77. data/lib/pg_eventstore/event_marker_index.rb +25 -0
  78. data/lib/pg_eventstore/event_serializer.rb +6 -0
  79. data/lib/pg_eventstore/extensions/acts_as_configurable.rb +88 -0
  80. data/lib/pg_eventstore/extensions/options_defaults.rb +25 -0
  81. data/lib/pg_eventstore/extensions/options_extension.rb +2 -2
  82. data/lib/pg_eventstore/feature_marker.rb +18 -0
  83. data/lib/pg_eventstore/maintenance.rb +11 -6
  84. data/lib/pg_eventstore/middleware/event_tracing.rb +53 -0
  85. data/lib/pg_eventstore/partition.rb +6 -0
  86. data/lib/pg_eventstore/pg_connection.rb +70 -14
  87. data/lib/pg_eventstore/queries/event_marker_queries.rb +74 -0
  88. data/lib/pg_eventstore/queries/event_queries.rb +7 -93
  89. data/lib/pg_eventstore/queries/events_global_index_queries.rb +131 -0
  90. data/lib/pg_eventstore/queries/index_filtering_queries.rb +179 -0
  91. data/lib/pg_eventstore/queries/links_resolver.rb +4 -4
  92. data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
  93. data/lib/pg_eventstore/queries/partition_queries.rb +45 -103
  94. data/lib/pg_eventstore/queries/replica_queries.rb +110 -0
  95. data/lib/pg_eventstore/queries/streams_global_index_queries.rb +163 -0
  96. data/lib/pg_eventstore/queries/transaction_queries.rb +52 -10
  97. data/lib/pg_eventstore/queries.rb +29 -1
  98. data/lib/pg_eventstore/query_builders/basic_filtering.rb +38 -5
  99. data/lib/pg_eventstore/query_builders/event_markers_filtering.rb +47 -0
  100. data/lib/pg_eventstore/query_builders/event_markers_index_filtering.rb +275 -0
  101. data/lib/pg_eventstore/query_builders/event_subscription_positions_filtering.rb +37 -0
  102. data/lib/pg_eventstore/query_builders/events_filtering.rb +15 -164
  103. data/lib/pg_eventstore/query_builders/events_global_index_filtering.rb +227 -0
  104. data/lib/pg_eventstore/query_builders/filters/collection.rb +324 -0
  105. data/lib/pg_eventstore/query_builders/filters/event_type_filter.rb +37 -0
  106. data/lib/pg_eventstore/query_builders/filters/filter_row.rb +54 -0
  107. data/lib/pg_eventstore/query_builders/filters/marker_filter.rb +20 -0
  108. data/lib/pg_eventstore/query_builders/filters/marker_filter_row.rb +49 -0
  109. data/lib/pg_eventstore/query_builders/filters/stream_filter.rb +48 -0
  110. data/lib/pg_eventstore/query_builders/index_based_events_filtering.rb +260 -0
  111. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +175 -51
  112. data/lib/pg_eventstore/query_builders/read_cursor/stream_cursor.rb +122 -0
  113. data/lib/pg_eventstore/query_builders/streams_global_index_filtering.rb +83 -0
  114. data/lib/pg_eventstore/query_builders/subscription_events_filtering.rb +79 -0
  115. data/lib/pg_eventstore/query_strategy/async.rb +64 -0
  116. data/lib/pg_eventstore/query_strategy/foreground.rb +25 -0
  117. data/lib/pg_eventstore/query_strategy.rb +19 -0
  118. data/lib/pg_eventstore/raw_event.rb +46 -0
  119. data/lib/pg_eventstore/rspec/test_helpers.rb +21 -8
  120. data/lib/pg_eventstore/sql_builder.rb +87 -8
  121. data/lib/pg_eventstore/stream.rb +13 -16
  122. data/lib/pg_eventstore/stream_global_index.rb +28 -0
  123. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +5 -17
  124. data/lib/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rb +36 -0
  125. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +12 -0
  126. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +21 -7
  127. data/lib/pg_eventstore/subscriptions/events_processor.rb +20 -18
  128. data/lib/pg_eventstore/subscriptions/events_processor_consumer/multiple.rb +63 -0
  129. data/lib/pg_eventstore/subscriptions/events_processor_consumer/replica.rb +68 -0
  130. data/lib/pg_eventstore/subscriptions/events_processor_consumer/single.rb +62 -0
  131. data/lib/pg_eventstore/subscriptions/events_processor_consumer.rb +44 -0
  132. data/lib/pg_eventstore/subscriptions/events_subscription_position_worker.rb +68 -0
  133. data/lib/pg_eventstore/subscriptions/queries/event_subscription_position_queries.rb +177 -0
  134. data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +78 -54
  135. data/lib/pg_eventstore/subscriptions/replica_subscription_handler.rb +140 -0
  136. data/lib/pg_eventstore/subscriptions/replica_subscription_runner.rb +9 -0
  137. data/lib/pg_eventstore/subscriptions/subscription.rb +2 -1
  138. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/collection.rb +36 -0
  139. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/index_read_strategy.rb +82 -0
  140. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/replication_strategy.rb +75 -0
  141. data/lib/pg_eventstore/subscriptions/subscription_feed_strategy.rb +31 -0
  142. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +24 -0
  143. data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +4 -2
  144. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -9
  145. data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
  146. data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +9 -18
  147. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
  148. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +74 -14
  149. data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
  150. data/lib/pg_eventstore/tasks/setup.rake +2 -2
  151. data/lib/pg_eventstore/utils.rb +31 -0
  152. data/lib/pg_eventstore/version.rb +1 -1
  153. data/lib/pg_eventstore/web/application.rb +100 -16
  154. data/lib/pg_eventstore/web/paginator/base_collection.rb +5 -1
  155. data/lib/pg_eventstore/web/paginator/event_types_collection.rb +14 -4
  156. data/lib/pg_eventstore/web/paginator/events_collection.rb +29 -33
  157. data/lib/pg_eventstore/web/paginator/helpers.rb +16 -9
  158. data/lib/pg_eventstore/web/paginator/markers_collection.rb +48 -0
  159. data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +13 -2
  160. data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +21 -11
  161. data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +18 -4
  162. data/lib/pg_eventstore/web/paginator/streams_collection.rb +95 -0
  163. data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +49 -22
  164. data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
  165. data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +1 -1
  166. data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
  167. data/lib/pg_eventstore/web/subscriptions/with_state/subscriptions.rb +1 -1
  168. data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -9
  169. data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +18 -5
  170. data/lib/pg_eventstore/web/views/home/partials/events.erb +38 -1
  171. data/lib/pg_eventstore/web/views/home/partials/markers_filter.erb +18 -0
  172. data/lib/pg_eventstore/web/views/layouts/application.erb +6 -0
  173. data/lib/pg_eventstore/web/views/streams/index.erb +110 -0
  174. data/lib/pg_eventstore/web/views/streams/partials/streams.erb +11 -0
  175. data/lib/pg_eventstore/web/views/subscriptions/index.erb +4 -1
  176. data/lib/pg_eventstore/web.rb +2 -0
  177. data/lib/pg_eventstore.rb +23 -60
  178. data/pg_eventstore.gemspec +6 -4
  179. data/sig/interfaces/_raw_events_handler.rbs +3 -0
  180. data/sig/interfaces/subscription_handler.rbs +1 -1
  181. data/sig/pg/result.rbs +9 -0
  182. data/sig/pg_eventstore/async_runner.rbs +13 -0
  183. data/sig/pg_eventstore/basic_config.rbs +9 -0
  184. data/sig/pg_eventstore/chunks/chunk.rbs +13 -0
  185. data/sig/pg_eventstore/chunks/read_api_events_index_chunk.rbs +30 -0
  186. data/sig/pg_eventstore/chunks/replica_events_index_chunk.rbs +11 -0
  187. data/sig/pg_eventstore/chunks/repository.rbs +22 -0
  188. data/sig/pg_eventstore/chunks/subscription_checkpoint_chunk.rbs +18 -0
  189. data/sig/pg_eventstore/chunks/subscription_events_index_chunk.rbs +37 -0
  190. data/sig/pg_eventstore/client.rbs +40 -42
  191. data/sig/pg_eventstore/commands/append.rbs +11 -25
  192. data/sig/pg_eventstore/commands/event_modifiers/prepare_link_event.rbs +9 -12
  193. data/sig/pg_eventstore/commands/event_modifiers/prepare_regular_event.rbs +5 -4
  194. data/sig/pg_eventstore/commands/link_to.rbs +6 -11
  195. data/sig/pg_eventstore/commands/read.rbs +2 -5
  196. data/sig/pg_eventstore/commands/read_grouped.rbs +7 -0
  197. data/sig/pg_eventstore/commands/read_streams.rbs +7 -0
  198. data/sig/pg_eventstore/commands/read_streams_paginated.rbs +16 -0
  199. data/sig/pg_eventstore/commands/regular_stream_read_paginated.rbs +9 -17
  200. data/sig/pg_eventstore/commands/revision_check/current_revision.rbs +25 -0
  201. data/sig/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rbs +14 -0
  202. data/sig/pg_eventstore/commands/revision_check/expected_revision.rbs +67 -0
  203. data/sig/pg_eventstore/commands/revision_check/stream_revision_check.rbs +20 -0
  204. data/sig/pg_eventstore/commands/revision_check/stream_revision_comparison.rbs +9 -0
  205. data/sig/pg_eventstore/commands/stream_revision.rbs +7 -0
  206. data/sig/pg_eventstore/commands/system_stream_read_paginated.rbs +9 -14
  207. data/sig/pg_eventstore/config.rbs +15 -6
  208. data/sig/pg_eventstore/connection.rbs +11 -18
  209. data/sig/pg_eventstore/errors.rbs +64 -34
  210. data/sig/pg_eventstore/event.rbs +16 -2
  211. data/sig/pg_eventstore/event_deserializer.rbs +5 -10
  212. data/sig/pg_eventstore/event_global_index.rbs +60 -0
  213. data/sig/pg_eventstore/event_marker.rbs +9 -0
  214. data/sig/pg_eventstore/event_marker_index.rbs +12 -0
  215. data/sig/pg_eventstore/extensions/acts_as_configurable.rbs +29 -0
  216. data/sig/pg_eventstore/extensions/options_defaults.rbs +7 -0
  217. data/sig/pg_eventstore/feature_marker.rbs +10 -0
  218. data/sig/pg_eventstore/maintenance.rbs +12 -8
  219. data/sig/pg_eventstore/middleware/event_trace.rbs +14 -0
  220. data/sig/pg_eventstore/partition.rbs +2 -4
  221. data/sig/pg_eventstore/pg_connection.rbs +8 -0
  222. data/sig/pg_eventstore/queries/event_marker_queries.rbs +21 -0
  223. data/sig/pg_eventstore/queries/event_queries.rbs +4 -36
  224. data/sig/pg_eventstore/queries/events_global_index_queries.rbs +37 -0
  225. data/sig/pg_eventstore/queries/index_filtering_queries.rbs +50 -0
  226. data/sig/pg_eventstore/queries/links_resolver.rbs +5 -9
  227. data/sig/pg_eventstore/queries/maintenance_queries.rbs +12 -9
  228. data/sig/pg_eventstore/queries/partition_queries.rbs +32 -67
  229. data/sig/pg_eventstore/queries/replica_queries.rbs +31 -0
  230. data/sig/pg_eventstore/queries/streams_global_index_queries.rbs +39 -0
  231. data/sig/pg_eventstore/queries/transaction_queries.rbs +2 -0
  232. data/sig/pg_eventstore/queries.rbs +11 -21
  233. data/sig/pg_eventstore/query_builders/basic_filtering.rbs +10 -3
  234. data/sig/pg_eventstore/query_builders/event_markers_filtering.rbs +17 -0
  235. data/sig/pg_eventstore/query_builders/event_markers_index_filtering.rbs +69 -0
  236. data/sig/pg_eventstore/query_builders/event_subscription_positions_filtering.rbs +15 -0
  237. data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +9 -51
  238. data/sig/pg_eventstore/query_builders/events_global_index_filtering.rbs +68 -0
  239. data/sig/pg_eventstore/query_builders/filters/collection.rbs +84 -0
  240. data/sig/pg_eventstore/query_builders/filters/event_type_filter.rbs +19 -0
  241. data/sig/pg_eventstore/query_builders/filters/filter_row.rbs +21 -0
  242. data/sig/pg_eventstore/query_builders/filters/marker_filter.rbs +13 -0
  243. data/sig/pg_eventstore/query_builders/filters/marker_filter_row.rbs +19 -0
  244. data/sig/pg_eventstore/query_builders/filters/stream_filter.rbs +24 -0
  245. data/sig/pg_eventstore/query_builders/index_based_events_filtering.rbs +60 -0
  246. data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +29 -10
  247. data/sig/pg_eventstore/query_builders/read_cursor/stream_cursor.rbs +47 -0
  248. data/sig/pg_eventstore/query_builders/streams_global_index_filtering.rbs +27 -0
  249. data/sig/pg_eventstore/query_builders/subscription_events_filtering.rbs +26 -0
  250. data/sig/pg_eventstore/query_strategy/async.rbs +17 -0
  251. data/sig/pg_eventstore/query_strategy/foreground.rbs +11 -0
  252. data/sig/pg_eventstore/query_strategy.rbs +7 -0
  253. data/sig/pg_eventstore/raw_event.rbs +20 -0
  254. data/sig/pg_eventstore/sql_builder.rbs +32 -10
  255. data/sig/pg_eventstore/stream.rbs +10 -18
  256. data/sig/pg_eventstore/stream_global_index.rbs +14 -0
  257. data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +5 -4
  258. data/sig/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rbs +9 -0
  259. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +17 -12
  260. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +17 -6
  261. data/sig/pg_eventstore/subscriptions/event_subscription_position_queries.rbs +30 -0
  262. data/sig/pg_eventstore/subscriptions/events_processor.rbs +12 -5
  263. data/sig/pg_eventstore/subscriptions/events_processor_consumer/multiple.rbs +17 -0
  264. data/sig/pg_eventstore/subscriptions/events_processor_consumer/replica.rbs +17 -0
  265. data/sig/pg_eventstore/subscriptions/events_processor_consumer/single.rbs +17 -0
  266. data/sig/pg_eventstore/subscriptions/events_processor_consumer.rbs +14 -0
  267. data/sig/pg_eventstore/subscriptions/events_subscription_position_worker.rbs +28 -0
  268. data/sig/pg_eventstore/subscriptions/queries/subscription_queries.rbs +7 -39
  269. data/sig/pg_eventstore/subscriptions/replica_subscription_handler.rbs +33 -0
  270. data/sig/pg_eventstore/subscriptions/replica_subscription_runner.rbs +4 -0
  271. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/collection.rbs +14 -0
  272. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/index_read_strategy.rbs +23 -0
  273. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/replication_strategy.rbs +23 -0
  274. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy.rbs +11 -0
  275. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +13 -10
  276. data/sig/pg_eventstore/subscriptions/subscription_handler_performance.rbs +1 -1
  277. data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +13 -2
  278. data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +1 -8
  279. data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +4 -6
  280. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +29 -43
  281. data/sig/pg_eventstore/synchronized_array.rbs +4 -0
  282. data/sig/pg_eventstore/utils.rbs +10 -11
  283. data/sig/pg_eventstore/web/application.rbs +13 -3
  284. data/sig/pg_eventstore/web/paginator/base_collection.rbs +2 -0
  285. data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +4 -0
  286. data/sig/pg_eventstore/web/paginator/events_collection.rbs +11 -11
  287. data/sig/pg_eventstore/web/paginator/helpers.rbs +6 -15
  288. data/sig/pg_eventstore/web/paginator/markers_collection.rbs +13 -0
  289. data/sig/pg_eventstore/web/paginator/stream_contexts_collection.rbs +4 -0
  290. data/sig/pg_eventstore/web/paginator/stream_names_collection.rbs +4 -0
  291. data/sig/pg_eventstore/web/paginator/streams_collection.rbs +30 -0
  292. data/sig/pg_eventstore.rbs +8 -26
  293. metadata +175 -20
  294. data/Dockerfile +0 -3
  295. data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +0 -69
  296. data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +0 -31
  297. data/lib/pg_eventstore/subscriptions/queries/subscription_service_queries.rb +0 -78
  298. data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +0 -15
  299. data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +0 -16
  300. data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +0 -8
  301. data/sig/pg_eventstore/subscriptions/queries/subscription_service_queries.rbs +0 -19
  302. data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
  303. /data/sig/interfaces/{_raw_event_handler.rbs → raw_event_handler.rbs} +0 -0
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ # @!visibility private
6
+ class EventMarkersIndexFiltering
7
+ include BasicFiltering
8
+
9
+ # @return [String]
10
+ PRIMARY_TABLE_NAME = 'event_markers_index'
11
+ # @return [Integer]
12
+ DEFAULT_LIMIT = 1_000
13
+
14
+ class << self
15
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
16
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
17
+ # @param seq_num [Integer]
18
+ # @return [PgEventstore::SQLBuilder]
19
+ def for_revision_validation_per_type(marker_filter_row, cursor, seq_num)
20
+ builder = filtering_from_filter_row(marker_filter_row, cursor).to_sql_builder
21
+ builder.unselect.select("stream_revision, #{seq_num} as sequence_number")
22
+ end
23
+
24
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
25
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
26
+ # @return [PgEventstore::SQLBuilder]
27
+ def for_read_grouped(marker_filter_row, cursor)
28
+ filtering_from_filter_row(marker_filter_row, cursor).to_sql_builder
29
+ end
30
+
31
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
32
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
33
+ # @return [PgEventstore::SQLBuilder]
34
+ def for_read_common(marker_filter_row, cursor)
35
+ filtering_from_filter_row(marker_filter_row, cursor).to_sql_builder
36
+ end
37
+
38
+ private
39
+
40
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
41
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
42
+ # @return [PgEventstore::QueryBuilders::EventMarkersIndexFiltering]
43
+ def filtering_from_filter_row(marker_filter_row, cursor)
44
+ index_filtering =
45
+ # This is an optimization for the case when user reads from "all" stream and filters by the stream. We do
46
+ # not have proper indexes to filter markers by stream and by global position. We calculate stream's revision
47
+ # based on events_global_index data and filter by that revision instead. Yes, it results in one additional
48
+ # subquery per marker filter, but it saves us from two additional indexes in event_markers_index table,
49
+ # which is decent.
50
+ if cursor.all_stream_cursor? && marker_filter_row.stream_filter&.stream?
51
+ stream_revision_based_filtering(cursor, marker_filter_row)
52
+ else
53
+ default_filtering(cursor)
54
+ end
55
+ index_filtering.add_marker_filter_row(marker_filter_row)
56
+ index_filtering
57
+ end
58
+
59
+ # @param index_filtering [PgEventstore::QueryBuilders::EventMarkersIndexFiltering]
60
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
61
+ # @return [void]
62
+ def add_direction_and_limit(index_filtering, cursor)
63
+ if cursor.all_stream_cursor?
64
+ index_filtering.add_global_position_direction(cursor.direction)
65
+ else
66
+ index_filtering.add_stream_revision_direction(cursor.direction)
67
+ end
68
+ index_filtering.add_limit(cursor.max_count)
69
+ end
70
+
71
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
72
+ # @return [PgEventstore::QueryBuilders::EventMarkersIndexFiltering]
73
+ def default_filtering(cursor)
74
+ index_filtering = new
75
+ add_direction_and_limit(index_filtering, cursor)
76
+ if cursor.all_stream_cursor?
77
+ index_filtering.from_position(cursor.from, cursor.direction)
78
+ index_filtering.to_position(cursor.to, cursor.direction)
79
+ else
80
+ index_filtering.from_revision(cursor.from, cursor.direction)
81
+ index_filtering.to_revision(cursor.to, cursor.direction)
82
+ end
83
+ index_filtering
84
+ end
85
+
86
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
87
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
88
+ # @return [PgEventstore::QueryBuilders::EventMarkersIndexFiltering]
89
+ def stream_revision_based_filtering(cursor, marker_filter_row)
90
+ index_filtering = new
91
+ # Translate from/to global position into from/to stream revision
92
+ revision_from, revision_to = %i[from to].map do |notation|
93
+ position = cursor.public_send(notation)
94
+ next unless position
95
+
96
+ events_idx_filtering = EventsGlobalIndexFiltering.new
97
+ if notation == :from
98
+ events_idx_filtering.add_global_position_direction(cursor.direction)
99
+ events_idx_filtering.from_position(position, cursor.direction)
100
+ else
101
+ events_idx_filtering.add_global_position_direction(reverse_order(cursor.direction))
102
+ events_idx_filtering.to_position(position, cursor.direction)
103
+ end
104
+ events_idx_filtering.add_filter_row(marker_filter_row.to_filter_row)
105
+ events_idx_filtering.add_limit(1)
106
+
107
+
108
+ events_idx_filtering.to_sql_builder.unselect.select('stream_revision')
109
+ end
110
+
111
+ index_filtering.add_stream_revision_direction(cursor.direction)
112
+ index_filtering.from_revision(revision_from, cursor.direction) if revision_from
113
+ index_filtering.to_revision(revision_to, cursor.direction) if revision_to
114
+ index_filtering.add_limit(cursor.max_count)
115
+ index_filtering
116
+ end
117
+ end
118
+
119
+ def initialize
120
+ @sql_builder = SQLBuilder.new
121
+ @sql_builder.select("#{to_table_name}.global_position, #{to_table_name}.event_type_partition_id")
122
+ @sql_builder.from(to_table_name)
123
+ end
124
+
125
+ def to_table_name
126
+ PRIMARY_TABLE_NAME
127
+ end
128
+
129
+ def to_sql_builder
130
+ @sql_builder
131
+ end
132
+
133
+ # @param marker_filter_row [PgEventstore::QueryBuilders::Filters::MarkerFilterRow]
134
+ # @return [void]
135
+ def add_marker_filter_row(marker_filter_row)
136
+ stream_filter = marker_filter_row.stream_filter
137
+ marker_filter = marker_filter_row.marker_filter
138
+
139
+ query_parts = []
140
+ if stream_filter&.stream?
141
+ streams_filter = StreamsGlobalIndexFiltering.new
142
+ filter_row = Filters::FilterRow.new(stream_filter:, event_type_filters: [])
143
+ streams_filter.add_filter_row(filter_row)
144
+ query_parts << [
145
+ "#{to_table_name}.streams_global_index_id = ?", streams_filter.to_sql_builder.select_id_only
146
+ ]
147
+ end
148
+
149
+ if marker_filter.event_type
150
+ event_type_filter = Filters::EventTypeFilter.new(event_type: marker_filter.event_type, prefix: false)
151
+ filter_row = Filters::FilterRow.new(stream_filter:, event_type_filters: [event_type_filter])
152
+ affected_partitions = PartitionsFiltering.from_filter_row(filter_row)
153
+ query_parts <<
154
+ if filter_row.ambiguous_event_type?
155
+ [
156
+ "#{to_table_name}.event_type_partition_id in ?",
157
+ affected_partitions.select_id_only,
158
+ ]
159
+ else
160
+ [
161
+ "#{to_table_name}.event_type_partition_id = ?",
162
+ affected_partitions.select_id_only,
163
+ ]
164
+ end
165
+ elsif stream_filter && !stream_filter.stream?
166
+ filter_row = Filters::FilterRow.new(stream_filter:, event_type_filters: [])
167
+ affected_partitions = PartitionsFiltering.from_filter_row(filter_row)
168
+ query_parts << [
169
+ "#{to_table_name}.event_type_partition_id = any(array(?)::bigint[])",
170
+ affected_partitions.select_id_only,
171
+ ]
172
+ end
173
+
174
+ markers_filtering = EventMarkersFiltering.new
175
+ if marker_filter.markers.size == 1
176
+ markers_filtering.add_name(marker_filter.markers.first)
177
+ query_parts << ["#{to_table_name}.marker_id = ?", markers_filtering.to_sql_builder.select_id_only]
178
+ else
179
+ markers_filtering.add_names(marker_filter.markers)
180
+ query_parts << [
181
+ "#{to_table_name}.marker_id = any(array(?)::bigint[])",
182
+ markers_filtering.to_sql_builder.select_id_only,
183
+ ]
184
+ end
185
+
186
+ attributes_sql, values = query_parts.transpose
187
+ @sql_builder.where_or(attributes_sql.join(' and '), *values)
188
+ end
189
+
190
+ # @param direction [String, Symbol, nil]
191
+ # @return [void]
192
+ def add_global_position_direction(direction)
193
+ @sql_builder.order("#{to_table_name}.global_position #{SQL_DIRECTIONS[direction]}")
194
+ end
195
+
196
+ # @param direction [String, Symbol, nil]
197
+ # @return [void]
198
+ def add_stream_revision_direction(direction)
199
+ @sql_builder.order("#{to_table_name}.stream_revision #{SQL_DIRECTIONS[direction]}")
200
+ end
201
+
202
+ # @param position [Integer, nil]
203
+ # @param direction [String, Symbol, nil]
204
+ # @return [void]
205
+ def from_position(position, direction)
206
+ return unless position
207
+
208
+ @sql_builder.where("#{to_table_name}.global_position #{direction_operator_from(direction)} ?", position)
209
+ end
210
+
211
+ # @param revision [Integer, PgEventstore::SQLBuilder, nil]
212
+ # @param direction [String, Symbol, nil]
213
+ # @return [void]
214
+ def from_revision(revision, direction)
215
+ return unless revision
216
+
217
+ @sql_builder.where("#{to_table_name}.stream_revision #{direction_operator_from(direction)} ?", revision)
218
+ end
219
+
220
+ # @param position [Integer, nil]
221
+ # @param direction [String, Symbol, nil]
222
+ # @return [void]
223
+ def to_position(position, direction)
224
+ return unless position
225
+
226
+ @sql_builder.where("#{to_table_name}.global_position #{direction_operator_to(direction)} ?", position)
227
+ end
228
+
229
+ # @param revision [Integer, PgEventstore::SQLBuilder, nil]
230
+ # @param direction [String, Symbol, nil]
231
+ # @return [void]
232
+ def to_revision(revision, direction)
233
+ return unless revision
234
+
235
+ @sql_builder.where("#{to_table_name}.stream_revision #{direction_operator_to(direction)} ?", revision)
236
+ end
237
+
238
+ # @param limit [Integer, nil]
239
+ # @return [void]
240
+ def add_limit(limit)
241
+ @sql_builder.limit(limit || DEFAULT_LIMIT)
242
+ end
243
+
244
+ # @return [void]
245
+ def for_subscription
246
+ spos_table_name = EventSubscriptionPositionsFiltering::PRIMARY_TABLE_NAME
247
+ @sql_builder.unselect.select(<<~SQL)
248
+ distinct #{to_table_name}.global_position, #{to_table_name}.event_type_partition_id,
249
+ #{spos_table_name}.subscription_position
250
+ SQL
251
+ @sql_builder.join("join #{spos_table_name} using(global_position)")
252
+ @sql_builder.where("#{spos_table_name}.subscription_position >= from_position")
253
+ @sql_builder.where("#{spos_table_name}.subscription_position <= to_position")
254
+ @sql_builder.where("#{to_table_name}.global_position >= from_gpos")
255
+ @sql_builder.where("#{to_table_name}.global_position <= to_gpos")
256
+ @sql_builder.order("#{spos_table_name}.subscription_position asc")
257
+ @sql_builder.limit('max_count')
258
+ end
259
+
260
+ private
261
+
262
+ # @param direction [String, Symbol, nil]
263
+ # @return [String]
264
+ def direction_operator_from(direction)
265
+ SQL_DIRECTIONS[direction] == 'ASC' ? '>=' : '<='
266
+ end
267
+
268
+ # @param direction [String, Symbol, nil]
269
+ # @return [String]
270
+ def direction_operator_to(direction)
271
+ SQL_DIRECTIONS[direction] == 'ASC' ? '<=' : '>='
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ # @!visibility private
6
+ class EventSubscriptionPositionsFiltering
7
+ include BasicFiltering
8
+
9
+ PRIMARY_TABLE_NAME = 'event_subscription_positions'
10
+
11
+ def initialize
12
+ @sql_builder = SQLBuilder.new
13
+ @sql_builder.select("#{to_table_name}.global_position, #{to_table_name}.subscription_position")
14
+ @sql_builder.from(to_table_name)
15
+ end
16
+
17
+ def to_table_name
18
+ PRIMARY_TABLE_NAME
19
+ end
20
+
21
+ def to_sql_builder
22
+ @sql_builder
23
+ end
24
+
25
+ # @param positions [Array<Integer>]
26
+ # @return [void]
27
+ def by_global_positions(positions)
28
+ @sql_builder.where('global_position = ANY(?::bigint[])', positions)
29
+ end
30
+
31
+ # @return [void]
32
+ def max_subscription_position
33
+ @sql_builder.unselect.select('max(subscription_position) as max_subscription_position')
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,102 +3,18 @@
3
3
  module PgEventstore
4
4
  module QueryBuilders
5
5
  # @!visibility private
6
- class EventsFiltering < BasicFiltering
6
+ class EventsFiltering
7
+ include BasicFiltering
8
+
7
9
  # @return [Integer]
8
10
  DEFAULT_LIMIT = 1_000
9
- # @return [Hash<String => String, Symbol => String>]
10
- SQL_DIRECTIONS = {
11
- 'asc' => 'ASC',
12
- 'desc' => 'DESC',
13
- :asc => 'ASC',
14
- :desc => 'DESC',
15
- 'Forwards' => 'ASC',
16
- 'Backwards' => 'DESC',
17
- }.tap do |directions|
18
- directions.default = 'ASC'
19
- end.freeze
20
- # @return [Array<Symbol>]
21
- SUBSCRIPTIONS_OPTIONS = %i[from_position resolve_link_tos filter max_count].freeze
22
-
23
- class << self
24
- # @param stream [PgEventstore::Stream]
25
- # @param options [Hash]
26
- # @return [PgEventstore::EventsFiltering]
27
- def events_filtering(stream, options)
28
- return all_stream_filtering(options) if stream.all_stream?
29
-
30
- if stream.system? && Stream::KNOWN_SYSTEM_STREAMS.include?(stream.context)
31
- return system_stream_filtering(stream, options)
32
- end
33
-
34
- specific_stream_filtering(stream, options)
35
- end
36
-
37
- # @param options [Hash]
38
- # @return [PgEventstore::QueryBuilders::EventsFiltering]
39
- def subscriptions_events_filtering(options)
40
- all_stream_filtering(options.slice(*SUBSCRIPTIONS_OPTIONS))
41
- end
42
-
43
- # @param options [Hash]
44
- # @return [PgEventstore::QueryBuilders::EventsFiltering]
45
- def all_stream_filtering(options)
46
- event_filter = new
47
- event_filter.add_event_types(extract_event_types_filter(options))
48
- event_filter.add_limit(options[:max_count])
49
- extract_streams_filter(options).each { |attrs| event_filter.add_stream_attrs(**attrs) }
50
- event_filter.add_global_position(options[:from_position], options[:direction])
51
- event_filter.add_all_stream_direction(options[:direction])
52
- event_filter
53
- end
54
-
55
- # @param stream [PgEventstore::Stream]
56
- # @param options [Hash]
57
- # @return [PgEventstore::QueryBuilders::EventsFiltering]
58
- def specific_stream_filtering(stream, options)
59
- event_filter = new
60
- event_filter.add_event_types(extract_event_types_filter(options))
61
- event_filter.add_limit(options[:max_count])
62
- event_filter.add_stream_attrs(**stream.to_hash)
63
- event_filter.add_revision(options[:from_revision], options[:direction])
64
- event_filter.add_stream_direction(options[:direction])
65
- event_filter
66
- end
67
-
68
- # @param stream [PgEventstore::Stream] system stream
69
- # @param options [Hash]
70
- # @return [PgEventstore::QueryBuilders::EventsFiltering]
71
- def system_stream_filtering(stream, options)
72
- all_stream_filtering(options).tap do |event_filter|
73
- event_filter.set_source(stream.context)
74
- end
75
- end
76
-
77
- # @param options [Hash]
78
- # @return [Array<String>]
79
- def extract_event_types_filter(options)
80
- options in { filter: { event_types: Array => event_types } }
81
- event_types = event_types&.select { _1.is_a?(String) }
82
- event_types || []
83
- end
84
11
 
85
- # @param options [Hash]
86
- # @return [Array<Hash[Symbol, String]>]
87
- def extract_streams_filter(options)
88
- options in { filter: { streams: Array => streams } }
89
- streams = streams&.map do |stream_attrs|
90
- stream_attrs in { context: String | NilClass => context }
91
- stream_attrs in { stream_name: String | NilClass => stream_name }
92
- stream_attrs in { stream_id: String | NilClass => stream_id }
93
- { context:, stream_name:, stream_id: }
94
- end
95
- streams || []
96
- end
12
+ def initialize
13
+ @sql_builder = SQLBuilder.new.select("#{to_table_name}.*").from(to_table_name)
97
14
  end
98
15
 
99
- def initialize
100
- super
101
- @sql_builder.limit(DEFAULT_LIMIT)
16
+ def to_sql_builder
17
+ @sql_builder
102
18
  end
103
19
 
104
20
  # @return [String]
@@ -106,19 +22,11 @@ module PgEventstore
106
22
  Event::PRIMARY_TABLE_NAME
107
23
  end
108
24
 
109
- # @param context [String, nil]
110
- # @param stream_name [String, nil]
111
- # @param stream_id [String, nil]
25
+ # @param context [String]
26
+ # @param stream_name [String]
112
27
  # @return [void]
113
- def add_stream_attrs(context: nil, stream_name: nil, stream_id: nil)
114
- stream_attrs = { context:, stream_name:, stream_id: }
115
- return unless correct_stream_filter?(stream_attrs)
116
-
117
- stream_attrs.compact!
118
- sql = stream_attrs.map do |attr, _|
119
- "#{to_table_name}.#{attr} = ?"
120
- end.join(' AND ')
121
- @sql_builder.where_or(sql, *stream_attrs.values)
28
+ def add_stream_attrs(context:, stream_name:)
29
+ @sql_builder.where_or('context = ? and stream_name = ?', context, stream_name)
122
30
  end
123
31
 
124
32
  # @param event_types [Array<String>]
@@ -129,76 +37,19 @@ module PgEventstore
129
37
  sql = event_types.size.times.map do
130
38
  '?'
131
39
  end.join(', ')
132
- @sql_builder.where("#{to_table_name}.type IN (#{sql})", *event_types)
133
- end
134
-
135
- # @param revision [Integer, nil]
136
- # @param direction [String, Symbol, nil]
137
- # @return [void]
138
- def add_revision(revision, direction)
139
- return unless revision
140
-
141
- @sql_builder.where("#{to_table_name}.stream_revision #{direction_operator(direction)} ?", revision)
142
- end
143
-
144
- # @param position [Integer, nil]
145
- # @param direction [String, Symbol, nil]
146
- # @return [void]
147
- def add_global_position(position, direction)
148
- return unless position
149
-
150
- @sql_builder.where("#{to_table_name}.global_position #{direction_operator(direction)} ?", position)
151
- end
152
-
153
- # @param direction [String, Symbol, nil]
154
- # @return [void]
155
- def add_stream_direction(direction)
156
- @sql_builder.order("#{to_table_name}.stream_revision #{SQL_DIRECTIONS[direction]}")
157
- end
158
-
159
- # @param direction [String, Symbol, nil]
160
- # @return [void]
161
- def add_all_stream_direction(direction)
162
- @sql_builder.order("#{to_table_name}.global_position #{SQL_DIRECTIONS[direction]}")
40
+ @sql_builder.where("#{to_table_name}.type #{comparison_operator(event_types)} (#{sql})", *event_types)
163
41
  end
164
42
 
165
43
  # @param limit [Integer, nil]
166
44
  # @return [void]
167
45
  def add_limit(limit)
168
- return unless limit
169
-
170
- @sql_builder.limit(limit)
171
- end
172
-
173
- # @param table_name [String] system stream view name
174
- # @return [void]
175
- # rubocop:disable Naming/AccessorMethodName
176
- def set_source(table_name)
177
- @sql_builder.from(%( "#{PG::Connection.escape(table_name)}" #{to_table_name} ))
46
+ @sql_builder.limit(limit || DEFAULT_LIMIT)
178
47
  end
179
- # rubocop:enable Naming/AccessorMethodName
180
48
 
181
49
  private
182
50
 
183
- # @param stream_attrs [Hash]
184
- # @return [Boolean]
185
- def correct_stream_filter?(stream_attrs)
186
- result = (stream_attrs in { context: String, stream_name: String, stream_id: String } |
187
- { context: String, stream_name: String, stream_id: nil } |
188
- { context: String, stream_name: nil, stream_id: nil })
189
- return true if result
190
-
191
- PgEventstore.logger&.debug(<<~TEXT)
192
- Ignoring unsupported stream filter format for searching #{stream_attrs.compact.inspect}. \
193
- See docs/reading_events.md docs for supported formats.
194
- TEXT
195
- false
196
- end
197
-
198
- # @param direction [String, Symbol, nil]
199
- # @return [String]
200
- def direction_operator(direction)
201
- SQL_DIRECTIONS[direction] == 'ASC' ? '>=' : '<='
51
+ def comparison_operator(event_types)
52
+ event_types.size > 1 ? 'in' : '='
202
53
  end
203
54
  end
204
55
  end