neo4j-ruby-driver 1.7.4 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -42
  3. data/lib/loader.rb +5 -3
  4. data/lib/neo4j/driver/auto_closable.rb +2 -2
  5. data/lib/neo4j/driver/exceptions/authentication_exception.rb +6 -1
  6. data/lib/neo4j/driver/exceptions/authorization_expired_exception.rb +14 -0
  7. data/lib/neo4j/driver/{types/bytes.rb → exceptions/certificate_exception.rb} +2 -2
  8. data/lib/neo4j/driver/exceptions/client_exception.rb +3 -0
  9. data/lib/neo4j/driver/exceptions/connection_read_timeout_exception.rb +14 -0
  10. data/lib/neo4j/driver/exceptions/database_exception.rb +3 -0
  11. data/lib/neo4j/driver/exceptions/discovery_exception.rb +16 -0
  12. data/lib/neo4j/driver/exceptions/fatal_discovery_exception.rb +13 -0
  13. data/lib/neo4j/driver/exceptions/protocol_exception.rb +7 -0
  14. data/lib/neo4j/driver/exceptions/result_consumed_exception.rb +13 -0
  15. data/lib/neo4j/driver/exceptions/security_exception.rb +5 -1
  16. data/lib/neo4j/driver/exceptions/service_unavailable_exception.rb +2 -0
  17. data/lib/neo4j/driver/exceptions/session_expired_exception.rb +4 -0
  18. data/lib/neo4j/driver/exceptions/token_expired_exception.rb +15 -0
  19. data/lib/neo4j/driver/exceptions/transaction_nesting_exception.rb +11 -0
  20. data/lib/neo4j/driver/exceptions/transient_exception.rb +3 -0
  21. data/lib/neo4j/driver/exceptions/untrusted_server_exception.rb +1 -0
  22. data/lib/neo4j/driver/exceptions/value/lossy_coercion.rb +15 -0
  23. data/lib/neo4j/driver/exceptions/value/not_multi_valued.rb +13 -0
  24. data/lib/neo4j/driver/exceptions/value/uncoercible.rb +15 -0
  25. data/lib/neo4j/driver/exceptions/value/unsizable.rb +12 -0
  26. data/lib/neo4j/driver/exceptions/value/value_exception.rb +12 -0
  27. data/lib/neo4j/driver/internal/bolt_server_address.rb +97 -0
  28. data/lib/neo4j/driver/internal/duration_normalizer.rb +1 -1
  29. data/lib/neo4j/driver/internal/validator.rb +5 -4
  30. data/{ffi/neo4j/driver/summary/statement_type.rb → lib/neo4j/driver/summary/query_type.rb} +1 -3
  31. data/lib/neo4j/driver/synchronizable.rb +23 -0
  32. data/lib/neo4j/driver/types/time.rb +4 -2
  33. data/lib/neo4j_ruby_driver.rb +5 -10
  34. data/{ffi → ruby}/neo4j/driver/access_mode.rb +2 -2
  35. data/ruby/neo4j/driver/auth_tokens.rb +34 -0
  36. data/ruby/neo4j/driver/bookmark.rb +21 -0
  37. data/ruby/neo4j/driver/config.rb +89 -0
  38. data/ruby/neo4j/driver/graph_database.rb +80 -0
  39. data/ruby/neo4j/driver/internal/async/connection/bolt_protocol_util.rb +51 -0
  40. data/ruby/neo4j/driver/internal/async/connection/bootstrap_factory.rb +22 -0
  41. data/ruby/neo4j/driver/internal/async/connection/channel_attributes.rb +31 -0
  42. data/ruby/neo4j/driver/internal/async/connection/channel_connected_listener.rb +32 -0
  43. data/ruby/neo4j/driver/internal/async/connection/channel_connector_impl.rb +83 -0
  44. data/ruby/neo4j/driver/internal/async/connection/channel_pipeline_builder_impl.rb +22 -0
  45. data/ruby/neo4j/driver/internal/async/connection/direct_connection.rb +30 -0
  46. data/ruby/neo4j/driver/internal/async/connection/event_loop_group_factory.rb +83 -0
  47. data/ruby/neo4j/driver/internal/async/connection/handshake_completed_listener.rb +27 -0
  48. data/ruby/neo4j/driver/internal/async/connection/handshake_handler.rb +113 -0
  49. data/ruby/neo4j/driver/internal/async/connection/netty_channel_initializer.rb +57 -0
  50. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver.rb +26 -0
  51. data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver_group.rb +19 -0
  52. data/ruby/neo4j/driver/internal/async/connection/routing_connection.rb +36 -0
  53. data/ruby/neo4j/driver/internal/async/connection/stream.rb +12 -0
  54. data/ruby/neo4j/driver/internal/async/connection/stream_reader.rb +16 -0
  55. data/ruby/neo4j/driver/internal/async/connection_context.rb +10 -0
  56. data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +24 -0
  57. data/ruby/neo4j/driver/internal/async/inbound/byte_buf_input.rb +30 -0
  58. data/ruby/neo4j/driver/internal/async/inbound/channel_error_handler.rb +77 -0
  59. data/ruby/neo4j/driver/internal/async/inbound/chunk_decoder.rb +41 -0
  60. data/ruby/neo4j/driver/internal/async/inbound/connect_timeout_handler.rb +32 -0
  61. data/ruby/neo4j/driver/internal/async/inbound/connection_read_timeout_handler.rb +17 -0
  62. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +171 -0
  63. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_handler.rb +42 -0
  64. data/ruby/neo4j/driver/internal/async/inbound/message_decoder.rb +51 -0
  65. data/ruby/neo4j/driver/internal/async/internal_async_session.rb +98 -0
  66. data/ruby/neo4j/driver/internal/async/internal_async_transaction.rb +13 -0
  67. data/ruby/neo4j/driver/internal/async/leak_logging_network_session.rb +34 -0
  68. data/ruby/neo4j/driver/internal/async/network_connection.rb +194 -0
  69. data/ruby/neo4j/driver/internal/async/network_session.rb +150 -0
  70. data/ruby/neo4j/driver/internal/async/outbound/chunk_aware_byte_buf_output.rb +110 -0
  71. data/ruby/neo4j/driver/internal/async/outbound/outbound_message_handler.rb +39 -0
  72. data/ruby/neo4j/driver/internal/async/pool/channel.rb +62 -0
  73. data/ruby/neo4j/driver/internal/async/pool/channel_pool.rb +31 -0
  74. data/ruby/neo4j/driver/internal/async/pool/channel_tracker.rb +135 -0
  75. data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +156 -0
  76. data/ruby/neo4j/driver/internal/async/pool/netty_channel_health_checker.rb +87 -0
  77. data/ruby/neo4j/driver/internal/async/pool/netty_channel_pool.rb +52 -0
  78. data/ruby/neo4j/driver/internal/async/pool/network_connection_factory.rb +21 -0
  79. data/ruby/neo4j/driver/internal/async/pool/pool_settings.rb +34 -0
  80. data/ruby/neo4j/driver/internal/async/pool/timed_stack.rb +15 -0
  81. data/ruby/neo4j/driver/internal/async/result_cursors_holder.rb +17 -0
  82. data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +212 -0
  83. data/ruby/neo4j/driver/internal/bookmark_holder.rb +9 -0
  84. data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +48 -0
  85. data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +14 -0
  86. data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +122 -0
  87. data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +10 -0
  88. data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +68 -0
  89. data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +125 -0
  90. data/ruby/neo4j/driver/internal/cluster/loadbalancing/round_robin_array_index.rb +13 -0
  91. data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +31 -0
  92. data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +147 -0
  93. data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +43 -0
  94. data/ruby/neo4j/driver/internal/cluster/routing_context.rb +77 -0
  95. data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +60 -0
  96. data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +35 -0
  97. data/ruby/neo4j/driver/internal/cluster/routing_settings.rb +24 -0
  98. data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +95 -0
  99. data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +121 -0
  100. data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +73 -0
  101. data/ruby/neo4j/driver/internal/connection_settings.rb +16 -0
  102. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +55 -0
  103. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_only_factory.rb +24 -0
  104. data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +61 -0
  105. data/ruby/neo4j/driver/internal/cursor/result_cursor_factory_impl.rb +24 -0
  106. data/ruby/neo4j/driver/internal/cursor/rx_result_cursor_impl.rb +110 -0
  107. data/ruby/neo4j/driver/internal/database_name_util.rb +37 -0
  108. data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +9 -0
  109. data/ruby/neo4j/driver/internal/default_domain_name_resolver.rb +11 -0
  110. data/ruby/neo4j/driver/internal/direct_connection_provider.rb +40 -0
  111. data/ruby/neo4j/driver/internal/driver_factory.rb +126 -0
  112. data/ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb +20 -0
  113. data/ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb +30 -0
  114. data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +25 -0
  115. data/ruby/neo4j/driver/internal/handlers/hello_response_handler.rb +65 -0
  116. data/ruby/neo4j/driver/internal/handlers/init_response_handler.rb +34 -0
  117. data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +199 -0
  118. data/ruby/neo4j/driver/internal/handlers/no_op_response_handler.rb +16 -0
  119. data/ruby/neo4j/driver/internal/handlers/ping_response_handler.rb +29 -0
  120. data/ruby/neo4j/driver/internal/handlers/pull_handlers.rb +32 -0
  121. data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +168 -0
  122. data/ruby/neo4j/driver/internal/handlers/pulln/basic_pull_response_handler.rb +298 -0
  123. data/ruby/neo4j/driver/internal/handlers/pulln/fetch_size_util.rb +20 -0
  124. data/ruby/neo4j/driver/internal/handlers/reset_response_handler.rb +34 -0
  125. data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +25 -0
  126. data/ruby/neo4j/driver/internal/handlers/route_message_response_handler.rb +21 -0
  127. data/ruby/neo4j/driver/internal/handlers/routing_response_handler.rb +70 -0
  128. data/ruby/neo4j/driver/internal/handlers/run_response_handler.rb +38 -0
  129. data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +34 -0
  130. data/ruby/neo4j/driver/internal/handlers/transaction_pull_response_completion_listener.rb +20 -0
  131. data/ruby/neo4j/driver/internal/impersonation_util.rb +22 -0
  132. data/ruby/neo4j/driver/internal/internal_bookmark.rb +36 -0
  133. data/ruby/neo4j/driver/internal/internal_database_name.rb +9 -0
  134. data/ruby/neo4j/driver/internal/internal_driver.rb +74 -0
  135. data/ruby/neo4j/driver/internal/internal_entity.rb +20 -0
  136. data/ruby/neo4j/driver/internal/internal_node.rb +21 -0
  137. data/ruby/neo4j/driver/internal/internal_pair.rb +9 -0
  138. data/ruby/neo4j/driver/internal/internal_path.rb +35 -0
  139. data/ruby/neo4j/driver/internal/internal_point2_d.rb +9 -0
  140. data/ruby/neo4j/driver/internal/internal_point3_d.rb +6 -0
  141. data/{ffi → ruby}/neo4j/driver/internal/internal_record.rb +2 -1
  142. data/ruby/neo4j/driver/internal/internal_relationship.rb +26 -0
  143. data/ruby/neo4j/driver/internal/internal_result.rb +49 -0
  144. data/ruby/neo4j/driver/internal/internal_session.rb +81 -0
  145. data/ruby/neo4j/driver/internal/internal_transaction.rb +48 -0
  146. data/ruby/neo4j/driver/internal/logging/channel_activity_logger.rb +29 -0
  147. data/ruby/neo4j/driver/internal/logging/channel_error_logger.rb +17 -0
  148. data/ruby/neo4j/driver/internal/logging/prefixed_logger.rb +19 -0
  149. data/ruby/neo4j/driver/internal/logging/reformatted_logger.rb +17 -0
  150. data/ruby/neo4j/driver/internal/messaging/abstract_message_writer.rb +23 -0
  151. data/ruby/neo4j/driver/internal/messaging/bolt_protocol.rb +30 -0
  152. data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +48 -0
  153. data/ruby/neo4j/driver/internal/messaging/common/common_message_reader.rb +51 -0
  154. data/ruby/neo4j/driver/internal/messaging/common/common_value.rb +31 -0
  155. data/ruby/neo4j/driver/internal/messaging/common/common_value_packer.rb +101 -0
  156. data/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +234 -0
  157. data/ruby/neo4j/driver/internal/messaging/encode/begin_message_encoder.rb +15 -0
  158. data/ruby/neo4j/driver/internal/messaging/encode/commit_message_encoder.rb +14 -0
  159. data/ruby/neo4j/driver/internal/messaging/encode/discard_all_message_encoder.rb +14 -0
  160. data/ruby/neo4j/driver/internal/messaging/encode/discard_message_encoder.rb +15 -0
  161. data/ruby/neo4j/driver/internal/messaging/encode/goodbye_message_encoder.rb +14 -0
  162. data/ruby/neo4j/driver/internal/messaging/encode/hello_message_encoder.rb +15 -0
  163. data/ruby/neo4j/driver/internal/messaging/encode/init_message_encoder.rb +16 -0
  164. data/ruby/neo4j/driver/internal/messaging/encode/pull_all_message_encoder.rb +14 -0
  165. data/ruby/neo4j/driver/internal/messaging/encode/pull_message_encoder.rb +15 -0
  166. data/ruby/neo4j/driver/internal/messaging/encode/reset_message_encoder.rb +14 -0
  167. data/ruby/neo4j/driver/internal/messaging/encode/rollback_message_encoder.rb +14 -0
  168. data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +24 -0
  169. data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +22 -0
  170. data/ruby/neo4j/driver/internal/messaging/encode/run_message_encoder.rb +16 -0
  171. data/ruby/neo4j/driver/internal/messaging/encode/run_with_metadata_message_encoder.rb +17 -0
  172. data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +25 -0
  173. data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +25 -0
  174. data/ruby/neo4j/driver/internal/messaging/request/commit_message.rb +20 -0
  175. data/ruby/neo4j/driver/internal/messaging/request/discard_all_message.rb +20 -0
  176. data/ruby/neo4j/driver/internal/messaging/request/discard_message.rb +23 -0
  177. data/ruby/neo4j/driver/internal/messaging/request/goodbye_message.rb +20 -0
  178. data/ruby/neo4j/driver/internal/messaging/request/hello_message.rb +31 -0
  179. data/ruby/neo4j/driver/internal/messaging/request/init_message.rb +19 -0
  180. data/ruby/neo4j/driver/internal/messaging/request/message_with_metadata.rb +10 -0
  181. data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +26 -0
  182. data/ruby/neo4j/driver/internal/messaging/request/pull_all_message.rb +23 -0
  183. data/ruby/neo4j/driver/internal/messaging/request/pull_message.rb +22 -0
  184. data/ruby/neo4j/driver/internal/messaging/request/reset_message.rb +32 -0
  185. data/ruby/neo4j/driver/internal/messaging/request/rollback_message.rb +20 -0
  186. data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +28 -0
  187. data/ruby/neo4j/driver/internal/messaging/request/run_message.rb +23 -0
  188. data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +49 -0
  189. data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +24 -0
  190. data/ruby/neo4j/driver/internal/messaging/response/failure_message.rb +40 -0
  191. data/ruby/neo4j/driver/internal/messaging/response/ignored_message.rb +29 -0
  192. data/ruby/neo4j/driver/internal/messaging/response/record_message.rb +33 -0
  193. data/ruby/neo4j/driver/internal/messaging/response/success_message.rb +34 -0
  194. data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +82 -0
  195. data/ruby/neo4j/driver/internal/messaging/v3/message_format_v3.rb +17 -0
  196. data/ruby/neo4j/driver/internal/messaging/v3/message_writer_v3.rb +27 -0
  197. data/ruby/neo4j/driver/internal/messaging/v4/bolt_protocol_v4.rb +29 -0
  198. data/ruby/neo4j/driver/internal/messaging/v4/message_format_v4.rb +17 -0
  199. data/ruby/neo4j/driver/internal/messaging/v4/message_writer_v4.rb +17 -0
  200. data/ruby/neo4j/driver/internal/messaging/v41/bolt_protocol_v41.rb +25 -0
  201. data/ruby/neo4j/driver/internal/messaging/v42/bolt_protocol_v42.rb +13 -0
  202. data/ruby/neo4j/driver/internal/messaging/v43/bolt_protocol_v43.rb +19 -0
  203. data/ruby/neo4j/driver/internal/messaging/v43/message_format_v43.rb +18 -0
  204. data/ruby/neo4j/driver/internal/messaging/v43/message_writer_v43.rb +20 -0
  205. data/ruby/neo4j/driver/internal/messaging/v44/bolt_protocol_v44.rb +17 -0
  206. data/ruby/neo4j/driver/internal/messaging/v44/message_format_v44.rb +18 -0
  207. data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +15 -0
  208. data/ruby/neo4j/driver/internal/metrics/connection_pool_metrics_listener.rb +34 -0
  209. data/ruby/neo4j/driver/internal/metrics/internal_abstract_metrics.rb +46 -0
  210. data/ruby/neo4j/driver/internal/metrics/internal_connection_pool_metrics.rb +105 -0
  211. data/ruby/neo4j/driver/internal/metrics/internal_metrics.rb +82 -0
  212. data/ruby/neo4j/driver/internal/metrics/internal_metrics_provider.rb +18 -0
  213. data/ruby/neo4j/driver/internal/metrics/listener_event.rb +17 -0
  214. data/ruby/neo4j/driver/internal/metrics/metrics_provider.rb +24 -0
  215. data/ruby/neo4j/driver/internal/metrics/time_recorder_listener_event.rb +15 -0
  216. data/ruby/neo4j/driver/internal/packstream/byte_array_incompatible_packer.rb +12 -0
  217. data/ruby/neo4j/driver/internal/packstream/pack_input.rb +47 -0
  218. data/ruby/neo4j/driver/internal/packstream/pack_output.rb +39 -0
  219. data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +326 -0
  220. data/ruby/neo4j/driver/internal/packstream/pack_type.rb +17 -0
  221. data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +13 -0
  222. data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +35 -0
  223. data/ruby/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +151 -0
  224. data/ruby/neo4j/driver/internal/revocation_strategy.rb +19 -0
  225. data/ruby/neo4j/driver/internal/scheme.rb +32 -0
  226. data/ruby/neo4j/driver/internal/security/internal_auth_token.rb +15 -0
  227. data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +48 -0
  228. data/ruby/neo4j/driver/internal/security_setting.rb +66 -0
  229. data/ruby/neo4j/driver/internal/session_factory_impl.rb +32 -0
  230. data/ruby/neo4j/driver/internal/spi/connection.rb +19 -0
  231. data/ruby/neo4j/driver/internal/spi/connection_pool.rb +9 -0
  232. data/ruby/neo4j/driver/internal/spi/response_handler.rb +23 -0
  233. data/ruby/neo4j/driver/internal/summary/internal_database_info.rb +7 -0
  234. data/ruby/neo4j/driver/internal/summary/internal_input_position.rb +11 -0
  235. data/ruby/neo4j/driver/internal/summary/internal_notification.rb +16 -0
  236. data/ruby/neo4j/driver/internal/summary/internal_plan.rb +41 -0
  237. data/ruby/neo4j/driver/internal/summary/internal_profiled_plan.rb +32 -0
  238. data/ruby/neo4j/driver/internal/summary/internal_result_summary.rb +33 -0
  239. data/ruby/neo4j/driver/internal/summary/internal_server_info.rb +6 -0
  240. data/ruby/neo4j/driver/internal/summary/internal_summary_counters.rb +18 -0
  241. data/ruby/neo4j/driver/internal/svm/netty_substitutions.rb +196 -0
  242. data/ruby/neo4j/driver/internal/svm/z_lib_substitutions.rb +21 -0
  243. data/ruby/neo4j/driver/internal/util/certificate_tool.rb +65 -0
  244. data/ruby/neo4j/driver/internal/util/clock.rb +29 -0
  245. data/ruby/neo4j/driver/internal/util/error_util.rb +104 -0
  246. data/ruby/neo4j/driver/internal/util/extract.rb +123 -0
  247. data/ruby/neo4j/driver/internal/util/format.rb +39 -0
  248. data/ruby/neo4j/driver/internal/util/futures.rb +99 -0
  249. data/ruby/neo4j/driver/internal/util/iterables.rb +35 -0
  250. data/ruby/neo4j/driver/internal/util/lock_util.rb +23 -0
  251. data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +107 -0
  252. data/ruby/neo4j/driver/internal/util/mutex.rb +9 -0
  253. data/ruby/neo4j/driver/internal/util/preconditions.rb +16 -0
  254. data/ruby/neo4j/driver/internal/util/result_holder.rb +72 -0
  255. data/ruby/neo4j/driver/internal/util/server_version.rb +60 -0
  256. data/ruby/neo4j/driver/logging1.rb +51 -0
  257. data/ruby/neo4j/driver/net/server_address.rb +9 -0
  258. data/ruby/neo4j/driver/query.rb +48 -0
  259. data/ruby/neo4j/driver/records.rb +13 -0
  260. data/ruby/neo4j/driver/transaction_config.rb +50 -0
  261. data/ruby/neo4j/driver/values.rb +26 -0
  262. data/{lib → ruby}/neo4j/driver/version.rb +1 -1
  263. data/ruby/neo4j/driver.rb +29 -0
  264. metadata +264 -101
  265. data/ffi/bolt/address.rb +0 -11
  266. data/ffi/bolt/address_resolver.rb +0 -12
  267. data/ffi/bolt/address_set.rb +0 -9
  268. data/ffi/bolt/auth.rb +0 -10
  269. data/ffi/bolt/auto_releasable.rb +0 -22
  270. data/ffi/bolt/boolean.rb +0 -9
  271. data/ffi/bolt/bytes.rb +0 -10
  272. data/ffi/bolt/config.rb +0 -45
  273. data/ffi/bolt/connection.rb +0 -44
  274. data/ffi/bolt/connector.rb +0 -17
  275. data/ffi/bolt/dictionary.rb +0 -15
  276. data/ffi/bolt/error.rb +0 -74
  277. data/ffi/bolt/float.rb +0 -9
  278. data/ffi/bolt/integer.rb +0 -9
  279. data/ffi/bolt/library.rb +0 -12
  280. data/ffi/bolt/lifecycle.rb +0 -9
  281. data/ffi/bolt/list.rb +0 -10
  282. data/ffi/bolt/log.rb +0 -16
  283. data/ffi/bolt/socket_options.rb +0 -14
  284. data/ffi/bolt/status.rb +0 -25
  285. data/ffi/bolt/string.rb +0 -9
  286. data/ffi/bolt/structure.rb +0 -10
  287. data/ffi/bolt/value.rb +0 -35
  288. data/ffi/neo4j/driver/auth_tokens.rb +0 -18
  289. data/ffi/neo4j/driver/config.rb +0 -40
  290. data/ffi/neo4j/driver/graph_database.rb +0 -52
  291. data/ffi/neo4j/driver/internal/async/access_mode_connection.rb +0 -19
  292. data/ffi/neo4j/driver/internal/async/direct_connection.rb +0 -106
  293. data/ffi/neo4j/driver/internal/bolt_server_address.rb +0 -18
  294. data/ffi/neo4j/driver/internal/bookmarks_holder.rb +0 -30
  295. data/ffi/neo4j/driver/internal/direct_connection_provider.rb +0 -28
  296. data/ffi/neo4j/driver/internal/driver_factory.rb +0 -125
  297. data/ffi/neo4j/driver/internal/error_handling.rb +0 -112
  298. data/ffi/neo4j/driver/internal/explicit_transaction.rb +0 -146
  299. data/ffi/neo4j/driver/internal/handlers/pull_all_response_handler.rb +0 -104
  300. data/ffi/neo4j/driver/internal/handlers/response_handler.rb +0 -49
  301. data/ffi/neo4j/driver/internal/handlers/run_response_handler.rb +0 -32
  302. data/ffi/neo4j/driver/internal/handlers/session_pull_all_response_handler.rb +0 -32
  303. data/ffi/neo4j/driver/internal/handlers/transaction_pull_all_response_handler.rb +0 -23
  304. data/ffi/neo4j/driver/internal/internal_driver.rb +0 -45
  305. data/ffi/neo4j/driver/internal/internal_logger.rb +0 -32
  306. data/ffi/neo4j/driver/internal/internal_resolver.rb +0 -31
  307. data/ffi/neo4j/driver/internal/internal_statement_result.rb +0 -52
  308. data/ffi/neo4j/driver/internal/messaging/bolt_protocol.rb +0 -24
  309. data/ffi/neo4j/driver/internal/messaging/v1/bolt_protocol_v1.rb +0 -59
  310. data/ffi/neo4j/driver/internal/messaging/v2/bolt_protocol_v2.rb +0 -16
  311. data/ffi/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +0 -63
  312. data/ffi/neo4j/driver/internal/network_session.rb +0 -129
  313. data/ffi/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +0 -80
  314. data/ffi/neo4j/driver/internal/session_factory_impl.rb +0 -28
  315. data/ffi/neo4j/driver/internal/summary/internal_result_summary.rb +0 -67
  316. data/ffi/neo4j/driver/internal/summary/internal_server_info.rb +0 -19
  317. data/ffi/neo4j/driver/internal/summary/internal_summary_counters.rb +0 -23
  318. data/ffi/neo4j/driver/internal/util/metadata_extractor.rb +0 -15
  319. data/ffi/neo4j/driver/internal/value/base_time_value.rb +0 -22
  320. data/ffi/neo4j/driver/internal/value/date_value.rb +0 -25
  321. data/ffi/neo4j/driver/internal/value/duration_value.rb +0 -27
  322. data/ffi/neo4j/driver/internal/value/local_date_time_value.rb +0 -24
  323. data/ffi/neo4j/driver/internal/value/local_time_value.rb +0 -19
  324. data/ffi/neo4j/driver/internal/value/node_value.rb +0 -18
  325. data/ffi/neo4j/driver/internal/value/offset_time_value.rb +0 -25
  326. data/ffi/neo4j/driver/internal/value/path_value.rb +0 -41
  327. data/ffi/neo4j/driver/internal/value/point2_d_value.rb +0 -24
  328. data/ffi/neo4j/driver/internal/value/point3_d_value.rb +0 -24
  329. data/ffi/neo4j/driver/internal/value/relationship_value.rb +0 -18
  330. data/ffi/neo4j/driver/internal/value/structure_value.rb +0 -42
  331. data/ffi/neo4j/driver/internal/value/time_with_zone_id_value.rb +0 -25
  332. data/ffi/neo4j/driver/internal/value/time_with_zone_offset_value.rb +0 -28
  333. data/ffi/neo4j/driver/internal/value/unbound_relationship_value.rb +0 -18
  334. data/ffi/neo4j/driver/internal/value/value_adapter.rb +0 -101
  335. data/ffi/neo4j/driver/net/server_address.rb +0 -13
  336. data/ffi/neo4j/driver/statement.rb +0 -15
  337. data/ffi/neo4j/driver/types/entity.rb +0 -21
  338. data/ffi/neo4j/driver/types/node.rb +0 -16
  339. data/ffi/neo4j/driver/types/path.rb +0 -35
  340. data/ffi/neo4j/driver/types/relationship.rb +0 -19
  341. data/ffi/neo4j/driver.rb +0 -61
  342. data/lib/neo4j/driver/internal/ruby_signature.rb +0 -18
@@ -0,0 +1,68 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ module Loadbalancing
5
+
6
+ # Load balancing strategy that finds server with the least amount of active (checked out of the pool) connections from given readers or writers. It finds a
7
+ # start index for iteration in a round-robin fashion. This is done to prevent choosing same first address over and over when all addresses have the same amount
8
+ # of active connections.
9
+ class LeastConnectedLoadBalancingStrategy
10
+
11
+ def initialize(connection_pool, logger)
12
+ @readers_index = RoundRobinArrayIndex.new
13
+ @writers_index = RoundRobinArrayIndex.new
14
+
15
+ @connection_pool = connection_pool
16
+ @log = logger
17
+ end
18
+
19
+ def select_reader(known_readers)
20
+ select(known_readers, @readers_index, 'reader')
21
+ end
22
+
23
+ def select_writer(known_writers)
24
+ select(known_writers, @writers_index, 'writer')
25
+ end
26
+
27
+ private
28
+
29
+ def select(addresses, addresses_index, address_type)
30
+ size = addresses.size
31
+ if size == 0
32
+ @log.debug("Unable to select #{address_type}, no known addresses given")
33
+ return
34
+ end
35
+
36
+ # choose start index for iteration in round-robin fashion
37
+ start_index = addresses_index.next(size)
38
+ index = start_index
39
+
40
+ least_connected_address = nil
41
+ least_active_connections = nil
42
+
43
+ # iterate over the array to find the least connected address
44
+ addresses = addresses.to_a
45
+ loop do
46
+ address = addresses[index]
47
+ active_connections = @connection_pool.in_use_connections(address)
48
+
49
+ if least_active_connections.nil? || active_connections < least_active_connections
50
+ least_connected_address = address
51
+ least_active_connections = active_connections
52
+ end
53
+
54
+ # loop over to the start of the array when end is reached
55
+ index = (index + 1) % size
56
+
57
+ break if index == start_index
58
+ end
59
+
60
+ @log.debug("Selected #{address_type} with address: '#{least_connected_address}' and active connections: #{least_active_connections}")
61
+
62
+ least_connected_address
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,125 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ module Loadbalancing
5
+ class LoadBalancer
6
+ CONNECTION_ACQUISITION_COMPLETION_FAILURE_MESSAGE = 'Connection acquisition failed for all available addresses.'
7
+ CONNECTION_ACQUISITION_COMPLETION_EXCEPTION_MESSAGE = "Failed to obtain connection towards %s server. Known routing table is: %s"
8
+ CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE = "Failed to obtain a connection towards address %s, will try other addresses if available. Complete failure is reported separately from this entry."
9
+ BOLT_SERVER_ADDRESSES_EMPTY_ARRAY = []
10
+
11
+ delegate :close, to: :@connection_pool
12
+
13
+ def initialize(initial_router, settings, connection_pool, event_executor_group, logger, load_balancing_strategy, resolver, &domain_name_resolver)
14
+ clock = Util::Clock::System
15
+ @connection_pool = connection_pool
16
+ @rediscovery = create_rediscovery(event_executor_group, initial_router, resolver, settings, clock, logger, domain_name_resolver)
17
+ @routing_tables = create_routing_tables(connection_pool, @rediscovery, settings, clock, logger)
18
+ @load_balancing_strategy = load_balancing_strategy
19
+ @event_executor_group = event_executor_group
20
+ @log = logger
21
+ end
22
+
23
+ def acquire_connection(context)
24
+ handler = @routing_tables.ensure_routing_table(context)
25
+ connection = acquire(context.mode, handler.routing_table)
26
+ Async::Connection::RoutingConnection.new(connection, context.database_name, context.mode,
27
+ context.impersonated_user, handler)
28
+ end
29
+
30
+ def verify_connectivity
31
+ @routing_tables.ensure_routing_table(Async::ImmutableConnectionContext.simple(supports_multi_db?))
32
+ rescue Exceptions::ServiceUnavailableException
33
+ raise Exceptions::ServiceUnavailableException,
34
+ 'Unable to connect to database management service, ensure the database is running and that there is a working network connection to it.'
35
+ end
36
+
37
+ def routing_table_registry
38
+ @routing_tables
39
+ end
40
+
41
+ def supports_multi_db?
42
+ addresses = @rediscovery.resolve
43
+ base_error = Exceptions::ServiceUnavailableException.new("Failed to perform multi-databases feature detection with the following servers: #{addresses}")
44
+ addresses.each do |address|
45
+ return private_suports_multi_db?(address)
46
+ rescue Exceptions::SecurityException
47
+ raise
48
+ rescue => error
49
+ Util::Futures.combine_errors(base_error, error)
50
+ end
51
+
52
+ raise base_error
53
+ end
54
+
55
+ private
56
+
57
+ def private_suports_multi_db?(address)
58
+ conn = @connection_pool.acquire(address)
59
+ Messaging::Request::MultiDatabaseUtil.supports_multi_database?(conn)
60
+ ensure
61
+ conn&.release
62
+ end
63
+
64
+ def acquire(mode, routing_table, attempt_errors = [])
65
+ addresses = addresses_by_mode(mode, routing_table)
66
+ address = select_address(mode, addresses)
67
+
68
+ unless address
69
+ error = Exceptions::SessionExpiredException.new(CONNECTION_ACQUISITION_COMPLETION_EXCEPTION_MESSAGE % [mode, routing_table])
70
+ attempt_errors.each(&error.method(:add_suppressed))
71
+ @log.error(CONNECTION_ACQUISITION_COMPLETION_FAILURE_MESSAGE)
72
+ @log.error(error)
73
+ raise error
74
+ end
75
+
76
+ begin
77
+ @connection_pool.acquire(address)
78
+ rescue Exceptions::ServiceUnavailableException => error
79
+ @log.warn { CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE % address }
80
+ @log.debug(error)
81
+ attempt_errors << error
82
+ routing_table.forget(address)
83
+ acquire(mode, routing_table, attempt_errors)
84
+ end
85
+ end
86
+
87
+ def addresses_by_mode(mode, routing_table)
88
+ case mode
89
+ when AccessMode::READ
90
+ routing_table.readers
91
+ when AccessMode::WRITE
92
+ routing_table.writers
93
+ else
94
+ raise unknown_mode mode
95
+ end
96
+ end
97
+
98
+ def select_address(mode, addresses)
99
+ case mode
100
+ when AccessMode::READ
101
+ @load_balancing_strategy.select_reader(addresses)
102
+ when AccessMode::WRITE
103
+ @load_balancing_strategy.select_writer(addresses)
104
+ else
105
+ raise unknown_mode mode
106
+ end
107
+ end
108
+
109
+ def create_routing_tables(connection_pool, rediscovery, settings, clock, logger)
110
+ RoutingTableRegistryImpl.new(connection_pool, rediscovery, clock, logger, settings.routing_table_purge_delay)
111
+ end
112
+
113
+ def create_rediscovery(event_executor_group, initial_router, resolver, settings, clock, logger, domain_name_resolver)
114
+ cluster_composition_provider = RoutingProcedureClusterCompositionProvider.new(clock, settings.routing_context)
115
+ RediscoveryImpl.new(initial_router, settings, cluster_composition_provider, event_executor_group, resolver, logger, domain_name_resolver)
116
+ end
117
+
118
+ def unknown_mode(mode)
119
+ ArgumentError.new("Mode '#{mode}' is not supported")
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,13 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ module Loadbalancing
5
+ class RoundRobinArrayIndex < Concurrent::AtomicFixnum
6
+ def next(array_length)
7
+ (increment - 1) % array_length if array_length.positive?
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+
5
+ # This implementation of the {@link RoutingProcedureRunner} works with multi database versions of Neo4j calling
6
+ # the procedure `dbms.routing.getRoutingTable`
7
+ class MultiDatabasesRoutingProcedureRunner < SingleDatabaseRoutingProcedureRunner
8
+ DATABASE_NAME = :database
9
+ MULTI_DB_GET_ROUTING_TABLE = "CALL dbms.routing.getRoutingTable($%s, $%s)" % [SingleDatabaseRoutingProcedureRunner::ROUTING_CONTEXT, DATABASE_NAME]
10
+
11
+ private
12
+
13
+ def bookmark_holder(bookmark)
14
+ ReadOnlyBookmarkHolder.new(bookmark)
15
+ end
16
+
17
+ def procedure_query(server_version, database_name)
18
+ map = {
19
+ SingleDatabaseRoutingProcedureRunner::ROUTING_CONTEXT => @context.to_h,
20
+ DATABASE_NAME => database_name.database_name
21
+ }
22
+ Query.new(MULTI_DB_GET_ROUTING_TABLE, **map)
23
+ end
24
+
25
+ def connection(connection)
26
+ Async::Connection::DirectConnection.new(connection, DatabaseNameUtil::SYSTEM_DATABASE, AccessMode::READ, nil)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,147 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ class RediscoveryImpl
5
+ NO_ROUTERS_AVAILABLE = "Could not perform discovery for database '%s'. No routing server available."
6
+ RECOVERABLE_ROUTING_ERROR = "Failed to update routing table with server '%s'."
7
+ RECOVERABLE_DISCOVERY_ERROR_WITH_SERVER = "Received a recoverable discovery error with server '%s', will continue discovery with other routing servers if available. Complete failure is reported separately from this entry."
8
+ INVALID_BOOKMARK_CODE = 'Neo.ClientError.Transaction.InvalidBookmark'
9
+ INVALID_BOOKMARK_MIXTURE_CODE = 'Neo.ClientError.Transaction.InvalidBookmarkMixture'
10
+
11
+ def initialize(initial_router, settings, provider, event_executor_group, resolver, logger, domain_name_resolver)
12
+ @initial_router = initial_router
13
+ @settings = settings
14
+ @log = logger
15
+ @provider = provider
16
+ @resolver = resolver
17
+ @event_executor_group = event_executor_group
18
+ @domain_name_resolver = Internal::Validator.require_non_nil!(domain_name_resolver)
19
+ end
20
+
21
+ # Given a database and its current routing table, and the global connection pool, use the global cluster composition provider to fetch a new cluster
22
+ # composition, which would be used to update the routing table of the given database and global connection pool.
23
+
24
+ # @param routingTable current routing table of the given database.
25
+ # @param connectionPool connection pool.
26
+ # @return new cluster composition and an optional set of resolved initial router addresses.
27
+ def lookup_cluster_composition(
28
+ routing_table, connection_pool, bookmark, impersonated_user,
29
+ failures = 0,
30
+ previous_delay = 0,
31
+ base_error = Exceptions::ServiceUnavailableException.new(NO_ROUTERS_AVAILABLE % routing_table.database.description))
32
+
33
+ lookup(routing_table, connection_pool, bookmark, impersonated_user, base_error) ||
34
+ if failures > @settings.max_routing_failures
35
+ # now we throw our saved error out
36
+ raise base_error
37
+ else
38
+ next_delay = [@settings.retry_timeout_delay, previous_delay * 2].max
39
+ @log.info("Unable to fetch new routing table, will try again in #{next_delay} ms")
40
+ sleep next_delay
41
+ lookup_cluster_composition(routing_table, connection_pool, bookmark, impersonated_user, failures + 1, next_delay, base_error)
42
+ end
43
+ end
44
+
45
+ def resolve
46
+ exception = nil
47
+
48
+ resolved_addresses = @resolver.call(@initial_router).flat_map do |server_address|
49
+ resolve_all_by_domain_name(server_address).unicast_stream
50
+ # rescue java.net.UnknownHostException => e
51
+ rescue SocketError => e
52
+ exception ||= e
53
+ []
54
+ end
55
+
56
+ # give up only if there are no addresses to work with at all
57
+ raise exception if resolved_addresses.empty? && exception
58
+
59
+ resolved_addresses
60
+ end
61
+
62
+ private
63
+
64
+ def lookup(routing_table, connection_pool, bookmark, impersonated_user, base_error)
65
+ if routing_table.prefer_initial_router
66
+ lookup_on_initial_router_then_on_known_routers(routing_table, connection_pool, bookmark, impersonated_user, base_error)
67
+ else
68
+ lookup_on_known_routers_then_on_initial_router(routing_table, connection_pool, bookmark, impersonated_user, base_error)
69
+ end
70
+ end
71
+
72
+ def lookup_on_known_routers_then_on_initial_router(routing_table, connection_pool, bookmark, impersonated_user, base_error)
73
+ seen_servers = Set.new
74
+ lookup_on_known_routers(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error) ||
75
+ lookup_on_initial_router(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
76
+ end
77
+
78
+ def lookup_on_initial_router_then_on_known_routers(routing_table, connection_pool, bookmark, impersonated_user, base_error)
79
+ lookup_on_initial_router(routing_table, connection_pool, Set.new, bookmark, impersonated_user, base_error) ||
80
+ lookup_on_known_routers(routing_table, connection_pool, Set.new, bookmark, impersonated_user, base_error)
81
+ end
82
+
83
+ def lookup_on_known_routers(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
84
+ routing_table.routers.lazy.map do |address|
85
+ lookup_on_router(address, true, routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
86
+ end.first&.then(&ClusterCompositionLookupResult.method(:new))
87
+ end
88
+
89
+ def lookup_on_initial_router(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
90
+ resolved_routers = resolve
91
+ (resolved_routers - seen_servers.to_a).lazy.filter_map do |address|
92
+ lookup_on_router(address, false, routing_table, connection_pool, nil, bookmark,
93
+ impersonated_user, base_error)
94
+ end.first&.then { |composition| ClusterCompositionLookupResult.new(composition, Set.new(resolved_routers)) }
95
+ end
96
+
97
+ def lookup_on_router(router_address, resolve_address, routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
98
+ address = resolve_address ? resolve_by_domain_name_or_throw_completion_exception(router_address, routing_table) : router_address
99
+ seen_servers&.send(:<<, address)
100
+ connection = connection_pool.acquire(address)
101
+ ImpersonationUtil.ensure_impersonation_support(connection, impersonated_user)
102
+ @provider.get_cluster_composition(connection, routing_table.database, bookmark, impersonated_user)
103
+ rescue => error
104
+ handle_routing_procedure_error(error, routing_table, router_address, base_error)
105
+ end
106
+
107
+ def handle_routing_procedure_error(error, routing_table, router_address, base_error)
108
+ raise error if must_abort_discovery(error)
109
+
110
+ # Retriable error happened during discovery.
111
+ discovery_error = Exceptions::DiscoveryException.new(nil, RECOVERABLE_ROUTING_ERROR % router_address, error)
112
+ Util::Futures.combine_errors(base_error, discovery_error) # we record each failure here
113
+ warning_message = RECOVERABLE_DISCOVERY_ERROR_WITH_SERVER % router_address
114
+ @log.warn(warning_message)
115
+ @log.debug(discovery_error)
116
+ routing_table.forget(router_address)
117
+ nil
118
+ end
119
+
120
+ def must_abort_discovery(error)
121
+ !error.is_a?(Exceptions::AuthorizationExpiredException) && error.is_a?(Exceptions::SecurityException) ||
122
+ error.is_a?(Exceptions::FatalDiscoveryException) ||
123
+ error.is_a?(Exceptions::IllegalStateException) &&
124
+ Spi::ConnectionPool::CONNECTION_POOL_CLOSED_ERROR_MESSAGE == error.message ||
125
+ error.is_a?(Exceptions::ClientException) &&
126
+ [INVALID_BOOKMARK_CODE, INVALID_BOOKMARK_MIXTURE_CODE].include?(error.code) ||
127
+ # Not sure why this is not im java
128
+ !error.is_a?(Exceptions::Neo4jException) ||
129
+ Util::ErrorUtil.fatal?(error)
130
+ end
131
+
132
+ def resolve_by_domain_name_or_throw_completion_exception(address, routing_table)
133
+ resolved_address = resolve_all_by_domain_name(address)
134
+ routing_table.replace_router_if_present(address, resolved_address)
135
+
136
+ resolved_address.unicast_stream.first or
137
+ raise Exceptions::IllegalStateException,
138
+ 'Unexpected condition, the ResolvedBoltServerAddress must always have at least one unicast address'
139
+ end
140
+
141
+ def resolve_all_by_domain_name(address)
142
+ ResolvedBoltServerAddress.new(address.host, address.port, *@domain_name_resolver.call(address.host))
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,43 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ # This implementation of the {@link RoutingProcedureRunner} access the routing procedure
5
+ # through the bolt's ROUTE message.
6
+ class RouteMessageRoutingProcedureRunner
7
+ attr_writer :routing_table
8
+
9
+ def initialize(routing_context)
10
+ @routing_context = routing_context.to_h
11
+ end
12
+
13
+ def run(connection, database_name, bookmark, impersonated_user)
14
+ direct_connection = to_direct_connection(connection, database_name, impersonated_user)
15
+ direct_connection.write_and_flush(
16
+ Messaging::Request::RouteMessage.new(@routing_context, bookmark, database_name.database_name,
17
+ impersonated_user),
18
+ Handlers::RouteMessageResponseHandler.new(self))
19
+ RoutingProcedureResponse.new(query(database_name), records: [to_record(@routing_table)])
20
+ rescue => e
21
+ RoutingProcedureResponse.new(query(database_name), error: e)
22
+ ensure
23
+ direct_connection.release
24
+ end
25
+
26
+ private
27
+
28
+ def to_record(routing_table)
29
+ InternalRecord.new(routing_table.keys, routing_table.values)
30
+ end
31
+
32
+ def to_direct_connection(connection, database_name, impersonated_user)
33
+ Async::Connection::DirectConnection.new(connection, database_name, AccessMode::READ, impersonated_user)
34
+ end
35
+
36
+ def query(database_name)
37
+ Query.new('ROUTE $routing_context $database_name', routing_context: @routing_context,
38
+ database_name: database_name.database_name)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,77 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ class RoutingContext
5
+ include Scheme
6
+ EMPTY = new
7
+ ROUTING_ADDRESS_KEY = :address
8
+
9
+ def initialize(uri = nil)
10
+ if uri
11
+ @server_routing_enabled = routing_scheme?(uri.scheme)
12
+ @context = parse_parameters(uri).freeze
13
+ else
14
+ @server_routing_enabled = true
15
+ @context = {}
16
+ end
17
+ end
18
+
19
+ def defined?
20
+ @context.size > 1
21
+ end
22
+
23
+ def to_h
24
+ @context
25
+ end
26
+
27
+ def server_routing_enabled?
28
+ @server_routing_enabled
29
+ end
30
+
31
+ def to_s
32
+ "RoutingContext #{@context} ServerRoutingEnabled=#{@server_routing_enabled}"
33
+ end
34
+
35
+ private
36
+
37
+ def parse_parameters(uri)
38
+ query = uri.query
39
+ address = "#{uri.host}:#{uri.port || BoltServerAddress::DEFAULT_PORT}"
40
+ parameters = { ROUTING_ADDRESS_KEY => address }
41
+ return parameters if query.blank?
42
+
43
+ pairs = query.split('&')
44
+ pairs.each do |pair|
45
+ key_value = pair.split('=')
46
+
47
+ if key_value.size != 2
48
+ raise ArgumentError, "Invalid parameters: '#{pair}' in URI '#{uri}'"
49
+ end
50
+
51
+ key = trim_and_verify_key(key_value[0], 'key', uri)
52
+ if parameters.key?(key)
53
+ raise ArgumentError, "Duplicated query parameters with key '#{key}' in URI '#{uri}'"
54
+ end
55
+ parameters[key] = trim_and_verify(key_value[1], 'value', uri)
56
+ end
57
+
58
+ parameters
59
+ end
60
+
61
+ def trim_and_verify_key(s, key, uri)
62
+ trim_and_verify(s, key, uri).tap do |trimmed|
63
+ if trimmed == ROUTING_ADDRESS_KEY
64
+ raise ArgumentError, "The key 'address' is reserved for routing context."
65
+ end
66
+ end
67
+ end
68
+
69
+ def trim_and_verify(string, name, uri)
70
+ string.strip.tap do |result|
71
+ raise ArgumentError, "Illegal empty #{name} in URI query '#{uri}'" if result.empty?
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,60 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ class RoutingProcedureClusterCompositionProvider
5
+ PROTOCOL_ERROR_MESSAGE = "Failed to parse '%s' result received from server due to "
6
+
7
+ def initialize(_clock, routing_context)
8
+ @single_database_routing_procedure_runner = SingleDatabaseRoutingProcedureRunner.new(routing_context)
9
+ @multi_database_routing_procedure_runner = MultiDatabasesRoutingProcedureRunner.new(routing_context)
10
+ @route_message_routing_procedure_runner = RouteMessageRoutingProcedureRunner.new(routing_context)
11
+ end
12
+
13
+ def get_cluster_composition(connection, database_name, bookmark, impersonated_user)
14
+ runner = if Messaging::Request::MultiDatabaseUtil.supports_route_message?(connection)
15
+ @route_message_routing_procedure_runner
16
+ elsif Messaging::Request::MultiDatabaseUtil.supports_multi_database?(connection)
17
+ @multi_database_routing_procedure_runner
18
+ else
19
+ @single_database_routing_procedure_runner
20
+ end
21
+
22
+ process_routing_response(runner.run(connection, database_name, bookmark, impersonated_user))
23
+ end
24
+
25
+ def process_routing_response(response)
26
+ unless response.success?
27
+ raise response.error, "Failed to run '#{invoked_procedure_string(response)}' on server. Please make sure that there is a Neo4j server or cluster up running."
28
+ end
29
+
30
+ records = response.records
31
+
32
+ # the record size is wrong
33
+ if records.size != 1
34
+ raise Exceptions::ProtocolException, "#{PROTOCOL_ERROR_MESSAGE % invoked_procedure_string(response)} records received '#{records.size}' is too few or too many."
35
+ end
36
+
37
+ # failed to parse the record
38
+ begin
39
+ cluster = ClusterComposition.parse(records[0], Time.now)
40
+ rescue Exceptions::Value::ValueException => e
41
+ raise Exceptions::ProtocolException, "#{PROTOCOL_ERROR_MESSAGE % invoked_procedure_string(response)} unparsable record received. #{e}"
42
+ end
43
+
44
+ # the cluster result is not a legal reply
45
+ if !cluster.has_routers_and_readers?
46
+ raise Exceptions::ProtocolException, "#{PROTOCOL_ERROR_MESSAGE % invoked_procedure_string(response)} no router or reader found in response."
47
+ end
48
+
49
+ # all good
50
+ cluster
51
+ end
52
+
53
+ def invoked_procedure_string(response)
54
+ query = response.procedure
55
+ "#{query.text} #{query.parameters}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,35 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ class RoutingProcedureResponse
5
+ attr_reader :procedure
6
+
7
+ def initialize(procedure, records: nil, error: nil)
8
+ @procedure = procedure
9
+ @records = records
10
+ @error = error
11
+ end
12
+
13
+ def success?
14
+ !@records.nil?
15
+ end
16
+
17
+ def records
18
+ if success?
19
+ @records
20
+ else
21
+ raise Exceptions::IllegalStateException, "Can't access records of a failed result #{@error}"
22
+ end
23
+ end
24
+
25
+ def error
26
+ if success?
27
+ raise Exceptions::IllegalStateException, "Can't access error of a succeeded result #{@records}"
28
+ else
29
+ @error
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module Neo4j::Driver
2
+ module Internal
3
+ module Cluster
4
+ class RoutingSettings
5
+ attr_reader :max_routing_failures, :retry_timeout_delay, :routing_context, :routing_table_purge_delay
6
+
7
+ def initialize(max_routing_failures, retry_timeout_delay, routing_table_purge_delay,
8
+ routing_context = RoutingContext::EMPTY)
9
+ @max_routing_failures = max_routing_failures
10
+ @retry_timeout_delay = retry_timeout_delay
11
+ @routing_context = routing_context
12
+ @routing_table_purge_delay = routing_table_purge_delay
13
+ end
14
+
15
+ STALE_ROUTING_TABLE_PURGE_DELAY = 30.seconds
16
+ DEFAULT = new(1, 5.seconds, STALE_ROUTING_TABLE_PURGE_DELAY)
17
+
18
+ def with_routing_context(new_routing_context)
19
+ self.class.new(@max_routing_failures, @retry_timeout_delay, @routing_table_purge_delay, new_routing_context)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end