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,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]
@@ -41,27 +43,67 @@ module PgEventstore
41
43
  # @param pg_connection [PG::Connection]
42
44
  # @return [void]
43
45
  def pg_transaction(level, read_only, pg_connection, &)
44
- pg_connection.transaction do
45
- if read_only
46
- pg_connection.exec("SET TRANSACTION ISOLATION LEVEL #{level} READ ONLY")
47
- else
48
- pg_connection.exec("SET TRANSACTION ISOLATION LEVEL #{level}")
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
49
55
  end
50
- yield
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
51
63
  end
52
- rescue PG::TRSerializationFailure, PG::TRDeadlockDetected
53
- retry
54
64
  rescue MissingPartitions => error
55
65
  error.event_types.each do |event_type|
56
66
  transaction do
57
67
  partition_queries.create_partitions(error.stream, event_type)
58
68
  end
59
- rescue PG::UniqueViolation
60
- retry
61
69
  end
62
70
  retry
63
71
  end
64
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
+
65
107
  # @return [PgEventstore::PartitionQueries]
66
108
  def partition_queries
67
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
@@ -3,9 +3,42 @@
3
3
  module PgEventstore
4
4
  module QueryBuilders
5
5
  # @!visibility private
6
- class BasicFiltering
7
- def initialize
8
- @sql_builder = SQLBuilder.new.select("#{to_table_name}.*").from(to_table_name)
6
+ module BasicFiltering
7
+ # @return [Hash<String => String, Symbol => String>]
8
+ SQL_DIRECTIONS = {
9
+ 'asc' => 'ASC',
10
+ 'desc' => 'DESC',
11
+ :asc => 'ASC',
12
+ :desc => 'DESC',
13
+ 'Forwards' => 'ASC',
14
+ 'Backwards' => 'DESC',
15
+ }.tap do |directions|
16
+ directions.default = 'ASC'
17
+ end.freeze
18
+
19
+ module ClassMethods
20
+ # @param order [String, Symbol, nil]
21
+ # @return [Symbol]
22
+ def reverse_order(order)
23
+ SQL_DIRECTIONS[order] == 'ASC' ? :desc : :asc
24
+ end
25
+
26
+ # @param order [String, Symbol, nil]
27
+ # @return [Boolean]
28
+ def ascending?(order)
29
+ SQL_DIRECTIONS[order] == 'ASC'
30
+ end
31
+
32
+ # @param order [String, Symbol, nil]
33
+ # @return [Boolean]
34
+ def descending?(order)
35
+ SQL_DIRECTIONS[order] == 'DESC'
36
+ end
37
+ end
38
+
39
+ def self.included(base)
40
+ super
41
+ base.extend(ClassMethods)
9
42
  end
10
43
 
11
44
  # @return [String]
@@ -15,12 +48,12 @@ module PgEventstore
15
48
 
16
49
  # @return [PgEventstore::SQLBuilder]
17
50
  def to_sql_builder
18
- @sql_builder
51
+ raise NotImplementedError
19
52
  end
20
53
 
21
54
  # @return [Array]
22
55
  def to_exec_params
23
- @sql_builder.to_exec_params
56
+ to_sql_builder.to_exec_params
24
57
  end
25
58
  end
26
59
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ # @!visibility private
6
+ class EventMarkersFiltering
7
+ include BasicFiltering
8
+
9
+ PRIMARY_TABLE_NAME = 'event_markers'
10
+
11
+ class << self
12
+ # @param names [Array<String>]
13
+ # @return [PgEventstore::SQLBuilder]
14
+ def sql_builder_by_names(names)
15
+ instance = new
16
+ instance.add_names(names)
17
+ instance.to_sql_builder
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @sql_builder = SQLBuilder.new.select("#{to_table_name}.id, #{to_table_name}.name").from(to_table_name)
23
+ end
24
+
25
+ def to_sql_builder
26
+ @sql_builder
27
+ end
28
+
29
+ # @return [String]
30
+ def to_table_name
31
+ PRIMARY_TABLE_NAME
32
+ end
33
+
34
+ # @param names [Array<String>]
35
+ # @return [void]
36
+ def add_names(names)
37
+ @sql_builder.where_or("#{to_table_name}.name = any(?::varchar[])", names)
38
+ end
39
+
40
+ # @param name [String]
41
+ # @return [void]
42
+ def add_name(name)
43
+ @sql_builder.where_or("#{to_table_name}.name = ?", name)
44
+ end
45
+ end
46
+ end
47
+ end