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,62 @@
1
+ CREATE TABLE public.events_global_index
2
+ (
3
+ global_position bigint NOT NULL,
4
+ stream_revision bigint NOT NULL,
5
+ context_partition_id bigint NOT NULL,
6
+ stream_name_partition_id bigint NOT NULL,
7
+ event_type_partition_id bigint NOT NULL,
8
+ streams_global_index_id bigint NOT NULL
9
+ );
10
+
11
+ CREATE INDEX idx_events_idx_on_global_position ON public.events_global_index
12
+ USING btree (global_position) INCLUDE (event_type_partition_id);
13
+
14
+ -- (context, global_position)
15
+ CREATE INDEX idx_events_idx_on_ctx_part_id_N_position ON public.events_global_index
16
+ USING btree (context_partition_id, global_position) INCLUDE (event_type_partition_id);
17
+
18
+ -- (context, stream_name, global_position)
19
+ CREATE INDEX idx_events_idx_on_stream_name_part_id_N_position ON public.events_global_index
20
+ USING btree (stream_name_partition_id, global_position) INCLUDE (event_type_partition_id);
21
+
22
+ -- (context, stream_name, event_type, global_position)
23
+ CREATE INDEX idx_events_idx_on_e_type_part_id_N_position ON public.events_global_index
24
+ USING btree (event_type_partition_id, global_position);
25
+
26
+ -- (context, stream_name, stream_id, global_position)
27
+ -- Including stream_revision keeps us from creating another index to cover event markers queries
28
+ CREATE INDEX idx_events_idx_on_streams_idx_id_N_position ON public.events_global_index
29
+ USING btree (streams_global_index_id, global_position) INCLUDE (event_type_partition_id, stream_revision);
30
+
31
+ -- (context, stream_name, stream_id, stream_revision)
32
+ CREATE INDEX idx_events_idx_on_streams_idx_id_N_revision ON public.events_global_index
33
+ USING btree (streams_global_index_id, stream_revision) INCLUDE (event_type_partition_id, global_position);
34
+
35
+ -- (context, stream_name, event_type, stream_id, global_position)
36
+ -- Including stream_revision keeps us from creating another index to cover event markers queries
37
+ CREATE INDEX idx_events_idx_on_streams_idx_id_N_e_type_part_id_n_position ON public.events_global_index
38
+ USING btree (streams_global_index_id, event_type_partition_id, global_position) INCLUDE (stream_revision);
39
+
40
+ -- (context, stream_name, event_type, stream_id, stream_revision)
41
+ CREATE INDEX idx_events_idx_on_streams_idx_id_N_e_type_part_id_n_revision ON public.events_global_index
42
+ USING btree (streams_global_index_id, event_type_partition_id, stream_revision) INCLUDE (global_position);
43
+
44
+ CREATE TABLE public.event_subscription_positions
45
+ (
46
+ global_position bigint NOT NULL,
47
+ subscription_position bigserial NOT NULL
48
+ );
49
+
50
+ CREATE TABLE public.event_subscription_positions_unprocessed
51
+ (
52
+ global_position bigint NOT NULL
53
+ );
54
+
55
+ CREATE INDEX idx_event_subscription_positions_sposition_N_gposition ON public.event_subscription_positions
56
+ USING btree (subscription_position, global_position);
57
+
58
+ CREATE INDEX idx_event_subscription_positions_gposition_N_sposition ON public.event_subscription_positions
59
+ USING btree (global_position, subscription_position);
60
+
61
+ CREATE INDEX idx_event_subscription_positions_unprocessed_gposition ON public.event_subscription_positions_unprocessed
62
+ USING btree (global_position);
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ CONCURRENCY = ENV['CONCURRENCY']&.to_i || 10
4
+
5
+ PgEventstore.configure(name: :_eventstore_db_connection) do |config|
6
+ config.connection_pool_size = CONCURRENCY
7
+ end
8
+
9
+ partitions = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
10
+ conn.exec('select * from partitions')
11
+ end.to_a
12
+ context_partitions = partitions.select { _1['stream_name'].nil? }.to_h { [_1['context'], _1['id']] }
13
+ event_type_partitions = partitions.reject { _1['stream_name'].nil? }.group_by { _1['context'] }
14
+ event_type_partitions = event_type_partitions.map do |context, context_parts|
15
+ context_parts = context_parts.group_by { |p| p['stream_name'] }
16
+ context_parts = context_parts.map do |stream_name, parts|
17
+ stream_name_part = parts.find { _1['event_type'].nil? }
18
+ event_type_parts = parts - [stream_name_part]
19
+ event_type_parts = event_type_parts.map do |part|
20
+ [part['event_type'], part]
21
+ end
22
+ [stream_name, { stream_name_part:, event_types: event_type_parts.to_h }]
23
+ end
24
+ [context, context_parts.to_h]
25
+ end.to_h
26
+
27
+ total_events = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
28
+ conn.exec('explain select * from events')
29
+ end.first['QUERY PLAN'][/rows=\d+/].sub('rows=', '').to_i
30
+
31
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
32
+ conn.exec('delete from events_global_index')
33
+ conn.exec('delete from event_subscription_positions_unprocessed')
34
+ end
35
+
36
+ puts "Indexing events. Events to process: #{total_events} (approximately). Concurrency is #{CONCURRENCY} concurrent writers."
37
+ puts
38
+ processed = 0
39
+ processed_was = 0
40
+ time = Time.now
41
+ lock = Thread::Mutex.new
42
+ threads = CONCURRENCY.times.map do |t|
43
+ Thread.new do
44
+ event_type_partitions.each_value do |stream_name_parts|
45
+ stream_name_parts.each_value do |partitions_info|
46
+ event_type_parts = partitions_info[:event_types]
47
+ stream_name_part = partitions_info[:stream_name_part]
48
+ event_type_parts.each_value do |event_type_part|
49
+ global_position = 0
50
+ loop do
51
+ events = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
52
+ conn.exec_params(<<~SQL, [global_position, CONCURRENCY, t])
53
+ select global_position, context, stream_name, type, stream_id, stream_revision
54
+ from #{event_type_part['table_name']}
55
+ where global_position > $1 and global_position % $2 = $3
56
+ order by global_position asc
57
+ limit 1_000
58
+ SQL
59
+ end.to_a
60
+ break if events.empty?
61
+
62
+ global_position = events.last['global_position']
63
+ stream_builders = events.map do |event|
64
+ sql_builder = PgEventstore::SQLBuilder.new.from('streams_global_index')
65
+ sql_builder.select(%( id, #{event['global_position']} as event_global_position ))
66
+ sql_builder.where(
67
+ 'stream_id = ? and partition_id = ?',
68
+ event['stream_id'], stream_name_part['id']
69
+ )
70
+ end
71
+ final_streams_builder = PgEventstore::SQLBuilder.union_builders(stream_builders)
72
+ stream_ids = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
73
+ conn.exec_params(*final_streams_builder.to_exec_params)
74
+ end.to_a
75
+ stream_ids = stream_ids.to_h { [_1['event_global_position'], _1['id']] }
76
+
77
+ events_idx_values = []
78
+ global_positions = []
79
+ events.each do |event|
80
+ stream_idx_id = stream_ids[event['global_position']]
81
+ ctx_partition_id = context_partitions[event['context']]
82
+ stream_name_partition_id = stream_name_part['id']
83
+ event_type_partition_id = event_type_part['id']
84
+ events_idx_values << "(#{event['global_position']}, #{ctx_partition_id}, #{stream_name_partition_id}, #{event_type_partition_id}, #{stream_idx_id}, #{event['stream_revision']})"
85
+ global_positions << "(#{event['global_position']})"
86
+ end
87
+
88
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
89
+ conn.exec(<<~SQL)
90
+ INSERT INTO events_global_index ("global_position", "context_partition_id", "stream_name_partition_id", "event_type_partition_id", "streams_global_index_id", "stream_revision") VALUES #{events_idx_values.join(',')};
91
+ INSERT INTO event_subscription_positions_unprocessed ("global_position") VALUES #{global_positions.join(',')};
92
+ SQL
93
+ end
94
+
95
+ lock.synchronize do
96
+ processed += events.size
97
+ next if Time.now - time < 2
98
+
99
+ time_was = time
100
+ time = Time.now
101
+
102
+ performance_info = <<~TEXT.strip
103
+ Processed: #{processed}. Left: #{total_events - processed}(approximately). \
104
+ Performance: #{((processed - processed_was) / (time - time_was)).round(2)} events/second.
105
+ TEXT
106
+ processed_was = processed
107
+ print "#{performance_info} \r"
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ threads.each(&:join)
116
+
117
+ puts
118
+
119
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
120
+ puts 'Running cluster on events_global_index. This may take some time.'
121
+ conn.exec('CLUSTER events_global_index USING idx_events_idx_on_global_position')
122
+ puts 'Running vacuum on events_global_index. This may take some time.'
123
+ conn.exec('VACUUM (ANALYZE) events_global_index')
124
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ event_subscription_position_queries = PgEventstore::EventSubscriptionPositionQueries.new(
4
+ PgEventstore.connection(:_eventstore_db_connection)
5
+ )
6
+
7
+ total_count = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
8
+ conn.exec('select count(*) as c_all from event_subscription_positions_unprocessed')
9
+ end.first['c_all'] || 0
10
+ processed = 0
11
+ puts "Assigning event subscription positions. Total count: #{total_count} events."
12
+
13
+ loop do
14
+ updated_num = event_subscription_position_queries.assign_subscription_position
15
+ break if updated_num == 0
16
+
17
+ processed += updated_num
18
+ print "Processed: #{processed}. Left: #{total_count - processed} \r"
19
+ end
20
+
21
+ puts
22
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
23
+ puts 'Running vacuum on event_subscription_positions_unprocessed. This may take some time.'
24
+ conn.exec('VACUUM (ANALYZE) event_subscription_positions_unprocessed')
25
+ end
@@ -0,0 +1,19 @@
1
+ update subscriptions
2
+ set current_position = (select subscription_position
3
+ from event_subscription_positions
4
+ where global_position <= subscriptions.current_position
5
+ order by global_position desc
6
+ limit 1)
7
+ where current_position is not null;
8
+
9
+ update subscriptions
10
+ set options = jsonb_set(
11
+ options,
12
+ '{from_position}',
13
+ to_jsonb(coalesce((select subscription_position
14
+ from event_subscription_positions
15
+ where global_position <= (subscriptions.options ->> 'from_position')::bigint
16
+ order by global_position desc
17
+ limit 1), 0))
18
+ )
19
+ where options ? 'from_position';
@@ -0,0 +1,2 @@
1
+ CREATE STATISTICS partition_parts_dep (dependencies) on context, stream_name, event_type FROM public.partitions;
2
+ ANALYZE public.partitions;
@@ -0,0 +1,2 @@
1
+ DROP INDEX IF EXISTS idx_events_0_stream_revision_global_position;
2
+ DROP VIEW IF EXISTS "$streams";
@@ -0,0 +1,5 @@
1
+ DROP INDEX idx_partitions_by_event_type;
2
+
3
+ CREATE INDEX idx_partitions_by_event_type_and_id ON public.partitions USING btree (event_type, id);
4
+ CREATE INDEX idx_partitions_by_context_and_stream_name_and_event_type_and_id ON public.partitions
5
+ USING btree (context, stream_name, event_type, id);
@@ -0,0 +1,3 @@
1
+ DROP INDEX IF EXISTS idx_events_stream_id_and_stream_revision;
2
+ DROP INDEX IF EXISTS idx_events_stream_id_and_global_position;
3
+ ALTER TABLE public.events DROP CONSTRAINT IF EXISTS events_pkey;
@@ -0,0 +1,16 @@
1
+ ALTER TABLE public.partitions
2
+ ADD COLUMN parent_stream_name_partition_id bigint;
3
+ ALTER TABLE public.partitions
4
+ ADD COLUMN parent_context_partition_id bigint;
5
+
6
+ UPDATE partitions p
7
+ SET parent_context_partition_id = (SELECT id
8
+ FROM partitions
9
+ WHERE context = p.context AND stream_name IS NULL AND event_type IS NULL)
10
+ WHERE stream_name IS NOT NULL;
11
+
12
+ UPDATE partitions p
13
+ SET parent_stream_name_partition_id = (SELECT id
14
+ FROM partitions
15
+ WHERE context = p.context AND stream_name = p.stream_name AND event_type IS NULL)
16
+ WHERE event_type IS NOT NULL;
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ puts <<~TEXT
4
+ Changing stream_revision from int to bigint. In order to do so we need to detach each event type partition, change \
5
+ stream_revision column type and attach the partition again to the partent table.
6
+ TEXT
7
+
8
+ filters = PgEventstore::QueryBuilders::Filters::Collection.new
9
+ partition_queries = PgEventstore::PartitionQueries.new(PgEventstore.connection(:_eventstore_db_connection))
10
+
11
+ event_type_partitions = partition_queries.partitions(filters)
12
+
13
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
14
+ event_type_partitions.each do |partition|
15
+ begin
16
+ partition_queries.detach_event_type_partition(partition)
17
+ rescue PG::Error
18
+ # the partition was already detached in previous runs. Just proceed to the next step
19
+ end
20
+
21
+ puts "Starting changing stream_revision from int to bigint for #{partition.event_type.inspect} event type partition."
22
+ conn.exec("ALTER TABLE #{partition.table_name} ALTER COLUMN stream_revision TYPE bigint")
23
+ end
24
+
25
+ conn.exec('ALTER TABLE events ALTER COLUMN stream_revision TYPE bigint')
26
+
27
+ event_type_partitions.each do |partition|
28
+ partition_queries.attach_event_type_partition(partition)
29
+ end
30
+ end
31
+
32
+ puts 'Running vacuum on events.'
33
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
34
+ conn.exec('VACUUM (ANALYZE) events')
35
+ end
@@ -0,0 +1,3 @@
1
+ DROP TRIGGER log_events_horizon ON events;
2
+ DROP FUNCTION log_events_horizon;
3
+ DROP TABLE events_horizon;
@@ -0,0 +1,8 @@
1
+ CREATE TABLE public.maintenance_tasks
2
+ (
3
+ task_name character varying COLLATE "POSIX" NOT NULL,
4
+ locked_at timestamp without time zone,
5
+ performed_at timestamp without time zone
6
+ );
7
+
8
+ CREATE UNIQUE INDEX idx_maintenance_tasks_task_name ON maintenance_tasks USING btree (task_name);
@@ -0,0 +1,35 @@
1
+ CREATE TABLE public.event_markers
2
+ (
3
+ id bigserial NOT NULL,
4
+ name text NOT NULL
5
+ );
6
+
7
+ CREATE UNIQUE INDEX idx_event_markers_on_name ON public.event_markers
8
+ USING btree (name) INCLUDE (id);
9
+
10
+ CREATE INDEX idx_event_markers_on_id ON public.event_markers
11
+ USING btree (id) INCLUDE (name);
12
+
13
+ CREATE TABLE public.event_markers_index
14
+ (
15
+ marker_id bigint NOT NULL,
16
+ streams_global_index_id bigint NOT NULL,
17
+ event_type_partition_id bigint NOT NULL,
18
+ global_position bigint NOT NULL,
19
+ stream_revision bigint NOT NULL
20
+ );
21
+
22
+ CREATE INDEX idx_event_markers_index_on_marker_N_stream_N_rev ON public.event_markers_index
23
+ USING btree (marker_id, streams_global_index_id, stream_revision) INCLUDE (global_position, event_type_partition_id);
24
+
25
+ CREATE INDEX idx_event_markers_index_on_marker_N_stream_N_partition_N_rev ON public.event_markers_index
26
+ USING btree (marker_id, streams_global_index_id, event_type_partition_id, stream_revision) INCLUDE (global_position);
27
+
28
+ CREATE INDEX idx_event_markers_index_on_marker_N_pos ON public.event_markers_index
29
+ USING btree (marker_id, global_position) INCLUDE (event_type_partition_id);
30
+
31
+ CREATE INDEX idx_event_markers_index_on_marker_N_partition_N_pos ON public.event_markers_index
32
+ USING btree (marker_id, event_type_partition_id, global_position);
33
+
34
+ CREATE INDEX idx_event_markers_index_on_pos ON public.event_markers_index
35
+ USING btree (global_position);
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
4
+ conn.transaction do
5
+ conn.exec(<<~SQL)
6
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
7
+ SQL
8
+ conn.exec(<<~SQL)
9
+ CREATE INDEX idx_partitions_context_and_stream_name_search ON public.partitions
10
+ USING GIN ((context || '#{PgEventstore::Event::SYSTEM_SYMBOL}' || stream_name) gin_trgm_ops)
11
+ WHERE stream_name IS NOT NULL AND event_type IS NULL;
12
+ SQL
13
+ conn.exec(<<~SQL)
14
+ CREATE INDEX idx_partitions_context_search ON public.partitions USING GIN (context gin_trgm_ops)
15
+ WHERE stream_name IS NULL and event_type IS NULL;
16
+ CREATE INDEX idx_partitions_event_type_search ON public.partitions USING GIN (event_type gin_trgm_ops)
17
+ WHERE event_type IS NOT NULL;
18
+
19
+ COMMENT ON INDEX idx_partitions_context_search IS 'Admin web UI search support.';
20
+ COMMENT ON INDEX idx_partitions_context_and_stream_name_search IS 'Admin web UI search support.';
21
+ COMMENT ON INDEX idx_partitions_event_type_search IS 'Admin web UI search support.';
22
+ SQL
23
+ end
24
+ end
@@ -0,0 +1 @@
1
+ ALTER TABLE public.events ALTER COLUMN id DROP DEFAULT;
@@ -0,0 +1,101 @@
1
+ CREATE FUNCTION public.create_context_partition_table()
2
+ RETURNS trigger
3
+ LANGUAGE plpgsql
4
+ AS $$
5
+ BEGIN
6
+ EXECUTE format(
7
+ 'CREATE TABLE public.%I PARTITION OF public.events FOR VALUES IN (%L) PARTITION BY LIST (stream_name)',
8
+ NEW.table_name,
9
+ NEW.context
10
+ );
11
+
12
+ EXECUTE format(
13
+ 'COMMENT ON TABLE public.%I IS %L',
14
+ NEW.table_name,
15
+ format('''%s'' context partition', NEW.context)
16
+ );
17
+
18
+ RETURN NEW;
19
+ END;
20
+ $$;
21
+
22
+ CREATE FUNCTION public.create_stream_name_partition_table()
23
+ RETURNS trigger
24
+ LANGUAGE plpgsql
25
+ AS $$
26
+ DECLARE
27
+ context_partition_name public.partitions.table_name%TYPE;
28
+ BEGIN
29
+ SELECT table_name
30
+ INTO STRICT context_partition_name
31
+ FROM public.partitions
32
+ WHERE id = NEW.parent_context_partition_id;
33
+
34
+ EXECUTE format(
35
+ 'CREATE TABLE public.%I PARTITION OF public.%I FOR VALUES IN (%L) PARTITION BY LIST (type)',
36
+ NEW.table_name,
37
+ context_partition_name,
38
+ NEW.stream_name
39
+ );
40
+
41
+ EXECUTE format(
42
+ 'COMMENT ON TABLE public.%I IS %L',
43
+ NEW.table_name,
44
+ format('''%s'' context and ''%s'' stream name partition', NEW.context, NEW.stream_name)
45
+ );
46
+
47
+ RETURN NEW;
48
+ END;
49
+ $$;
50
+
51
+ CREATE FUNCTION public.create_event_type_partition_table()
52
+ RETURNS trigger
53
+ LANGUAGE plpgsql
54
+ AS $$
55
+ DECLARE
56
+ stream_name_partition_name public.partitions.table_name%TYPE;
57
+ BEGIN
58
+ SELECT table_name
59
+ INTO STRICT stream_name_partition_name
60
+ FROM public.partitions
61
+ WHERE id = NEW.parent_stream_name_partition_id;
62
+
63
+ EXECUTE format(
64
+ 'CREATE TABLE public.%I PARTITION OF public.%I FOR VALUES IN (%L)',
65
+ NEW.table_name,
66
+ stream_name_partition_name,
67
+ NEW.event_type
68
+ );
69
+
70
+ EXECUTE format(
71
+ 'COMMENT ON TABLE public.%I IS %L',
72
+ NEW.table_name,
73
+ format(
74
+ '''%s'' context and ''%s'' stream name and ''%s'' event type partition',
75
+ NEW.context,
76
+ NEW.stream_name,
77
+ NEW.event_type
78
+ )
79
+ );
80
+
81
+ RETURN NEW;
82
+ END;
83
+ $$;
84
+
85
+ CREATE TRIGGER create_context_partition_table
86
+ AFTER INSERT ON public.partitions
87
+ FOR EACH ROW
88
+ WHEN (NEW.stream_name IS NULL AND NEW.event_type IS NULL)
89
+ EXECUTE FUNCTION public.create_context_partition_table();
90
+
91
+ CREATE TRIGGER create_stream_name_partition_table
92
+ AFTER INSERT ON public.partitions
93
+ FOR EACH ROW
94
+ WHEN (NEW.stream_name IS NOT NULL AND NEW.event_type IS NULL)
95
+ EXECUTE FUNCTION public.create_stream_name_partition_table();
96
+
97
+ CREATE TRIGGER create_event_type_partition_table
98
+ AFTER INSERT ON public.partitions
99
+ FOR EACH ROW
100
+ WHEN (NEW.event_type IS NOT NULL)
101
+ EXECUTE FUNCTION public.create_event_type_partition_table();