pg_eventstore 1.13.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -2
  3. data/CHANGELOG.md +195 -16
  4. data/README.md +33 -10
  5. data/db/migrations/10_setup_pg_cron.rb +1 -0
  6. data/db/migrations/11_add_events_link_global_position.sql +1 -0
  7. data/db/migrations/12_migrate_legacy_links.rb +83 -0
  8. data/db/migrations/13_remove_events_link_id.sql +6 -0
  9. data/db/migrations/14_remove_ids_events_id_index.sql +1 -0
  10. data/db/migrations/15_create_streams_global_index.sql +17 -0
  11. data/db/migrations/16_populate_streams_global_index.rb +150 -0
  12. data/db/migrations/17_create_events_global_index.sql +62 -0
  13. data/db/migrations/18_populate_events_global_index.rb +124 -0
  14. data/db/migrations/19_populate_events_global_index_p2.rb +25 -0
  15. data/db/migrations/20_migrate_subscriptions_current_position.sql +19 -0
  16. data/db/migrations/21_create_partitions_stats.sql +2 -0
  17. data/db/migrations/22_drop_streams_sql_view.sql +2 -0
  18. data/db/migrations/23_adjust_partitions_indexes.sql +5 -0
  19. data/db/migrations/24_drop_legacy_events_indexes.sql +3 -0
  20. data/db/migrations/25_add_parent_partitions_info_to_partitions.sql +16 -0
  21. data/db/migrations/26_change_events_stream_revision_type.rb +35 -0
  22. data/db/migrations/27_drop_events_horizon.sql +3 -0
  23. data/db/migrations/28_create_maintenance_tasks.sql +8 -0
  24. data/db/migrations/29_create_event_markers.sql +35 -0
  25. data/db/migrations/30_improve_partitions_search_in_admin_ui.rb +24 -0
  26. data/db/migrations/31_drop_events_id_default_value.sql +1 -0
  27. data/db/migrations/32_create_parittions_triggers.sql +101 -0
  28. data/db/migrations/9_create_events_horizon.sql +21 -0
  29. data/docs/AGENTS.fragment.md +696 -0
  30. data/docs/admin_ui.md +3 -1
  31. data/docs/appending_events.md +110 -49
  32. data/docs/configuration.md +69 -36
  33. data/docs/event_tracing.md +55 -0
  34. data/docs/events_and_streams.md +54 -30
  35. data/docs/how_it_works.md +57 -16
  36. data/docs/linking_events.md +2 -1
  37. data/docs/maintenance.md +14 -5
  38. data/docs/multiple_commands.md +30 -6
  39. data/docs/reading_events.md +208 -43
  40. data/docs/reading_streams.md +50 -0
  41. data/docs/replication.md +99 -0
  42. data/docs/subscriptions.md +165 -35
  43. data/docs/writing_middleware.md +25 -8
  44. data/ext/pg_eventstore_ext/extconf.rb +5 -0
  45. data/ext/pg_eventstore_ext/pg_eventstore_ext.c +74 -0
  46. data/lib/pg_eventstore/async_runner.rb +63 -0
  47. data/lib/pg_eventstore/basic_config.rb +15 -0
  48. data/lib/pg_eventstore/callbacks.rb +7 -5
  49. data/lib/pg_eventstore/chunks/chunk.rb +31 -0
  50. data/lib/pg_eventstore/chunks/read_api_events_index_chunk.rb +83 -0
  51. data/lib/pg_eventstore/chunks/replica_events_index_chunk.rb +35 -0
  52. data/lib/pg_eventstore/chunks/repository.rb +73 -0
  53. data/lib/pg_eventstore/chunks/subscription_checkpoint_chunk.rb +48 -0
  54. data/lib/pg_eventstore/chunks/subscription_events_index_chunk.rb +99 -0
  55. data/lib/pg_eventstore/chunks.rb +14 -0
  56. data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +2 -2
  57. data/lib/pg_eventstore/client.rb +167 -52
  58. data/lib/pg_eventstore/commands/append.rb +62 -43
  59. data/lib/pg_eventstore/commands/delete_event.rb +7 -8
  60. data/lib/pg_eventstore/commands/delete_stream.rb +3 -3
  61. data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +11 -6
  62. data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
  63. data/lib/pg_eventstore/commands/link_to.rb +9 -9
  64. data/lib/pg_eventstore/commands/multiple.rb +2 -2
  65. data/lib/pg_eventstore/commands/read.rb +23 -5
  66. data/lib/pg_eventstore/commands/read_grouped.rb +46 -0
  67. data/lib/pg_eventstore/commands/read_streams.rb +18 -0
  68. data/lib/pg_eventstore/commands/read_streams_paginated.rb +56 -0
  69. data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +4 -4
  70. data/lib/pg_eventstore/commands/revision_check/current_revision.rb +51 -0
  71. data/lib/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rb +46 -0
  72. data/lib/pg_eventstore/commands/revision_check/expected_revision.rb +135 -0
  73. data/lib/pg_eventstore/commands/revision_check/stream_revision_check.rb +98 -0
  74. data/lib/pg_eventstore/commands/revision_check/stream_revision_comparison.rb +36 -0
  75. data/lib/pg_eventstore/commands/stream_revision.rb +14 -0
  76. data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +4 -4
  77. data/lib/pg_eventstore/commands.rb +4 -2
  78. data/lib/pg_eventstore/config.rb +30 -9
  79. data/lib/pg_eventstore/connection.rb +22 -37
  80. data/lib/pg_eventstore/errors.rb +137 -11
  81. data/lib/pg_eventstore/event.rb +39 -7
  82. data/lib/pg_eventstore/event_deserializer.rb +5 -4
  83. data/lib/pg_eventstore/event_global_index.rb +118 -0
  84. data/lib/pg_eventstore/event_marker.rb +16 -0
  85. data/lib/pg_eventstore/event_marker_index.rb +25 -0
  86. data/lib/pg_eventstore/event_serializer.rb +6 -0
  87. data/lib/pg_eventstore/extensions/acts_as_configurable.rb +88 -0
  88. data/lib/pg_eventstore/extensions/options_defaults.rb +25 -0
  89. data/lib/pg_eventstore/extensions/options_extension.rb +42 -13
  90. data/lib/pg_eventstore/feature_marker.rb +18 -0
  91. data/lib/pg_eventstore/maintenance.rb +11 -6
  92. data/lib/pg_eventstore/middleware/event_tracing.rb +53 -0
  93. data/lib/pg_eventstore/partition.rb +6 -0
  94. data/lib/pg_eventstore/pg_connection.rb +70 -14
  95. data/lib/pg_eventstore/queries/event_marker_queries.rb +74 -0
  96. data/lib/pg_eventstore/queries/event_queries.rb +9 -95
  97. data/lib/pg_eventstore/queries/events_global_index_queries.rb +131 -0
  98. data/lib/pg_eventstore/queries/index_filtering_queries.rb +179 -0
  99. data/lib/pg_eventstore/queries/links_resolver.rb +10 -7
  100. data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
  101. data/lib/pg_eventstore/queries/partition_queries.rb +46 -104
  102. data/lib/pg_eventstore/queries/replica_queries.rb +110 -0
  103. data/lib/pg_eventstore/queries/streams_global_index_queries.rb +163 -0
  104. data/lib/pg_eventstore/queries/transaction_queries.rb +58 -10
  105. data/lib/pg_eventstore/queries.rb +29 -1
  106. data/lib/pg_eventstore/query_builders/basic_filtering.rb +38 -5
  107. data/lib/pg_eventstore/query_builders/event_markers_filtering.rb +47 -0
  108. data/lib/pg_eventstore/query_builders/event_markers_index_filtering.rb +275 -0
  109. data/lib/pg_eventstore/query_builders/event_subscription_positions_filtering.rb +37 -0
  110. data/lib/pg_eventstore/query_builders/events_filtering.rb +15 -164
  111. data/lib/pg_eventstore/query_builders/events_global_index_filtering.rb +227 -0
  112. data/lib/pg_eventstore/query_builders/filters/collection.rb +324 -0
  113. data/lib/pg_eventstore/query_builders/filters/event_type_filter.rb +37 -0
  114. data/lib/pg_eventstore/query_builders/filters/filter_row.rb +54 -0
  115. data/lib/pg_eventstore/query_builders/filters/marker_filter.rb +20 -0
  116. data/lib/pg_eventstore/query_builders/filters/marker_filter_row.rb +49 -0
  117. data/lib/pg_eventstore/query_builders/filters/stream_filter.rb +48 -0
  118. data/lib/pg_eventstore/query_builders/index_based_events_filtering.rb +260 -0
  119. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +175 -51
  120. data/lib/pg_eventstore/query_builders/read_cursor/stream_cursor.rb +122 -0
  121. data/lib/pg_eventstore/query_builders/streams_global_index_filtering.rb +83 -0
  122. data/lib/pg_eventstore/query_builders/subscription_events_filtering.rb +79 -0
  123. data/lib/pg_eventstore/query_strategy/async.rb +64 -0
  124. data/lib/pg_eventstore/query_strategy/foreground.rb +25 -0
  125. data/lib/pg_eventstore/query_strategy.rb +19 -0
  126. data/lib/pg_eventstore/raw_event.rb +46 -0
  127. data/lib/pg_eventstore/rspec/test_helpers.rb +21 -8
  128. data/lib/pg_eventstore/sql_builder.rb +87 -8
  129. data/lib/pg_eventstore/stream.rb +14 -17
  130. data/lib/pg_eventstore/stream_global_index.rb +28 -0
  131. data/lib/pg_eventstore/subscriptions/basic_runner.rb +4 -4
  132. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +5 -17
  133. data/lib/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rb +36 -0
  134. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +13 -1
  135. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +19 -14
  136. data/lib/pg_eventstore/subscriptions/events_processor.rb +21 -19
  137. data/lib/pg_eventstore/subscriptions/events_processor_consumer/multiple.rb +63 -0
  138. data/lib/pg_eventstore/subscriptions/events_processor_consumer/replica.rb +68 -0
  139. data/lib/pg_eventstore/subscriptions/events_processor_consumer/single.rb +62 -0
  140. data/lib/pg_eventstore/subscriptions/events_processor_consumer.rb +44 -0
  141. data/lib/pg_eventstore/subscriptions/events_subscription_position_worker.rb +68 -0
  142. data/lib/pg_eventstore/subscriptions/queries/event_subscription_position_queries.rb +177 -0
  143. data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +5 -5
  144. data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +80 -56
  145. data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +2 -2
  146. data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_queries.rb +1 -1
  147. data/lib/pg_eventstore/subscriptions/replica_subscription_handler.rb +140 -0
  148. data/lib/pg_eventstore/subscriptions/replica_subscription_runner.rb +9 -0
  149. data/lib/pg_eventstore/subscriptions/subscription.rb +9 -7
  150. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/collection.rb +36 -0
  151. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/index_read_strategy.rb +82 -0
  152. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/replication_strategy.rb +75 -0
  153. data/lib/pg_eventstore/subscriptions/subscription_feed_strategy.rb +31 -0
  154. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +26 -2
  155. data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +5 -5
  156. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -22
  157. data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
  158. data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +10 -11
  159. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
  160. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +84 -29
  161. data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
  162. data/lib/pg_eventstore/tasks/setup.rake +32 -33
  163. data/lib/pg_eventstore/utils.rb +39 -0
  164. data/lib/pg_eventstore/version.rb +1 -1
  165. data/lib/pg_eventstore/web/application.rb +104 -20
  166. data/lib/pg_eventstore/web/paginator/base_collection.rb +5 -1
  167. data/lib/pg_eventstore/web/paginator/event_types_collection.rb +14 -4
  168. data/lib/pg_eventstore/web/paginator/events_collection.rb +29 -33
  169. data/lib/pg_eventstore/web/paginator/helpers.rb +19 -12
  170. data/lib/pg_eventstore/web/paginator/markers_collection.rb +48 -0
  171. data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +13 -2
  172. data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +21 -11
  173. data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +18 -4
  174. data/lib/pg_eventstore/web/paginator/streams_collection.rb +95 -0
  175. data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +49 -22
  176. data/lib/pg_eventstore/web/subscriptions/helpers.rb +2 -2
  177. data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
  178. data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +1 -1
  179. data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
  180. data/lib/pg_eventstore/web/subscriptions/with_state/subscriptions.rb +1 -1
  181. data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -9
  182. data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +18 -5
  183. data/lib/pg_eventstore/web/views/home/partials/events.erb +38 -1
  184. data/lib/pg_eventstore/web/views/home/partials/markers_filter.erb +18 -0
  185. data/lib/pg_eventstore/web/views/layouts/application.erb +6 -0
  186. data/lib/pg_eventstore/web/views/streams/index.erb +110 -0
  187. data/lib/pg_eventstore/web/views/streams/partials/streams.erb +11 -0
  188. data/lib/pg_eventstore/web/views/subscriptions/index.erb +4 -1
  189. data/lib/pg_eventstore/web.rb +2 -0
  190. data/lib/pg_eventstore.rb +26 -63
  191. data/pg_eventstore.gemspec +6 -4
  192. data/sig/interfaces/_raw_events_handler.rbs +3 -0
  193. data/sig/interfaces/subscription_handler.rbs +1 -1
  194. data/sig/pg/result.rbs +9 -0
  195. data/sig/pg_eventstore/async_runner.rbs +13 -0
  196. data/sig/pg_eventstore/basic_config.rbs +9 -0
  197. data/sig/pg_eventstore/chunks/chunk.rbs +13 -0
  198. data/sig/pg_eventstore/chunks/read_api_events_index_chunk.rbs +30 -0
  199. data/sig/pg_eventstore/chunks/replica_events_index_chunk.rbs +11 -0
  200. data/sig/pg_eventstore/chunks/repository.rbs +22 -0
  201. data/sig/pg_eventstore/chunks/subscription_checkpoint_chunk.rbs +18 -0
  202. data/sig/pg_eventstore/chunks/subscription_events_index_chunk.rbs +37 -0
  203. data/sig/pg_eventstore/client.rbs +41 -43
  204. data/sig/pg_eventstore/commands/append.rbs +11 -25
  205. data/sig/pg_eventstore/commands/event_modifiers/prepare_link_event.rbs +9 -12
  206. data/sig/pg_eventstore/commands/event_modifiers/prepare_regular_event.rbs +5 -4
  207. data/sig/pg_eventstore/commands/link_to.rbs +6 -11
  208. data/sig/pg_eventstore/commands/multiple.rbs +1 -1
  209. data/sig/pg_eventstore/commands/read.rbs +2 -5
  210. data/sig/pg_eventstore/commands/read_grouped.rbs +7 -0
  211. data/sig/pg_eventstore/commands/read_streams.rbs +7 -0
  212. data/sig/pg_eventstore/commands/read_streams_paginated.rbs +16 -0
  213. data/sig/pg_eventstore/commands/regular_stream_read_paginated.rbs +9 -17
  214. data/sig/pg_eventstore/commands/revision_check/current_revision.rbs +25 -0
  215. data/sig/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rbs +14 -0
  216. data/sig/pg_eventstore/commands/revision_check/expected_revision.rbs +67 -0
  217. data/sig/pg_eventstore/commands/revision_check/stream_revision_check.rbs +20 -0
  218. data/sig/pg_eventstore/commands/revision_check/stream_revision_comparison.rbs +9 -0
  219. data/sig/pg_eventstore/commands/stream_revision.rbs +7 -0
  220. data/sig/pg_eventstore/commands/system_stream_read_paginated.rbs +9 -14
  221. data/sig/pg_eventstore/config.rbs +15 -6
  222. data/sig/pg_eventstore/connection.rbs +11 -18
  223. data/sig/pg_eventstore/errors.rbs +64 -34
  224. data/sig/pg_eventstore/event.rbs +20 -6
  225. data/sig/pg_eventstore/event_deserializer.rbs +5 -10
  226. data/sig/pg_eventstore/event_global_index.rbs +60 -0
  227. data/sig/pg_eventstore/event_marker.rbs +9 -0
  228. data/sig/pg_eventstore/event_marker_index.rbs +12 -0
  229. data/sig/pg_eventstore/extensions/acts_as_configurable.rbs +29 -0
  230. data/sig/pg_eventstore/extensions/options_defaults.rbs +7 -0
  231. data/sig/pg_eventstore/extensions/options_extension.rbs +9 -1
  232. data/sig/pg_eventstore/feature_marker.rbs +10 -0
  233. data/sig/pg_eventstore/maintenance.rbs +12 -8
  234. data/sig/pg_eventstore/middleware/event_trace.rbs +14 -0
  235. data/sig/pg_eventstore/partition.rbs +2 -4
  236. data/sig/pg_eventstore/pg_connection.rbs +8 -0
  237. data/sig/pg_eventstore/queries/event_marker_queries.rbs +21 -0
  238. data/sig/pg_eventstore/queries/event_queries.rbs +6 -38
  239. data/sig/pg_eventstore/queries/events_global_index_queries.rbs +37 -0
  240. data/sig/pg_eventstore/queries/index_filtering_queries.rbs +50 -0
  241. data/sig/pg_eventstore/queries/links_resolver.rbs +7 -11
  242. data/sig/pg_eventstore/queries/maintenance_queries.rbs +12 -9
  243. data/sig/pg_eventstore/queries/partition_queries.rbs +32 -67
  244. data/sig/pg_eventstore/queries/replica_queries.rbs +31 -0
  245. data/sig/pg_eventstore/queries/streams_global_index_queries.rbs +39 -0
  246. data/sig/pg_eventstore/queries/transaction_queries.rbs +4 -2
  247. data/sig/pg_eventstore/queries.rbs +11 -21
  248. data/sig/pg_eventstore/query_builders/basic_filtering.rbs +10 -3
  249. data/sig/pg_eventstore/query_builders/event_markers_filtering.rbs +17 -0
  250. data/sig/pg_eventstore/query_builders/event_markers_index_filtering.rbs +69 -0
  251. data/sig/pg_eventstore/query_builders/event_subscription_positions_filtering.rbs +15 -0
  252. data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +9 -51
  253. data/sig/pg_eventstore/query_builders/events_global_index_filtering.rbs +68 -0
  254. data/sig/pg_eventstore/query_builders/filters/collection.rbs +84 -0
  255. data/sig/pg_eventstore/query_builders/filters/event_type_filter.rbs +19 -0
  256. data/sig/pg_eventstore/query_builders/filters/filter_row.rbs +21 -0
  257. data/sig/pg_eventstore/query_builders/filters/marker_filter.rbs +13 -0
  258. data/sig/pg_eventstore/query_builders/filters/marker_filter_row.rbs +19 -0
  259. data/sig/pg_eventstore/query_builders/filters/stream_filter.rbs +24 -0
  260. data/sig/pg_eventstore/query_builders/index_based_events_filtering.rbs +60 -0
  261. data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +29 -10
  262. data/sig/pg_eventstore/query_builders/read_cursor/stream_cursor.rbs +47 -0
  263. data/sig/pg_eventstore/query_builders/streams_global_index_filtering.rbs +27 -0
  264. data/sig/pg_eventstore/query_builders/subscription_events_filtering.rbs +26 -0
  265. data/sig/pg_eventstore/query_strategy/async.rbs +17 -0
  266. data/sig/pg_eventstore/query_strategy/foreground.rbs +11 -0
  267. data/sig/pg_eventstore/query_strategy.rbs +7 -0
  268. data/sig/pg_eventstore/raw_event.rbs +20 -0
  269. data/sig/pg_eventstore/sql_builder.rbs +32 -10
  270. data/sig/pg_eventstore/stream.rbs +10 -18
  271. data/sig/pg_eventstore/stream_global_index.rbs +14 -0
  272. data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +5 -4
  273. data/sig/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rbs +9 -0
  274. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +17 -12
  275. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +16 -8
  276. data/sig/pg_eventstore/subscriptions/event_subscription_position_queries.rbs +30 -0
  277. data/sig/pg_eventstore/subscriptions/events_processor.rbs +12 -5
  278. data/sig/pg_eventstore/subscriptions/events_processor_consumer/multiple.rbs +17 -0
  279. data/sig/pg_eventstore/subscriptions/events_processor_consumer/replica.rbs +17 -0
  280. data/sig/pg_eventstore/subscriptions/events_processor_consumer/single.rbs +17 -0
  281. data/sig/pg_eventstore/subscriptions/events_processor_consumer.rbs +14 -0
  282. data/sig/pg_eventstore/subscriptions/events_subscription_position_worker.rbs +28 -0
  283. data/sig/pg_eventstore/subscriptions/queries/subscription_queries.rbs +18 -50
  284. data/sig/pg_eventstore/subscriptions/replica_subscription_handler.rbs +33 -0
  285. data/sig/pg_eventstore/subscriptions/replica_subscription_runner.rbs +4 -0
  286. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/collection.rbs +14 -0
  287. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/index_read_strategy.rbs +23 -0
  288. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/replication_strategy.rbs +23 -0
  289. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy.rbs +11 -0
  290. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +13 -10
  291. data/sig/pg_eventstore/subscriptions/subscription_handler_performance.rbs +1 -1
  292. data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +13 -5
  293. data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +5 -5
  294. data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +4 -6
  295. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +29 -43
  296. data/sig/pg_eventstore/synchronized_array.rbs +4 -0
  297. data/sig/pg_eventstore/utils.rbs +18 -11
  298. data/sig/pg_eventstore/web/application.rbs +13 -3
  299. data/sig/pg_eventstore/web/paginator/base_collection.rbs +2 -0
  300. data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +4 -0
  301. data/sig/pg_eventstore/web/paginator/events_collection.rbs +11 -11
  302. data/sig/pg_eventstore/web/paginator/helpers.rbs +6 -15
  303. data/sig/pg_eventstore/web/paginator/markers_collection.rbs +13 -0
  304. data/sig/pg_eventstore/web/paginator/stream_contexts_collection.rbs +4 -0
  305. data/sig/pg_eventstore/web/paginator/stream_names_collection.rbs +4 -0
  306. data/sig/pg_eventstore/web/paginator/streams_collection.rbs +30 -0
  307. data/sig/pg_eventstore.rbs +8 -26
  308. metadata +181 -21
  309. data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +0 -69
  310. data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +0 -31
  311. data/lib/pg_eventstore/subscriptions/queries/service_queries.rb +0 -73
  312. data/lib/pg_eventstore/subscriptions/subscription_position_evaluation.rb +0 -195
  313. data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +0 -15
  314. data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +0 -16
  315. data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +0 -8
  316. data/sig/pg_eventstore/subscriptions/queries/service_queries.rbs +0 -15
  317. data/sig/pg_eventstore/subscriptions/subscription_position_evaluation.rbs +0 -53
  318. data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
  319. /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: 94443dc3aa36701d5e126573a5babdd4585932d803b766b06b94c861f6b4ef5a
4
- data.tar.gz: 86fcca21a3a2d3da35f3c760542b23cc97ae93ce97bae7b9baf011a957a9ffea
3
+ metadata.gz: 4425f2f4cffd24b06156465726034e4c8da383fed7f0576837851a65390068e0
4
+ data.tar.gz: bb809d708eaea98368d54bae14a8d35f36265a7a5ad66c4b8fd9b5dc57293163
5
5
  SHA512:
6
- metadata.gz: 4fb5ed37edce4c557082ba60cfa18f7209f795fa29b98dff9f848bb83b0b1cec889f4a7ca7f77bef5c0d9c6b4065d53765dcfcdf50dffe2628c3e3ffb57d26ad
7
- data.tar.gz: 0bc33722f13c57c11888ac205873ac93a3610ef5218e8dccf7c3e64d671852757e9d755ffe17ea8176b238dbe1722df47db522b5252ddceed52e14534c164c45
6
+ metadata.gz: e86a98610d16b95eeb4bffa5a3b0d74055ac7901a30f20d297aee1b37786e06f94ff483c135f99df255cd23f50b3cac4d9360c6338ccbdab7d58d14b05fc5578
7
+ data.tar.gz: cdfe9c6093814265d91905f1405ff39bbd347a4e844c75961f3b3db56eb0fb4f32537b4cd26a0bb7c46b82abb21d25cb2aa7831623466cd3e2a3d5d50caf4a42
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ plugins:
2
2
  - rubocop-rspec
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: '3.0'
5
+ TargetRubyVersion: '3.2'
6
6
  NewCops: enable
7
7
  Exclude:
8
8
  - 'benchmark/*'
@@ -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,5 +1,142 @@
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
+
120
+ ## [2.0.0]
121
+
122
+ - **Breaking change**: `pg_eventstore` now requires [pg_cron](https://github.com/citusdata/pg_cron) extension
123
+ - **Breaking change**: `pg_eventstore` now requires PostgreSQL v16+
124
+ - Greatly decreased the number of connections, used by `pg_eventstore` subscriptions
125
+ - **Breaking change**: drop support of Ruby v3.0 and v3.1. The gem now requires Ruby v3.2+
126
+ - **Breaking change**: `PgEventstore::Extensions::OptionsExtension::Options` class is no longer a child of `Set` class -
127
+ it has independent implementation now
128
+ - Add support of Ruby v4.0
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`
134
+
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.**
139
+
3
140
  ## [1.13.4]
4
141
 
5
142
  - Fix subscriptions potentially skipping events when multiple events are appended in concurrent transactions
@@ -18,7 +155,8 @@
18
155
 
19
156
  ## [1.13.0]
20
157
 
21
- - 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.
22
160
  - Resolve ambiguity in usage of `PgEventstore.config` method. It now returns the frozen object.
23
161
 
24
162
  ## [1.12.0]
@@ -27,12 +165,17 @@
27
165
 
28
166
  ## [1.11.0]
29
167
 
30
- - 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.
31
170
  - Improve long payloads in JSON preview in admin web UI in the way it does not moves content out of the visible area.
32
- - 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
33
173
 
34
174
  ## [1.10.0]
35
- - 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
36
179
  - Admin IU: fixed several potential XSS vulnerabilities
37
180
  - Admin IU: Add "Copy to clipboard" button near stream id that copies ruby stream definition
38
181
  - Admin UI: allow deletion of streams with empty attribute values
@@ -44,23 +187,33 @@
44
187
  - Add "Delete event" and "Delete stream" buttons into admin UI
45
188
 
46
189
  ## [1.8.0]
47
- - 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
48
193
  - Fix pagination of events in admin UI
49
194
  - Improve partial index for `$streams` system stream
50
195
 
51
196
  ## [1.7.0]
197
+
52
198
  - Implement reading from `"$streams"` system stream
53
199
  - Disable Host authorization introduced in sinatra v4.1
54
200
 
55
201
  ## [1.6.0]
56
- - 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.
57
207
 
58
208
  ## [1.5.0]
209
+
59
210
  - Add ability to toggle link events in the admin UI
60
211
  - Mark linked events in the admin UI with "link" icon
61
212
 
62
213
  ## [1.4.0]
63
- - 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:
64
217
 
65
218
  ```ruby
66
219
  # Set it globally, for all subscriptions
@@ -77,18 +230,24 @@ subscriptions_manager.subscribe(
77
230
  ```
78
231
 
79
232
  ## [1.3.4]
80
- - 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
81
236
 
82
237
  ## [1.3.3]
238
+
83
239
  - Adjust default value of `subscription_max_retries` setting
84
240
 
85
241
  ## [1.3.2]
242
+
86
243
  - Fix UI when switching subscription status
87
244
 
88
245
  ## [1.3.1]
246
+
89
247
  - Swap "Search" button and "Add filter" button on Dashboard page
90
248
 
91
249
  ## [1.3.0]
250
+
92
251
  - Add ability to filter subscriptions by state in admin UI
93
252
  - Reset error-related subscription's attributes on subscription restore
94
253
  - Reset total processed events number when user changes subscription's position
@@ -96,9 +255,12 @@ subscriptions_manager.subscribe(
96
255
  - Relax sinatra version requirement to v3+
97
256
 
98
257
  ## [1.2.0]
258
+
99
259
  - Implement `failed_subscription_notifier` subscription hook.
100
260
 
101
- 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:
102
264
 
103
265
  ```ruby
104
266
  PgEventstore.configure do |config|
@@ -122,36 +284,48 @@ subscriptions_manager.subscribe(
122
284
  ```
123
285
 
124
286
  ## [1.1.5]
125
- - 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.
126
290
 
127
291
  ## [1.1.4]
292
+
128
293
  - Add rbs signatures
129
294
 
130
295
  ## [1.1.3]
296
+
131
297
  - Fix issue with assets caching between different gem's versions
132
298
 
133
299
  ## [1.1.2]
300
+
134
301
  - Improve web app compatibility with rails
135
302
 
136
303
  ## [1.1.1]
304
+
137
305
  - Allow case insensitive search by context, stream name and event type in admin UI
138
306
 
139
307
  ## [1.1.0]
308
+
140
309
  - Add "Reset position" button on Subscriptions Admin UI page
141
310
 
142
- **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.
143
313
 
144
314
  ## [1.0.4]
315
+
145
316
  - Fix bug which caused slow Subscriptions to stop processing new events
146
317
  - Optimize Subscriptions update queries
147
318
 
148
319
  ## [1.0.3]
320
+
149
321
  - Do no update `Subscription#last_chunk_fed_at` if the chunk is empty
150
322
 
151
323
  ## [1.0.2]
324
+
152
325
  - UI: Fix opening of SubscriptionsSet tab of non-existing SubscriptionsSet
153
326
 
154
327
  ## [1.0.1]
328
+
155
329
  - Adjust assets urls to correctly act when mounting sinatra app under non-root url
156
330
 
157
331
  ## [1.0.0]
@@ -189,7 +363,8 @@ subscriptions_manager.subscribe(
189
363
 
190
364
  ## [0.8.0] - 2024-02-20
191
365
 
192
- - 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)
193
368
 
194
369
  ## [0.7.2] - 2024-02-14
195
370
 
@@ -201,7 +376,8 @@ subscriptions_manager.subscribe(
201
376
 
202
377
  ## [0.7.0] - 2024-02-09
203
378
 
204
- - 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.
205
381
  - Drop legacy migrations
206
382
 
207
383
  ## [0.6.0] - 2024-02-08
@@ -246,9 +422,11 @@ subscriptions_manager.subscribe(
246
422
 
247
423
  ## [0.2.4] - 2023-12-20
248
424
 
249
- 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.**
250
427
 
251
- **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`.
252
430
 
253
431
  ## [0.2.3] - 2023-12-18
254
432
 
@@ -260,7 +438,8 @@ Due to performance issues under certain circumstances, searching by event type w
260
438
 
261
439
  ## [0.2.1] - 2023-12-14
262
440
 
263
- 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.
264
443
 
265
444
  ## [0.2.0] - 2023-12-14
266
445
 
data/README.md CHANGED
@@ -4,27 +4,40 @@ 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 database with jsonb data type support (which means you need to have v9.2+). However it is recommended to use a non [EOL](https://www.postgresql.org/support/versioning/) PostgreSQL version, because the development of this gem is targeted at current PostgreSQL versions.
8
- - It is recommended you to have the default value set for `default_transaction_isolation` PostgreSQL config setting(`"read committed"`) as the implementation relies on it. All other transaction isolation levels(`"repeatable read"` and `"serializable"`) may cause unexpected serialization errors.
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
+ - `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.
9
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.
10
- - `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
+ ```
11
22
 
12
23
  ## Installation
13
24
 
14
25
  Install the gem and add to the application's Gemfile by executing:
15
-
16
- $ bundle add pg_eventstore
26
+ ```bash
27
+ bundle add pg_eventstore
28
+ ```
17
29
 
18
30
  If bundler is not being used to manage dependencies, install the gem by executing:
19
-
20
- $ gem install pg_eventstore
31
+ ```bash
32
+ gem install pg_eventstore
33
+ ```
21
34
 
22
35
  ## Usage
23
36
 
24
37
  Before start using the gem - you have to create the database. Please include this line into your `Rakefile`:
25
38
 
26
39
  ```ruby
27
- load "pg_eventstore/tasks/setup.rake"
40
+ load 'pg_eventstore/tasks/setup.rake'
28
41
  ```
29
42
 
30
43
  This will include necessary rake tasks. You can now run
@@ -44,11 +57,17 @@ Documentation chapters:
44
57
  - [Appending events](docs/appending_events.md)
45
58
  - [Linking events](docs/linking_events.md)
46
59
  - [Reading events](docs/reading_events.md)
60
+ - [Reading streams](docs/reading_streams.md)
61
+ - [Tracing events](docs/event_tracing.md)
47
62
  - [Subscriptions](docs/subscriptions.md)
48
63
  - [Maintenance functions](docs/maintenance.md)
49
64
  - [Writing middlewares](docs/writing_middleware.md)
50
65
  - [How to make multiple commands atomic](docs/multiple_commands.md)
51
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`.
52
71
 
53
72
  ## CLI
54
73
 
@@ -104,14 +123,18 @@ end
104
123
  After checking out the repo, run:
105
124
  - `bundle` to install dependencies
106
125
  - `docker compose up` to start dev/test services
126
+ - `bundle exec rake compile` to compile native extension
107
127
  - `bin/setup_db` to create/re-create development and test databases, tables and related objects
108
128
  - `bundle exec rbs collection install` to install external rbs definitions
109
129
 
110
- 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.
111
134
 
112
135
  To install this gem onto your local machine, run `bundle exec rake install`.
113
136
 
114
- 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`.
115
138
 
116
139
  ### Benchmarks
117
140
 
@@ -0,0 +1 @@
1
+ # pg_cron was removed. This file is kept for informative purpose.
@@ -0,0 +1 @@
1
+ ALTER TABLE public.events ADD COLUMN link_global_position bigint;
@@ -0,0 +1,83 @@
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 id, table_name from partitions')
11
+ end
12
+ partitions = partitions.to_h { |attrs| [attrs['id'], attrs['table_name']] }
13
+
14
+ total_links = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
15
+ conn.exec_params(
16
+ 'select count(*) all_count from events where events.type = $1 and link_global_position is null',
17
+ [PgEventstore::Event::LINK_TYPE]
18
+ )
19
+ end.first['all_count']
20
+
21
+ puts "Migrating legacy links. Links to process: #{total_links}. Concurrency is #{CONCURRENCY} concurrent writers."
22
+ processed = 0
23
+ processed_was = 0
24
+ time = Time.now
25
+ lock = Thread::Mutex.new
26
+ threads = CONCURRENCY.times.map do |t|
27
+ Thread.new do
28
+ loop do
29
+ link_events = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
30
+ conn.exec_params(<<~SQL, [PgEventstore::Event::LINK_TYPE, CONCURRENCY, t])
31
+ select * from events
32
+ where events.type = $1 and events.link_global_position is null and global_position % $2 = $3
33
+ limit 1_000
34
+ SQL
35
+ end.to_a
36
+ break if link_events.empty?
37
+
38
+ lock.synchronize { processed += link_events.size }
39
+ link_events = link_events.to_h { [_1['global_position'], _1] }
40
+ builders = link_events.values.map do |event|
41
+ builder = PgEventstore::SQLBuilder.new
42
+ builder.select("global_position, #{event['global_position']} as link_event_global_position")
43
+ builder.from(partitions[event['link_partition_id']]).where('id = ?', event['link_id'])
44
+ end
45
+ final_builder = PgEventstore::SQLBuilder.union_builders(builders)
46
+
47
+ positions_map = PgEventstore.connection(:_eventstore_db_connection).with do |conn|
48
+ conn.exec_params(*final_builder.to_exec_params)
49
+ end.to_a
50
+
51
+ update_queries = positions_map.map do |attrs|
52
+ <<~SQL
53
+ UPDATE events SET link_global_position = #{attrs['global_position']}
54
+ WHERE global_position = #{attrs['link_event_global_position']};
55
+ SQL
56
+ end
57
+
58
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
59
+ conn.exec(update_queries.join("\n"))
60
+ end
61
+
62
+ # Only log from the first thread to prevent messages spam
63
+ next unless t == 0
64
+
65
+ lock.synchronize do
66
+ time_was = time
67
+ time = Time.now
68
+
69
+ performance_info = <<~TEXT.strip
70
+ Processed: #{processed}. Left: #{total_links - processed}. \
71
+ Performance: #{((processed - processed_was) / (time - time_was)).round(2)} events/second.
72
+ TEXT
73
+ processed_was = processed
74
+ print "#{performance_info} \r"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ threads.each(&:join)
80
+
81
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
82
+ conn.exec('VACUUM (ANALYZE) events;')
83
+ end
@@ -0,0 +1,6 @@
1
+ -- We need to drop view which uses link_id and re-create it after we remove link_id column
2
+ DROP VIEW "$streams";
3
+
4
+ ALTER TABLE public.events DROP COLUMN link_id;
5
+
6
+ CREATE VIEW "$streams" AS SELECT * FROM events WHERE stream_revision = 0;
@@ -0,0 +1 @@
1
+ DROP INDEX idx_events_id;
@@ -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);