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
@@ -6,19 +6,22 @@ require_relative 'basic_runner'
6
6
  require_relative 'runner_recovery_strategy'
7
7
  require_relative 'runner_recovery_strategies'
8
8
  require_relative 'subscription'
9
- require_relative 'synchronized_array'
9
+ require_relative 'events_processor_consumer'
10
10
  require_relative 'events_processor'
11
11
  require_relative 'subscription_handler_performance'
12
12
  require_relative 'subscription_runner'
13
- require_relative 'subscription_position_evaluation'
14
13
  require_relative 'subscriptions_set'
14
+ require_relative 'subscription_feed_strategy'
15
15
  require_relative 'subscription_runners_feeder'
16
16
  require_relative 'subscriptions_set_lifecycle'
17
17
  require_relative 'subscriptions_lifecycle'
18
18
  require_relative 'callback_handlers/subscription_feeder_handlers'
19
19
  require_relative 'callback_handlers/subscription_runner_handlers'
20
20
  require_relative 'callback_handlers/events_processor_handlers'
21
+ require_relative 'callback_handlers/events_subscription_position_worker_handlers'
21
22
  require_relative 'callback_handlers/commands_handler_handlers'
23
+ require_relative 'commands_handler'
24
+ require_relative 'events_subscription_position_worker'
22
25
  require_relative 'subscription_feeder'
23
26
  require_relative 'extensions/command_class_lookup_extension'
24
27
  require_relative 'extensions/base_command_extension'
@@ -26,10 +29,11 @@ require_relative 'subscription_feeder_commands'
26
29
  require_relative 'subscription_runner_commands'
27
30
  require_relative 'queries/subscription_command_queries'
28
31
  require_relative 'queries/subscription_queries'
32
+ require_relative 'queries/event_subscription_position_queries'
29
33
  require_relative 'queries/subscriptions_set_command_queries'
30
34
  require_relative 'queries/subscriptions_set_queries'
31
- require_relative 'queries/service_queries'
32
- require_relative 'commands_handler'
35
+ require_relative 'replica_subscription_handler'
36
+ require_relative 'replica_subscription_runner'
33
37
 
34
38
  module PgEventstore
35
39
  # The public Subscriptions API, available to the user.
@@ -42,7 +46,6 @@ module PgEventstore
42
46
  private :config
43
47
 
44
48
  def_delegators :@subscription_feeder, :stop, :running?
45
- def_delegators :@subscriptions_lifecycle, :force_lock!
46
49
 
47
50
  # @param config [PgEventstore::Config]
48
51
  # @param set_name [String]
@@ -61,10 +64,10 @@ module PgEventstore
61
64
  }
62
65
  )
63
66
  @subscriptions_lifecycle = SubscriptionsLifecycle.new(
64
- config_name, @subscriptions_set_lifecycle, force_lock: force_lock
67
+ config_name, @subscriptions_set_lifecycle, force_lock:
65
68
  )
66
69
  @subscription_feeder = SubscriptionFeeder.new(
67
- config_name: config_name,
70
+ config_name:,
68
71
  subscriptions_set_lifecycle: @subscriptions_set_lifecycle,
69
72
  subscriptions_lifecycle: @subscriptions_lifecycle
70
73
  )
@@ -72,8 +75,8 @@ module PgEventstore
72
75
 
73
76
  # @param subscription_name [String] subscription's name
74
77
  # @param handler [#call] subscription's handler
75
- # @param options [Hash] request options
76
- # @option options [Integer, Symbol] :from_position a starting subscription position
78
+ # @param options [Hash] subscription options
79
+ # @option options [Integer] :from_position a starting subscription position
77
80
  # @option options [Boolean] :resolve_link_tos When using projections to create new events you
78
81
  # can set whether the generated events are pointers to existing events. Setting this option to true tells
79
82
  # PgEventstore to return the original event instead a link event.
@@ -92,6 +95,7 @@ module PgEventstore
92
95
  # notification about failed subscription.
93
96
  # @param graceful_shutdown_timeout [integer, Float] the number of seconds to wait until force-shutdown the
94
97
  # subscription during the stop process
98
+ # @param in_batches [Boolean] whether a batch of events should be yielded instead a single event
95
99
  # @return [void]
96
100
  def subscribe(subscription_name, handler:, options: {}, middlewares: nil,
97
101
  pull_interval: config.subscription_pull_interval,
@@ -99,23 +103,78 @@ module PgEventstore
99
103
  retries_interval: config.subscription_retries_interval,
100
104
  restart_terminator: config.subscription_restart_terminator,
101
105
  failed_subscription_notifier: config.failed_subscription_notifier,
102
- graceful_shutdown_timeout: config.subscription_graceful_shutdown_timeout)
106
+ graceful_shutdown_timeout: config.subscription_graceful_shutdown_timeout,
107
+ in_batches: false)
103
108
  subscription = Subscription.using_connection(config.name).new(
104
- set: @set_name, name: subscription_name, options: options, chunk_query_interval: pull_interval,
109
+ set: @set_name, name: subscription_name, options:, chunk_query_interval: pull_interval,
105
110
  max_restarts_number: max_retries, time_between_restarts: retries_interval
106
111
  )
112
+ consumer_class = EventsProcessorConsumer.consumer_class(in_batches)
107
113
  runner = SubscriptionRunner.new(
108
114
  stats: SubscriptionHandlerPerformance.new,
109
115
  events_processor: EventsProcessor.new(
110
- create_raw_event_handler(middlewares, handler),
111
- graceful_shutdown_timeout: graceful_shutdown_timeout,
116
+ graceful_shutdown_timeout:,
117
+ consumer: consumer_class.create_consumer(handler, deserializer(middlewares)),
118
+ recovery_strategies: recovery_strategies(subscription, restart_terminator, failed_subscription_notifier)
119
+ ),
120
+ subscription:
121
+ )
122
+
123
+ @subscriptions_lifecycle.runners.push(runner)
124
+ true
125
+ end
126
+
127
+ # @param subscription_name [String] subscription's name. For example, you can name it after the target database:
128
+ # "Target: my_eventstore_replica1"
129
+ # @param replica_config_name [Symbol] the destination replica database config to replicate events into. You have to
130
+ # have it preconfigured with PgEventstore.configure(). Example:
131
+ # PgEventstore.configure(name: :my_eventstore_replica1) do |config|
132
+ # config.pg_uri = 'postgresql://postgres:postgres@localhost:5432/my_eventstore_replica1'
133
+ # end
134
+ # Refer to the docs/configuration.md for more info
135
+ # @param options [Hash] subscription options
136
+ # @option options [Integer] :from_position a starting subscription position
137
+ # @option options [Hash] :filter provide it to filter events. It works the same way as a :filter option of
138
+ # {PgEventstore::Client#read} method. Use this option when you want to replicate certain set of events.
139
+ # @param max_events_to_replicate [Integer] set the upper limit of how many events the handler can copy per turn
140
+ # @param pull_interval [Integer, Float] an interval in seconds to determine how often to query new events of the
141
+ # given subscription.
142
+ # @param max_retries [Integer] max number of retries of failed Subscription
143
+ # @param retries_interval [Integer, Float] a delay between retries of failed Subscription
144
+ # @param restart_terminator [#call, nil] a callable object which is invoked with PgEventstore::Subscription instance
145
+ # to determine whether restarts should be stopped(true - stops restarts, false - continues restarts)
146
+ # @param failed_subscription_notifier [#call, nil] a callable object which is invoked with
147
+ # PgEventstore::Subscription instance and error instance after the related subscription died due to error and no
148
+ # longer can be automatically restarted due to max retries number reached. You can use this hook to send a
149
+ # notification about failed subscription.
150
+ # @param graceful_shutdown_timeout [Integer, Float] the number of seconds to wait until force-shutdown the
151
+ # subscription during the stop process
152
+ # @return [void]
153
+ def create_replication(subscription_name,
154
+ replica_config_name,
155
+ options: {},
156
+ max_events_to_replicate: config.max_events_to_replicate,
157
+ pull_interval: config.subscription_pull_interval,
158
+ max_retries: config.subscription_max_retries,
159
+ retries_interval: config.subscription_retries_interval,
160
+ restart_terminator: config.subscription_restart_terminator,
161
+ failed_subscription_notifier: config.failed_subscription_notifier,
162
+ graceful_shutdown_timeout: config.subscription_graceful_shutdown_timeout)
163
+ Utils.assert_node_role!(config, [Config::NodeRole::PRIMARY])
164
+ subscription = Subscription.using_connection(config.name).new(
165
+ set: @set_name, name: subscription_name, options:, chunk_query_interval: pull_interval,
166
+ max_restarts_number: max_retries, time_between_restarts: retries_interval
167
+ )
168
+ runner = ReplicaSubscriptionRunner.new(
169
+ stats: SubscriptionHandlerPerformance.new,
170
+ events_processor: EventsProcessor.new(
171
+ graceful_shutdown_timeout:,
172
+ consumer: EventsProcessorConsumer::Replica.create_consumer(config_name, replica_config_name),
112
173
  recovery_strategies: recovery_strategies(subscription, restart_terminator, failed_subscription_notifier)
113
174
  ),
114
- subscription: subscription,
115
- position_evaluation: SubscriptionPositionEvaluation.new(
116
- config_name: config.name,
117
- filter_options: options[:filter] || {}
118
- )
175
+ subscription:,
176
+ max_events_per_chunk: max_events_to_replicate,
177
+ initial_events_per_chunk: max_events_to_replicate
119
178
  )
120
179
 
121
180
  @subscriptions_lifecycle.runners.push(runner)
@@ -156,18 +215,14 @@ module PgEventstore
156
215
  private
157
216
 
158
217
  # @return [Object] the result of the passed block
159
- def run_cli_callbacks(&blk)
218
+ def run_cli_callbacks(&)
160
219
  return yield unless defined?(::PgEventstore::CLI)
161
220
 
162
- PgEventstore::CLI.callbacks.run_callbacks(:start_manager, self, &blk)
221
+ PgEventstore::CLI.callbacks.run_callbacks(:start_manager, self, &)
163
222
  end
164
223
 
165
- # @param middlewares [Array<Symbol>, nil]
166
- # @param handler [#call]
167
- # @return [Proc]
168
- def create_raw_event_handler(middlewares, handler)
169
- deserializer = EventDeserializer.new(select_middlewares(middlewares), config.event_class_resolver)
170
- ->(raw_event) { handler.call(deserializer.deserialize(raw_event)) }
224
+ def deserializer(middlewares)
225
+ EventDeserializer.new(select_middlewares(middlewares), config.event_class_resolver)
171
226
  end
172
227
 
173
228
  # @param middlewares [Array, nil]
@@ -186,9 +241,9 @@ module PgEventstore
186
241
  [
187
242
  RunnerRecoveryStrategies::RestoreConnection.new(config_name),
188
243
  RunnerRecoveryStrategies::RestoreSubscriptionRunner.new(
189
- subscription: subscription,
190
- restart_terminator: restart_terminator,
191
- failed_subscription_notifier: failed_subscription_notifier
244
+ subscription:,
245
+ restart_terminator:,
246
+ failed_subscription_notifier:
192
247
  ),
193
248
  ]
194
249
  end
@@ -13,6 +13,9 @@ module PgEventstore
13
13
  alias old_clear clear
14
14
  alias old_size size
15
15
  alias old_empty? empty?
16
+ alias old_slice slice
17
+ alias old_slice! slice!
18
+ alias old_at at
16
19
 
17
20
  def shift(...)
18
21
  synchronize { old_shift(...) }
@@ -37,5 +40,17 @@ module PgEventstore
37
40
  def empty?(...)
38
41
  synchronize { old_empty?(...) }
39
42
  end
43
+
44
+ def slice(...)
45
+ synchronize { old_slice(...) }
46
+ end
47
+
48
+ def slice!(...)
49
+ synchronize { old_slice!(...) }
50
+ end
51
+
52
+ def at(...)
53
+ synchronize { old_at(...) }
54
+ end
40
55
  end
41
56
  end
@@ -2,50 +2,53 @@
2
2
 
3
3
  require 'uri'
4
4
 
5
- helpers = Class.new do
6
- class << self
7
- def postgres_uri
8
- @postgres_uri ||=
9
- begin
10
- uri = URI.parse(ENV.fetch('PG_EVENTSTORE_URI'))
11
- uri.path = '/postgres'
12
- uri.to_s
13
- end
14
- end
5
+ module PgEventstore
6
+ class MigrationHelpers
7
+ class << self
8
+ def postgres_uri
9
+ @postgres_uri ||=
10
+ begin
11
+ uri = URI.parse(ENV.fetch('PG_EVENTSTORE_URI'))
12
+ uri.path = '/postgres'
13
+ uri.to_s
14
+ end
15
+ end
15
16
 
16
- def db_name
17
- @db_name ||= URI.parse(ENV.fetch('PG_EVENTSTORE_URI')).path&.delete('/')
17
+ def db_name
18
+ @db_name ||= URI.parse(ENV.fetch('PG_EVENTSTORE_URI')).path&.delete('/')
19
+ end
18
20
  end
19
21
  end
20
22
  end
21
23
 
24
+ PgEventstore.configure(name: :_postgres_db_connection) do |config|
25
+ config.pg_uri = PgEventstore::MigrationHelpers.postgres_uri
26
+ end
27
+
28
+ PgEventstore.configure(name: :_eventstore_db_connection) do |config|
29
+ config.pg_uri = ENV['PG_EVENTSTORE_URI']
30
+ end
31
+
22
32
  namespace :pg_eventstore do
23
33
  desc 'Creates events table, indexes, etc.'
24
34
  task :create do
25
- PgEventstore.configure do |config|
26
- config.pg_uri = helpers.postgres_uri
27
- end
28
-
29
- PgEventstore.connection.with do |conn|
35
+ PgEventstore.connection(:_postgres_db_connection).with do |conn|
30
36
  exists =
31
- conn.exec_params('SELECT 1 as exists FROM pg_database where datname = $1', [helpers.db_name]).
37
+ conn.exec_params('SELECT 1 as exists FROM pg_database where datname = $1', [PgEventstore::MigrationHelpers.db_name]).
32
38
  first&.dig('exists')
33
39
  if exists
34
- puts "#{helpers.db_name} already exists. Skipping."
40
+ puts "#{PgEventstore::MigrationHelpers.db_name} already exists. Skipping."
35
41
  else
36
- conn.exec("CREATE DATABASE #{conn.escape_string(helpers.db_name)} WITH OWNER #{conn.escape_string(conn.user)}")
42
+ escaped_db_name = conn.escape_string(PgEventstore::MigrationHelpers.db_name)
43
+ conn.exec("CREATE DATABASE #{escaped_db_name} WITH OWNER #{conn.escape_string(conn.user)}")
37
44
  end
38
45
  end
39
46
  end
40
47
 
41
48
  task :migrate do
42
- PgEventstore.configure do |config|
43
- config.pg_uri = ENV['PG_EVENTSTORE_URI']
44
- end
49
+ migration_files_root = File.join(Gem.loaded_specs.fetch('pg_eventstore').gem_dir, 'db/migrations')
45
50
 
46
- migration_files_root = "#{Gem::Specification.find_by_name('pg_eventstore').gem_dir}/db/migrations"
47
-
48
- PgEventstore.connection.with do |conn|
51
+ PgEventstore.connection(:_eventstore_db_connection).with do |conn|
49
52
  conn.exec('CREATE TABLE IF NOT EXISTS migrations (number int NOT NULL)')
50
53
  latest_migration =
51
54
  conn.exec('SELECT number FROM migrations ORDER BY number DESC LIMIT 1').to_a.dig(0, 'number') || -1
@@ -56,7 +59,7 @@ namespace :pg_eventstore do
56
59
  next if latest_migration >= number
57
60
 
58
61
  if File.extname(f_name) == '.rb'
59
- load f_name
62
+ load(f_name, true)
60
63
  else
61
64
  conn.exec(File.read(f_name))
62
65
  end
@@ -68,12 +71,8 @@ namespace :pg_eventstore do
68
71
 
69
72
  desc 'Drops events table and related pg_eventstore objects.'
70
73
  task :drop do
71
- PgEventstore.configure do |config|
72
- config.pg_uri = helpers.postgres_uri
73
- end
74
-
75
- PgEventstore.connection.with do |conn|
76
- conn.exec("DROP DATABASE IF EXISTS #{conn.escape_string(helpers.db_name)}")
74
+ PgEventstore.connection(:_postgres_db_connection).with do |conn|
75
+ conn.exec("DROP DATABASE IF EXISTS #{conn.escape_string(PgEventstore::MigrationHelpers.db_name)}")
77
76
  end
78
77
  end
79
78
  end
@@ -124,6 +124,45 @@ module PgEventstore
124
124
 
125
125
  wrapped_exception
126
126
  end
127
+
128
+ # Yields the given block and measures its execution time
129
+ # @return [Float] number of seconds the block took to execute
130
+ def benchmark
131
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
132
+ yield
133
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
134
+ end
135
+
136
+ # @param truthy [boolish]
137
+ # @param message [String, nil]
138
+ # @raise ArgumentError
139
+ def assert!(truthy, message = nil)
140
+ raise ArgumentError, message unless truthy
141
+ end
142
+
143
+ # @param obj
144
+ # @raise NotImplementedError
145
+ def missing_implementation!(obj)
146
+ raise NotImplementedError, obj.inspect
147
+ end
148
+
149
+ # @param config [PgEventstore::Config]
150
+ # @param expected_roles [Array<Symbol>]
151
+ # @return [void]
152
+ def assert_node_role!(config, expected_roles)
153
+ return if expected_roles.include?(config.eventstore_role)
154
+
155
+ raise "You can't perform this operation on #{config.eventstore_role.inspect} node!"
156
+ end
157
+
158
+ # Takes a collection of partition ids, the number of partitions per call and determines the position in the
159
+ # partition_ids at which we have max_partitions_to_resolve_per_call unique partitions
160
+ # @param partition_ids [Array<Integer>]
161
+ # @param max_partitions_to_resolve_per_call [Integer]
162
+ # @return [Range]
163
+ def range_to_slice(partition_ids, max_partitions_to_resolve_per_call)
164
+ # This is stub, used for indexing
165
+ end
127
166
  end
128
167
  end
129
168
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = '1.13.4'
5
+ VERSION = '3.0.0'
6
6
  end
@@ -32,7 +32,7 @@ module PgEventstore
32
32
  # @return [Array<Hash>, nil]
33
33
  # rubocop:disable Style/HashConversion
34
34
  def streams_filter
35
- streams = QueryBuilders::EventsFiltering.extract_streams_filter(params)
35
+ streams = extract_streams_filter(params)
36
36
  streams = streams.select { _1 in { context: String, stream_name: String, stream_id: String } }
37
37
  streams = streams.map do |stream_attrs|
38
38
  Hash[stream_attrs.reject { |_, value| value == '' }].transform_keys(&:to_sym)
@@ -41,17 +41,32 @@ module PgEventstore
41
41
  end
42
42
  # rubocop:enable Style/HashConversion
43
43
 
44
- # @return [String, nil]
45
- def system_stream
46
- params in { filter: { system_stream: String => system_stream } }
47
- system_stream if Stream::KNOWN_SYSTEM_STREAMS.include?(system_stream)
48
- end
49
-
50
- # @return [Array<String>, nil]
44
+ # @return [Array<String, Hash>]
51
45
  def events_filter
52
46
  event_filters = { filter: { event_types: params.dig(:filter, :events) } }
53
- events = QueryBuilders::EventsFiltering.extract_event_types_filter(event_filters)
54
- events.reject { _1 == '' }
47
+ event_types = extract_event_types_filter(event_filters)
48
+ event_types.map do |filter|
49
+ case filter
50
+ when String
51
+ next if filter == ''
52
+ when Hash
53
+ next if filter[:type] == ''
54
+
55
+ filter[:markers] = normalize_markers(filter)
56
+ else
57
+ next
58
+ end
59
+
60
+ filter
61
+ end.compact
62
+ end
63
+
64
+ # @return [Array<String>]
65
+ def markers_filter
66
+ params in { filter: Hash => filter }
67
+ return [] unless filter
68
+
69
+ normalize_markers(filter)
55
70
  end
56
71
 
57
72
  # @return [Symbol]
@@ -87,7 +102,7 @@ module PgEventstore
87
102
  attrs.transform_values { escape_empty_string(_1) }
88
103
  end
89
104
  halt 200, {
90
- results: results,
105
+ results:,
91
106
  pagination: { more: !collection.next_page_starting_id.nil?, starting_id: collection.next_page_starting_id },
92
107
  }.to_json
93
108
  end
@@ -137,13 +152,55 @@ module PgEventstore
137
152
  def unescape_empty_string(string)
138
153
  string == EMPTY_STRING_SIGN ? '' : string
139
154
  end
155
+
156
+ # @param options [Hash]
157
+ # @return [Array<String, Hash>]
158
+ def extract_event_types_filter(options)
159
+ options in { filter: { event_types: Array => event_types } }
160
+ event_types = event_types&.select do |event_type|
161
+ event_type.is_a?(String) || (event_type in { type: String })
162
+ end
163
+ event_types || []
164
+ end
165
+
166
+ # @param options [Hash]
167
+ # @return [Array<Hash[Symbol, String]>]
168
+ def extract_streams_filter(options)
169
+ options in { filter: { streams: Array => streams } }
170
+ streams = streams&.map do |stream_attrs|
171
+ stream_attrs in { context: String | NilClass => context }
172
+ stream_attrs in { stream_name: String | NilClass => stream_name }
173
+ stream_attrs in { stream_id: String | NilClass => stream_id }
174
+ { context:, stream_name:, stream_id: }
175
+ end
176
+ streams || []
177
+ end
178
+
179
+ private
180
+
181
+ # @param hash [Hash]
182
+ # @return [Array<String>]
183
+ def normalize_markers(hash)
184
+ hash in { markers: Array => markers }
185
+ markers ||= []
186
+ markers.grep(String).reject { _1 == '' }
187
+ end
140
188
  end
141
189
 
142
190
  get '/' do
143
191
  streams_filter = self.streams_filter&.map do |attrs|
144
192
  attrs.transform_values { unescape_empty_string(_1) }
145
193
  end
146
- events_filter = self.events_filter&.map(&method(:unescape_empty_string))
194
+ events_filter = self.events_filter.map do |event_type|
195
+ next unescape_empty_string(event_type) if event_type.is_a?(String)
196
+
197
+ event_type[:type] = unescape_empty_string(event_type[:type])
198
+ event_type[:markers] = event_type[:markers]&.grep(String) || []
199
+ event_type
200
+ end
201
+ markers_filter = self.markers_filter.map(&method(:unescape_empty_string))
202
+ events_filter.push({ markers: markers_filter }) if markers_filter.any?
203
+
147
204
  @collection = Paginator::EventsCollection.new(
148
205
  current_config,
149
206
  starting_id: params[:starting_id]&.to_i,
@@ -152,14 +209,20 @@ module PgEventstore
152
209
  options: {
153
210
  filter: { event_types: events_filter, streams: streams_filter },
154
211
  resolve_link_tos: resolve_link_tos?,
155
- },
156
- system_stream: system_stream
212
+ }
157
213
  )
214
+ @gp_to_sp_map = EventSubscriptionPositionQueries.new(
215
+ PgEventstore.connection(current_config)
216
+ ).subscription_positions_from_db(@collection.collection)
158
217
 
159
218
  if request.xhr?
160
219
  content_type 'application/json'
161
220
  halt 200, {
162
- events: erb(:'home/partials/events', { layout: false }, { events: @collection.collection }),
221
+ events: erb(
222
+ :'home/partials/events',
223
+ { layout: false },
224
+ { events: @collection.collection, gp_to_sp_map: @gp_to_sp_map }
225
+ ),
163
226
  total_count: total_count(@collection.total_count),
164
227
  pagination: erb(:'home/partials/pagination_links', { layout: false }, { collection: @collection }),
165
228
  }.to_json
@@ -168,6 +231,16 @@ module PgEventstore
168
231
  end
169
232
  end
170
233
 
234
+ get '/streams' do
235
+ @collection = Paginator::StreamsCollection.new(
236
+ current_config,
237
+ starting_id: params[:starting_id]&.to_i,
238
+ per_page: Paginator::StreamsCollection::PER_PAGE[params[:per_page]],
239
+ order: Paginator::StreamsCollection::SQL_DIRECTIONS[params[:order]]
240
+ )
241
+ erb :'streams/index'
242
+ end
243
+
171
244
  get '/subscriptions' do
172
245
  @set_collection = Subscriptions::SetCollection.new(connection)
173
246
  @current_set = params[:set_name] || @set_collection.names.first
@@ -188,8 +261,8 @@ module PgEventstore
188
261
  connection, @current_set, state: params[:state]
189
262
  ).subscriptions
190
263
  @association = Subscriptions::SubscriptionsToSetAssociation.new(
191
- subscriptions_set: subscriptions_set,
192
- subscriptions: subscriptions
264
+ subscriptions_set:,
265
+ subscriptions:
193
266
  )
194
267
  erb :'subscriptions/index'
195
268
  end
@@ -247,6 +320,17 @@ module PgEventstore
247
320
  paginated_json_response(collection)
248
321
  end
249
322
 
323
+ get '/markers_filtering', provides: :json do
324
+ collection = Paginator::MarkersCollection.new(
325
+ current_config,
326
+ starting_id: unescape_empty_string(params[:starting_id]),
327
+ per_page: Paginator::MarkersCollection::PER_PAGE,
328
+ order: :asc,
329
+ options: { query: params[:term] }
330
+ )
331
+ paginated_json_response(collection)
332
+ end
333
+
250
334
  post '/subscription_cmd/:set_id/:id/:cmd' do
251
335
  validate_subscription_cmd(params[:cmd])
252
336
  cmd_class = SubscriptionRunnerCommands.command_class(params[:cmd])
@@ -279,14 +363,14 @@ module PgEventstore
279
363
  end
280
364
 
281
365
  post '/delete_subscription/:id' do
282
- SubscriptionQueries.new(connection).delete(Integer(params[:id]))
366
+ SubscriptionQueries.new(connection, QueryStrategy::Foreground.new(connection)).delete(Integer(params[:id]))
283
367
 
284
368
  redirect redirect_back_url(fallback_url: url('/subscriptions'))
285
369
  end
286
370
 
287
371
  post '/delete_all_subscriptions' do
288
372
  params[:ids].each do |id|
289
- SubscriptionQueries.new(connection).delete(Integer(id))
373
+ SubscriptionQueries.new(connection, QueryStrategy::Foreground.new(connection)).delete(Integer(id))
290
374
  end
291
375
 
292
376
  redirect redirect_back_url(fallback_url: url('/subscriptions'))
@@ -301,7 +385,7 @@ module PgEventstore
301
385
  ).first
302
386
  if event&.global_position == global_position
303
387
  begin
304
- PgEventstore.maintenance(current_config).delete_event(event, force: force)
388
+ PgEventstore.maintenance(current_config).delete_event(event, force:)
305
389
  self.flash_message = {
306
390
  message: "An event at global position #{event.global_position} has been deleted successfully.",
307
391
  kind: 'success',
@@ -4,6 +4,9 @@ module PgEventstore
4
4
  module Web
5
5
  module Paginator
6
6
  class BaseCollection
7
+ # This is the minimum amount of symbols requeried to perform search using trigrams gin index
8
+ MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH = 3
9
+
7
10
  # @!attribute config_name
8
11
  # @return [Symbol]
9
12
  attr_reader :config_name
@@ -30,7 +33,8 @@ module PgEventstore
30
33
  @starting_id = starting_id
31
34
  @per_page = per_page
32
35
  @order = order
33
- @options = options
36
+ @options =
37
+ options.is_a?(Sinatra::IndifferentHash) ? Utils.deep_transform_keys(Hash[options], &:to_sym) : options
34
38
  end
35
39
 
36
40
  # @return [Array]
@@ -12,9 +12,9 @@ module PgEventstore
12
12
  @collection ||=
13
13
  begin
14
14
  sql_builder = SQLBuilder.new.select('event_type').from('partitions')
15
- sql_builder.where('context is not null and stream_name is not null')
15
+ sql_builder.where('event_type is not null')
16
16
  sql_builder.where("event_type #{direction_operator} ?", starting_id) if starting_id
17
- sql_builder.where('event_type ilike ?', "%#{options[:query]}%")
17
+ compute_event_type_filter(sql_builder)
18
18
  sql_builder.group('event_type').order("event_type #{order}").limit(per_page)
19
19
  connection.with do |conn|
20
20
  conn.exec_params(*sql_builder.to_exec_params)
@@ -28,9 +28,8 @@ module PgEventstore
28
28
 
29
29
  starting_id = collection.first['event_type']
30
30
  sql_builder = SQLBuilder.new.select('event_type').from('partitions')
31
- sql_builder.where('context is not null and stream_name is not null')
32
31
  sql_builder.where("event_type #{direction_operator} ?", starting_id)
33
- sql_builder.where('event_type ilike ?', "%#{options[:query]}%")
32
+ compute_event_type_filter(sql_builder)
34
33
  sql_builder.group('event_type').order("event_type #{order}").limit(1).offset(per_page)
35
34
 
36
35
  connection.with do |conn|
@@ -44,6 +43,17 @@ module PgEventstore
44
43
  def direction_operator
45
44
  order == :asc ? '>=' : '<='
46
45
  end
46
+
47
+ # @param builder [PgEventstore::SQLBuilder]
48
+ # @return [void]
49
+ def compute_event_type_filter(builder)
50
+ query = options[:query].to_s
51
+ if query.size < MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH
52
+ builder.where('event_type like ?', "#{query}%")
53
+ else
54
+ builder.where('event_type ilike ?', "%#{query}%")
55
+ end
56
+ end
47
57
  end
48
58
  end
49
59
  end