neo4j-ruby-driver 6.0.3.alpha.0 → 6.2.1.beta.2

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