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
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module SubscriptionFeedStrategy
5
+ # @!visibility private
6
+ class Collection < Array
7
+ # This number is used to determine how many subscriptions we fetch per SQL query.
8
+ # @return [Integer]
9
+ DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY = 10
10
+
11
+ class << self
12
+ # @param runners [Array<PgEventstore::SubscriptionRunner>]
13
+ # @param connection [PgEventstore::Connection]
14
+ # @param query_strategy [PgEventstore::QueryStrategy]
15
+ # @param subscriptions_per_query [Integer]
16
+ # @return [PgEventstore::SubscriptionFeedStrategy::Collection]
17
+ def create(runners, connection, query_strategy, subscriptions_per_query: DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY)
18
+ instance = new
19
+ replica_runners = runners.grep(ReplicaSubscriptionRunner)
20
+ common_runners = runners - replica_runners
21
+ common_runners.each_slice(subscriptions_per_query).each do |runners_slice|
22
+ strategy = IndexReadStrategy.new(connection, query_strategy)
23
+ strategy.add(*runners_slice)
24
+ instance.push(strategy)
25
+ end
26
+ replica_runners.each_slice(subscriptions_per_query) do |runners_slice|
27
+ strategy = ReplicationStrategy.new(connection, query_strategy)
28
+ strategy.add(*runners_slice)
29
+ instance.push(strategy)
30
+ end
31
+ instance
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module SubscriptionFeedStrategy
5
+ # @!visibility private
6
+ class IndexReadStrategy
7
+ include SubscriptionFeedStrategy
8
+
9
+ # Allow subscriptions to scan through up to this amount of events per a single query. This allows to make query
10
+ # plan more predictable. Downside: let's say subscription1 targets "Foo" event type, but between
11
+ # SubscriptionRunnersFeeder#feed runs more than this amount of events other than "Foo" event type are published -
12
+ # it will require at least one more loop to pick that event.
13
+ # @return [Integer]
14
+ INDEX_LOOK_UP_DISTANCE = 100_000
15
+
16
+ # @param connection [PgEventstore::Connection]
17
+ # @param query_strategy [PgEventstore::QueryStrategy]
18
+ def initialize(connection, query_strategy)
19
+ @connection = connection
20
+ @query_strategy = query_strategy
21
+ @runners = []
22
+ end
23
+
24
+ # @param runners [Array<PgEventstore::SubscriptionRunner>]
25
+ # @return [Array<PgEventstore::SubscriptionRunner>]
26
+ def add(*runners)
27
+ @runners.push(*runners)
28
+ end
29
+
30
+ # @return [Integer]
31
+ def size
32
+ @runners.size
33
+ end
34
+
35
+ # @return [Boolean]
36
+ def any?
37
+ @runners.any?
38
+ end
39
+
40
+ # @return [void]
41
+ def feed
42
+ runners_query_options = @runners.to_h do |runner|
43
+ next_chunk_query_opts = runner.next_chunk_query_opts
44
+ next_chunk_query_opts[:to_position] =
45
+ [next_chunk_query_opts[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
46
+ [runner.id, next_chunk_query_opts]
47
+ end
48
+ grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(runners_query_options)
49
+ @runners.each do |runner|
50
+ if grouped_indexes[runner.id]
51
+ chunk = Chunks::SubscriptionEventsIndexChunk.new(
52
+ grouped_indexes[runner.id],
53
+ @connection,
54
+ QueryStrategy::Foreground.new(@connection),
55
+ runners_query_options[runner.id][:resolve_link_tos]
56
+ )
57
+ runner.feed(chunk)
58
+ else
59
+ runner.feed(Chunks::SubscriptionCheckpointChunk.new(runners_query_options[runner.id][:to_position]))
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # @return [Integer]
67
+ def safe_position
68
+ event_subscription_position_queries.max_subscription_position || 0
69
+ end
70
+
71
+ # @return [PgEventstore::EventsGlobalIndexQueries]
72
+ def events_global_index_queries
73
+ EventsGlobalIndexQueries.new(@connection, @query_strategy)
74
+ end
75
+
76
+ # @return [PgEventstore::EventSubscriptionPositionQueries]
77
+ def event_subscription_position_queries
78
+ EventSubscriptionPositionQueries.new(@connection)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module SubscriptionFeedStrategy
5
+ # @!visibility private
6
+ class ReplicationStrategy
7
+ include SubscriptionFeedStrategy
8
+
9
+ # Allow subscriptions to scan through up to this amount of events per a single query. This allows to make query
10
+ # plan more predictable.
11
+ # @return [Integer]
12
+ INDEX_LOOK_UP_DISTANCE = 100_000
13
+
14
+ # @param connection [PgEventstore::Connection]
15
+ # @param query_strategy [PgEventstore::QueryStrategy]
16
+ def initialize(connection, query_strategy)
17
+ @connection = connection
18
+ @query_strategy = query_strategy
19
+ @runners = []
20
+ end
21
+
22
+ # @param runners [Array<PgEventstore::ReplicaSubscriptionRunner>]
23
+ # @return [Array<PgEventstore::ReplicaSubscriptionRunner>]
24
+ def add(*runners)
25
+ @runners.push(*runners)
26
+ end
27
+
28
+ # @return [Integer]
29
+ def size
30
+ @runners.size
31
+ end
32
+
33
+ # @return [Boolean]
34
+ def any?
35
+ @runners.any?
36
+ end
37
+
38
+ # @return [void]
39
+ def feed
40
+ runners_query_options = @runners.to_h do |runner|
41
+ next_chunk_query_opts = runner.next_chunk_query_opts
42
+ next_chunk_query_opts[:to_position] =
43
+ [next_chunk_query_opts[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
44
+ [runner.id, next_chunk_query_opts]
45
+ end
46
+ grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(runners_query_options)
47
+ @runners.each do |runner|
48
+ if grouped_indexes[runner.id]
49
+ chunk = Chunks::ReplicaEventsIndexChunk.new(grouped_indexes[runner.id])
50
+ runner.feed(chunk)
51
+ else
52
+ runner.feed(Chunks::SubscriptionCheckpointChunk.new(runners_query_options[runner.id][:to_position]))
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # @return [Integer]
60
+ def safe_position
61
+ event_subscription_position_queries.max_subscription_position || 0
62
+ end
63
+
64
+ # @return [PgEventstore::EventsGlobalIndexQueries]
65
+ def events_global_index_queries
66
+ EventsGlobalIndexQueries.new(@connection, @query_strategy)
67
+ end
68
+
69
+ # @return [PgEventstore::EventSubscriptionPositionQueries]
70
+ def event_subscription_position_queries
71
+ EventSubscriptionPositionQueries.new(@connection)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ # @!visibility private
5
+ module SubscriptionFeedStrategy
6
+ # @param runners [PgEventstore::SubscriptionRunner, Array<PgEventstore::SubscriptionRunner>]
7
+ # @return [Array<PgEventstore::SubscriptionRunner>]
8
+ def add(*runners)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ # @return [Integer]
13
+ def size
14
+ raise NotImplementedError
15
+ end
16
+
17
+ # @return [Boolean]
18
+ def any?
19
+ raise NotImplementedError
20
+ end
21
+
22
+ # @return [void]
23
+ def feed
24
+ raise NotImplementedError
25
+ end
26
+ end
27
+ end
28
+
29
+ require_relative 'subscription_feed_strategies/collection'
30
+ require_relative 'subscription_feed_strategies/index_read_strategy'
31
+ require_relative 'subscription_feed_strategies/replication_strategy'
@@ -10,7 +10,7 @@ module PgEventstore
10
10
  # Determines how often to fetch events from the event store.
11
11
  # @see PgEventstore::Subscription::MIN_EVENTS_PULL_INTERVAL
12
12
  # @return [Float]
13
- EVENTS_PULL_INTERVAL = 0.1 # seconds
13
+ EVENTS_PULL_INTERVAL = 0.2 # seconds
14
14
  private_constant :EVENTS_PULL_INTERVAL
15
15
 
16
16
  attr_reader :config_name
@@ -30,6 +30,9 @@ module PgEventstore
30
30
  @subscriptions_set_lifecycle = subscriptions_set_lifecycle
31
31
  @subscriptions_lifecycle = subscriptions_lifecycle
32
32
  @commands_handler = CommandsHandler.new(@config_name, self, @subscriptions_lifecycle.runners)
33
+ if requires_subscription_position_assignment?
34
+ @events_subscription_position_worker = EventsSubscriptionPositionWorker.new(@config_name)
35
+ end
33
36
  attach_runner_callbacks
34
37
  end
35
38
 
@@ -77,6 +80,14 @@ module PgEventstore
77
80
  :before_runner_started, :before,
78
81
  SubscriptionFeederHandlers.setup_handler(:start_cmds_handler, @commands_handler)
79
82
  )
83
+ if requires_subscription_position_assignment?
84
+ @basic_runner.define_callback(
85
+ :before_runner_started, :before,
86
+ SubscriptionFeederHandlers.setup_handler(
87
+ :start_events_subscription_position_worker, @events_subscription_position_worker
88
+ )
89
+ )
90
+ end
80
91
 
81
92
  @basic_runner.define_callback(
82
93
  :after_runner_died, :before,
@@ -108,6 +119,14 @@ module PgEventstore
108
119
  :after_runner_stopped, :before,
109
120
  SubscriptionFeederHandlers.setup_handler(:stop_commands_handler, @commands_handler)
110
121
  )
122
+ if requires_subscription_position_assignment?
123
+ @basic_runner.define_callback(
124
+ :after_runner_stopped, :before,
125
+ SubscriptionFeederHandlers.setup_handler(
126
+ :stop_events_subscription_position_worker, @events_subscription_position_worker
127
+ )
128
+ )
129
+ end
111
130
 
112
131
  @basic_runner.define_callback(
113
132
  :before_runner_restored, :after,
@@ -122,9 +141,14 @@ module PgEventstore
122
141
  [
123
142
  RunnerRecoveryStrategies::RestoreConnection.new(config_name),
124
143
  RunnerRecoveryStrategies::RestoreSubscriptionFeeder.new(
125
- subscriptions_set_lifecycle: subscriptions_set_lifecycle
144
+ subscriptions_set_lifecycle:
126
145
  ),
127
146
  ]
128
147
  end
148
+
149
+ # @return [Boolean]
150
+ def requires_subscription_position_assignment?
151
+ Config::NodeRole.writable.include?(PgEventstore.config(@config_name).eventstore_role)
152
+ end
129
153
  end
130
154
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'benchmark'
4
-
5
3
  module PgEventstore
6
4
  # This class measures the performance of Subscription's handler and returns the average time required to process an
7
5
  # event.
@@ -10,7 +8,7 @@ module PgEventstore
10
8
  extend Forwardable
11
9
 
12
10
  # @return [Integer] the number of measurements to keep
13
- TIMINGS_TO_KEEP = 100
11
+ TIMINGS_TO_KEEP = 10
14
12
 
15
13
  def_delegators :@lock, :synchronize
16
14
 
@@ -20,10 +18,12 @@ module PgEventstore
20
18
  end
21
19
 
22
20
  # Yields the given block to measure its execution time
21
+ # @param events_number [Integer] number of events processed by the given block
23
22
  # @return [Object] the result of yielded block
24
- def track_exec_time
23
+ def track_exec_time(events_number)
25
24
  result = nil
26
- time = Benchmark.realtime { result = yield }
25
+ time = Utils.benchmark { result = yield }
26
+ time /= events_number
27
27
  synchronize do
28
28
  @timings.shift if @timings.size == TIMINGS_TO_KEEP
29
29
  @timings.push(time)
@@ -11,50 +11,48 @@ module PgEventstore
11
11
  extend Forwardable
12
12
 
13
13
  # @return [Integer]
14
- MAX_EVENTS_PER_CHUNK = 1_000
14
+ DEFAULT_MAX_EVENTS_PER_CHUNK = 1_000
15
15
  # @return [Integer]
16
16
  MIN_EVENTS_PER_CHUNK = 10
17
17
  # @return [Integer]
18
- INITIAL_EVENTS_PER_CHUNK = 10
18
+ DEFAULT_INITIAL_EVENTS_PER_CHUNK = 10
19
19
 
20
20
  # @!attribute subscription
21
21
  # @return [PgEventstore::Subscription]
22
22
  attr_reader :subscription
23
23
 
24
24
  def_delegators :@events_processor, :start, :stop, :stop_async, :feed, :wait_for_finish, :restore, :state, :running?,
25
- :clear_chunk, :within_state
25
+ :clear_events_repository, :within_state
26
26
  def_delegators :@subscription, :lock!, :id
27
27
 
28
28
  # @param stats [PgEventstore::SubscriptionHandlerPerformance]
29
29
  # @param events_processor [PgEventstore::EventsProcessor]
30
30
  # @param subscription [PgEventstore::Subscription]
31
- # @param position_evaluation [PgEventstore::SubscriptionPositionEvaluation]
32
- def initialize(stats:, events_processor:, subscription:, position_evaluation:)
31
+ # @param max_events_per_chunk [Integer]
32
+ def initialize(stats:, events_processor:, subscription:,
33
+ initial_events_per_chunk: DEFAULT_INITIAL_EVENTS_PER_CHUNK,
34
+ max_events_per_chunk: DEFAULT_MAX_EVENTS_PER_CHUNK)
33
35
  @stats = stats
34
36
  @events_processor = events_processor
35
37
  @subscription = subscription
36
- @position_evaluation = position_evaluation
38
+ @max_events_per_chunk = max_events_per_chunk
39
+ @initial_events_per_chunk = initial_events_per_chunk
37
40
 
38
41
  attach_callbacks
39
42
  end
40
43
 
41
44
  # @return [Hash]
42
45
  def next_chunk_query_opts
43
- @subscription.options.merge(
46
+ {
44
47
  from_position: next_chunk_global_position,
45
48
  max_count: estimate_events_number,
46
- to_position: @position_evaluation.last_safe_position
47
- )
49
+ resolve_link_tos: @subscription.options[:resolve_link_tos] || false,
50
+ }
48
51
  end
49
52
 
50
53
  # @return [Boolean]
51
54
  def time_to_feed?
52
- @subscription.last_chunk_fed_at + @subscription.chunk_query_interval <= Time.now.utc
53
- end
54
-
55
- # @return [Boolean]
56
- def next_chunk_safe?
57
- estimate_events_number == 0 ? false : @position_evaluation.evaluate(next_chunk_global_position).safe?
55
+ estimate_events_number > 0 && @subscription.last_chunk_fed_at + @subscription.chunk_query_interval <= Time.now.utc
58
56
  end
59
57
 
60
58
  private
@@ -69,10 +67,10 @@ module PgEventstore
69
67
 
70
68
  # @return [Integer]
71
69
  def estimate_events_number
72
- return INITIAL_EVENTS_PER_CHUNK if @stats.average_event_processing_time == 0
70
+ return @initial_events_per_chunk if @stats.average_event_processing_time == 0
73
71
 
74
72
  events_per_chunk = (@subscription.chunk_query_interval / @stats.average_event_processing_time).round
75
- events_to_fetch = [events_per_chunk, MAX_EVENTS_PER_CHUNK].min - @events_processor.events_left_in_chunk
73
+ events_to_fetch = [events_per_chunk, @max_events_per_chunk].min - @events_processor.events_left_in_repo
76
74
  return 0 if events_to_fetch < 0 # We still have a lot of events in the chunk - no need to fetch more
77
75
 
78
76
  [events_to_fetch, MIN_EVENTS_PER_CHUNK].max
@@ -99,6 +97,11 @@ module PgEventstore
99
97
  SubscriptionRunnerHandlers.setup_handler(:update_subscription_chunk_stats, @subscription)
100
98
  )
101
99
 
100
+ @events_processor.define_callback(
101
+ :checkpoint, :after,
102
+ SubscriptionRunnerHandlers.setup_handler(:checkpoint, @subscription)
103
+ )
104
+
102
105
  @events_processor.define_callback(
103
106
  :restart, :after,
104
107
  SubscriptionRunnerHandlers.setup_handler(:update_subscription_restarts, @subscription)
@@ -108,11 +111,6 @@ module PgEventstore
108
111
  :change_state, :after,
109
112
  SubscriptionRunnerHandlers.setup_handler(:update_subscription_state, @subscription)
110
113
  )
111
- # Prevent dangling position evaluation runner when subscription changes the state to something except 'running'
112
- @events_processor.define_callback(
113
- :change_state, :after,
114
- SubscriptionRunnerHandlers.setup_handler(:stop_position_evaluation, @position_evaluation)
115
- )
116
114
  end
117
115
  end
118
116
  end
@@ -16,7 +16,7 @@ module PgEventstore
16
16
  # @return [void]
17
17
  def exec_cmd(subscription_runner)
18
18
  subscription_runner.within_state(:stopped) do
19
- subscription_runner.clear_chunk
19
+ subscription_runner.clear_events_repository
20
20
  subscription_runner.subscription.update(
21
21
  last_chunk_greatest_position: nil, current_position: data['position'], total_processed_events: 0
22
22
  )
@@ -12,15 +12,19 @@ module PgEventstore
12
12
  # @param runners [Array<PgEventstore::SubscriptionRunner>]
13
13
  # @return [void]
14
14
  def feed(runners)
15
- runners = runners.select(&:running?).select(&:time_to_feed?).select(&:next_chunk_safe?)
15
+ runners = runners.select(&:running?).select(&:time_to_feed?)
16
16
  return if runners.empty?
17
17
 
18
- runners_query_options = runners.to_h { |runner| [runner.id, runner.next_chunk_query_opts] }
19
- grouped_events = subscription_queries.subscriptions_events(runners_query_options)
20
-
21
- runners.each do |runner|
22
- runner.feed(grouped_events[runner.id]) if grouped_events[runner.id]
18
+ feed_strategies_collection = SubscriptionFeedStrategy::Collection.create(
19
+ runners,
20
+ connection,
21
+ QueryStrategy::Async.new(connection)
22
+ )
23
+ query_runner = AsyncRunner.new
24
+ feed_strategies_collection.each do |strategy|
25
+ query_runner.async { strategy.feed }
23
26
  end
27
+ query_runner.run
24
28
  end
25
29
 
26
30
  private
@@ -29,10 +33,5 @@ module PgEventstore
29
33
  def connection
30
34
  PgEventstore.connection(@config_name)
31
35
  end
32
-
33
- # @return [PgEventstore::SubscriptionQueries]
34
- def subscription_queries
35
- SubscriptionQueries.new(connection)
36
- end
37
36
  end
38
37
  end
@@ -56,18 +56,6 @@ module PgEventstore
56
56
  @runners.map(&:subscription)
57
57
  end
58
58
 
59
- # Sets the force_lock flag to true. If set - all related Subscriptions will ignore their lock state and will be
60
- # locked by the new SubscriptionsSet.
61
- # @return [void]
62
- def force_lock!
63
- message = <<~TEXT
64
- Usage of #force_lock! is deprecated and will be removed in v2. Please pass :force_lock keyword argument when \
65
- initializing SubscriptionsManager. Example: PgEventstore.subscriptions_manager(force_lock: true)
66
- TEXT
67
- Utils.deprecation_warning(message)
68
- @force_lock = true
69
- end
70
-
71
59
  # @return [Boolean]
72
60
  def force_locked?
73
61
  @force_lock