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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4799e200314277c53e9991483c429267213a9243a93c0ea85e7a23fa8c9b1ad
4
- data.tar.gz: 38c68fd540c903d19ac316aca849007f3b4c27ab94dc09cfb2e71a9b9acdd734
3
+ metadata.gz: 4425f2f4cffd24b06156465726034e4c8da383fed7f0576837851a65390068e0
4
+ data.tar.gz: bb809d708eaea98368d54bae14a8d35f36265a7a5ad66c4b8fd9b5dc57293163
5
5
  SHA512:
6
- metadata.gz: 347354eb5327bf7a38df0682710f9b5a676488910ed3d0e7e967c9bcccd1d4e4b784c87e178dc6f4b5fcea2c8f681bfa0209618cf58f76dd5b36db1577d656f0
7
- data.tar.gz: a567b9bfee1112e16d4f8b96b130e0f01c12b1f5349429c1ac873f4f3b4daa36628239b041eeecdc0f69a9807210d36bb30352dcb05b4100ef14eda37f1c6133
6
+ metadata.gz: e86a98610d16b95eeb4bffa5a3b0d74055ac7901a30f20d297aee1b37786e06f94ff483c135f99df255cd23f50b3cac4d9360c6338ccbdab7d58d14b05fc5578
7
+ data.tar.gz: cdfe9c6093814265d91905f1405ff39bbd347a4e844c75961f3b3db56eb0fb4f32537b4cd26a0bb7c46b82abb21d25cb2aa7831623466cd3e2a3d5d50caf4a42
data/.rubocop.yml CHANGED
@@ -27,7 +27,7 @@ RSpec/IndexedLet:
27
27
  Enabled: false
28
28
 
29
29
  RSpec/NestedGroups:
30
- Max: 6
30
+ Enabled: false
31
31
 
32
32
  RSpec/LetSetup:
33
33
  Enabled: false
@@ -124,3 +124,12 @@ Gemspec/RequiredRubyVersion:
124
124
 
125
125
  Gemspec/RequireMFA:
126
126
  Enabled: false
127
+
128
+ Style/StructInheritance:
129
+ Enabled: false
130
+
131
+ Lint/NonLocalExitFromIterator:
132
+ Enabled: false
133
+
134
+ Style/RegexpLiteral:
135
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,17 +1,141 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [3.0.0]
4
+
5
+ - New feature: pg_eventstore replication
6
+ - **Breaking change**: drop support of Ruby v3.2. The gem now requires Ruby v3.3+
7
+ - New feature: event tracing. You can now configure pg_eventstore to trace causation dependencies of events.
8
+ Read more [here](docs/event_tracing.md)
9
+ - **Breaking change**: when event is persisted using `#append_to_stream` - it now gets deserialized via each registered
10
+ middleware by default. Previously this wasn't the case - the deserialization phase was skipped. Such behavior was
11
+ creating ambiguity about assumptions when deserialization happens. If you need old behavior - you have to create
12
+ another middleware class which skips deserialization when publishing events and use it instead. Example:
13
+ Let's say you have this middleware configured
14
+ ```ruby
15
+ class MyMiddleware
16
+ include PgEventstore::Middleware
17
+
18
+ def serialize(event)
19
+ # do something with event before persisting it
20
+ end
21
+
22
+ def deserialize(event)
23
+ # do something with event after reading it from db
24
+ end
25
+ end
26
+
27
+ PgEventstore.configure do |config|
28
+ config.middleware = { my_middleware: MyMiddleware.new }
29
+ end
30
+ ```
31
+
32
+ Now define another middleware that has empty `#deserialize` method
33
+
34
+ ```ruby
35
+ class MyMiddlewareWithoutDeserialize < MyMiddleware
36
+ def deserialize(event)
37
+ end
38
+ end
39
+
40
+ PgEventstore.configure do |config|
41
+ config.middleware = {
42
+ my_middleware: MyMiddleware.new,
43
+ my_middleware_wo_deserialize: MyMiddlewareWithoutDeserialize.new
44
+ }
45
+ end
46
+ ```
47
+ and use only it when publishing events:
48
+
49
+ ```ruby
50
+ PgEventstore.client.append_to_stream(stream, event, middlewares: [:my_middleware_wo_deserialize])
51
+ ```
52
+
53
+ Alternatively, if you don't rely on `#multiple`, you can create another config specially for write operations:
54
+
55
+ ```ruby
56
+ # this is your default
57
+ PgEventstore.configure do |config|
58
+ config.middleware = { my_middleware: MyMiddleware.new }
59
+ end
60
+
61
+ PgEventstore.configure(name: :write) do |config|
62
+ config.middleware = { my_middleware: MyMiddlewareWithoutDeserialize.new }
63
+ end
64
+ ```
65
+
66
+ and use it for write operations:
67
+
68
+ ```ruby
69
+ PgEventstore.client(:write).append_to_stream(stream, event)
70
+ ```
71
+
72
+ - **Breaking change**: `Event#id` uniqueness is no longer guaranteed. It was dropped because there is not much usage of
73
+ it internally. The default value was moved from the database(it was `gen_random_uuid()`) to the application level and
74
+ is `SecureRandom.uuid_v7` now.
75
+ - **Breaking change**: event types, stream attributes, event metadata keys, markers that start from `▒`(`"\u2592"`
76
+ Unicode character) character are now reserved by pg_eventstore. It is less likely you have any, but if you do - you have
77
+ to adjust your implementation to no longer rely on it.
78
+ - New feature: event markers. You can now mark an event and later use those markers to read events, build a projection
79
+ using subscriptions or validate stream revision scoped to an event type and specific markers when publishing events as
80
+ a part of Dynamic Consistency Boundaries. Find more in [docs](docs/appending_events.md#event-markers)
81
+ - New config option `config.events_subscription_position_update_interval`. See more
82
+ in [Configuration](docs/configuration.md) docs
83
+ - **Breaking change**: `:from_position` option of `SubscriptionManager#subscribe`, "Current position" column in admin
84
+ web UI, "Reset position" form in admin web UI now refers to the event subscription position instead
85
+ `Event#global_position`. See more information in related
86
+ docs [Subscription position](docs/subscriptions.md#subscription-position)
87
+ - **Breaking change**: Drop `pg_cron` dependency
88
+ - Remove deprecated `SubscriptionsManager#force_lock!`
89
+ - New feature: explicit support of Dynamic Consistency Boundaries when publishing events. You can now provide "event
90
+ type"-to-"expected stream revision" hash map into `:expected_revision` option. It allows to validate against stream
91
+ revision at the specific event type instead validating the whole stream. Example:
92
+ `PgEventstore.client.append_to_stream(stream, events, options: { expected_revision: { 'Foo' => 10, 'Bar' => 20 } })`
93
+ - **Breaking change**: `WrongExpectedRevisionError#new` now requires `:verdict` keyword argument.
94
+ `Client#append_to_stream` now also may raise `WrongExpectedTypesRevisionError` error in case `:expected_revision`
95
+ option is "event type"-to-"expected stream revision" hash map
96
+ - Remove any practical limit of `Event#stream_revision` by changing its type to `bigint`. Stream size can be practically
97
+ unreachable now
98
+ - Fix issue when incomplete transaction block may be interrupted half way and commited successfully. The issue is
99
+ related to incorrect transaction termination flow during non-graceful thread shutdown(e.g. via `Thread.exit`)
100
+ - New feature: `Client#stream_revision` allows to quickly get stream's revision. Example:
101
+ `PgEventstore.client.stream_revision(stream) #=> number, greater than or equal to 0 or -1 if stream does not exist`
102
+ - **Breaking change**: `Client#read_grouped` now requires to provide `event_types` filter option explicitly. Example:
103
+ `PgEventstore.client.read_grouped(stream, options: { filter: { event_types: ['Foo', 'Bar'] } })`
104
+ - New feature: Subscription event types filter now support prefixes. So, if you have subscriptions like
105
+ `manager.subscribe('MyAwesomeSubscription', handler: my_handler, options: { filter: { event_types: ['Foo1', 'Foo2', 'Foo3'] } })`,
106
+ you can replace its filter as follows: `{ event_types: [{ prefix: 'Foo' }] }`. **Read API does not support this
107
+ feature yet**
108
+ - New feature: Read API now supports `:to_position`(and `:to_revision` when reading from specific stream) option. Use it
109
+ if you want to limit your result alongside `:max_count` option. Example:
110
+ `PgEventstore.client.read(PgEventstore::Stream.all_stream, options: { from_position: 100, to_position: 200 })`
111
+ - **Breaking change**: `Middleware#serialize` is now called outside SQL transaction that inserts an event tuple.
112
+ Previously it was happening right before an `INSERT` statement
113
+ - **Breaking change**: Database structure and the implementation of `#append_to_stream` was changed in a way it is not
114
+ compatible with v2. Public API stays the same
115
+ - New feature: allow subscription to process events in batches. You can now provide `in_batches: true` to `#subscribe`
116
+ to yield an array of events. See [docs](docs/subscriptions.md#processing-events-in-batches) for more info
117
+ - Introduce `PgEventstore::BasicConfig` and `PgEventstore::Extensions::ActsAsConfigurable` to allow to easily create
118
+ configurable objects
119
+
3
120
  ## [2.0.0]
4
121
 
5
122
  - **Breaking change**: `pg_eventstore` now requires [pg_cron](https://github.com/citusdata/pg_cron) extension
6
123
  - **Breaking change**: `pg_eventstore` now requires PostgreSQL v16+
7
124
  - Greatly decreased the number of connections, used by `pg_eventstore` subscriptions
8
125
  - **Breaking change**: drop support of Ruby v3.0 and v3.1. The gem now requires Ruby v3.2+
9
- - **Breaking change**: `PgEventstore::Extensions::OptionsExtension::Options` class is no longer a child of `Set` class - it has independent implementation now
126
+ - **Breaking change**: `PgEventstore::Extensions::OptionsExtension::Options` class is no longer a child of `Set` class -
127
+ it has independent implementation now
10
128
  - Add support of Ruby v4.0
11
- - `Client#multiple` method now accepts `read_only` keyword argument. When it is set to true - transaction is run in read-only mode
12
- - **Breaking change**: rework links implementation. This change boosts performance, but affects the database structure, so your previous database dumps become incompatible with this change. `PgEventstore::Event#link_id` was replaced by `PgEventstore::Event#link_global_position`
129
+ - `Client#multiple` method now accepts `read_only` keyword argument. When it is set to true - transaction is run in
130
+ read-only mode
131
+ - **Breaking change**: rework links implementation. This change boosts performance, but affects the database structure,
132
+ so your previous database dumps become incompatible with this change. `PgEventstore::Event#link_id` was replaced by
133
+ `PgEventstore::Event#link_global_position`
13
134
 
14
- Changes above require you to run migrations - `bundle exec rake pg_eventstore:migrate`. One of the migrations also migrates existing data using several concurrent workers(threads). You can adjust the number of workers using `CONCURRENCY` environment variable. Default number of concurrent workers is `10`. **Migrations require a downtime - no reads/writes should be performed during the time of the migrations, so plan your maintenance downtime accordingly.**
135
+ Changes above require you to run migrations - `bundle exec rake pg_eventstore:migrate`. One of the migrations also
136
+ migrates existing data using several concurrent workers(threads). You can adjust the number of workers using
137
+ `CONCURRENCY` environment variable. Default number of concurrent workers is `10`. **Migrations require a downtime - no
138
+ reads/writes should be performed during the time of the migrations, so plan your maintenance downtime accordingly.**
15
139
 
16
140
  ## [1.13.4]
17
141
 
@@ -31,7 +155,8 @@ Changes above require you to run migrations - `bundle exec rake pg_eventstore:mi
31
155
 
32
156
  ## [1.13.0]
33
157
 
34
- - Introduce automatic subscriptions recovery from connection errors. This way if a subscription process loses the connection to the database - it will be trying to reconnect until the connection is restored.
158
+ - Introduce automatic subscriptions recovery from connection errors. This way if a subscription process loses the
159
+ connection to the database - it will be trying to reconnect until the connection is restored.
35
160
  - Resolve ambiguity in usage of `PgEventstore.config` method. It now returns the frozen object.
36
161
 
37
162
  ## [1.12.0]
@@ -40,12 +165,17 @@ Changes above require you to run migrations - `bundle exec rake pg_eventstore:mi
40
165
 
41
166
  ## [1.11.0]
42
167
 
43
- - Add a global position that caused an error to the subscription's error JSON info. This will help you understand what event caused your subscription to fail.
168
+ - Add a global position that caused an error to the subscription's error JSON info. This will help you understand what
169
+ event caused your subscription to fail.
44
170
  - Improve long payloads in JSON preview in admin web UI in the way it does not moves content out of the visible area.
45
- - Admin UI: adjust events filtering and displaying of stream context, stream name, stream id and event type when values of them contain empty strings or non-displayable characters
171
+ - Admin UI: adjust events filtering and displaying of stream context, stream name, stream id and event type when values
172
+ of them contain empty strings or non-displayable characters
46
173
 
47
174
  ## [1.10.0]
48
- - Admin UI: Adjust `SubscriptionSet` "Stop"/"Delete" buttons appearance. Now if `SubscriptionsSet` is not alive anymore(the related process is dead or does not exist anymore) - "Delete" button is shown. If `SubscriptionSet` is alive - "Stop" button is shown
175
+
176
+ - Admin UI: Adjust `SubscriptionSet` "Stop"/"Delete" buttons appearance. Now if `SubscriptionsSet` is not alive anymore(
177
+ the related process is dead or does not exist anymore) - "Delete" button is shown. If `SubscriptionSet` is alive - "
178
+ Stop" button is shown
49
179
  - Admin IU: fixed several potential XSS vulnerabilities
50
180
  - Admin IU: Add "Copy to clipboard" button near stream id that copies ruby stream definition
51
181
  - Admin UI: allow deletion of streams with empty attribute values
@@ -57,23 +187,33 @@ Changes above require you to run migrations - `bundle exec rake pg_eventstore:mi
57
187
  - Add "Delete event" and "Delete stream" buttons into admin UI
58
188
 
59
189
  ## [1.8.0]
60
- - Introduce default config for admin web UI. Now if you define `:admin_web_ui` config - it will be preferred over default config
190
+
191
+ - Introduce default config for admin web UI. Now if you define `:admin_web_ui` config - it will be preferred over
192
+ default config
61
193
  - Fix pagination of events in admin UI
62
194
  - Improve partial index for `$streams` system stream
63
195
 
64
196
  ## [1.7.0]
197
+
65
198
  - Implement reading from `"$streams"` system stream
66
199
  - Disable Host authorization introduced in sinatra v4.1
67
200
 
68
201
  ## [1.6.0]
69
- - Introduce subscriptions CLI. Type `pg-eventstore subscriptions --help` to see available commands. The main purpose of it is to provide the single way to start/stop subscription processes. Check [Subscriptions](docs/subscriptions.md#creating-a-subscription) docs about the new way to start and keep running a subscriptions process.
202
+
203
+ - Introduce subscriptions CLI. Type `pg-eventstore subscriptions --help` to see available commands. The main purpose of
204
+ it is to provide the single way to start/stop subscription processes.
205
+ Check [Subscriptions](docs/subscriptions.md#creating-a-subscription) docs about the new way to start and keep running
206
+ a subscriptions process.
70
207
 
71
208
  ## [1.5.0]
209
+
72
210
  - Add ability to toggle link events in the admin UI
73
211
  - Mark linked events in the admin UI with "link" icon
74
212
 
75
213
  ## [1.4.0]
76
- - Add an ability to configure subscription graceful shutdown timeout globally and per subscription. Default value is 15 seconds. Previously it was hardcoded to 5 seconds. Examples:
214
+
215
+ - Add an ability to configure subscription graceful shutdown timeout globally and per subscription. Default value is 15
216
+ seconds. Previously it was hardcoded to 5 seconds. Examples:
77
217
 
78
218
  ```ruby
79
219
  # Set it globally, for all subscriptions
@@ -90,18 +230,24 @@ subscriptions_manager.subscribe(
90
230
  ```
91
231
 
92
232
  ## [1.3.4]
93
- - Fix `NoMethodError` error in `Client#read_paginated` when stream does not exist or when there are no events matching the given filter
233
+
234
+ - Fix `NoMethodError` error in `Client#read_paginated` when stream does not exist or when there are no events matching
235
+ the given filter
94
236
 
95
237
  ## [1.3.3]
238
+
96
239
  - Adjust default value of `subscription_max_retries` setting
97
240
 
98
241
  ## [1.3.2]
242
+
99
243
  - Fix UI when switching subscription status
100
244
 
101
245
  ## [1.3.1]
246
+
102
247
  - Swap "Search" button and "Add filter" button on Dashboard page
103
248
 
104
249
  ## [1.3.0]
250
+
105
251
  - Add ability to filter subscriptions by state in admin UI
106
252
  - Reset error-related subscription's attributes on subscription restore
107
253
  - Reset total processed events number when user changes subscription's position
@@ -109,9 +255,12 @@ subscriptions_manager.subscribe(
109
255
  - Relax sinatra version requirement to v3+
110
256
 
111
257
  ## [1.2.0]
258
+
112
259
  - Implement `failed_subscription_notifier` subscription hook.
113
260
 
114
- Now you are able to define a function that is called when subscription fails and no longer can be automatically restarted because it hit max number of retries. You can define the hook globally in the config and per subscription. Examples:
261
+ Now you are able to define a function that is called when subscription fails and no longer can be automatically
262
+ restarted because it hit max number of retries. You can define the hook globally in the config and per subscription.
263
+ Examples:
115
264
 
116
265
  ```ruby
117
266
  PgEventstore.configure do |config|
@@ -135,36 +284,48 @@ subscriptions_manager.subscribe(
135
284
  ```
136
285
 
137
286
  ## [1.1.5]
138
- - Review the way to handle SubscriptionAlreadyLockedError error. This removes noise when attempting to lock an already locked subscription.
287
+
288
+ - Review the way to handle SubscriptionAlreadyLockedError error. This removes noise when attempting to lock an already
289
+ locked subscription.
139
290
 
140
291
  ## [1.1.4]
292
+
141
293
  - Add rbs signatures
142
294
 
143
295
  ## [1.1.3]
296
+
144
297
  - Fix issue with assets caching between different gem's versions
145
298
 
146
299
  ## [1.1.2]
300
+
147
301
  - Improve web app compatibility with rails
148
302
 
149
303
  ## [1.1.1]
304
+
150
305
  - Allow case insensitive search by context, stream name and event type in admin UI
151
306
 
152
307
  ## [1.1.0]
308
+
153
309
  - Add "Reset position" button on Subscriptions Admin UI page
154
310
 
155
- **Note** This release includes a migration to support new functional. Please don't forget to run `rake pg_eventstore:migrate` to apply latest db changes.
311
+ **Note** This release includes a migration to support new functional. Please don't forget to run
312
+ `rake pg_eventstore:migrate` to apply latest db changes.
156
313
 
157
314
  ## [1.0.4]
315
+
158
316
  - Fix bug which caused slow Subscriptions to stop processing new events
159
317
  - Optimize Subscriptions update queries
160
318
 
161
319
  ## [1.0.3]
320
+
162
321
  - Do no update `Subscription#last_chunk_fed_at` if the chunk is empty
163
322
 
164
323
  ## [1.0.2]
324
+
165
325
  - UI: Fix opening of SubscriptionsSet tab of non-existing SubscriptionsSet
166
326
 
167
327
  ## [1.0.1]
328
+
168
329
  - Adjust assets urls to correctly act when mounting sinatra app under non-root url
169
330
 
170
331
  ## [1.0.0]
@@ -202,7 +363,8 @@ subscriptions_manager.subscribe(
202
363
 
203
364
  ## [0.8.0] - 2024-02-20
204
365
 
205
- - Allow float values for `subscription_pull_interval`. The default value of it was also set to `1.0`(it was `2` previously)
366
+ - Allow float values for `subscription_pull_interval`. The default value of it was also set to `1.0`(it was `2`
367
+ previously)
206
368
 
207
369
  ## [0.7.2] - 2024-02-14
208
370
 
@@ -214,7 +376,8 @@ subscriptions_manager.subscribe(
214
376
 
215
377
  ## [0.7.0] - 2024-02-09
216
378
 
217
- - Refactor `pg_eventstore:create` and `pg_eventstore:drop` rake tasks. They now actually create/drop the database. You will have to execute `delete from migrations where number > 6` query before deploying this version.
379
+ - Refactor `pg_eventstore:create` and `pg_eventstore:drop` rake tasks. They now actually create/drop the database. You
380
+ will have to execute `delete from migrations where number > 6` query before deploying this version.
218
381
  - Drop legacy migrations
219
382
 
220
383
  ## [0.6.0] - 2024-02-08
@@ -259,9 +422,11 @@ subscriptions_manager.subscribe(
259
422
 
260
423
  ## [0.2.4] - 2023-12-20
261
424
 
262
- Due to performance issues under certain circumstances, searching by event type was giving bad performance. I decided to extract `type` column from `events` table into separated table. **No breaking changes in public API though.**
425
+ Due to performance issues under certain circumstances, searching by event type was giving bad performance. I decided to
426
+ extract `type` column from `events` table into separated table. **No breaking changes in public API though.**
263
427
 
264
- **Warning** The migrations this version has, requires you to shut down applications that use `pg_eventstore` and only then run `rake pg_eventstore:migrate`.
428
+ **Warning** The migrations this version has, requires you to shut down applications that use `pg_eventstore` and only
429
+ then run `rake pg_eventstore:migrate`.
265
430
 
266
431
  ## [0.2.3] - 2023-12-18
267
432
 
@@ -273,7 +438,8 @@ Due to performance issues under certain circumstances, searching by event type w
273
438
 
274
439
  ## [0.2.1] - 2023-12-14
275
440
 
276
- Under certain circumstances `PG::TRSerializationFailure` exception wasn't retried. Adjust connection's states list to fix that.
441
+ Under certain circumstances `PG::TRSerializationFailure` exception wasn't retried. Adjust connection's states list to
442
+ fix that.
277
443
 
278
444
  ## [0.2.0] - 2023-12-14
279
445
 
data/README.md CHANGED
@@ -4,11 +4,21 @@ Implements database and API to store and read events in event sourced systems.
4
4
 
5
5
  ## Requirements
6
6
 
7
- - `pg_eventstore` requires a PostgreSQL v16+ with [pg_cron](https://github.com/citusdata/pg_cron) extension installed.
8
- - `pg_evenstore` requires a separate detabase. However, it is recommended that you spin it up on a separate PostgreSQL instance in a production environment.
7
+ - `pg_eventstore` requires a PostgreSQL v16+.
8
+ - `pg_evenstore` requires a separate database. However, it is recommended that you spin it up on a separate PostgreSQL instance in a production environment.
9
9
  - `pg_eventstore` requires `default_transaction_isolation` server config option to be set to `'read committed'` (default behavior). Having this value set to move strict isolation level may result in unexpected behavior.
10
10
  - It is recommended to use a connection pooler (for example [PgBouncer](https://www.pgbouncer.org/)) in `transaction` pool mode to lower the load on a database.
11
- - `pg_eventstore` requires ruby v3+. The development of this gem is targeted at [current](https://endoflife.date/ruby) ruby versions.
11
+ - `pg_eventstore` requires ruby v3.3+. The development of this gem is targeted at [current](https://endoflife.date/ruby) ruby versions.
12
+
13
+ ### Migrating to v3
14
+
15
+ If you are migrating from v2 - please don't forget to delete cron jobs and `pg_cron` extension after migration to v3.
16
+ You can remove cron jobs as follows:
17
+
18
+ ```sql
19
+ SELECT cron.unschedule('prune_eventstore_events_horizon');
20
+ SELECT cron.unschedule('delete-job-run-details');
21
+ ```
12
22
 
13
23
  ## Installation
14
24
 
@@ -47,26 +57,22 @@ Documentation chapters:
47
57
  - [Appending events](docs/appending_events.md)
48
58
  - [Linking events](docs/linking_events.md)
49
59
  - [Reading events](docs/reading_events.md)
60
+ - [Reading streams](docs/reading_streams.md)
61
+ - [Tracing events](docs/event_tracing.md)
50
62
  - [Subscriptions](docs/subscriptions.md)
51
63
  - [Maintenance functions](docs/maintenance.md)
52
64
  - [Writing middlewares](docs/writing_middleware.md)
53
65
  - [How to make multiple commands atomic](docs/multiple_commands.md)
54
66
  - [Admin UI](docs/admin_ui.md)
67
+ - [Replication](docs/replication.md)
68
+
69
+ To help your AI assistant to better recognize the capabilities of this gem - there is a crafted [instructions](docs/AGENTS.fragment.md)
70
+ you can include into your `AGENTS.md`.
55
71
 
56
72
  ## CLI
57
73
 
58
74
  The gem is shipped with its own CLI. Use `pg-eventstore --help` to find out its capabilities.
59
75
 
60
- ## Maintenance
61
-
62
- You may want to backup your eventstore database. It is important to mention that you don't want to dump/restore records of `events_horizon` table. `events_horizon` table is used to supply subscriptions functionality and contains temporary data which is scoped to the PostgreSQL cluster they were created in. **Thus, it is even may be harmful if you restore records from this table into a new PostgreSQL cluster. Simply exclude that table's data when performing backups.** Example:
63
-
64
- ```bash
65
- pg_dump --exclude-table-data=events_horizon eventstore -U postgres > eventstore.sql
66
- ```
67
-
68
- Also, it is important you create and migrate new database via provided rake commands - they include an important setup of `pg_cron` jobs as well. **Even if you would like to restore your db backup on clean PostgreSQL instance - please initialize pg_eventstore via built-in tools first.**
69
-
70
76
  ## RSpec
71
77
 
72
78
  ### Clean up test db
@@ -117,14 +123,18 @@ end
117
123
  After checking out the repo, run:
118
124
  - `bundle` to install dependencies
119
125
  - `docker compose up` to start dev/test services
126
+ - `bundle exec rake compile` to compile native extension
120
127
  - `bin/setup_db` to create/re-create development and test databases, tables and related objects
121
128
  - `bundle exec rbs collection install` to install external rbs definitions
122
129
 
123
- Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
130
+ Then, run `bin/rspec` to run the tests(or just `rspec` if you want to temporary skip rbs checks). Optional env variables for testing:
131
+ - `DEBUG=1 bin/rspec`. Enable nicely formatted and highlighted SQL output into stdout.
132
+
133
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
124
134
 
125
135
  To install this gem onto your local machine, run `bundle exec rake install`.
126
136
 
127
- To run admin UI web server - run `puma` in your terminal. By default it will start web server on `http://0.0.0.0:9292`.
137
+ To run admin UI web server - run `puma` in your terminal. By default, it will start web server on `http://0.0.0.0:9292`.
128
138
 
129
139
  ### Benchmarks
130
140
 
@@ -1,23 +1 @@
1
- # frozen_string_literal: true
2
-
3
- PgEventstore.connection(:_postgres_db_connection).with do |conn|
4
- conn.exec(<<~SQL)
5
- CREATE EXTENSION IF NOT EXISTS pg_cron
6
- SQL
7
- conn.exec_params(<<~SQL, ["prune_#{PgEventstore::MigrationHelpers.db_name}_events_horizon", PgEventstore::MigrationHelpers.db_name])
8
- SELECT cron.schedule_in_database(
9
- $1,
10
- '*/10 * * * *',
11
- $$DELETE FROM events_horizon WHERE xact_id <= (SELECT xact_id FROM events_horizon ORDER BY xact_id DESC OFFSET 100 LIMIT 1)$$,
12
- $2
13
- )
14
- SQL
15
- # Store information about finished cron jobs for 1 day
16
- conn.exec(<<~SQL)
17
- SELECT cron.schedule(
18
- 'delete-job-run-details',
19
- '0 12 * * *',
20
- $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '1 day'$$
21
- );
22
- SQL
23
- end
1
+ # pg_cron was removed. This file is kept for informative purpose.
@@ -0,0 +1,17 @@
1
+ CREATE TABLE public.streams_global_index
2
+ (
3
+ id bigserial NOT NULL,
4
+ partition_id bigint NOT NULL,
5
+ stream_id character varying COLLATE "POSIX" NOT NULL,
6
+ stream_revision bigint NOT NULL,
7
+ starting_position bigint NOT NULL
8
+ );
9
+
10
+ ALTER TABLE ONLY public.streams_global_index
11
+ ADD CONSTRAINT streams_global_index_pkey PRIMARY KEY (id);
12
+
13
+ CREATE UNIQUE INDEX idx_streams_global_index_on_stream_id_and_partition_id ON public.streams_global_index
14
+ USING btree (stream_id, partition_id) INCLUDE (id);
15
+
16
+ CREATE INDEX idx_streams_global_index_on_starting_position ON public.streams_global_index
17
+ USING btree (starting_position);
@@ -0,0 +1,150 @@
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 * 10
7
+ end
8
+
9
+ partitions = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
10
+ conn.exec('select * from partitions where event_type is null')
11
+ end.to_a
12
+
13
+ stream_name_partitions = partitions.reject { _1['stream_name'].nil? }.group_by { _1['context'] }
14
+ stream_name_partitions = stream_name_partitions.to_h do |context, context_parts|
15
+ context_parts = context_parts.to_h { |p| [p['stream_name'], p['id']] }
16
+ [context, context_parts]
17
+ end
18
+
19
+ total_streams = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
20
+ conn.exec('explain select * from "$streams"')
21
+ end.first['QUERY PLAN'][/rows=\d+/].sub('rows=', '').to_i
22
+
23
+ tables = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
24
+ conn.exec('select table_name from partitions where event_type is not null')
25
+ end.to_a.map { _1['table_name'] }
26
+
27
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
28
+ conn.exec('delete from streams_global_index')
29
+ end
30
+
31
+ puts "Indexing streams. Approximate number of streams to process: #{total_streams}. Concurrency is #{CONCURRENCY} concurrent writers."
32
+ puts <<~TEXT
33
+ This migration consumes CONCURRENCY x10 connections. Make sure you have plenty of them. Migrations are idempotent, \
34
+ so you can break, adjust settings if needed and then start over.
35
+ TEXT
36
+ puts
37
+
38
+ processed = 0
39
+ processed_was = 0
40
+ time = Time.now
41
+ lock = Thread::Mutex.new
42
+ sub_partitions_count_cache = {}
43
+ threads = CONCURRENCY.times.map do |t|
44
+ Thread.new do
45
+ tables.each do |table|
46
+ global_position = 0
47
+ loop do
48
+ events = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
49
+ conn.exec_params(<<~SQL, [global_position, CONCURRENCY, t])
50
+ select context, stream_name, stream_id, global_position
51
+ from "#{table}"
52
+ where global_position > $1 and global_position % $2 = $3 and stream_revision = 0
53
+ group by context, stream_name, stream_id, global_position
54
+ order by global_position asc
55
+ limit 100
56
+ SQL
57
+ end.to_a
58
+ break if events.empty?
59
+
60
+ global_position = events.last['global_position']
61
+
62
+ # retrieve stream revision
63
+ events.each_slice(10) do |sliced_events|
64
+ query_runner = PgEventstore::AsyncRunner.new
65
+ sliced_events.each do |event|
66
+ query_runner.async do
67
+ query_strategy = PgEventstore::QueryStrategy::Async.new(PgEventstore.connection(:_eventstore_db_connection))
68
+ context = event['context']
69
+ stream_name = event['stream_name']
70
+ stream_id = event['stream_id']
71
+ sub_partitions_count_cache[[context, stream_name]] ||= query_strategy.exec_params(
72
+ 'select count(*) as c_all from partitions where context = $1 and stream_name = $2',
73
+ [context, stream_name]
74
+ ).first['c_all']
75
+ if sub_partitions_count_cache[[context, stream_name]] > 50
76
+ sub_partitions = query_strategy.exec_params(<<~SQL, [context, stream_name]).map { _1['table_name'] }
77
+ select table_name from partitions where context = $1 and stream_name = $2 and event_type is not null
78
+ SQL
79
+ max_revisions = []
80
+ sub_partitions.each_slice(50) do |table_names|
81
+ q_parts = table_names.map do |table_name|
82
+ <<~SQL
83
+ select max(stream_revision) as stream_revision
84
+ from #{table_name}
85
+ where context = $1 and stream_name = $2 and stream_id = $3
86
+ SQL
87
+ end
88
+ q_parts = q_parts.map { "(#{_1})" }.join(' union all ')
89
+ q = "select coalesce(max(stream_revision), -1) as stream_revision from (#{q_parts})"
90
+ max_revisions.push(
91
+ query_strategy.exec_params(q, [context, stream_name, stream_id]).first['stream_revision']
92
+ )
93
+ end
94
+ event['stream_revision'] = max_revisions.max
95
+ else
96
+ q = <<~SQL
97
+ select max(stream_revision) as stream_revision
98
+ from events
99
+ where context = $1 and stream_name = $2 and stream_id = $3
100
+ SQL
101
+ attrs = query_strategy.exec_params(q, [context, stream_name, stream_id]).to_a.first
102
+ event['stream_revision'] = attrs['stream_revision']
103
+ end
104
+ end
105
+ end
106
+ query_runner.run
107
+ end
108
+
109
+ values = events.map do |event|
110
+ partition_id = stream_name_partitions[event['context']][event['stream_name']]
111
+ stream_id = PG::Connection.escape(event['stream_id'])
112
+ stream_revision = event['stream_revision']
113
+ starting_position = event['global_position']
114
+ "(#{partition_id}, '#{stream_id}', #{stream_revision}, #{starting_position})"
115
+ end.join(',')
116
+
117
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
118
+ conn.exec(<<~SQL)
119
+ INSERT INTO streams_global_index
120
+ ("partition_id", "stream_id", "stream_revision", "starting_position")
121
+ VALUES #{values}
122
+ SQL
123
+ end
124
+
125
+ lock.synchronize do
126
+ processed += events.size
127
+ next if Time.now - time < 2
128
+
129
+ time_was = time
130
+ time = Time.now
131
+
132
+ performance_info = <<~TEXT.strip
133
+ Processed: #{processed}. Left: #{total_streams - processed} (approximately). \
134
+ Performance: #{((processed - processed_was) / (time - time_was)).round(2)} streams/second.
135
+ TEXT
136
+ processed_was = processed
137
+ print "#{performance_info} \r"
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ threads.each(&:join)
144
+
145
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
146
+ puts 'Running cluster on streams_global_index. This may take some time.'
147
+ conn.exec('CLUSTER streams_global_index USING idx_streams_global_index_on_starting_position')
148
+ puts 'Running vacuum on streams_global_index. This may take some time.'
149
+ conn.exec('VACUUM (ANALYZE) streams_global_index')
150
+ end