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,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Extensions
5
+ # @!visibility private
6
+ module OptionsDefaults
7
+ def ==(other)
8
+ return false unless other.is_a?(self.class)
9
+
10
+ attributes_hash == other.attributes_hash
11
+ end
12
+ alias eql? ==
13
+
14
+ # @return [Integer]
15
+ def hash
16
+ attributes_hash.hash
17
+ end
18
+
19
+ # @return [self]
20
+ def dup
21
+ self.class.new(**Utils.deep_dup(options_hash))
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module PgEventstore
6
4
  module Extensions
7
5
  # A very simple extension that implements a DSL for adding attr_accessors with default values,
@@ -72,16 +70,47 @@ module PgEventstore
72
70
  end
73
71
  end
74
72
 
75
- class Options < Set
76
- def add(option)
77
- @hash[option] = option
78
- self
73
+ class Options
74
+ include Enumerable
75
+
76
+ attr_reader :options
77
+ protected :options
78
+
79
+ # @param options [Array<PgEventstore::Extensions::OptionsExtension::Option>]
80
+ def initialize(options = [])
81
+ @options = options.to_h { [_1, true] }
79
82
  end
80
83
 
81
- # @param option [Symbol]
84
+ # @param option_name [Symbol]
82
85
  # @return [PgEventstore::Extensions::OptionsExtension::Option, nil]
83
- def [](option)
84
- @hash[Option.new(option)]
86
+ def [](option_name)
87
+ option = Option.new(option_name)
88
+ options.find { |key, _| key == option }&.dig(0)
89
+ end
90
+
91
+ # @param other [PgEventstore::Extensions::OptionsExtension::Options]
92
+ # @return [PgEventstore::Extensions::OptionsExtension::Options]
93
+ def +(other)
94
+ self.class.new(options.keys + other.options.keys)
95
+ end
96
+
97
+ # @param option [PgEventstore::Extensions::OptionsExtension::Option]
98
+ # @return [Boolean]
99
+ def include?(option)
100
+ options.key?(option)
101
+ end
102
+
103
+ # @return [Boolean]
104
+ def dup
105
+ self.class.new(options.keys)
106
+ end
107
+
108
+ def each(...)
109
+ options.keys.each(...)
110
+ end
111
+
112
+ def ==(other)
113
+ options.keys == other.options.keys
85
114
  end
86
115
  end
87
116
 
@@ -92,7 +121,7 @@ module PgEventstore
92
121
  # context of your object to determine the default value of the option
93
122
  # @return [Symbol]
94
123
  def option(opt_name, metadata: nil, &blk)
95
- self.options = (options + Options.new([Option.new(opt_name, metadata: metadata)])).freeze
124
+ self.options = (options + Options.new([Option.new(opt_name, metadata:)])).freeze
96
125
  warn_already_defined(opt_name)
97
126
  warn_already_defined(:"#{opt_name}=")
98
127
  define_method "#{opt_name}=" do |value|
@@ -112,7 +141,7 @@ module PgEventstore
112
141
 
113
142
  def inherited(klass)
114
143
  super
115
- klass.options = Options.new(options).freeze
144
+ klass.options = options.dup.freeze
116
145
  end
117
146
 
118
147
  private
@@ -144,8 +173,8 @@ module PgEventstore
144
173
  # value
145
174
  # @return [Hash]
146
175
  def options_hash
147
- self.class.options.each_with_object({}) do |option, res|
148
- res[option.name] = public_send(option.name)
176
+ self.class.options.to_h do |option|
177
+ [option.name, public_send(option.name)]
149
178
  end
150
179
  end
151
180
  alias attributes_hash options_hash
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class FeatureMarker
5
+ include Extensions::OptionsExtension
6
+ include Extensions::OptionsDefaults
7
+
8
+ # @!attribute marker
9
+ # @return [String]
10
+ attribute(:marker)
11
+ # @!attribute description
12
+ # @return [String, nil] the description of the maker. Can be used as human-friendly explanation of the purpose
13
+ attribute(:description)
14
+ # @!attribute purpose
15
+ # @return [Symbol, nil] the purpose of the maker. Can be used as an identifier in your implementation
16
+ attribute(:purpose)
17
+ end
18
+ end
@@ -15,17 +15,17 @@ module PgEventstore
15
15
  # @param stream [PgEventstore::Stream]
16
16
  # @return [Boolean] whether a stream was deleted successfully
17
17
  def delete_stream(stream)
18
- Commands::DeleteStream.new(
19
- Queries.new(transactions: transaction_queries, maintenance: maintenance_queries)
20
- ).call(stream)
18
+ Utils.assert_node_role!(config, Config::NodeRole.maintainable)
19
+ queries = Queries.new(maintenance: maintenance_queries)
20
+ Commands::DeleteStream.new(queries).call(stream)
21
21
  end
22
22
 
23
23
  # @param event [PgEventstore::Event] persisted event
24
24
  # @return [Boolean] whether an event was deleted successfully
25
25
  def delete_event(event, force: false)
26
- Commands::DeleteEvent.new(
27
- Queries.new(transactions: transaction_queries, maintenance: maintenance_queries)
28
- ).call(event, force: force)
26
+ Utils.assert_node_role!(config, Config::NodeRole.maintainable)
27
+ queries = Queries.new(maintenance: maintenance_queries, events_global_index: events_global_index_queries)
28
+ Commands::DeleteEvent.new(queries).call(event, force:)
29
29
  end
30
30
 
31
31
  private
@@ -44,5 +44,10 @@ module PgEventstore
44
44
  def connection
45
45
  PgEventstore.connection(config.name)
46
46
  end
47
+
48
+ # @return [PgEventstore::EventsGlobalIndexQueries]
49
+ def events_global_index_queries
50
+ EventsGlobalIndexQueries.new(connection, QueryStrategy::Foreground.new(connection))
51
+ end
47
52
  end
48
53
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Middleware
5
+ class EventTracing
6
+ include Middleware
7
+
8
+ # @return [String]
9
+ CAUSATION_ID_KEY = "#{Event::SYSTEM_SYMBOL}CaId".freeze
10
+ # @return [String]
11
+ CORRELATION_ID_KEY = "#{Event::SYSTEM_SYMBOL}CoId".freeze
12
+
13
+ class << self
14
+ # @param marker [String]
15
+ # @return [PgEventstore::FeatureMarker]
16
+ def causation_marker(marker)
17
+ FeatureMarker.new(marker:, purpose: :causation_id, description: 'Causation ID')
18
+ end
19
+
20
+ # @param marker [String]
21
+ # @return [PgEventstore::FeatureMarker]
22
+ def correlation_marker(marker)
23
+ FeatureMarker.new(marker:, purpose: :correlation_id, description: 'Correlation ID')
24
+ end
25
+ end
26
+
27
+ def serialize(event)
28
+ if event.caused_by
29
+ event.causation_id = event.caused_by.id.dup
30
+ event.metadata[CAUSATION_ID_KEY] = event.causation_id
31
+ event.feature_markers.push(self.class.causation_marker(event.causation_id))
32
+ end
33
+
34
+ event.correlation_id = event.caused_by&.correlation_id&.dup || SecureRandom.uuid_v7
35
+ event.metadata[CORRELATION_ID_KEY] = event.correlation_id
36
+ event.feature_markers.push(self.class.correlation_marker(event.correlation_id))
37
+ end
38
+
39
+ # rubocop:disable Style/GuardClause
40
+ def deserialize(event)
41
+ if event.metadata[CAUSATION_ID_KEY]
42
+ event.causation_id = event.metadata[CAUSATION_ID_KEY]
43
+ event.feature_markers.push(self.class.causation_marker(event.causation_id))
44
+ end
45
+ if event.metadata[CORRELATION_ID_KEY]
46
+ event.correlation_id = event.metadata[CORRELATION_ID_KEY]
47
+ event.feature_markers.push(self.class.correlation_marker(event.correlation_id))
48
+ end
49
+ end
50
+ # rubocop:enable Style/GuardClause
51
+ end
52
+ end
53
+ end
@@ -19,5 +19,11 @@ module PgEventstore
19
19
  # @!attribute table_name
20
20
  # @return [String]
21
21
  option(:table_name)
22
+ # @!attribute parent_context_partition_id
23
+ # @return [Integer, nil]
24
+ option(:parent_context_partition_id)
25
+ # @!attribute parent_stream_name_partition_id
26
+ # @return [Integer, nil]
27
+ option(:parent_stream_name_partition_id)
22
28
  end
23
29
  end
@@ -3,6 +3,17 @@
3
3
  module PgEventstore
4
4
  # @!visibility private
5
5
  class PgConnection < PG::Connection
6
+ PLACEHOLDER_RE = /
7
+ '(?:''|[^'])*' | # string literal
8
+ "(?:""|[^"])*" | # quoted identifier
9
+ --[^\n]* | # line comment
10
+ \/\*.*?\*\/ | # block comment
11
+ \$\$.*?\$\$ | # dollar-quoted string. E.g. $$ $1 $$
12
+ \$(?<__dq_tag>[A-Za-z_][A-Za-z_0-9]*)\$.*?\$\k<__dq_tag>\$ | # named dollar-quoted string. E.g. $foo$ $1 $foo$
13
+ (?<placeholder>\$(?:[1-9]\d*)) # placeholder we are interested in
14
+ /mx
15
+ private_constant :PLACEHOLDER_RE
16
+
6
17
  def exec(sql)
7
18
  log(sql, [])
8
19
  super
@@ -13,37 +24,82 @@ module PgEventstore
13
24
  super
14
25
  end
15
26
 
16
- private
27
+ def send_query_params(sql, params)
28
+ log(sql, params)
29
+ super
30
+ end
17
31
 
18
- def log(sql, params)
19
- return unless PgEventstore.logger&.debug?
32
+ def send_query(sql)
33
+ log(sql, [])
34
+ super
35
+ end
36
+
37
+ # This is a partial copy-paste of https://github.com/ged/ruby-pg/pull/726. Because maintainers think it is too risky
38
+ # to accept those changes because of possible sql injections in the long run - I copy-pasted it here and simplified
39
+ # for pg_eventstore use cases.
40
+ # Why is it safe for pg_eventstore? The main concern there is that if new string literal would be introduced in some
41
+ # future PostgreSQL version - the implementation would become vulnerable to sql injections in case that new string
42
+ # literal is used in queries along with #compile. Because pg_eventstore uses only two forms of string literals
43
+ # (those are $$ my string $$ and 'my string') - we are totally safe. Also, there is only one place so far where this
44
+ # method is used(create subscription table function) - all other places use exec/exec_params. To conclude: careful
45
+ # usage + very low risk of future exposure to sql injections makes the usage of this method acceptable for
46
+ # pg_eventstore.
47
+ #
48
+ # Compiles your prepared SQL statement and the given positional arguments into plain SQL string.
49
+ #
50
+ # The resulting SQL string can be used with +conn.exec+ like the prepared SQL statement and parameters with
51
+ # +conn.exec_params+. +conn.exec_params+ is usually preferred because it's faster and safer.
52
+ #
53
+ # Example:
54
+ # res = conn.compile('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
55
+ # # => "SELECT '1' AS a, '2' AS b, NULL AS c"
56
+ # @param sql [String]
57
+ # @param params [Array]
58
+ # @return [String]
59
+ def compile(sql, params)
60
+ return sql if params.empty?
20
61
 
21
- unless params&.empty?
22
- sql = sql.gsub(/\$\d+/).each do |matched|
23
- value = params[matched[1..].to_i - 1]
24
- value = encode_value(value)
25
- normalize_value(value)
26
- end
62
+ sql.gsub(PLACEHOLDER_RE).each do |matched|
63
+ placeholder = Regexp.last_match[:placeholder]
64
+ # Do not replace non-positional args string and pass it as is
65
+ next matched unless placeholder
66
+
67
+ prepared_value(params[placeholder[1..].to_i - 1])
27
68
  end
28
- PgEventstore.logger.debug(sql)
29
69
  end
30
70
 
71
+ # Converts the given object into its PostgreSQL representation
72
+ def prepared_value(value)
73
+ value = encode_value(value)
74
+ normalize_value(value)
75
+ end
76
+
77
+ private
78
+
31
79
  def encode_value(value)
32
80
  encoder = type_map_for_queries[value.class]
33
81
  return type_map_for_queries.send(encoder, value).encode(value) if encoder.is_a?(Symbol)
82
+ # format == 1 stands for binary format
83
+ return value if encoder.nil? || encoder.format == 1
34
84
 
35
- type_map_for_queries[value.class]&.encode(value) || value
85
+ encoder.encode(value)
36
86
  end
37
87
 
38
88
  def normalize_value(value)
39
89
  case value
40
- when String
41
- "'#{value}'"
90
+ when TrueClass, FalseClass
91
+ value.to_s
42
92
  when NilClass
43
93
  'NULL'
44
94
  else
45
- value
95
+ "'#{escape(value.to_s)}'"
46
96
  end
47
97
  end
98
+
99
+ def log(sql, params)
100
+ return unless PgEventstore.logger&.debug?
101
+
102
+ PgEventstore.logger.debug(compile(sql, params))
103
+ end
48
104
  end
49
105
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ # @!visibility private
5
+ class EventMarkerQueries
6
+ # @!attribute connection
7
+ # @return [PgEventstore::Connection]
8
+ attr_reader :connection
9
+ private :connection
10
+
11
+ # @param connection [PgEventstore::Connection]
12
+ # @param query_strategy [PgEventstore::QueryStrategy]
13
+ def initialize(connection, query_strategy)
14
+ @connection = connection
15
+ @query_strategy = query_strategy
16
+ end
17
+
18
+ # @param names [Array<String>]
19
+ # @return [Array<PgEventstore::EventMarker>]
20
+ def find_or_create_by(names)
21
+ builder = QueryBuilders::EventMarkersFiltering.sql_builder_by_names(names)
22
+ existing = @query_strategy.exec_params(*builder.to_exec_params)
23
+ existing = deserialize(existing)
24
+ rest = names - existing.map(&:name)
25
+ return existing if rest.empty?
26
+
27
+ values = rest.map do |name|
28
+ "('#{PG::Connection.escape(name)}')"
29
+ end.join(',')
30
+ table_name = QueryBuilders::EventMarkersFiltering::PRIMARY_TABLE_NAME
31
+ rest = @query_strategy.exec(<<~SQL)
32
+ insert into #{table_name} ("name") values #{values} returning *
33
+ SQL
34
+ existing + deserialize(rest)
35
+ end
36
+
37
+ # @param stream_idx_id [Integer]
38
+ # @param write_api_events_index [Array<PgEventstore::EventGlobalIndex::WriteApiRepr>]
39
+ # @param revision_to_marker_ids_map [Hash<Integer, Set<Integer>>]
40
+ # @return [void]
41
+ def create_indexes(stream_idx_id, write_api_events_index, revision_to_marker_ids_map)
42
+ offset = write_api_events_index.first.stream_revision
43
+ values = revision_to_marker_ids_map.flat_map do |stream_revision, marker_ids|
44
+ event_index = write_api_events_index[stream_revision - offset]
45
+ marker_ids.map do |marker_id|
46
+ values = [
47
+ marker_id,
48
+ stream_idx_id,
49
+ event_index.global_position,
50
+ event_index.stream_revision,
51
+ event_index.event_type_partition_id,
52
+ ]
53
+ "(#{values.join(',')})"
54
+ end
55
+ end
56
+ values = values.join(', ')
57
+ table_name = QueryBuilders::EventMarkersIndexFiltering::PRIMARY_TABLE_NAME
58
+ @query_strategy.exec(<<~SQL)
59
+ insert into #{table_name}
60
+ ("marker_id", "streams_global_index_id", "global_position", "stream_revision", "event_type_partition_id")
61
+ values
62
+ #{values}
63
+ SQL
64
+ end
65
+
66
+ private
67
+
68
+ # @param pg_result [PG::Result]
69
+ # @return [Array<PgEventstore::EventMarker>]
70
+ def deserialize(pg_result)
71
+ pg_result.map { EventMarker.new(**_1.transform_keys(&:to_sym)) }
72
+ end
73
+ end
74
+ end
@@ -6,85 +6,19 @@ module PgEventstore
6
6
  # @!attribute connection
7
7
  # @return [PgEventstore::Connection]
8
8
  attr_reader :connection
9
- # @!attribute serializer
10
- # @return [PgEventstore::EventSerializer]
11
- attr_reader :serializer
12
- # @!attribute deserializer
13
- # @return [PgEventstore::EventDeserializer]
14
- attr_reader :deserializer
15
- private :connection, :serializer, :deserializer
9
+ private :connection
16
10
 
17
11
  # @param connection [PgEventstore::Connection]
18
- # @param serializer [PgEventstore::EventSerializer]
19
- # @param deserializer [PgEventstore::EventDeserializer]
20
- def initialize(connection, serializer, deserializer)
12
+ def initialize(connection)
21
13
  @connection = connection
22
- @serializer = serializer
23
- @deserializer = deserializer
24
- end
25
-
26
- # @param event [PgEventstore::Event]
27
- # @return [Boolean]
28
- def event_exists?(event)
29
- return false if event.id.nil? || event.stream.nil?
30
-
31
- sql_builder = SQLBuilder.new.select('1 as exists').from(Event::PRIMARY_TABLE_NAME).where('id = ?', event.id)
32
- sql_builder.limit(1)
33
- sql_builder.where(
34
- 'context = ? and stream_name = ? and type = ?', event.stream.context, event.stream.stream_name, event.type
35
- )
36
- connection.with do |conn|
37
- conn.exec_params(*sql_builder.to_exec_params)
38
- end.to_a.dig(0, 'exists') == 1
39
- end
40
-
41
- # Takes an array of potentially persisted events and loads their ids from db. Those ids can be later used to check
42
- # whether events are actually existing events.
43
- # @param events [Array<PgEventstore::Event>]
44
- # @return [Array<String>]
45
- def ids_from_db(events)
46
- sql_builder = SQLBuilder.new.from(Event::PRIMARY_TABLE_NAME).select('id')
47
- partition_attrs = events.map { |event| [event.stream&.context, event.stream&.stream_name, event.type] }.uniq
48
- partition_attrs.each do |context, stream_name, event_type|
49
- sql_builder.where_or('context = ? and stream_name = ? and type = ?', context, stream_name, event_type)
50
- end
51
- sql_builder.where('id = ANY(?::uuid[])', events.map(&:id))
52
- raw_events = PgEventstore.connection.with do |conn|
53
- conn.exec_params(*sql_builder.to_exec_params)
54
- end.to_a
55
- raw_events.map { |attrs| attrs['id'] }
56
- end
57
-
58
- # @param stream [PgEventstore::Stream]
59
- # @return [Integer, nil]
60
- def stream_revision(stream)
61
- sql_builder = SQLBuilder.new.from(Event::PRIMARY_TABLE_NAME).select('stream_revision')
62
- sql_builder.where('context = ? and stream_name = ? and stream_id = ?', *stream.to_a)
63
- sql_builder.order('stream_revision DESC').limit(1)
64
- connection.with do |conn|
65
- conn.exec_params(*sql_builder.to_exec_params)
66
- end.to_a.dig(0, 'stream_revision')
67
- end
68
-
69
- # @see PgEventstore::Client#read for more info
70
- # @param stream [PgEventstore::Stream]
71
- # @param options [Hash]
72
- # @return [Array<PgEventstore::Event>]
73
- def stream_events(stream, options)
74
- exec_params = QueryBuilders::EventsFiltering.events_filtering(stream, options).to_exec_params
75
- raw_events = connection.with do |conn|
76
- conn.exec_params(*exec_params)
77
- end.to_a
78
- raw_events = links_resolver.resolve(raw_events) if options[:resolve_link_tos]
79
- deserializer.deserialize_many(raw_events)
80
14
  end
81
15
 
82
16
  # @param stream [PgEventstore::Stream]
83
17
  # @param events [Array<PgEventstore::Event>]
84
- # @return [Array<PgEventstore::Event>]
18
+ # @return [Array<Hash>]
85
19
  def insert(stream, events)
86
20
  sql_rows_for_insert, values = prepared_statements(stream, events)
87
- columns = %w[id data metadata stream_revision link_id link_partition_id type context stream_name stream_id]
21
+ columns = %w[id data metadata stream_revision link_global_position link_partition_id type context stream_name stream_id]
88
22
 
89
23
  sql = <<~SQL
90
24
  INSERT INTO events (#{columns.join(', ')})
@@ -92,30 +26,11 @@ module PgEventstore
92
26
  RETURNING *
93
27
  SQL
94
28
 
95
- raw_events = connection.with do |conn|
29
+ result = connection.with do |conn|
96
30
  conn.exec_params(sql, values)
97
31
  end
98
- raw_events.map do |raw_event|
99
- deserializer.without_middlewares.deserialize(raw_event)
100
- end
101
- end
102
-
103
- # @param stream [PgEventstore::Stream]
104
- # @param options_by_event_type [Array<Hash>] a set of options per an event type
105
- # @param options [Hash]
106
- # @option options [Boolean] :resolve_link_tos
107
- # @return [Array<PgEventstore::Event>]
108
- def grouped_events(stream, options_by_event_type, **options)
109
- builders = options_by_event_type.map do |filter|
110
- QueryBuilders::EventsFiltering.events_filtering(stream, filter)
111
- end
112
- final_builder = SQLBuilder.union_builders(builders.map(&:to_sql_builder))
113
-
114
- raw_events = connection.with do |conn|
115
- conn.exec_params(*final_builder.to_exec_params)
116
- end.to_a
117
- raw_events = links_resolver.resolve(raw_events) if options[:resolve_link_tos]
118
- deserializer.deserialize_many(raw_events)
32
+ # "returning" statement has no guarantees about the order in which rows are returned. Thus, sort them explicitly
33
+ result.sort_by { _1['stream_revision'] }
119
34
  end
120
35
 
121
36
  private
@@ -127,9 +42,8 @@ module PgEventstore
127
42
  positional_counter = 1
128
43
  values = []
129
44
  sql_rows_for_insert = events.map do |event|
130
- event = serializer.serialize(event)
131
45
  attributes = event.options_hash.slice(
132
- :id, :data, :metadata, :stream_revision, :link_id, :link_partition_id, :type
46
+ :id, :data, :metadata, :stream_revision, :link_global_position, :link_partition_id, :type
133
47
  )
134
48
 
135
49
  attributes = attributes.merge(stream.to_hash)
@@ -150,7 +64,7 @@ module PgEventstore
150
64
 
151
65
  # @return [PgEventstore::LinksResolver]
152
66
  def links_resolver
153
- LinksResolver.new(connection)
67
+ LinksResolver.new(connection, QueryStrategy::Foreground.new(connection))
154
68
  end
155
69
  end
156
70
  end