Class: Neo4j::Driver::Driver
- Inherits:
-
Object
- Object
- Neo4j::Driver::Driver
- Defined in:
- lib/neo4j/driver/driver.rb
Overview
Driver for connecting to Neo4j.
Holds a single ConnectionProvider (Direct for bolt://, Routing for
neo4j://) chosen at construction. All connection work delegates to
the provider — no scheme branching here.
Constant Summary collapse
- DEFAULT_MAX_POOL_SIZE =
100- DEFAULT_ACQUISITION_TIMEOUT =
60- ENCRYPTED_SCHEMES =
URI schemes that imply transport encryption.
%w[bolt+s bolt+ssc neo4j+s neo4j+ssc].freeze
- VERIFY_AUTH_NEGATIVE_CODES =
Security failure codes that mean "these credentials are not valid" (verify_authentication returns false). Every other security error — AuthorizationExpired, an unknown Security.* code, a rate-limit — is a condition the caller must see, so it propagates. Mirrors Java's verifyAuthentication NEGATIVE/PROPAGATE split.
%w[ Neo.ClientError.Security.Unauthorized Neo.ClientError.Security.CredentialsExpired Neo.ClientError.Security.Forbidden Neo.ClientError.Security.TokenExpired ].freeze
Instance Attribute Summary collapse
-
#connection_provider ⇒ Object
readonly
Returns the value of attribute connection_provider.
Instance Method Summary collapse
- #close ⇒ Object
- #closed? ⇒ Boolean
-
#encrypted? ⇒ Boolean
True iff the driver was constructed to enforce transport encryption — either via a +s/+ssc URI scheme or an explicit
encryption: trueoption. -
#execute_query(cypher, params = {}, config = {}) ⇒ Object
Run a query in a managed transaction and return EagerResult (keys, materialised records, summary).
-
#initialize(uri, options, connection_provider, clock: Internal::Clock.new) ⇒ Driver
constructor
The ConnectionProvider is assembled by the DriverFactory (which wires in non-public hooks like the domain-name resolver), so the Driver itself never sees those extension points — it just uses the provider.
-
#metrics ⇒ Object
Driver metrics.
-
#routing_table(database) ⇒ Object
Routing table for
database(testkit GetRoutingTable). -
#routing_table_refresh(database, bookmarks) ⇒ Object
Force a routing-table refresh (testkit ForcedRoutingTableUpdate).
- #session(**options) ⇒ Object
- #supports_multi_db? ⇒ Boolean
-
#supports_session_auth? ⇒ Boolean
Whether the connected server supports per-session re-authentication (Bolt 5.1+).
- #verify_authentication(auth_token) ⇒ Object
- #verify_connectivity ⇒ Object
Constructor Details
#initialize(uri, options, connection_provider, clock: Internal::Clock.new) ⇒ Driver
The ConnectionProvider is assembled by the DriverFactory (which wires in non-public hooks like the domain-name resolver), so the Driver itself never sees those extension points — it just uses the provider.
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/neo4j/driver/driver.rb', line 20 def initialize(uri, , connection_provider, clock: Internal::Clock.new) @uri = URI(uri) @options = @clock = clock @closed = false @connection_provider = connection_provider # Driver-wide default BookmarkManager for execute_query: shared across # calls so they're causally chained (a read after a write sees it). # Built once here rather than lazily so concurrent first calls can't # race into two managers. Mirrors Java's default query bookmark manager. @query_bookmark_manager = BookmarkManagers.default_manager end |
Instance Attribute Details
#connection_provider ⇒ Object (readonly)
Returns the value of attribute connection_provider.
265 266 267 |
# File 'lib/neo4j/driver/driver.rb', line 265 def connection_provider @connection_provider end |
Instance Method Details
#close ⇒ Object
70 71 72 73 74 75 |
# File 'lib/neo4j/driver/driver.rb', line 70 def close return if @closed @closed = true @connection_provider.close end |
#closed? ⇒ Boolean
93 94 95 |
# File 'lib/neo4j/driver/driver.rb', line 93 def closed? @closed end |
#encrypted? ⇒ Boolean
True iff the driver was constructed to enforce transport encryption
— either via a +s/+ssc URI scheme or an explicit encryption: true
option. Mirrors Java's Driver.isEncrypted().
100 101 102 |
# File 'lib/neo4j/driver/driver.rb', line 100 def encrypted? ENCRYPTED_SCHEMES.include?(@uri.scheme) || @options[:encryption] == true end |
#execute_query(cypher, params = {}, config = {}) ⇒ Object
Run a query in a managed transaction and return EagerResult (keys, materialised records, summary). Convenience for "I just want results, don't make me build a session".
Signature: (query, params = {}, config = {}). Same shape
Session#run uses.
config keys (snake_case):
:database — string, the database to target.
:routing — RoutingControl::READ / ::WRITE.
:bookmark_manager — BookmarkManager for cross-session
causal consistency. Pass `nil`
explicitly to disable; omit to use
the driver default.
:impersonated_user — server runs the query as if this user
had issued it (Bolt 4.4+, requires
impersonator privileges).
:metadata — map echoed back in summary; used by
query-log / monitoring tooling.
:timeout — seconds (or ActiveSupport::Duration);
server-side transaction timeout.
:auth_token — run this query under a specific identity
(Bolt 5.1+ re-auths the connection via
LOGOFF/LOGON); omit to use the driver's.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/neo4j/driver/driver.rb', line 139 def execute_query(cypher, params = {}, config = {}) routing = config[:routing] || RoutingControl::WRITE # `bookmark_manager: nil` is meaningful (explicitly disables the # manager) and differs from omitting the key, so it's added # conditionally rather than via .compact. `:database` / # `:impersonated_user` / `:auth_token` keep .compact semantics # (nil == omitted). A per-query `:auth_token` pins the session (and so # its connection) to that identity — the provider re-auths on Bolt 5.1+ # (Feature:API:Driver.ExecuteQuery:WithAuth). session_opts = { database: config[:database], default_access_mode: routing, impersonated_user: config[:impersonated_user], auth_token: config[:auth_token] }.compact # execute_query chains bookmarks across successive calls via a # driver-wide default BookmarkManager, so a read after a write sees # the write (matches Java's Driver.executeQuery). A caller can override # per call; `bookmark_manager: nil` explicitly disables the chaining. session_opts[:bookmark_manager] = config.key?(:bookmark_manager) ? config[:bookmark_manager] : @query_bookmark_manager # The managed tx this session runs reports TELEMETRY api = 3 # (DRIVER_EXECUTE_QUERY), not a plain managed transaction (api 0). # Carried on the session since execute_query drives it through # execute_read/write. session_opts[:telemetry_api] = 3 # Forwarded to the managed-tx call below — the BEGIN extras # honour metadata/timeout per-tx, not session-wide. tx_kwargs = { metadata: config[:metadata], timeout: config[:timeout] }.compact keys = nil records = nil summary = nil session(**session_opts) do |s| method = routing == RoutingControl::READ ? :execute_read : :execute_write s.send(method, **tx_kwargs) do |tx| result = tx.run(cypher, **params) records = result.to_a keys = result.keys summary = result.consume end end EagerResult.new(keys, records, summary) end |
#metrics ⇒ Object
Driver metrics. Currently exposes per-server connection-pool metrics
(in-use / idle counts per address) via metrics.connection_pool_metrics
— what testkit's GetConnectionPoolMetrics reads. The provider owns the
pools and produces the snapshots.
249 250 251 |
# File 'lib/neo4j/driver/driver.rb', line 249 def metrics Internal::Metrics.new(@connection_provider) end |
#routing_table(database) ⇒ Object
Routing table for database (testkit GetRoutingTable). Returns the
routing table from the load balancer; testkit reads routers/writers/
readers + ttl off it without knowing this internal path.
256 257 258 |
# File 'lib/neo4j/driver/driver.rb', line 256 def routing_table(database) connection_provider.routing_table_registry.routing_table_handler(database).routing_table end |
#routing_table_refresh(database, bookmarks) ⇒ Object
Force a routing-table refresh (testkit ForcedRoutingTableUpdate).
261 262 263 |
# File 'lib/neo4j/driver/driver.rb', line 261 def routing_table_refresh(database, bookmarks) connection_provider.routing_table_registry.refresh(database, bookmarks) end |
#session(**options) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/neo4j/driver/driver.rb', line 33 def session(**) raise Exceptions::ClientException, 'Driver is closed' if @closed = @options.merge() # A nil session fetch_size means "inherit the driver default", so it # must not clobber a driver-level value the plain merge just overwrote # (callers — the testkit backend included — may pass fetch_size: nil # when the session didn't set one). Mirrors Java's SessionConfig # falling back to Config#fetchSize. [:fetch_size] ||= @options[:fetch_size] session = Session.new(@connection_provider, , clock: @clock) return session unless block_given? begin result = yield session rescue StandardError => e # Block raised; preserve as primary, attach close-time failures # as suppressed (Java try-with-resources semantics). begin session.close rescue Exceptions::Neo4jException => close_error e.add_suppressed(close_error) if e.respond_to?(:add_suppressed) end raise else # Block exited cleanly. Any close-time error here comes from # draining a result the user never iterated — Java semantics # treat that as cancellation, not a real failure. begin session.close rescue Exceptions::Neo4jException end result end end |
#supports_multi_db? ⇒ Boolean
89 90 91 |
# File 'lib/neo4j/driver/driver.rb', line 89 def supports_multi_db? @connection_provider.supports_multi_db? end |
#supports_session_auth? ⇒ Boolean
Whether the connected server supports per-session re-authentication (Bolt 5.1+). Probes a connection to read the negotiated protocol's capability flag. Today our handshake only advertises Bolt 4.4, so this returns false until the Bolt 5.x HELLO/LOGON work lands.
108 109 110 111 112 113 |
# File 'lib/neo4j/driver/driver.rb', line 108 def supports_session_auth? connection = @connection_provider.acquire(access_mode: :read) connection.protocol.supports_re_auth? ensure @connection_provider.release(connection) if connection end |
#verify_authentication(auth_token) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/neo4j/driver/driver.rb', line 214 def verify_authentication(auth_token) # Acquire a read connection AS the supplied identity, routing through # `system` exactly like Java's verifyAuthentication: for neo4j:// this # discovers (ROUTE on system) then reaches a reader; for bolt:// it # uses the single server. We then force a LOGON with the token: a clean # response means the credentials are good. The connection is one-shot — # discarded, never pooled under the probe identity. connection = @connection_provider.acquire( access_mode: AccessMode::READ, database: 'system', auth: auth_token ) begin # Force a fresh LOGON even when the pooled connection already holds # this identity (warm verify, or verifying the driver's own token): # verification has to actually round-trip the credentials to the # server, not trust a cached auth state. On a freshly built # connection this is a redundant re-auth the scripts tolerate; on a # reused one it's the whole point. Synchronous (pipelined: false): verify # discards the connection with no operation to carry and drain the # LOGOFF/LOGON replies, so it must read the LOGON result itself. connection.authenticate(auth_token, force: true, pipelined: false) ensure connection.discard_on_release = true @connection_provider.release(connection) end true rescue Exceptions::SecurityException => e raise unless VERIFY_AUTH_NEGATIVE_CODES.include?(e.code) false end |
#verify_connectivity ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/neo4j/driver/driver.rb', line 77 def verify_connectivity @connection_provider.verify_connectivity rescue Exceptions::Neo4jException # Propagate driver exceptions (auth/security/client/service- # unavailable) as-is, like Java — wrapping them would hide the # type and message callers assert on. Only unexpected non-driver # errors get the contextual wrapper. raise rescue StandardError => e raise Exceptions::ServiceUnavailableException, "Failed to verify connectivity: #{e.}" end |