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.
- checksums.yaml +4 -4
- data/README.md +113 -411
- data/{ruby → lib}/neo4j/driver/access_mode.rb +2 -1
- data/lib/neo4j/driver/auth_token_managers.rb +31 -0
- data/lib/neo4j/driver/auth_tokens.rb +41 -0
- data/lib/neo4j/driver/auto_commit_retries_mode.rb +18 -0
- data/lib/neo4j/driver/bolt/bolt_version.rb +67 -0
- data/lib/neo4j/driver/bolt/connection.rb +1091 -0
- data/lib/neo4j/driver/bolt/handshake.rb +236 -0
- data/lib/neo4j/driver/bolt/message/failure.rb +139 -0
- data/lib/neo4j/driver/bolt/message/ignored.rb +27 -0
- data/lib/neo4j/driver/bolt/message/record.rb +30 -0
- data/lib/neo4j/driver/bolt/message/success.rb +34 -0
- data/lib/neo4j/driver/bolt/message.rb +112 -0
- data/lib/neo4j/driver/bolt/pool.rb +255 -0
- data/lib/neo4j/driver/bolt/protocol/base.rb +146 -0
- data/lib/neo4j/driver/bolt/protocol/v3.rb +43 -0
- data/lib/neo4j/driver/bolt/protocol/v4.rb +24 -0
- data/lib/neo4j/driver/bolt/protocol/v43.rb +28 -0
- data/lib/neo4j/driver/bolt/protocol/v44.rb +23 -0
- data/lib/neo4j/driver/bolt/protocol/v5.rb +27 -0
- data/lib/neo4j/driver/bolt/protocol/v51.rb +24 -0
- data/lib/neo4j/driver/bolt/protocol/v52.rb +28 -0
- data/lib/neo4j/driver/bolt/protocol/v53.rb +27 -0
- data/lib/neo4j/driver/bolt/protocol/v54.rb +14 -0
- data/lib/neo4j/driver/bolt/protocol/v55.rb +17 -0
- data/lib/neo4j/driver/bolt/protocol/v56.rb +14 -0
- data/lib/neo4j/driver/bolt/protocol/v57.rb +31 -0
- data/lib/neo4j/driver/bolt/protocol/v58.rb +15 -0
- data/lib/neo4j/driver/bolt/protocol/v6.rb +43 -0
- data/lib/neo4j/driver/bolt/protocol/v61.rb +21 -0
- data/lib/neo4j/driver/bolt/protocol_version_handler.rb +39 -0
- data/lib/neo4j/driver/bolt/record_buffer.rb +143 -0
- data/lib/neo4j/driver/bolt/stream_handler.rb +53 -0
- data/lib/neo4j/driver/bolt/tls_config.rb +129 -0
- data/lib/neo4j/driver/bolt/wire.rb +321 -0
- data/lib/neo4j/driver/bookmark.rb +15 -0
- data/lib/neo4j/driver/bookmark_managers.rb +39 -0
- data/lib/neo4j/driver/client_certificate.rb +19 -0
- data/lib/neo4j/driver/client_certificate_manager.rb +37 -0
- data/lib/neo4j/driver/client_certificate_managers.rb +15 -0
- data/lib/neo4j/driver/client_certificates.rb +14 -0
- data/lib/neo4j/driver/direct/connection_provider.rb +262 -0
- data/lib/neo4j/driver/driver.rb +268 -0
- data/lib/neo4j/driver/eager_result.rb +10 -0
- data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
- data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
- data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
- data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
- data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
- data/lib/neo4j/driver/graph_database.rb +29 -0
- data/lib/neo4j/driver/internal/clock.rb +20 -0
- data/lib/neo4j/driver/internal/clock_adapter.rb +21 -0
- data/lib/neo4j/driver/internal/default_bookmark_manager.rb +54 -0
- data/lib/neo4j/driver/internal/deprecator.rb +9 -5
- data/lib/neo4j/driver/internal/driver_factory.rb +128 -0
- data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -39
- data/lib/neo4j/driver/internal/extras.rb +23 -0
- data/lib/neo4j/driver/internal/home_db_cache.rb +57 -0
- data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
- data/lib/neo4j/driver/internal/metrics.rb +31 -0
- data/lib/neo4j/driver/internal/security/static_auth_token_manager.rb +25 -0
- data/lib/neo4j/driver/internal/validator.rb +16 -2
- data/lib/neo4j/driver/net.rb +29 -0
- data/lib/neo4j/driver/packstream/markers.rb +49 -0
- data/lib/neo4j/driver/packstream/packer.rb +335 -0
- data/lib/neo4j/driver/packstream/structure.rb +27 -0
- data/lib/neo4j/driver/packstream/unpacker.rb +170 -0
- data/lib/neo4j/driver/record.rb +43 -0
- data/lib/neo4j/driver/result.rb +282 -0
- data/lib/neo4j/driver/routing/load_balancer.rb +706 -0
- data/lib/neo4j/driver/routing/routed_connection.rb +132 -0
- data/lib/neo4j/driver/routing/routing_table.rb +127 -0
- data/lib/neo4j/driver/routing/server_address.rb +25 -0
- data/lib/neo4j/driver/routing_control.rb +13 -0
- data/lib/neo4j/driver/session.rb +577 -0
- data/lib/neo4j/driver/summary/database_info.rb +25 -0
- data/lib/neo4j/driver/summary/gql_notification.rb +38 -0
- data/lib/neo4j/driver/summary/gql_status_object.rb +35 -0
- data/lib/neo4j/driver/summary/notification.rb +46 -0
- data/lib/neo4j/driver/summary/plan.rb +23 -0
- data/lib/neo4j/driver/summary/profile.rb +28 -0
- data/lib/neo4j/driver/summary/query.rb +19 -0
- data/lib/neo4j/driver/summary/query_type.rb +5 -1
- data/lib/neo4j/driver/summary/result_summary.rb +215 -0
- data/lib/neo4j/driver/summary/server_info.rb +33 -0
- data/lib/neo4j/driver/summary/summary_counters.rb +72 -0
- data/lib/neo4j/driver/transaction.rb +344 -0
- data/lib/neo4j/driver/types/duration.rb +76 -0
- data/lib/neo4j/driver/types/entity.rb +40 -0
- data/lib/neo4j/driver/types/local_date_time.rb +77 -8
- data/lib/neo4j/driver/types/local_time.rb +50 -7
- data/lib/neo4j/driver/types/node.rb +18 -0
- data/lib/neo4j/driver/types/offset_time.rb +78 -7
- data/lib/neo4j/driver/types/path.rb +68 -0
- data/lib/neo4j/driver/types/point.rb +38 -18
- data/lib/neo4j/driver/types/relationship.rb +32 -0
- data/lib/neo4j/driver/types/temporal_value.rb +44 -0
- data/lib/neo4j/driver/types/unbound_relationship.rb +24 -0
- data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
- data/lib/neo4j/driver/types/unsupported_type.rb +25 -0
- data/lib/neo4j/driver/types/uuid.rb +29 -0
- data/{ruby → lib}/neo4j/driver/version.rb +1 -1
- data/lib/neo4j/driver.rb +15 -0
- data/lib/neo4j-ruby-driver_loader.rb +34 -20
- metadata +115 -441
- data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
- data/lib/neo4j/driver/synchronizable.rb +0 -23
- data/lib/neo4j/driver/types/time.rb +0 -45
- data/ruby/neo4j/driver/auth_tokens.rb +0 -34
- data/ruby/neo4j/driver/bookmark.rb +0 -21
- data/ruby/neo4j/driver/config.rb +0 -89
- data/ruby/neo4j/driver/graph_database.rb +0 -78
- data/ruby/neo4j/driver/internal/async/connection/bolt_protocol_util.rb +0 -51
- data/ruby/neo4j/driver/internal/async/connection/bootstrap_factory.rb +0 -22
- data/ruby/neo4j/driver/internal/async/connection/channel_attributes.rb +0 -31
- data/ruby/neo4j/driver/internal/async/connection/channel_connected_listener.rb +0 -32
- data/ruby/neo4j/driver/internal/async/connection/channel_connector_impl.rb +0 -83
- data/ruby/neo4j/driver/internal/async/connection/channel_pipeline_builder_impl.rb +0 -22
- data/ruby/neo4j/driver/internal/async/connection/direct_connection.rb +0 -30
- data/ruby/neo4j/driver/internal/async/connection/event_loop_group_factory.rb +0 -83
- data/ruby/neo4j/driver/internal/async/connection/handshake_completed_listener.rb +0 -27
- data/ruby/neo4j/driver/internal/async/connection/handshake_handler.rb +0 -113
- data/ruby/neo4j/driver/internal/async/connection/netty_channel_initializer.rb +0 -57
- data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver.rb +0 -26
- data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver_group.rb +0 -19
- data/ruby/neo4j/driver/internal/async/connection/routing_connection.rb +0 -36
- data/ruby/neo4j/driver/internal/async/connection/stream.rb +0 -12
- data/ruby/neo4j/driver/internal/async/connection/stream_reader.rb +0 -16
- data/ruby/neo4j/driver/internal/async/connection_context.rb +0 -10
- data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +0 -24
- data/ruby/neo4j/driver/internal/async/inbound/byte_buf_input.rb +0 -30
- data/ruby/neo4j/driver/internal/async/inbound/channel_error_handler.rb +0 -77
- data/ruby/neo4j/driver/internal/async/inbound/chunk_decoder.rb +0 -41
- data/ruby/neo4j/driver/internal/async/inbound/connect_timeout_handler.rb +0 -32
- data/ruby/neo4j/driver/internal/async/inbound/connection_read_timeout_handler.rb +0 -17
- data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +0 -171
- data/ruby/neo4j/driver/internal/async/inbound/inbound_message_handler.rb +0 -42
- data/ruby/neo4j/driver/internal/async/inbound/message_decoder.rb +0 -51
- data/ruby/neo4j/driver/internal/async/internal_async_session.rb +0 -92
- data/ruby/neo4j/driver/internal/async/internal_async_transaction.rb +0 -13
- data/ruby/neo4j/driver/internal/async/leak_logging_network_session.rb +0 -34
- data/ruby/neo4j/driver/internal/async/network_connection.rb +0 -194
- data/ruby/neo4j/driver/internal/async/network_session.rb +0 -150
- data/ruby/neo4j/driver/internal/async/outbound/chunk_aware_byte_buf_output.rb +0 -110
- data/ruby/neo4j/driver/internal/async/outbound/outbound_message_handler.rb +0 -39
- data/ruby/neo4j/driver/internal/async/pool/channel.rb +0 -66
- data/ruby/neo4j/driver/internal/async/pool/channel_pool.rb +0 -31
- data/ruby/neo4j/driver/internal/async/pool/channel_tracker.rb +0 -135
- data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +0 -156
- data/ruby/neo4j/driver/internal/async/pool/netty_channel_health_checker.rb +0 -87
- data/ruby/neo4j/driver/internal/async/pool/netty_channel_pool.rb +0 -52
- data/ruby/neo4j/driver/internal/async/pool/network_connection_factory.rb +0 -21
- data/ruby/neo4j/driver/internal/async/pool/pool_settings.rb +0 -34
- data/ruby/neo4j/driver/internal/async/pool/timed_stack.rb +0 -15
- data/ruby/neo4j/driver/internal/async/result_cursors_holder.rb +0 -17
- data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +0 -212
- data/ruby/neo4j/driver/internal/bookmark_holder.rb +0 -9
- data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +0 -48
- data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +0 -14
- data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +0 -122
- data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +0 -10
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +0 -68
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +0 -125
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/round_robin_array_index.rb +0 -13
- data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +0 -31
- data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +0 -147
- data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +0 -43
- data/ruby/neo4j/driver/internal/cluster/routing_context.rb +0 -77
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +0 -60
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +0 -35
- data/ruby/neo4j/driver/internal/cluster/routing_settings.rb +0 -24
- data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +0 -95
- data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +0 -121
- data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +0 -73
- data/ruby/neo4j/driver/internal/connection_settings.rb +0 -16
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +0 -55
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_only_factory.rb +0 -24
- data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +0 -61
- data/ruby/neo4j/driver/internal/cursor/result_cursor_factory_impl.rb +0 -24
- data/ruby/neo4j/driver/internal/cursor/rx_result_cursor_impl.rb +0 -110
- data/ruby/neo4j/driver/internal/database_name_util.rb +0 -37
- data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +0 -9
- data/ruby/neo4j/driver/internal/default_domain_name_resolver.rb +0 -11
- data/ruby/neo4j/driver/internal/direct_connection_provider.rb +0 -40
- data/ruby/neo4j/driver/internal/driver_factory.rb +0 -126
- data/ruby/neo4j/driver/internal/eager_result_value.rb +0 -5
- data/ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb +0 -20
- data/ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb +0 -30
- data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +0 -23
- data/ruby/neo4j/driver/internal/handlers/hello_response_handler.rb +0 -65
- data/ruby/neo4j/driver/internal/handlers/init_response_handler.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +0 -199
- data/ruby/neo4j/driver/internal/handlers/no_op_response_handler.rb +0 -16
- data/ruby/neo4j/driver/internal/handlers/ping_response_handler.rb +0 -29
- data/ruby/neo4j/driver/internal/handlers/pull_handlers.rb +0 -32
- data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +0 -169
- data/ruby/neo4j/driver/internal/handlers/pulln/basic_pull_response_handler.rb +0 -298
- data/ruby/neo4j/driver/internal/handlers/pulln/fetch_size_util.rb +0 -20
- data/ruby/neo4j/driver/internal/handlers/reset_response_handler.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +0 -25
- data/ruby/neo4j/driver/internal/handlers/route_message_response_handler.rb +0 -21
- data/ruby/neo4j/driver/internal/handlers/routing_response_handler.rb +0 -79
- data/ruby/neo4j/driver/internal/handlers/run_response_handler.rb +0 -38
- data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/transaction_pull_response_completion_listener.rb +0 -20
- data/ruby/neo4j/driver/internal/impersonation_util.rb +0 -22
- data/ruby/neo4j/driver/internal/internal_bookmark.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_database_name.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_driver.rb +0 -83
- data/ruby/neo4j/driver/internal/internal_entity.rb +0 -21
- data/ruby/neo4j/driver/internal/internal_node.rb +0 -21
- data/ruby/neo4j/driver/internal/internal_pair.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_path.rb +0 -35
- data/ruby/neo4j/driver/internal/internal_point2_d.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_point3_d.rb +0 -6
- data/ruby/neo4j/driver/internal/internal_record.rb +0 -27
- data/ruby/neo4j/driver/internal/internal_relationship.rb +0 -27
- data/ruby/neo4j/driver/internal/internal_result.rb +0 -49
- data/ruby/neo4j/driver/internal/internal_session.rb +0 -91
- data/ruby/neo4j/driver/internal/internal_transaction.rb +0 -48
- data/ruby/neo4j/driver/internal/logging/channel_activity_logger.rb +0 -29
- data/ruby/neo4j/driver/internal/logging/channel_error_logger.rb +0 -17
- data/ruby/neo4j/driver/internal/logging/prefixed_logger.rb +0 -19
- data/ruby/neo4j/driver/internal/logging/reformatted_logger.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/abstract_message_writer.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/bolt_protocol.rb +0 -32
- data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +0 -48
- data/ruby/neo4j/driver/internal/messaging/common/common_message_reader.rb +0 -51
- data/ruby/neo4j/driver/internal/messaging/common/common_value.rb +0 -33
- data/ruby/neo4j/driver/internal/messaging/common/common_value_packer.rb +0 -104
- data/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +0 -256
- data/ruby/neo4j/driver/internal/messaging/encode/begin_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/commit_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/discard_all_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/discard_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/goodbye_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/hello_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/init_message_encoder.rb +0 -16
- data/ruby/neo4j/driver/internal/messaging/encode/logoff_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/logon_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/pull_all_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/pull_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/reset_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/rollback_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +0 -24
- data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +0 -22
- data/ruby/neo4j/driver/internal/messaging/encode/run_message_encoder.rb +0 -16
- data/ruby/neo4j/driver/internal/messaging/encode/run_with_metadata_message_encoder.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/request/commit_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/discard_all_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/discard_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/goodbye_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/hello_message.rb +0 -30
- data/ruby/neo4j/driver/internal/messaging/request/init_message.rb +0 -19
- data/ruby/neo4j/driver/internal/messaging/request/logoff_message.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/request/logon_message.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/request/message_with_metadata.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +0 -26
- data/ruby/neo4j/driver/internal/messaging/request/pull_all_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/pull_message.rb +0 -22
- data/ruby/neo4j/driver/internal/messaging/request/reset_message.rb +0 -32
- data/ruby/neo4j/driver/internal/messaging/request/rollback_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +0 -28
- data/ruby/neo4j/driver/internal/messaging/request/run_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +0 -49
- data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +0 -24
- data/ruby/neo4j/driver/internal/messaging/response/failure_message.rb +0 -40
- data/ruby/neo4j/driver/internal/messaging/response/ignored_message.rb +0 -29
- data/ruby/neo4j/driver/internal/messaging/response/record_message.rb +0 -33
- data/ruby/neo4j/driver/internal/messaging/response/success_message.rb +0 -34
- data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +0 -82
- data/ruby/neo4j/driver/internal/messaging/v3/message_format_v3.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v3/message_writer_v3.rb +0 -27
- data/ruby/neo4j/driver/internal/messaging/v4/bolt_protocol_v4.rb +0 -29
- data/ruby/neo4j/driver/internal/messaging/v4/message_format_v4.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/v4/message_writer_v4.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v41/bolt_protocol_v41.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/v42/bolt_protocol_v42.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/v43/bolt_protocol_v43.rb +0 -19
- data/ruby/neo4j/driver/internal/messaging/v43/message_format_v43.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v43/message_writer_v43.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/v44/bolt_protocol_v44.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v44/message_format_v44.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/v5/bolt_protocol_v5.rb +0 -21
- data/ruby/neo4j/driver/internal/messaging/v5/message_format_v5.rb +0 -18
- data/ruby/neo4j/driver/internal/messaging/v5/value_unpacker_v5.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v51/bolt_protocol_v51.rb +0 -40
- data/ruby/neo4j/driver/internal/messaging/v51/message_format_v51.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v51/message_writer_v51.rb +0 -17
- data/ruby/neo4j/driver/internal/metrics/connection_pool_metrics_listener.rb +0 -34
- data/ruby/neo4j/driver/internal/metrics/internal_abstract_metrics.rb +0 -46
- data/ruby/neo4j/driver/internal/metrics/internal_connection_pool_metrics.rb +0 -105
- data/ruby/neo4j/driver/internal/metrics/internal_metrics.rb +0 -82
- data/ruby/neo4j/driver/internal/metrics/internal_metrics_provider.rb +0 -18
- data/ruby/neo4j/driver/internal/metrics/listener_event.rb +0 -17
- data/ruby/neo4j/driver/internal/metrics/metrics_provider.rb +0 -24
- data/ruby/neo4j/driver/internal/metrics/time_recorder_listener_event.rb +0 -15
- data/ruby/neo4j/driver/internal/packstream/byte_array_incompatible_packer.rb +0 -12
- data/ruby/neo4j/driver/internal/packstream/pack_input.rb +0 -47
- data/ruby/neo4j/driver/internal/packstream/pack_output.rb +0 -39
- data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +0 -324
- data/ruby/neo4j/driver/internal/packstream/pack_type.rb +0 -17
- data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +0 -13
- data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +0 -35
- data/ruby/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +0 -150
- data/ruby/neo4j/driver/internal/revocation_strategy.rb +0 -19
- data/ruby/neo4j/driver/internal/scheme.rb +0 -32
- data/ruby/neo4j/driver/internal/security/internal_auth_token.rb +0 -15
- data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +0 -48
- data/ruby/neo4j/driver/internal/security_setting.rb +0 -66
- data/ruby/neo4j/driver/internal/session_factory_impl.rb +0 -32
- data/ruby/neo4j/driver/internal/spi/connection.rb +0 -19
- data/ruby/neo4j/driver/internal/spi/connection_pool.rb +0 -9
- data/ruby/neo4j/driver/internal/spi/response_handler.rb +0 -23
- data/ruby/neo4j/driver/internal/summary/internal_database_info.rb +0 -7
- data/ruby/neo4j/driver/internal/summary/internal_input_position.rb +0 -11
- data/ruby/neo4j/driver/internal/summary/internal_notification.rb +0 -16
- data/ruby/neo4j/driver/internal/summary/internal_plan.rb +0 -41
- data/ruby/neo4j/driver/internal/summary/internal_profiled_plan.rb +0 -32
- data/ruby/neo4j/driver/internal/summary/internal_result_summary.rb +0 -33
- data/ruby/neo4j/driver/internal/summary/internal_server_info.rb +0 -6
- data/ruby/neo4j/driver/internal/summary/internal_summary_counters.rb +0 -18
- data/ruby/neo4j/driver/internal/svm/netty_substitutions.rb +0 -196
- data/ruby/neo4j/driver/internal/svm/z_lib_substitutions.rb +0 -21
- data/ruby/neo4j/driver/internal/util/certificate_tool.rb +0 -65
- data/ruby/neo4j/driver/internal/util/clock.rb +0 -29
- data/ruby/neo4j/driver/internal/util/error_util.rb +0 -104
- data/ruby/neo4j/driver/internal/util/extract.rb +0 -123
- data/ruby/neo4j/driver/internal/util/format.rb +0 -39
- data/ruby/neo4j/driver/internal/util/futures.rb +0 -99
- data/ruby/neo4j/driver/internal/util/iterables.rb +0 -35
- data/ruby/neo4j/driver/internal/util/lock_util.rb +0 -23
- data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +0 -101
- data/ruby/neo4j/driver/internal/util/mutex.rb +0 -9
- data/ruby/neo4j/driver/internal/util/preconditions.rb +0 -16
- data/ruby/neo4j/driver/internal/util/result_holder.rb +0 -72
- data/ruby/neo4j/driver/internal/util/server_version.rb +0 -60
- data/ruby/neo4j/driver/logging1.rb +0 -51
- data/ruby/neo4j/driver/net/server_address.rb +0 -9
- data/ruby/neo4j/driver/query.rb +0 -48
- data/ruby/neo4j/driver/records.rb +0 -13
- data/ruby/neo4j/driver/transaction_config.rb +0 -50
- data/ruby/neo4j/driver/values.rb +0 -26
- 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
|