neo4j-ruby-driver 6.0.0.alpha.2 → 6.2.1.beta.1

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 (346) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -411
  3. data/{ruby → lib}/neo4j/driver/access_mode.rb +2 -1
  4. data/lib/neo4j/driver/auth_token_managers.rb +31 -0
  5. data/lib/neo4j/driver/auth_tokens.rb +41 -0
  6. data/lib/neo4j/driver/auto_commit_retries_mode.rb +18 -0
  7. data/lib/neo4j/driver/bolt/bolt_version.rb +67 -0
  8. data/lib/neo4j/driver/bolt/connection.rb +1054 -0
  9. data/lib/neo4j/driver/bolt/handshake.rb +232 -0
  10. data/lib/neo4j/driver/bolt/message/failure.rb +139 -0
  11. data/lib/neo4j/driver/bolt/message/ignored.rb +27 -0
  12. data/lib/neo4j/driver/bolt/message/record.rb +30 -0
  13. data/lib/neo4j/driver/bolt/message/success.rb +34 -0
  14. data/lib/neo4j/driver/bolt/message.rb +112 -0
  15. data/lib/neo4j/driver/bolt/pool.rb +255 -0
  16. data/lib/neo4j/driver/bolt/protocol/base.rb +146 -0
  17. data/lib/neo4j/driver/bolt/protocol/v3.rb +43 -0
  18. data/lib/neo4j/driver/bolt/protocol/v4.rb +24 -0
  19. data/lib/neo4j/driver/bolt/protocol/v43.rb +28 -0
  20. data/lib/neo4j/driver/bolt/protocol/v44.rb +23 -0
  21. data/lib/neo4j/driver/bolt/protocol/v5.rb +27 -0
  22. data/lib/neo4j/driver/bolt/protocol/v51.rb +24 -0
  23. data/lib/neo4j/driver/bolt/protocol/v52.rb +28 -0
  24. data/lib/neo4j/driver/bolt/protocol/v53.rb +27 -0
  25. data/lib/neo4j/driver/bolt/protocol/v54.rb +14 -0
  26. data/lib/neo4j/driver/bolt/protocol/v55.rb +17 -0
  27. data/lib/neo4j/driver/bolt/protocol/v56.rb +14 -0
  28. data/lib/neo4j/driver/bolt/protocol/v57.rb +31 -0
  29. data/lib/neo4j/driver/bolt/protocol/v58.rb +15 -0
  30. data/lib/neo4j/driver/bolt/protocol/v6.rb +43 -0
  31. data/lib/neo4j/driver/bolt/protocol/v61.rb +21 -0
  32. data/lib/neo4j/driver/bolt/protocol_version_handler.rb +39 -0
  33. data/lib/neo4j/driver/bolt/record_buffer.rb +136 -0
  34. data/lib/neo4j/driver/bolt/stream_handler.rb +53 -0
  35. data/lib/neo4j/driver/bolt/tls_config.rb +129 -0
  36. data/lib/neo4j/driver/bolt/wire.rb +321 -0
  37. data/lib/neo4j/driver/bookmark.rb +15 -0
  38. data/lib/neo4j/driver/bookmark_managers.rb +39 -0
  39. data/lib/neo4j/driver/client_certificate.rb +19 -0
  40. data/lib/neo4j/driver/client_certificate_manager.rb +37 -0
  41. data/lib/neo4j/driver/client_certificate_managers.rb +15 -0
  42. data/lib/neo4j/driver/client_certificates.rb +14 -0
  43. data/lib/neo4j/driver/direct/connection_provider.rb +258 -0
  44. data/lib/neo4j/driver/driver.rb +268 -0
  45. data/lib/neo4j/driver/eager_result.rb +10 -0
  46. data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
  47. data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
  48. data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
  49. data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
  50. data/lib/neo4j/driver/graph_database.rb +29 -0
  51. data/lib/neo4j/driver/internal/clock.rb +20 -0
  52. data/lib/neo4j/driver/internal/clock_adapter.rb +21 -0
  53. data/lib/neo4j/driver/internal/default_bookmark_manager.rb +54 -0
  54. data/lib/neo4j/driver/internal/deprecator.rb +9 -5
  55. data/lib/neo4j/driver/internal/driver_factory.rb +128 -0
  56. data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -41
  57. data/lib/neo4j/driver/internal/extras.rb +23 -0
  58. data/lib/neo4j/driver/internal/home_db_cache.rb +57 -0
  59. data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
  60. data/lib/neo4j/driver/internal/metrics.rb +31 -0
  61. data/lib/neo4j/driver/internal/security/static_auth_token_manager.rb +25 -0
  62. data/lib/neo4j/driver/internal/validator.rb +10 -0
  63. data/lib/neo4j/driver/net.rb +29 -0
  64. data/lib/neo4j/driver/packstream/markers.rb +49 -0
  65. data/lib/neo4j/driver/packstream/packer.rb +333 -0
  66. data/lib/neo4j/driver/packstream/structure.rb +27 -0
  67. data/lib/neo4j/driver/packstream/unpacker.rb +168 -0
  68. data/lib/neo4j/driver/record.rb +49 -0
  69. data/lib/neo4j/driver/result.rb +282 -0
  70. data/lib/neo4j/driver/routing/load_balancer.rb +690 -0
  71. data/lib/neo4j/driver/routing/routed_connection.rb +132 -0
  72. data/lib/neo4j/driver/routing/routing_table.rb +127 -0
  73. data/lib/neo4j/driver/routing/server_address.rb +25 -0
  74. data/lib/neo4j/driver/routing_control.rb +13 -0
  75. data/lib/neo4j/driver/session.rb +575 -0
  76. data/lib/neo4j/driver/summary/database_info.rb +25 -0
  77. data/lib/neo4j/driver/summary/gql_notification.rb +38 -0
  78. data/lib/neo4j/driver/summary/gql_status_object.rb +35 -0
  79. data/lib/neo4j/driver/summary/notification.rb +46 -0
  80. data/lib/neo4j/driver/summary/plan.rb +23 -0
  81. data/lib/neo4j/driver/summary/profile.rb +28 -0
  82. data/lib/neo4j/driver/summary/query.rb +19 -0
  83. data/lib/neo4j/driver/summary/query_type.rb +5 -1
  84. data/lib/neo4j/driver/summary/result_summary.rb +213 -0
  85. data/lib/neo4j/driver/summary/server_info.rb +33 -0
  86. data/lib/neo4j/driver/summary/summary_counters.rb +72 -0
  87. data/lib/neo4j/driver/transaction.rb +341 -0
  88. data/lib/neo4j/driver/types/duration.rb +76 -0
  89. data/lib/neo4j/driver/types/local_date_time.rb +52 -8
  90. data/lib/neo4j/driver/types/local_time.rb +42 -7
  91. data/lib/neo4j/driver/types/node.rb +27 -0
  92. data/lib/neo4j/driver/types/offset_time.rb +71 -7
  93. data/lib/neo4j/driver/types/path.rb +68 -0
  94. data/lib/neo4j/driver/types/point.rb +38 -18
  95. data/lib/neo4j/driver/types/relationship.rb +41 -0
  96. data/lib/neo4j/driver/types/temporal_value.rb +43 -0
  97. data/lib/neo4j/driver/types/unbound_relationship.rb +29 -0
  98. data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
  99. data/lib/neo4j/driver/types/unsupported_type.rb +25 -0
  100. data/lib/neo4j/driver/types/uuid.rb +29 -0
  101. data/{ruby → lib}/neo4j/driver/version.rb +1 -1
  102. data/lib/neo4j/driver.rb +16 -0
  103. data/lib/neo4j-ruby-driver_loader.rb +34 -20
  104. metadata +132 -390
  105. data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
  106. data/lib/neo4j/driver/synchronizable.rb +0 -23
  107. data/lib/neo4j/driver/types/time.rb +0 -45
  108. data/ruby/neo4j/driver/auth_tokens.rb +0 -34
  109. data/ruby/neo4j/driver/bookmark.rb +0 -21
  110. data/ruby/neo4j/driver/config.rb +0 -89
  111. data/ruby/neo4j/driver/graph_database.rb +0 -78
  112. data/ruby/neo4j/driver/internal/async/connection/bolt_protocol_util.rb +0 -51
  113. data/ruby/neo4j/driver/internal/async/connection/bootstrap_factory.rb +0 -22
  114. data/ruby/neo4j/driver/internal/async/connection/channel_attributes.rb +0 -31
  115. data/ruby/neo4j/driver/internal/async/connection/channel_connected_listener.rb +0 -32
  116. data/ruby/neo4j/driver/internal/async/connection/channel_connector_impl.rb +0 -83
  117. data/ruby/neo4j/driver/internal/async/connection/channel_pipeline_builder_impl.rb +0 -22
  118. data/ruby/neo4j/driver/internal/async/connection/direct_connection.rb +0 -30
  119. data/ruby/neo4j/driver/internal/async/connection/event_loop_group_factory.rb +0 -83
  120. data/ruby/neo4j/driver/internal/async/connection/handshake_completed_listener.rb +0 -27
  121. data/ruby/neo4j/driver/internal/async/connection/handshake_handler.rb +0 -113
  122. data/ruby/neo4j/driver/internal/async/connection/netty_channel_initializer.rb +0 -57
  123. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver.rb +0 -26
  124. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver_group.rb +0 -19
  125. data/ruby/neo4j/driver/internal/async/connection/routing_connection.rb +0 -36
  126. data/ruby/neo4j/driver/internal/async/connection/stream.rb +0 -12
  127. data/ruby/neo4j/driver/internal/async/connection/stream_reader.rb +0 -16
  128. data/ruby/neo4j/driver/internal/async/connection_context.rb +0 -10
  129. data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +0 -24
  130. data/ruby/neo4j/driver/internal/async/inbound/byte_buf_input.rb +0 -30
  131. data/ruby/neo4j/driver/internal/async/inbound/channel_error_handler.rb +0 -77
  132. data/ruby/neo4j/driver/internal/async/inbound/chunk_decoder.rb +0 -41
  133. data/ruby/neo4j/driver/internal/async/inbound/connect_timeout_handler.rb +0 -32
  134. data/ruby/neo4j/driver/internal/async/inbound/connection_read_timeout_handler.rb +0 -17
  135. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +0 -171
  136. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_handler.rb +0 -42
  137. data/ruby/neo4j/driver/internal/async/inbound/message_decoder.rb +0 -51
  138. data/ruby/neo4j/driver/internal/async/internal_async_session.rb +0 -92
  139. data/ruby/neo4j/driver/internal/async/internal_async_transaction.rb +0 -13
  140. data/ruby/neo4j/driver/internal/async/leak_logging_network_session.rb +0 -34
  141. data/ruby/neo4j/driver/internal/async/network_connection.rb +0 -194
  142. data/ruby/neo4j/driver/internal/async/network_session.rb +0 -150
  143. data/ruby/neo4j/driver/internal/async/outbound/chunk_aware_byte_buf_output.rb +0 -110
  144. data/ruby/neo4j/driver/internal/async/outbound/outbound_message_handler.rb +0 -39
  145. data/ruby/neo4j/driver/internal/async/pool/channel.rb +0 -66
  146. data/ruby/neo4j/driver/internal/async/pool/channel_pool.rb +0 -31
  147. data/ruby/neo4j/driver/internal/async/pool/channel_tracker.rb +0 -135
  148. data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +0 -156
  149. data/ruby/neo4j/driver/internal/async/pool/netty_channel_health_checker.rb +0 -87
  150. data/ruby/neo4j/driver/internal/async/pool/netty_channel_pool.rb +0 -52
  151. data/ruby/neo4j/driver/internal/async/pool/network_connection_factory.rb +0 -21
  152. data/ruby/neo4j/driver/internal/async/pool/pool_settings.rb +0 -34
  153. data/ruby/neo4j/driver/internal/async/pool/timed_stack.rb +0 -15
  154. data/ruby/neo4j/driver/internal/async/result_cursors_holder.rb +0 -17
  155. data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +0 -212
  156. data/ruby/neo4j/driver/internal/bookmark_holder.rb +0 -9
  157. data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +0 -48
  158. data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +0 -14
  159. data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +0 -122
  160. data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +0 -10
  161. data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +0 -68
  162. data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +0 -125
  163. data/ruby/neo4j/driver/internal/cluster/loadbalancing/round_robin_array_index.rb +0 -13
  164. data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +0 -31
  165. data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +0 -147
  166. data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +0 -43
  167. data/ruby/neo4j/driver/internal/cluster/routing_context.rb +0 -77
  168. data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +0 -60
  169. data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +0 -35
  170. data/ruby/neo4j/driver/internal/cluster/routing_settings.rb +0 -24
  171. data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +0 -95
  172. data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +0 -121
  173. data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +0 -73
  174. data/ruby/neo4j/driver/internal/connection_settings.rb +0 -16
  175. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +0 -55
  176. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_only_factory.rb +0 -24
  177. data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +0 -61
  178. data/ruby/neo4j/driver/internal/cursor/result_cursor_factory_impl.rb +0 -24
  179. data/ruby/neo4j/driver/internal/cursor/rx_result_cursor_impl.rb +0 -110
  180. data/ruby/neo4j/driver/internal/database_name_util.rb +0 -37
  181. data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +0 -9
  182. data/ruby/neo4j/driver/internal/default_domain_name_resolver.rb +0 -11
  183. data/ruby/neo4j/driver/internal/direct_connection_provider.rb +0 -40
  184. data/ruby/neo4j/driver/internal/driver_factory.rb +0 -126
  185. data/ruby/neo4j/driver/internal/eager_result_value.rb +0 -5
  186. data/ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb +0 -20
  187. data/ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb +0 -30
  188. data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +0 -23
  189. data/ruby/neo4j/driver/internal/handlers/hello_response_handler.rb +0 -65
  190. data/ruby/neo4j/driver/internal/handlers/init_response_handler.rb +0 -34
  191. data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +0 -199
  192. data/ruby/neo4j/driver/internal/handlers/no_op_response_handler.rb +0 -16
  193. data/ruby/neo4j/driver/internal/handlers/ping_response_handler.rb +0 -29
  194. data/ruby/neo4j/driver/internal/handlers/pull_handlers.rb +0 -32
  195. data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +0 -169
  196. data/ruby/neo4j/driver/internal/handlers/pulln/basic_pull_response_handler.rb +0 -298
  197. data/ruby/neo4j/driver/internal/handlers/pulln/fetch_size_util.rb +0 -20
  198. data/ruby/neo4j/driver/internal/handlers/reset_response_handler.rb +0 -34
  199. data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +0 -25
  200. data/ruby/neo4j/driver/internal/handlers/route_message_response_handler.rb +0 -21
  201. data/ruby/neo4j/driver/internal/handlers/routing_response_handler.rb +0 -79
  202. data/ruby/neo4j/driver/internal/handlers/run_response_handler.rb +0 -38
  203. data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +0 -34
  204. data/ruby/neo4j/driver/internal/handlers/transaction_pull_response_completion_listener.rb +0 -20
  205. data/ruby/neo4j/driver/internal/impersonation_util.rb +0 -22
  206. data/ruby/neo4j/driver/internal/internal_bookmark.rb +0 -9
  207. data/ruby/neo4j/driver/internal/internal_database_name.rb +0 -9
  208. data/ruby/neo4j/driver/internal/internal_driver.rb +0 -83
  209. data/ruby/neo4j/driver/internal/internal_entity.rb +0 -21
  210. data/ruby/neo4j/driver/internal/internal_node.rb +0 -21
  211. data/ruby/neo4j/driver/internal/internal_pair.rb +0 -9
  212. data/ruby/neo4j/driver/internal/internal_path.rb +0 -35
  213. data/ruby/neo4j/driver/internal/internal_point2_d.rb +0 -9
  214. data/ruby/neo4j/driver/internal/internal_point3_d.rb +0 -6
  215. data/ruby/neo4j/driver/internal/internal_record.rb +0 -27
  216. data/ruby/neo4j/driver/internal/internal_relationship.rb +0 -27
  217. data/ruby/neo4j/driver/internal/internal_result.rb +0 -49
  218. data/ruby/neo4j/driver/internal/internal_session.rb +0 -91
  219. data/ruby/neo4j/driver/internal/internal_transaction.rb +0 -48
  220. data/ruby/neo4j/driver/internal/logging/channel_activity_logger.rb +0 -29
  221. data/ruby/neo4j/driver/internal/logging/channel_error_logger.rb +0 -17
  222. data/ruby/neo4j/driver/internal/logging/prefixed_logger.rb +0 -19
  223. data/ruby/neo4j/driver/internal/logging/reformatted_logger.rb +0 -17
  224. data/ruby/neo4j/driver/internal/messaging/abstract_message_writer.rb +0 -23
  225. data/ruby/neo4j/driver/internal/messaging/bolt_protocol.rb +0 -32
  226. data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +0 -48
  227. data/ruby/neo4j/driver/internal/messaging/common/common_message_reader.rb +0 -51
  228. data/ruby/neo4j/driver/internal/messaging/common/common_value.rb +0 -33
  229. data/ruby/neo4j/driver/internal/messaging/common/common_value_packer.rb +0 -104
  230. data/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +0 -256
  231. data/ruby/neo4j/driver/internal/messaging/encode/begin_message_encoder.rb +0 -15
  232. data/ruby/neo4j/driver/internal/messaging/encode/commit_message_encoder.rb +0 -14
  233. data/ruby/neo4j/driver/internal/messaging/encode/discard_all_message_encoder.rb +0 -14
  234. data/ruby/neo4j/driver/internal/messaging/encode/discard_message_encoder.rb +0 -15
  235. data/ruby/neo4j/driver/internal/messaging/encode/goodbye_message_encoder.rb +0 -14
  236. data/ruby/neo4j/driver/internal/messaging/encode/hello_message_encoder.rb +0 -15
  237. data/ruby/neo4j/driver/internal/messaging/encode/init_message_encoder.rb +0 -16
  238. data/ruby/neo4j/driver/internal/messaging/encode/logoff_message_encoder.rb +0 -14
  239. data/ruby/neo4j/driver/internal/messaging/encode/logon_message_encoder.rb +0 -15
  240. data/ruby/neo4j/driver/internal/messaging/encode/pull_all_message_encoder.rb +0 -14
  241. data/ruby/neo4j/driver/internal/messaging/encode/pull_message_encoder.rb +0 -15
  242. data/ruby/neo4j/driver/internal/messaging/encode/reset_message_encoder.rb +0 -14
  243. data/ruby/neo4j/driver/internal/messaging/encode/rollback_message_encoder.rb +0 -14
  244. data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +0 -24
  245. data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +0 -22
  246. data/ruby/neo4j/driver/internal/messaging/encode/run_message_encoder.rb +0 -16
  247. data/ruby/neo4j/driver/internal/messaging/encode/run_with_metadata_message_encoder.rb +0 -17
  248. data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +0 -25
  249. data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +0 -25
  250. data/ruby/neo4j/driver/internal/messaging/request/commit_message.rb +0 -20
  251. data/ruby/neo4j/driver/internal/messaging/request/discard_all_message.rb +0 -20
  252. data/ruby/neo4j/driver/internal/messaging/request/discard_message.rb +0 -23
  253. data/ruby/neo4j/driver/internal/messaging/request/goodbye_message.rb +0 -20
  254. data/ruby/neo4j/driver/internal/messaging/request/hello_message.rb +0 -30
  255. data/ruby/neo4j/driver/internal/messaging/request/init_message.rb +0 -19
  256. data/ruby/neo4j/driver/internal/messaging/request/logoff_message.rb +0 -13
  257. data/ruby/neo4j/driver/internal/messaging/request/logon_message.rb +0 -13
  258. data/ruby/neo4j/driver/internal/messaging/request/message_with_metadata.rb +0 -17
  259. data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +0 -26
  260. data/ruby/neo4j/driver/internal/messaging/request/pull_all_message.rb +0 -23
  261. data/ruby/neo4j/driver/internal/messaging/request/pull_message.rb +0 -22
  262. data/ruby/neo4j/driver/internal/messaging/request/reset_message.rb +0 -32
  263. data/ruby/neo4j/driver/internal/messaging/request/rollback_message.rb +0 -20
  264. data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +0 -28
  265. data/ruby/neo4j/driver/internal/messaging/request/run_message.rb +0 -23
  266. data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +0 -49
  267. data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +0 -24
  268. data/ruby/neo4j/driver/internal/messaging/response/failure_message.rb +0 -40
  269. data/ruby/neo4j/driver/internal/messaging/response/ignored_message.rb +0 -29
  270. data/ruby/neo4j/driver/internal/messaging/response/record_message.rb +0 -33
  271. data/ruby/neo4j/driver/internal/messaging/response/success_message.rb +0 -34
  272. data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +0 -82
  273. data/ruby/neo4j/driver/internal/messaging/v3/message_format_v3.rb +0 -17
  274. data/ruby/neo4j/driver/internal/messaging/v3/message_writer_v3.rb +0 -27
  275. data/ruby/neo4j/driver/internal/messaging/v4/bolt_protocol_v4.rb +0 -29
  276. data/ruby/neo4j/driver/internal/messaging/v4/message_format_v4.rb +0 -13
  277. data/ruby/neo4j/driver/internal/messaging/v4/message_writer_v4.rb +0 -17
  278. data/ruby/neo4j/driver/internal/messaging/v41/bolt_protocol_v41.rb +0 -25
  279. data/ruby/neo4j/driver/internal/messaging/v42/bolt_protocol_v42.rb +0 -13
  280. data/ruby/neo4j/driver/internal/messaging/v43/bolt_protocol_v43.rb +0 -19
  281. data/ruby/neo4j/driver/internal/messaging/v43/message_format_v43.rb +0 -14
  282. data/ruby/neo4j/driver/internal/messaging/v43/message_writer_v43.rb +0 -20
  283. data/ruby/neo4j/driver/internal/messaging/v44/bolt_protocol_v44.rb +0 -17
  284. data/ruby/neo4j/driver/internal/messaging/v44/message_format_v44.rb +0 -14
  285. data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +0 -15
  286. data/ruby/neo4j/driver/internal/messaging/v5/bolt_protocol_v5.rb +0 -21
  287. data/ruby/neo4j/driver/internal/messaging/v5/message_format_v5.rb +0 -18
  288. data/ruby/neo4j/driver/internal/messaging/v5/value_unpacker_v5.rb +0 -17
  289. data/ruby/neo4j/driver/internal/messaging/v51/bolt_protocol_v51.rb +0 -40
  290. data/ruby/neo4j/driver/internal/messaging/v51/message_format_v51.rb +0 -14
  291. data/ruby/neo4j/driver/internal/messaging/v51/message_writer_v51.rb +0 -17
  292. data/ruby/neo4j/driver/internal/metrics/connection_pool_metrics_listener.rb +0 -34
  293. data/ruby/neo4j/driver/internal/metrics/internal_abstract_metrics.rb +0 -46
  294. data/ruby/neo4j/driver/internal/metrics/internal_connection_pool_metrics.rb +0 -105
  295. data/ruby/neo4j/driver/internal/metrics/internal_metrics.rb +0 -82
  296. data/ruby/neo4j/driver/internal/metrics/internal_metrics_provider.rb +0 -18
  297. data/ruby/neo4j/driver/internal/metrics/listener_event.rb +0 -17
  298. data/ruby/neo4j/driver/internal/metrics/metrics_provider.rb +0 -24
  299. data/ruby/neo4j/driver/internal/metrics/time_recorder_listener_event.rb +0 -15
  300. data/ruby/neo4j/driver/internal/packstream/byte_array_incompatible_packer.rb +0 -12
  301. data/ruby/neo4j/driver/internal/packstream/pack_input.rb +0 -47
  302. data/ruby/neo4j/driver/internal/packstream/pack_output.rb +0 -39
  303. data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +0 -324
  304. data/ruby/neo4j/driver/internal/packstream/pack_type.rb +0 -17
  305. data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +0 -13
  306. data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +0 -35
  307. data/ruby/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +0 -150
  308. data/ruby/neo4j/driver/internal/revocation_strategy.rb +0 -19
  309. data/ruby/neo4j/driver/internal/scheme.rb +0 -32
  310. data/ruby/neo4j/driver/internal/security/internal_auth_token.rb +0 -15
  311. data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +0 -48
  312. data/ruby/neo4j/driver/internal/security_setting.rb +0 -66
  313. data/ruby/neo4j/driver/internal/session_factory_impl.rb +0 -32
  314. data/ruby/neo4j/driver/internal/spi/connection.rb +0 -19
  315. data/ruby/neo4j/driver/internal/spi/connection_pool.rb +0 -9
  316. data/ruby/neo4j/driver/internal/spi/response_handler.rb +0 -23
  317. data/ruby/neo4j/driver/internal/summary/internal_database_info.rb +0 -7
  318. data/ruby/neo4j/driver/internal/summary/internal_input_position.rb +0 -11
  319. data/ruby/neo4j/driver/internal/summary/internal_notification.rb +0 -16
  320. data/ruby/neo4j/driver/internal/summary/internal_plan.rb +0 -41
  321. data/ruby/neo4j/driver/internal/summary/internal_profiled_plan.rb +0 -32
  322. data/ruby/neo4j/driver/internal/summary/internal_result_summary.rb +0 -33
  323. data/ruby/neo4j/driver/internal/summary/internal_server_info.rb +0 -6
  324. data/ruby/neo4j/driver/internal/summary/internal_summary_counters.rb +0 -18
  325. data/ruby/neo4j/driver/internal/svm/netty_substitutions.rb +0 -196
  326. data/ruby/neo4j/driver/internal/svm/z_lib_substitutions.rb +0 -21
  327. data/ruby/neo4j/driver/internal/util/certificate_tool.rb +0 -65
  328. data/ruby/neo4j/driver/internal/util/clock.rb +0 -29
  329. data/ruby/neo4j/driver/internal/util/error_util.rb +0 -104
  330. data/ruby/neo4j/driver/internal/util/extract.rb +0 -123
  331. data/ruby/neo4j/driver/internal/util/format.rb +0 -39
  332. data/ruby/neo4j/driver/internal/util/futures.rb +0 -99
  333. data/ruby/neo4j/driver/internal/util/iterables.rb +0 -35
  334. data/ruby/neo4j/driver/internal/util/lock_util.rb +0 -23
  335. data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +0 -101
  336. data/ruby/neo4j/driver/internal/util/mutex.rb +0 -9
  337. data/ruby/neo4j/driver/internal/util/preconditions.rb +0 -16
  338. data/ruby/neo4j/driver/internal/util/result_holder.rb +0 -72
  339. data/ruby/neo4j/driver/internal/util/server_version.rb +0 -60
  340. data/ruby/neo4j/driver/logging1.rb +0 -51
  341. data/ruby/neo4j/driver/net/server_address.rb +0 -9
  342. data/ruby/neo4j/driver/query.rb +0 -48
  343. data/ruby/neo4j/driver/records.rb +0 -13
  344. data/ruby/neo4j/driver/transaction_config.rb +0 -50
  345. data/ruby/neo4j/driver/values.rb +0 -26
  346. data/ruby/neo4j/driver.rb +0 -30
@@ -0,0 +1,575 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ # Per-operation connection acquisition: each `run` / `begin_transaction` /
6
+ # `execute_read` / `execute_write` acquires a fresh connection from the
7
+ # driver with that operation's access mode and database, and releases it
8
+ # back to the pool when the operation completes (Result fully consumed,
9
+ # or transaction committed/rolled back). The session itself does not
10
+ # hold a connection — only its in-flight operation does.
11
+ class Session
12
+ include Internal::DurationNormalizer
13
+ def initialize(connection_provider, options = {}, clock: Internal::Clock.new)
14
+ @connection_provider = connection_provider
15
+ @options = options
16
+ @clock = clock
17
+ @transaction = nil
18
+ @open = true
19
+ @last_bookmarks = Set.new
20
+ @current_result = nil
21
+ # The home database this session pinned after its first resolution
22
+ # (Optimization:HomeDatabaseCache); nil until then / for explicit-db sessions.
23
+ @pinned_database = nil
24
+ @used_guess = false # whether an operation optimistically guessed the home db
25
+ @bookmark_manager = options[:bookmark_manager]
26
+ # The bookmark snapshot we sent on the most recent BEGIN — used
27
+ # as `previous_bookmarks` when forwarding to the manager so it
28
+ # set-difference-drops what it knew before. Computed at BEGIN
29
+ # time (not update time) because the manager may have grown
30
+ # between the two via another session.
31
+ @bookmarks_used_on_begin = nil
32
+
33
+ # Initialize with provided bookmarks if any
34
+ if options[:bookmarks]
35
+ initial_bookmarks = options[:bookmarks]
36
+ initial_bookmarks = [initial_bookmarks] unless initial_bookmarks.is_a?(Enumerable)
37
+ initial_bookmarks.each do |bookmark|
38
+ @last_bookmarks << (bookmark.is_a?(Bookmark) ? bookmark : Bookmark.from(bookmark))
39
+ end
40
+ end
41
+ end
42
+
43
+ def run(query, parameters = {}, config = {})
44
+ Internal::Validator.require_query_text!(query)
45
+ parameters ||= {}
46
+
47
+ unless parameters.is_a?(Hash)
48
+ raise ArgumentError,
49
+ "The parameters should be provided as Map type. Unsupported parameters type: #{parameters.class}"
50
+ end
51
+
52
+ raise Exceptions::ClientException, 'Session is closed' unless @open
53
+ raise Exceptions::ClientException, 'You cannot run a query directly on a session while a transaction is open' if @transaction&.open?
54
+
55
+ drain_current_result
56
+
57
+ timeout = config.delete(:timeout)
58
+ tx_metadata = config.delete(:metadata)
59
+
60
+ # `mode` is sent for read sessions (matches routing-server
61
+ # expectations); writers are the default and omit the field.
62
+ # `bookmarks` is the current bookmark snapshot so server-side
63
+ # causal consistency works across auto-commit runs without an
64
+ # explicit transaction.
65
+ # `imp_user` (Bolt 4.4+) makes the server run the query as if
66
+ # the named user had issued it — auth as the session's user,
67
+ # authz as the impersonated one.
68
+ bookmarks = current_bookmarks_for_extra
69
+ # Acquire (with its own bookmark snapshot) so the home-db cache can pick
70
+ # the routing table: on a cache guess the RUN sends db=nil (server
71
+ # re-resolves), otherwise it pins the discovered name. run_db carries
72
+ # whichever applies; run_extra keeps this operation's bookmark snapshot.
73
+ connection, run_db = acquire_for_operation(session_access_mode)
74
+ run_extra = {
75
+ db: run_db,
76
+ mode: (session_access_mode == :read ? 'r' : nil),
77
+ tx_timeout: timeout_to_milliseconds(timeout),
78
+ tx_metadata:,
79
+ imp_user: @options[:impersonated_user],
80
+ bookmarks: bookmarks
81
+ }
82
+ run_extra.reject!(&Internal::Extras::BLANK)
83
+
84
+ fetch_size = effective_fetch_size
85
+ # Feature:IdempotentRetries — an auto-commit RUN whose failure the server
86
+ # flags `_idempotent` may be retried once: RESET clears the FAILED state
87
+ # (and drains the pipelined PULL's IGNORED), then RUN+PULL are re-sent
88
+ # (TELEMETRY is not). Only the RUN reply is eligible — a telemetry failure
89
+ # or a later stream (PULL) failure is raised as usual.
90
+ retries_left = auto_commit_retries_enabled? ? 1 : 0
91
+
92
+ buffer = handler = run_response = nil
93
+ telemetry_sent = false # TELEMETRY 2 = auto-commit; sent once, never resent on a retry
94
+ telemetry_pending = false # its (opted-in) SUCCESS is still owed
95
+ loop do
96
+ # A fresh stream per attempt — a prior attempt's buffer saw the IGNORED.
97
+ buffer = Bolt::RecordBuffer.new(fetch_size: fetch_size)
98
+ handler = Bolt::StreamHandler.new(buffer)
99
+ retry_run = false
100
+ # `stage` tells the rescue which reply raised: only a :run failure is
101
+ # idempotent-retryable (not :telemetry, nor a send/flush transport error).
102
+ stage = nil
103
+ run_response =
104
+ begin
105
+ # TELEMETRY is pipelined ahead of the first RUN when the server opted
106
+ # in; kept inside this block so a send failure still releases the lease.
107
+ unless telemetry_sent
108
+ telemetry_sent = true
109
+ telemetry_pending = connection.telemetry(2, disabled: @options[:telemetry_disabled])
110
+ end
111
+ # Auto-commit RUN carries this session's NotificationsConfig (5.2+);
112
+ # the tx path puts it on BEGIN instead. nil / non-5.2 => absent.
113
+ connection.send_message(connection.protocol.build_run(query, parameters, run_extra,
114
+ notification_config: @options[:notification_config]))
115
+ connection.send_message(connection.protocol.build_pull(n: fetch_size), handler)
116
+ connection.flush
117
+ if telemetry_pending
118
+ telemetry_pending = false
119
+ stage = :telemetry
120
+ connection.fetch_response.assert_success!
121
+ end
122
+ stage = :run
123
+ connection.fetch_response.assert_success!
124
+ rescue Exceptions::Neo4jException => e
125
+ # Classify first: this notifies the auth-token manager (and, for a
126
+ # security failure, sets auth_failed so the guards below hold) and,
127
+ # for routed connections, fires on_write_failure / deactivate and
128
+ # swaps NotALeader for SessionExpired. assert_success! raises
129
+ # *outside* RoutedConnection's wrapper, so this is the only place the
130
+ # classifier sees FAILURE responses to RUN.
131
+ classified = connection.classify_failure(e)
132
+ if stage == :run && retries_left.positive? && idempotent_error?(e) && !connection.auth_failed
133
+ retries_left -= 1
134
+ # RESET clears FAILED and drains the abandoned RUN/PULL replies.
135
+ connection.reset!
136
+ retry_run = true
137
+ nil
138
+ else
139
+ # Server is in FAILED state; RESET so the connection is immediately
140
+ # reusable — but not if it's being discarded (auth failure: the
141
+ # server closes it, RESET would just error).
142
+ connection.reset! unless connection.auth_failed
143
+ @connection_provider.release(connection)
144
+ raise classified
145
+ end
146
+ rescue StandardError
147
+ # Transport-level failure (IO/socket) — the connection is likely
148
+ # dead. Return it to the pool either way so this lease doesn't leak;
149
+ # the next user will rediscover the breakage.
150
+ @connection_provider.release(connection)
151
+ raise
152
+ end
153
+ break unless retry_run
154
+ end
155
+
156
+ # A home-db RUN that sent db=nil comes back with the server's resolved
157
+ # home database — cache it so the next same-identity session can guess.
158
+ cache_home_db_from(run_response)
159
+
160
+ keys = (run_response.metadata[:fields] || run_response.metadata['fields'] || []).map(&:to_sym)
161
+ @current_result = Result.new(
162
+ connection, keys, buffer: buffer, handler: handler,
163
+ query_text: query, parameters: parameters, run_metadata: run_response.metadata,
164
+ fetch_size: fetch_size,
165
+ on_summary: method(:harvest_auto_commit_bookmark),
166
+ on_release: -> { @connection_provider.release(connection) }
167
+ )
168
+ end
169
+
170
+ def begin_transaction(timeout: nil, metadata: nil, &block)
171
+ raise Exceptions::ClientException, 'Session is closed' unless @open
172
+ if @transaction&.open?
173
+ raise Exceptions::ClientException,
174
+ "You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session."
175
+ end
176
+
177
+ drain_current_result
178
+ @current_result = nil
179
+
180
+ tx_options = @options.merge(
181
+ # The resolved home database is added in open_transaction, where the
182
+ # BEGIN bookmark snapshot is computed (so resolution and BEGIN share
183
+ # one snapshot).
184
+ timeout: timeout_to_milliseconds(timeout),
185
+ metadata:,
186
+ # BEGIN carries `mode: "r"` for read sessions (write is the
187
+ # server default and omits it), matching the auto-commit path.
188
+ # The Transaction reads tx_options[:access_mode]; without this an
189
+ # explicit read transaction dropped the field (session stores the
190
+ # mode under :default_access_mode, not :access_mode).
191
+ access_mode: (session_access_mode == :read ? 'r' : nil)
192
+ ).compact
193
+
194
+ # TELEMETRY 1 = unmanaged (explicit) transaction.
195
+ @transaction = open_transaction(session_access_mode, tx_options, telemetry_api: 1)
196
+
197
+ if block_given?
198
+ begin
199
+ result = yield @transaction
200
+ # Explicit-block transactions default to rollback; user must call
201
+ # tx.commit to persist changes (matches Java driver semantics).
202
+ @transaction.rollback if @transaction.open?
203
+ result
204
+ rescue => e
205
+ @transaction.rollback if @transaction.open?
206
+ raise e
207
+ ensure
208
+ @transaction = nil
209
+ end
210
+ else
211
+ @transaction
212
+ end
213
+ end
214
+
215
+ def execute_read(timeout: nil, metadata: nil, &block)
216
+ execute_transaction(AccessMode::READ, timeout:, metadata:, &block)
217
+ end
218
+
219
+ def execute_write(timeout: nil, metadata: nil, &block)
220
+ execute_transaction(AccessMode::WRITE, timeout:, metadata:, &block)
221
+ end
222
+
223
+ # Close any pending result by asking the server to abandon
224
+ # remaining records (DISCARD) rather than pulling them just to
225
+ # throw them away client-side. Matches Java's behaviour and is
226
+ # required by testkit's session_run.test_discard_on_session_close_*
227
+ # scripts which lie about has_more=true to verify the driver
228
+ # eventually sends DISCARD. With pagination, draining via buffer
229
+ # would loop forever on those scripts.
230
+ def close
231
+ return unless @open
232
+
233
+ pending_error = nil
234
+ begin
235
+ @transaction&.close # tx releases its own connection
236
+ if @current_result
237
+ connection = @current_result.connection
238
+ begin
239
+ @current_result.consume
240
+ rescue Exceptions::Neo4jException => e
241
+ pending_error = e
242
+ end
243
+ # Any failure during the consume leaves the connection in
244
+ # the server's FAILED state; RESET so the next borrower starts
245
+ # from a clean READY state.
246
+ connection.reset! if @current_result.failed?
247
+ @current_result.discard! # idempotent; releases the connection
248
+ end
249
+ ensure
250
+ @open = false
251
+ end
252
+
253
+ raise pending_error if pending_error
254
+ end
255
+
256
+ def open?
257
+ @open
258
+ end
259
+
260
+ def last_bookmarks
261
+ @last_bookmarks
262
+ end
263
+
264
+ def update_bookmarks(bookmarks)
265
+ # Replace bookmarks (don't accumulate) — matches Java driver behavior.
266
+ # Each committed transaction generates a new bookmark that replaces the previous one.
267
+ new_set = Set.new(Array(bookmarks).map(&Bookmark.method(:from)))
268
+ # Forward to the BookmarkManager (if configured) so cross-session
269
+ # causal consistency works. Pass the bookmarks we *sent* in BEGIN
270
+ # as `previous` — Java's manager uses set-difference, so passing
271
+ # the session's own bookmarks alongside the manager's is safe.
272
+ @bookmark_manager&.update_bookmarks(@bookmarks_used_on_begin || @last_bookmarks, new_set)
273
+ @last_bookmarks = new_set
274
+ end
275
+
276
+ private
277
+
278
+ # The database name to put on the wire for this session's operations.
279
+ # An explicit `:database` is used as-is. For a home-db session (nil) on
280
+ # a routing driver, resolve the name from discovery once and reuse it
281
+ # (Bolt 4.4+/5.x return it in the ROUTE reply); the direct provider and
282
+ # the procedure-based protocols return nil, leaving the server to
283
+ # resolve the home db from an absent `db`.
284
+ # `bookmarks` is the snapshot the caller is already using for this
285
+ # operation's wire `bookmarks` — passed in (not recomputed) so a single
286
+ # operation never takes two different BookmarkManager snapshots.
287
+ # Acquire the operation's connection and decide the db it should send
288
+ # (Optimization:HomeDatabaseCache). Returns [connection, run_db]:
289
+ # - explicit :database -> acquire against it, send it.
290
+ # - home-db + cache guess (SSR pool) -> acquire against the guessed home
291
+ # db (reuses its routing table, no discovery) but send db=nil so the
292
+ # server re-resolves; unless the acquired connection turns out non-SSR
293
+ # (a mixed cluster can't re-route), in which case fall back to explicit
294
+ # discovery and pin the resolved name.
295
+ # - home-db, no guess -> discover, pin, send the resolved name.
296
+ def acquire_for_operation(access_mode)
297
+ # Its own bookmark snapshot for discovery/acquire — distinct from the
298
+ # RUN/BEGIN extra's snapshot, so a BookmarkManager supplier is consulted
299
+ # once per acquire (matching the reference drivers / the bookmark-manager
300
+ # tests), and home-db discovery routes with a fresh set.
301
+ bookmarks = current_bookmarks_for_extra
302
+ explicit = @options[:database]
303
+ return [do_acquire(access_mode, explicit, bookmarks), explicit] if explicit
304
+ # Once this session has resolved its home db, pin it for the rest of the
305
+ # session — later runs send it explicitly so a re-resolving server can't
306
+ # move the session to a different database mid-flight.
307
+ return [do_acquire(access_mode, @pinned_database, bookmarks), @pinned_database] if @pinned_database
308
+
309
+ imp_user = @options[:impersonated_user]
310
+ auth = @options[:auth_token]
311
+ # Guess only pays off when we already hold a fresh routing table for it:
312
+ # acquire against that table and send db=nil so the server resolves the
313
+ # real home db. If its table is stale (or the connection turns out
314
+ # non-SSR), fall through to explicit discovery — a ROUTE with db=nil that
315
+ # authoritatively resolves and pins the home db. The optimistic acquire
316
+ # and the fallback share one acquisition-timeout deadline.
317
+ guess = @connection_provider.home_db_guess(imp_user, auth)
318
+ if guess && @connection_provider.routing_table_fresh?(guess, access_mode)
319
+ deadline = acquisition_deadline
320
+ connection = do_acquire(access_mode, guess, bookmarks, deadline)
321
+ if connection.ssr_enabled?
322
+ # A real guess: the reply's db will pin the session (cache_home_db_from).
323
+ @used_guess = true
324
+ return [connection, nil]
325
+ end
326
+
327
+ @connection_provider.release(connection)
328
+ resolved = @connection_provider.home_database(bookmarks, imp_user, auth)
329
+ @pinned_database = resolved
330
+ return [do_acquire(access_mode, resolved, bookmarks, deadline), resolved]
331
+ end
332
+ resolved = @connection_provider.home_database(bookmarks, imp_user, auth)
333
+ @pinned_database = resolved
334
+ [do_acquire(access_mode, resolved, bookmarks), resolved]
335
+ end
336
+
337
+ def do_acquire(access_mode, database, bookmarks, deadline = nil)
338
+ @connection_provider.acquire(access_mode: access_mode, database: database, bookmarks: bookmarks,
339
+ imp_user: @options[:impersonated_user], auth: @options[:auth_token],
340
+ deadline: deadline)
341
+ end
342
+
343
+ # A single acquisition-timeout deadline (monotonic seconds) so a guessed
344
+ # acquire and its fallback share one budget, matching the reference drivers.
345
+ def acquisition_deadline
346
+ @clock.monotonic + (@options[:connection_acquisition_timeout]&.to_f || 60)
347
+ end
348
+
349
+ # Record the server's resolved home database from a RUN/BEGIN reply, so a
350
+ # later same-identity session guesses it. No-op for explicit-db sessions
351
+ # (their reply omits db) and the direct provider (cache_home_db is a no-op).
352
+ def cache_home_db_from(response)
353
+ db = response.metadata[:db]
354
+ return if db.nil? || @options[:database]
355
+
356
+ # First guess-based run learns the real home db from the reply — pin it
357
+ # for the session (only when we actually guessed; direct drivers never
358
+ # pin) and remember it for the driver-wide cache.
359
+ @pinned_database ||= db if @used_guess
360
+ @connection_provider.cache_home_db(@options[:impersonated_user], @options[:auth_token], db)
361
+ end
362
+
363
+ # Current bookmark snapshot as a plain array of strings, ready
364
+ # for the wire. Returns nil when empty so the BEGIN/RUN extras
365
+ # hash's `reject!` strips the key entirely (testkit's stub
366
+ # scripts strictly compare against omission).
367
+ #
368
+ # Folds in the BookmarkManager's bookmarks too (when configured)
369
+ # so cross-session causal consistency works. Records the merged
370
+ # set on @bookmarks_used_on_begin so update_bookmarks can pass
371
+ # it as `previous` to the manager. A custom manager can return
372
+ # plain strings rather than Bookmark instances, so coerce
373
+ # everything via Bookmark.from before reading .value.
374
+ def current_bookmarks_for_extra
375
+ from_manager = Array(@bookmark_manager&.bookmarks).map(&Bookmark.method(:from))
376
+ merged = @last_bookmarks | from_manager
377
+ @bookmarks_used_on_begin = merged
378
+ bookmarks = merged.to_a.map(&:value)
379
+ bookmarks unless bookmarks.empty?
380
+ end
381
+
382
+ # Records per PULL batch. Matches Java/Python defaults (1000). The
383
+ # user can override per-driver via the `fetch_size` option and the
384
+ # special value `-1` means "pull all records in one batch".
385
+ def effective_fetch_size
386
+ size = @options[:fetch_size]
387
+ size.nil? ? 1000 : size
388
+ end
389
+
390
+ # Feature:IdempotentRetries. A session-level AutoCommitRetriesMode wins;
391
+ # otherwise fall back to the driver-level `auto_commit_retries_disabled`
392
+ # default (merged into the session options), which defaults to enabled.
393
+ def auto_commit_retries_enabled?
394
+ case @options[:auto_commit_retries_mode]
395
+ when AutoCommitRetriesMode::ENABLED then true
396
+ when AutoCommitRetriesMode::DISABLED then false
397
+ else !@options[:auto_commit_retries_disabled]
398
+ end
399
+ end
400
+
401
+ # A server FAILURE the server marks retryable via `_idempotent` in its
402
+ # diagnostic record.
403
+ def idempotent_error?(error)
404
+ error.diagnostic_record&.[](:_idempotent) == true
405
+ end
406
+
407
+ # Default access mode for the session — drives connection routing for
408
+ # auto-commit `run` calls. execute_read / execute_write override per
409
+ # operation when going through `execute_transaction`.
410
+ def session_access_mode
411
+ case @options[:default_access_mode]
412
+ when AccessMode::READ then :read
413
+ else :write
414
+ end
415
+ end
416
+
417
+ # Auto-commit hook called from Result when its stream ends in SUCCESS.
418
+ # Explicit-tx Results don't trigger this — Transaction#commit harvests
419
+ # the bookmark from the COMMIT response itself.
420
+ def harvest_auto_commit_bookmark(summary)
421
+ # `metadata` is private on ResultSummary (deliberately not part of
422
+ # the public Java-shaped API). Bookmark only ever lives on the wire
423
+ # SUCCESS metadata; no public Summary accessor for it because Java's
424
+ # ResultSummary doesn't expose one either. send() bypasses the
425
+ # private check for this internal-only consumer.
426
+ bookmark = summary.send(:metadata)[:bookmark]
427
+ update_bookmarks(bookmark) if bookmark
428
+ end
429
+
430
+ # Pull any pending auto-commit result's records into memory so they
431
+ # remain accessible from the user's reference after the connection is
432
+ # released. The Result self-releases its connection on the SUCCESS
433
+ # this drives. If draining surfaces a FAILURE we RESET the connection
434
+ # before releasing so it goes back to the pool clean.
435
+ def drain_current_result
436
+ return unless @current_result
437
+
438
+ # Detach first so any failure path doesn't leave @current_result
439
+ # pointing at a connection we've already returned to the pool —
440
+ # a later session.close would otherwise reset!/discard! a slot
441
+ # that may already belong to another session.
442
+ result = @current_result
443
+ @current_result = nil
444
+ connection = result.connection
445
+
446
+ begin
447
+ result.buffer
448
+ rescue Exceptions::Neo4jException
449
+ connection.reset!
450
+ result.discard! # idempotent; routes through Result#release_connection
451
+ raise
452
+ end
453
+
454
+ if result.failed?
455
+ connection.reset!
456
+ result.discard!
457
+ end
458
+ # Successful drain: Result already released its connection from on_success.
459
+ end
460
+
461
+
462
+ def execute_transaction(access_mode, timeout: nil, metadata: nil, &block)
463
+ raise Exceptions::ClientException, 'Session is closed' unless @open
464
+
465
+ # Drain any pending auto-commit result so its connection is released
466
+ # and its bookmark is harvested before the managed tx acquires its
467
+ # own connection (likely a different server in routing mode).
468
+ drain_current_result
469
+
470
+ # Default matches Java's Config.DEFAULT_MAX_TRANSACTION_RETRY_TIME
471
+ # (30s). The window matters for cluster failover: a leader election
472
+ # can take several seconds, during which the router keeps returning
473
+ # the old/no leader, so the managed-tx retry must keep rediscovering
474
+ # (with exponential backoff) well past a second or two.
475
+ max_retry_time = @options[:max_transaction_retry_time] || 30
476
+ start_time = @clock.realtime
477
+ errors = []
478
+ # TELEMETRY 0 (managed tx) is reported until the server acknowledges it:
479
+ # re-sent on each attempt whose telemetry never landed (e.g. the
480
+ # connection died before its SUCCESS), but not once acked — matching the
481
+ # reference drivers and the retry stub script. The callback flips this
482
+ # the moment a Transaction reads the telemetry SUCCESS.
483
+ telemetry_acked = false
484
+ on_telemetry_ack = -> { telemetry_acked = true }
485
+
486
+ op_mode = (access_mode == AccessMode::READ ? :read : :write)
487
+ tx_options = @options.merge(
488
+ # BEGIN carries `mode: "r"` for read; write is the server default
489
+ # and omits the field (reference drivers and the stub scripts
490
+ # expect `BEGIN {"db": ...}`, not `…"mode": "w"`). Matches the
491
+ # auto-commit and explicit-begin paths; `compact` drops the nil.
492
+ access_mode: (access_mode == AccessMode::READ ? 'r' : nil),
493
+ timeout: timeout_to_milliseconds(timeout),
494
+ metadata:
495
+ ).compact
496
+
497
+ loop do
498
+ begin
499
+ # api 0 = managed tx; execute_query's session overrides it to 3
500
+ # (DRIVER_EXECUTE_QUERY). nil once the server has acked the report.
501
+ telemetry_api = telemetry_acked ? nil : (@options[:telemetry_api] || 0)
502
+ return run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &block)
503
+ rescue Exceptions::ServiceUnavailableException, Exceptions::SessionExpiredException,
504
+ Exceptions::TransientException,
505
+ # AuthorizationExpired (server authz-cache expiry) is always
506
+ # retryable; SecurityRetryableException is an auth failure the
507
+ # token manager has flagged retryable (token refreshed) — both
508
+ # warrant another attempt on a fresh connection/identity.
509
+ Exceptions::AuthorizationExpiredException, Exceptions::SecurityRetryableException => e
510
+ errors << e
511
+
512
+ if @clock.realtime - start_time >= max_retry_time
513
+ # Retries exhausted — re-raise the LAST error with its real
514
+ # type (SessionExpired / Transient / ServiceUnavailable)
515
+ # rather than flattening everything to ServiceUnavailable;
516
+ # Java's transaction executor likewise rethrows the last
517
+ # error. testkit asserts on the type (e.g. a routed reader
518
+ # interruption must surface as SessionExpired), so the
519
+ # generic wrapper masked it. Earlier attempts ride along as
520
+ # suppressed.
521
+ e.add_suppressed(*errors[0...-1])
522
+ raise e
523
+ end
524
+
525
+ # Exponential backoff: 1s, 2s, 4s, ... matching Java driver defaults
526
+ sleep(2 ** (errors.size - 1))
527
+ end
528
+ end
529
+ end
530
+
531
+ def run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &block)
532
+ if @transaction&.open?
533
+ raise Exceptions::ClientException,
534
+ "You cannot begin a transaction on a session with an open transaction"
535
+ end
536
+
537
+ # TELEMETRY 0 = managed transaction function; api is nil once acked so a
538
+ # retry doesn't repeat it.
539
+ @transaction = open_transaction(op_mode, tx_options, telemetry_api: telemetry_api,
540
+ telemetry_ack: on_telemetry_ack)
541
+
542
+ begin
543
+ result = yield @transaction
544
+ @transaction.commit if @transaction.open?
545
+ result
546
+ rescue => e
547
+ @transaction.rollback if @transaction.open?
548
+ raise e
549
+ ensure
550
+ @transaction = nil
551
+ end
552
+ end
553
+
554
+ def open_transaction(op_mode, tx_options, telemetry_api:, telemetry_ack: nil)
555
+ # Acquire and decide the BEGIN db together (Optimization:HomeDatabaseCache):
556
+ # a cache guess picks the routing table and sends db=nil; otherwise the
557
+ # discovered/pinned name is sent. It takes its own bookmark snapshot for
558
+ # discovery. begin_db is dropped when nil via compact.
559
+ connection, begin_db = acquire_for_operation(op_mode)
560
+ # The BEGIN's own snapshot, taken last so current_bookmarks_for_extra
561
+ # records @bookmarks_used_on_begin as the set actually sent on BEGIN (the
562
+ # commit-time update_bookmarks reports it as `previous`).
563
+ bookmarks = current_bookmarks_for_extra
564
+ tx_options = tx_options.merge(database: begin_db).compact
565
+ Transaction.new(connection, self, bookmarks, tx_options, telemetry_api: telemetry_api,
566
+ telemetry_ack: telemetry_ack,
567
+ # executeQuery's session reports telemetry api 3 (DRIVER_EXECUTE_QUERY);
568
+ # only that path pipelines BEGIN + RUN + PULL (Optimization:ExecuteQueryPipelining).
569
+ pipelined: @options[:telemetry_api] == 3,
570
+ on_begin: method(:cache_home_db_from),
571
+ on_release: -> { @connection_provider.release(connection) })
572
+ end
573
+ end
574
+ end
575
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # Which database the result came from. Java's
7
+ # org.neo4j.driver.summary.DatabaseInfo.
8
+ class DatabaseInfo
9
+ attr_reader :name
10
+
11
+ def initialize(db_data)
12
+ @name =
13
+ case db_data
14
+ when String then db_data
15
+ when Hash then db_data[:name]
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ @name || 'unknown'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # A GqlStatusObject whose status also maps to a Neo4j notification
7
+ # (the wire status carries a `neo4j_code`). It exposes the extra
8
+ # notification facets — position, classification and severity —
9
+ # which live in the diagnostic record under `_position` /
10
+ # `_classification` / `_severity`.
11
+ #
12
+ # The parsed accessors return nil for a value outside the known enum
13
+ # (the backend renders that as "UNKNOWN"); the raw_* accessors hand
14
+ # back whatever the server sent, untouched.
15
+ class GqlNotification < GqlStatusObject
16
+ def position
17
+ @data.dig(:diagnostic_record, :_position)&.then { Notification::Position.new(it) }
18
+ end
19
+
20
+ def raw_classification
21
+ @data.dig(:diagnostic_record, :_classification)
22
+ end
23
+
24
+ def classification
25
+ raw_classification if Notification::KNOWN_CATEGORIES.include?(raw_classification)
26
+ end
27
+
28
+ def raw_severity
29
+ @data.dig(:diagnostic_record, :_severity)
30
+ end
31
+
32
+ def severity
33
+ raw_severity if Notification::KNOWN_SEVERITIES.include?(raw_severity)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Summary
6
+ # A GQL status object, sourced from a Bolt 5.6+ summary `statuses`
7
+ # entry. Mirrors org.neo4j.driver.summary.GqlStatusObject. A plain
8
+ # status (e.g. "00000 — successful completion") carries only a status
9
+ # code, a description and a diagnostic record; the GqlNotification
10
+ # subtype adds the notification facets (position / classification /
11
+ # severity).
12
+ class GqlStatusObject
13
+ # The server may omit the standard diagnostic-record keys; the
14
+ # driver fills these defaults (matching Java) while leaving every
15
+ # other key — including ones the server set to an explicit null —
16
+ # exactly as received.
17
+ DIAGNOSTIC_RECORD_DEFAULTS = {
18
+ OPERATION: '', OPERATION_CODE: '0', CURRENT_SCHEMA: '/'
19
+ }.freeze
20
+
21
+ attr_reader :gql_status, :status_description
22
+
23
+ def initialize(data)
24
+ @data = data
25
+ @gql_status = data[:gql_status]
26
+ @status_description = data[:status_description]
27
+ end
28
+
29
+ def diagnostic_record
30
+ DIAGNOSTIC_RECORD_DEFAULTS.merge(@data[:diagnostic_record] || {})
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end