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
@@ -8,36 +8,6 @@ require_relative 'pg_connection'
8
8
 
9
9
  module PgEventstore
10
10
  class Connection
11
- # Starting from ruby v3.1 ConnectionPool closes connections after forking by default. For ruby v3 we need this patch
12
- # to correctly reload the ConnectionPool. Otherwise the same connection will leak into another process which will
13
- # result in disaster.
14
- # @!visibility private
15
- module Ruby30Patch
16
- def initialize(**)
17
- @current_pid = Process.pid
18
- @mutext = Mutex.new
19
- super
20
- end
21
-
22
- def with(&blk)
23
- reload_after_fork
24
- super
25
- end
26
-
27
- private
28
-
29
- def reload_after_fork
30
- return if @current_pid == Process.pid
31
-
32
- @mutext.synchronize do
33
- return if @current_pid == Process.pid
34
-
35
- @pool.reload(&:close)
36
- @current_pid = Process.pid
37
- end
38
- end
39
- end
40
-
41
11
  # @!attribute uri
42
12
  # @return [String]
43
13
  attr_reader :uri
@@ -61,17 +31,22 @@ module PgEventstore
61
31
  end
62
32
 
63
33
  # A shorthand from ConnectionPool#with.
64
- # @yieldparam connection [PG::Connection] PostgreSQL connection instance
34
+ # @yieldparam connection [PgEventstore::PgConnection] PostgreSQL connection instance
65
35
  # @return [Object] a value of a given block
66
- def with(&_blk)
36
+ def with(&)
67
37
  should_retry = true
68
38
  @pool.with do |conn|
69
39
  yield conn
70
40
  rescue PG::ConnectionBad, PG::UnableToSend
41
+ unless should_retry
42
+ # Connection is broken. We should throw it away
43
+ @pool.discard_current_connection
44
+ raise
45
+ end
46
+
71
47
  # Recover a connection after fork or when we lost a connection to PostgreSQL. We retry only once and without any
72
48
  # delay.
73
49
  conn.sync_reset
74
- raise unless should_retry
75
50
 
76
51
  should_retry = false
77
52
  retry
@@ -83,6 +58,20 @@ module PgEventstore
83
58
  @pool.shutdown(&:close)
84
59
  end
85
60
 
61
+ # Restore connection after shutdown by re-initializing connection pool
62
+ # @return [void]
63
+ def establish_connection
64
+ @pool = nil
65
+ init_pool
66
+ end
67
+
68
+ # @return [void]
69
+ def discard_current_connection
70
+ @pool.discard_current_connection do |conn|
71
+ conn.close unless conn.finished?
72
+ end
73
+ end
74
+
86
75
  private
87
76
 
88
77
  # @return [ConnectionPool]
@@ -107,7 +96,3 @@ module PgEventstore
107
96
  end
108
97
  end
109
98
  end
110
-
111
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1')
112
- PgEventstore::Connection.prepend(PgEventstore::Connection::Ruby30Patch)
113
- end
@@ -55,14 +55,19 @@ module PgEventstore
55
55
  # @!attribute expected_revision
56
56
  # @return [Integer, Symbol]
57
57
  attr_reader :expected_revision
58
+ # @!attribute verdict
59
+ # @return [Symbol]
60
+ attr_reader :verdict
58
61
 
59
62
  # @param revision [Integer]
60
63
  # @param expected_revision [Integer, Symbol]
61
64
  # @param stream [PgEventstore::Stream]
62
- def initialize(revision:, expected_revision:, stream:)
65
+ # @param verdict [Symbol]
66
+ def initialize(revision:, expected_revision:, stream:, verdict:)
63
67
  @revision = revision
64
68
  @expected_revision = expected_revision
65
69
  @stream = stream
70
+ @verdict = verdict
66
71
  super(user_friendly_message)
67
72
  end
68
73
 
@@ -70,13 +75,18 @@ module PgEventstore
70
75
 
71
76
  # @return [String]
72
77
  def user_friendly_message
73
- if revision == Stream::NON_EXISTING_STREAM_REVISION && expected_revision == :stream_exists
74
- return expected_stream_exists
78
+ case verdict
79
+ when :expected_to_have_stream_with_given_revision
80
+ current_no_stream
81
+ when :unmatched_stream_revision
82
+ unmatched_stream_revision
83
+ when :expected_to_have_stream
84
+ expected_stream_exists
85
+ when :expected_not_to_have_stream
86
+ expected_no_stream
87
+ else
88
+ Utils.missing_implementation!(verdict)
75
89
  end
76
- return expected_no_stream if revision > Stream::NON_EXISTING_STREAM_REVISION && expected_revision == :no_stream
77
- return current_no_stream if revision == Stream::NON_EXISTING_STREAM_REVISION && expected_revision.is_a?(Integer)
78
-
79
- unmatched_stream_revision
80
90
  end
81
91
 
82
92
  # @return [String]
@@ -108,16 +118,129 @@ module PgEventstore
108
118
  end
109
119
  end
110
120
 
121
+ class WrongExpectedTypesRevisionError < Error
122
+ class Verdict
123
+ include Extensions::OptionsExtension
124
+ include Extensions::OptionsDefaults
125
+
126
+ # @!attribute verdict
127
+ # @return [Symbol]
128
+ attribute(:verdict)
129
+ # @!attribute event_type
130
+ # @return [String, Symbol]
131
+ attribute(:event_type)
132
+ # @!attribute current_revision
133
+ # @return [Integer]
134
+ attribute(:current_revision)
135
+ # @!attribute expected_revision
136
+ # @return [Integer, Symbol]
137
+ attribute(:expected_revision)
138
+ # @!attribute expected_markers
139
+ # @return [Array<String>, nil]
140
+ attribute(:expected_markers)
141
+ end
142
+
143
+ # @!attribute stream
144
+ # @return [PgEventstore::Stream]
145
+ attr_reader :stream
146
+ # @!attribute verdicts
147
+ # @return [Array<PgEventstore::WrongExpectedTypesRevisionError::Verdict>]
148
+ attr_reader :verdicts
149
+
150
+ # @param stream [PgEventstore::Stream]
151
+ # @param verdicts [Array<PgEventstore::WrongExpectedTypesRevisionError::Verdict>]
152
+ def initialize(stream:, verdicts:)
153
+ @stream = stream
154
+ @verdicts = verdicts
155
+ super(user_friendly_message)
156
+ end
157
+
158
+ private
159
+
160
+ # @return [String]
161
+ def user_friendly_message
162
+ messages =
163
+ verdicts.map do |verdict|
164
+ case verdict.verdict
165
+ when :event_is_absent
166
+ event_is_absent(verdict)
167
+ when :event_revision_does_not_match
168
+ event_revision_does_not_match(verdict)
169
+ when :event_is_present
170
+ event_is_present(verdict)
171
+ else
172
+ Utils.missing_implementation!(verdict.verdict)
173
+ end
174
+ end
175
+ messages.join('; ')
176
+ end
177
+
178
+ # @param verdict [PgEventstore::WrongExpectedTypesRevisionError::Verdict]
179
+ # @return [String]
180
+ def event_is_absent(verdict)
181
+ <<~TEXT.strip
182
+ Expected #{stream_descr} stream to contain #{event_descr(verdict)} with #{revision_descr(verdict)}, \
183
+ but this event does not exist.
184
+ TEXT
185
+ end
186
+
187
+ # @param verdict [PgEventstore::WrongExpectedTypesRevisionError::Verdict]
188
+ # @return [String]
189
+ def event_is_present(verdict)
190
+ "Expected #{stream_descr} stream not to contain #{event_descr(verdict)}, but it actually exists."
191
+ end
192
+
193
+ # @param verdict [PgEventstore::WrongExpectedTypesRevisionError::Verdict]
194
+ # @return [String]
195
+ def event_revision_does_not_match(verdict)
196
+ <<~TEXT.strip
197
+ Expected #{stream_descr} stream to contain #{event_descr(verdict)} with #{revision_descr(verdict)}, \
198
+ but it actually has #{verdict.current_revision} revision.
199
+ TEXT
200
+ end
201
+
202
+ # @return [String]
203
+ def stream_descr
204
+ stream.to_hash.inspect
205
+ end
206
+
207
+ # @param verdict [PgEventstore::WrongExpectedTypesRevisionError::Verdict]
208
+ # @return [String]
209
+ def event_descr(verdict)
210
+ if verdict.event_type == :any
211
+ markers = verdict.expected_markers.map(&:inspect).join(', ')
212
+ "any event with some of #{markers} marker(s)"
213
+ elsif verdict.expected_markers
214
+ markers = verdict.expected_markers.map(&:inspect).join(', ')
215
+ "#{verdict.event_type.inspect} event with some of #{markers} marker(s)"
216
+ else
217
+ "#{verdict.event_type.inspect} event"
218
+ end
219
+ end
220
+
221
+ # @param verdict [PgEventstore::WrongExpectedTypesRevisionError::Verdict]
222
+ # @return [String]
223
+ def revision_descr(verdict)
224
+ if verdict.expected_revision.is_a?(Integer)
225
+ "#{verdict.expected_revision} revision"
226
+ elsif verdict.expected_revision == :event_exists
227
+ 'some revision'
228
+ else
229
+ "#{verdict.expected_revision.inspect} revision"
230
+ end
231
+ end
232
+ end
233
+
111
234
  class RecordNotFound < Error
112
235
  # @!attribute table_name
113
236
  # @return [String]
114
237
  attr_reader :table_name
115
238
  # @!attribute id
116
- # @return [Integer, String]
239
+ # @return [Object]
117
240
  attr_reader :id
118
241
 
119
242
  # @param table_name [String]
120
- # @param id [Integer, String]
243
+ # @param id [Object]
121
244
  def initialize(table_name, id)
122
245
  @table_name = table_name
123
246
  @id = id
@@ -126,7 +249,7 @@ module PgEventstore
126
249
 
127
250
  # @return [String]
128
251
  def user_friendly_message
129
- "Could not find/update #{table_name.inspect} record with #{id.inspect} id."
252
+ "Could not find/update/delete #{table_name.inspect} record by #{@id.inspect}."
130
253
  end
131
254
  end
132
255
 
@@ -203,7 +326,7 @@ module PgEventstore
203
326
 
204
327
  # @return [String]
205
328
  def user_friendly_message
206
- "Event with #id #{event.id.inspect} must be present, but it could not be found."
329
+ "Event #{event.inspect} is not persisted a persisted event."
207
330
  end
208
331
  end
209
332
 
@@ -255,4 +378,7 @@ module PgEventstore
255
378
  super()
256
379
  end
257
380
  end
381
+
382
+ class NotSupportedError < Error
383
+ end
258
384
  end
@@ -7,11 +7,17 @@ module PgEventstore
7
7
  # @return [String] a type of link event
8
8
  LINK_TYPE = '$>'
9
9
  # @return [String]
10
+ SYSTEM_SYMBOL = "\u2592"
11
+ # @return [String]
12
+ MARKERS_METADATA_KEY = "#{SYSTEM_SYMBOL}m".freeze
13
+ # @return [String]
10
14
  PRIMARY_TABLE_NAME = 'events'
15
+ # @return [Integer]
16
+ NON_EXISTING_EVENT_REVISION = -1
11
17
 
12
18
  # @!attribute id
13
- # @return [String] UUIDv4 string
14
- attribute(:id)
19
+ # @return [String] UUIDv7
20
+ attribute(:id) { SecureRandom.uuid_v7 }
15
21
  # @!attribute type
16
22
  # @return [String] event type
17
23
  attribute(:type) { self.class.name }
@@ -27,13 +33,19 @@ module PgEventstore
27
33
  # @!attribute data
28
34
  # @return [Hash] event's data
29
35
  attribute(:data) { {} }
36
+ # @!attribute markers
37
+ # @return [Array<String>] event markers
38
+ attribute(:markers) { [] }
39
+ # @!attribute feature_markers
40
+ # @return [Array<FeatureMarker>] feature-specific markers that can't be put under #markers
41
+ attribute(:feature_markers) { [] }
30
42
  # @!attribute metadata
31
43
  # @return [Hash] event's metadata
32
44
  attribute(:metadata) { {} }
33
- # @!attribute link_id
34
- # @return [String, nil] UUIDv4 of an event the current event points to. If it is not nil, then the current
35
- # event is a link
36
- attribute(:link_id)
45
+ # @!attribute link_global_position
46
+ # @return [Integer, nil] global_position of an event the current event points to. If it is not nil, then the
47
+ # current event is a link
48
+ attribute(:link_global_position)
37
49
  # @!attribute link_partition_id
38
50
  # @return [Integer, nil] a partition id of an event the link event points to. It is used to load original event
39
51
  # when resolve_link_tos: true option is provided when reading events.
@@ -45,6 +57,15 @@ module PgEventstore
45
57
  # @!attribute created_at
46
58
  # @return [Time, nil] a timestamp an event was created at
47
59
  attribute(:created_at)
60
+ # @!attribute caused_by
61
+ # @return [PgEventstore::Event, nil]
62
+ attribute(:caused_by)
63
+ # @!attribute correlation_id
64
+ # @return [String, nil] UUIDv7
65
+ attribute(:correlation_id)
66
+ # @!attribute causation_id
67
+ # @return [String, nil] UUIDv7
68
+ attribute(:causation_id)
48
69
 
49
70
  # Implements comparison of `PgEventstore::Event`-s. Two events matches if all of their attributes matches
50
71
  # @param other [Object, PgEventstore::Event]
@@ -54,11 +75,17 @@ module PgEventstore
54
75
 
55
76
  attributes_hash.except(:link) == other.attributes_hash.except(:link)
56
77
  end
78
+ alias eql? ==
57
79
 
58
80
  # Detect whether an event is a link event
59
81
  # @return [Boolean]
60
82
  def link?
61
- !link_id.nil?
83
+ !link_global_position.nil?
84
+ end
85
+
86
+ # @return [Integer]
87
+ def hash
88
+ attributes_hash.except(:link).hash
62
89
  end
63
90
 
64
91
  # Detect whether an event is a system event
@@ -66,5 +93,10 @@ module PgEventstore
66
93
  def system?
67
94
  type.start_with?('$')
68
95
  end
96
+
97
+ # @return [self]
98
+ def dup
99
+ self.class.new(**Utils.deep_dup(options_hash))
100
+ end
69
101
  end
70
102
  end
@@ -4,20 +4,20 @@ module PgEventstore
4
4
  # @!visibility private
5
5
  class EventDeserializer
6
6
  # @!attribute middlewares
7
- # @return [Array<#deserialize, #serialize>]
7
+ # @return [Array<Middleware>]
8
8
  attr_reader :middlewares
9
9
  # @!attribute event_class_resolver
10
10
  # @return [#call]
11
11
  attr_reader :event_class_resolver
12
12
 
13
- # @param middlewares [Array<Object<#deserialize, #serialize>>]
13
+ # @param middlewares [Array<Middleware>]
14
14
  # @param event_class_resolver [#call]
15
15
  def initialize(middlewares, event_class_resolver)
16
16
  @middlewares = middlewares
17
17
  @event_class_resolver = event_class_resolver
18
18
  end
19
19
 
20
- # @param raw_events [Array<Hash>]
20
+ # @param raw_events [Array<Hash>, Enumerator<Hash>]
21
21
  # @return [Array<PgEventstore::Event>]
22
22
  def deserialize_many(raw_events)
23
23
  raw_events.map(&method(:deserialize))
@@ -33,7 +33,8 @@ module PgEventstore
33
33
  event.stream = PgEventstore::Stream.new(
34
34
  **attrs.slice('context', 'stream_name', 'stream_id').transform_keys(&:to_sym)
35
35
  )
36
- event.link = without_middlewares.deserialize(attrs['link']) if attrs.key?('link')
36
+ event.link = deserialize(attrs['link']) if attrs.key?('link')
37
+ event.markers = event.metadata[Event::MARKERS_METADATA_KEY] || []
37
38
  event
38
39
  end
39
40
 
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ # @!visibility private
5
+ class EventGlobalIndex
6
+ include Extensions::OptionsExtension
7
+ include Extensions::OptionsDefaults
8
+
9
+ # @!attribute global_position
10
+ # @return [Integer]
11
+ attribute(:global_position)
12
+ # @!attribute stream_revision
13
+ # @return [Integer]
14
+ attribute(:stream_revision)
15
+ # @!attribute context_partition_id
16
+ # @return [Integer]
17
+ attribute(:context_partition_id)
18
+ # @!attribute stream_name_partition_id
19
+ # @return [Integer]
20
+ attribute(:stream_name_partition_id)
21
+ # @!attribute event_type_partition_id
22
+ # @return [Integer]
23
+ attribute(:event_type_partition_id)
24
+ # @!attribute streams_global_index_id
25
+ # @return [Integer]
26
+ attribute(:streams_global_index_id)
27
+
28
+ # EventGlobalIndex representation that is used in subscriptions
29
+ class SubscriptionRepr
30
+ include Extensions::OptionsExtension
31
+ include Extensions::OptionsDefaults
32
+
33
+ # @!attribute global_position
34
+ # @return [Integer]
35
+ attribute(:global_position)
36
+ # @!attribute subscription_position
37
+ # @return [Integer]
38
+ attribute(:subscription_position)
39
+ # @!attribute event_type_partition_id
40
+ # @return [Integer]
41
+ attribute(:event_type_partition_id)
42
+
43
+ # @return [Hash]
44
+ def to_subscription_position_attrs
45
+ { global_position:, subscription_position: }
46
+ end
47
+ end
48
+
49
+ # EventGlobalIndex representation that is used in Read API
50
+ class ReadApiRepr
51
+ include Extensions::OptionsExtension
52
+ include Extensions::OptionsDefaults
53
+
54
+ # @!attribute global_position
55
+ # @return [Integer]
56
+ attribute(:global_position)
57
+ # @!attribute event_type_partition_id
58
+ # @return [Integer]
59
+ attribute(:event_type_partition_id)
60
+ end
61
+
62
+ # EventGlobalIndex representation that is used in Write API
63
+ class WriteApiRepr
64
+ include Extensions::OptionsExtension
65
+ include Extensions::OptionsDefaults
66
+
67
+ # @!attribute global_position
68
+ # @return [Integer]
69
+ attribute(:global_position)
70
+ # @!attribute event_type_partition_id
71
+ # @return [Integer]
72
+ attribute(:event_type_partition_id)
73
+ # @!attribute stream_revision
74
+ # @return [Integer]
75
+ attribute(:stream_revision)
76
+ end
77
+
78
+ # EventGlobalIndex representation for revision validation in #append_to_stream
79
+ class RevisionCheckRepr
80
+ include Extensions::OptionsExtension
81
+ include Extensions::OptionsDefaults
82
+
83
+ # @!attribute stream_revision
84
+ # @return [Integer]
85
+ attribute(:stream_revision)
86
+ # @!attribute sequence_number
87
+ # @return [Integer]
88
+ attribute(:sequence_number)
89
+ end
90
+
91
+ module ReprType
92
+ SUBSCRIPTION = :subscription
93
+ READ_API = :read_api
94
+ WRITE_API = :write_api
95
+ REVISION_CHECK = :revision_check
96
+ end
97
+
98
+ class << self
99
+ # @param attributes [Hash<Symbol, Object>]
100
+ # @param repr [Symbol, nil]
101
+ # @return [EventGlobalIndex, SubscriptionRepr, ReadApiRepr, WriteApiRepr, RevisionCheckRepr]
102
+ def create_representation(attributes, repr: nil)
103
+ case repr
104
+ when ReprType::SUBSCRIPTION
105
+ SubscriptionRepr.new(**attributes)
106
+ when ReprType::READ_API
107
+ ReadApiRepr.new(**attributes)
108
+ when ReprType::WRITE_API
109
+ WriteApiRepr.new(**attributes)
110
+ when ReprType::REVISION_CHECK
111
+ RevisionCheckRepr.new(**attributes)
112
+ else
113
+ new(**attributes)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ # @!visibility private
5
+ class EventMarker
6
+ include Extensions::OptionsExtension
7
+ include Extensions::OptionsDefaults
8
+
9
+ # @!attribute id
10
+ # @return [Integer]
11
+ attribute(:id)
12
+ # @!attribute name
13
+ # @return [String]
14
+ attribute(:name)
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ # @!visibility private
5
+ class EventMarkerIndex
6
+ include Extensions::OptionsExtension
7
+ include Extensions::OptionsDefaults
8
+
9
+ # @!attribute marker_id
10
+ # @return [Integer]
11
+ attribute(:marker_id)
12
+ # @!attribute streams_global_index_id
13
+ # @return [Integer]
14
+ attribute(:streams_global_index_id)
15
+ # @!attribute event_type_partition_id
16
+ # @return [Integer]
17
+ attribute(:event_type_partition_id)
18
+ # @!attribute global_position
19
+ # @return [Integer]
20
+ attribute(:global_position)
21
+ # @!attribute stream_revision
22
+ # @return [Integer]
23
+ attribute(:stream_revision)
24
+ end
25
+ end
@@ -18,6 +18,12 @@ module PgEventstore
18
18
  @middlewares.each do |middleware|
19
19
  middleware.serialize(event)
20
20
  end
21
+ event.markers.uniq!
22
+ if event.markers.any?
23
+ event.metadata[Event::MARKERS_METADATA_KEY] = event.markers
24
+ else
25
+ event.metadata.delete(Event::MARKERS_METADATA_KEY)
26
+ end
21
27
  event
22
28
  end
23
29
 
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Extensions
5
+ module ActsAsConfigurable
6
+ # @param config_class [Class<PgEventstore:::BasicConfig>]
7
+ # @param default_config [Symbol]
8
+ # @return [void]
9
+ def acts_as_configurable(config_class:, default_config: :default)
10
+ const_set(:DEFAULT_CONFIG, default_config)
11
+ const_set(:CONFIG_CLASS, config_class)
12
+ extend Configurable
13
+ init_variables
14
+ end
15
+
16
+ module Configurable
17
+ # @!attribute mutex
18
+ # @return [Thread::Mutex]
19
+ attr_reader :mutex
20
+ private :mutex
21
+
22
+ # Creates a Config if not exists and yields it to the given block.
23
+ # @param name [Symbol] a name to assign to a config
24
+ # @return [Object] a result of the given block
25
+ def configure(name: const_get(:DEFAULT_CONFIG))
26
+ config_class = const_get(:CONFIG_CLASS)
27
+ mutex.synchronize do
28
+ @config[name] = @config[name] ? config_class.new(name:, **@config[name].options_hash) : config_class.new(name:)
29
+ connection_config_was = connection_options(@config[name])
30
+
31
+ yield(@config[name]).tap do
32
+ @config[name].freeze
33
+ next if connection_config_was == connection_options(@config[name])
34
+
35
+ # Reset the connection if user decided to reconfigure connection's options
36
+ @connection.delete(name)
37
+ end
38
+ end
39
+ end
40
+
41
+ # @return [Array<Symbol>]
42
+ def available_configs
43
+ @config.keys
44
+ end
45
+
46
+ # @param name [Symbol]
47
+ # @return [PgEventstore::Config]
48
+ def config(name = const_get(:DEFAULT_CONFIG))
49
+ return @config[name] if @config[name]
50
+
51
+ error_message = <<~TEXT
52
+ Could not find #{name.inspect} config. You can define it like this:
53
+ #{self}.configure(name: #{name.inspect}) do |config|
54
+ # your config goes here
55
+ end
56
+ TEXT
57
+ raise error_message
58
+ end
59
+
60
+ # Look ups and returns a Connection, based on the given config. If not exists - it creates one. This operation
61
+ # is a thread-safe
62
+ # @param name [Symbol]
63
+ # @return [PgEventstore::Connection]
64
+ def connection(name = const_get(:DEFAULT_CONFIG))
65
+ mutex.synchronize do
66
+ @connection[name] ||= Connection.new(**connection_options(config(name)))
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ # @param config [PgEventstore::Extensions::OptionsExtension]
73
+ # @return [Hash] { uri: String, pool_size: Integer, pool_timeout: Integer }
74
+ def connection_options(config)
75
+ raise NotImplementedError
76
+ end
77
+
78
+ # @return [void]
79
+ def init_variables
80
+ default_config = const_get(:DEFAULT_CONFIG)
81
+ @config = { default_config => const_get(:CONFIG_CLASS).new(name: default_config) }
82
+ @connection = {}
83
+ @mutex = Thread::Mutex.new
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end