Skip to content

Commit a7258f8

Browse files
committed
New verify_connectivity + add get_server_info
1 parent 88847cc commit a7258f8

27 files changed

+810
-304
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@
7878
It now raises a `ResultConsumedError`.
7979
- New method `Result.closed()` can be used to check for this condition if
8080
necessary.
81+
- `driver.verify_connectivity()`
82+
- All keyword arguments have been deprecated (they were experimental).
83+
They are now ignored and will be removed in a future release.
84+
- The undocumented return value has been removed. If you need information
85+
about the remote server, use `driver.get_server_info()` instead.
8186

8287

8388
## Version 4.4

bin/make-unasync

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ def apply_unasync(files):
213213
additional_test_replacements = {
214214
"_async": "_sync",
215215
"mark_async_test": "mark_sync_test",
216+
"assert_awaited_once": "assert_called_once",
216217
}
217218
additional_testkit_backend_replacements = {}
218219
rules = [

docs/source/api.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ This object holds the details required to establish connections with a Neo4j dat
145145
Closing a driver will immediately shut down all connections in the pool.
146146

147147
.. autoclass:: neo4j.Driver()
148-
:members: session, encrypted, close
148+
:members: session, encrypted, close, verify_connectivity, get_server_info
149149

150150

151151
.. _driver-configuration-ref:

docs/source/async_api.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Async API Documentation
1010
This means everything documented on this page might be removed or change
1111
its API at any time (including in patch releases).
1212

13+
.. versionadded:: 5.0
14+
1315
******************
1416
AsyncGraphDatabase
1517
******************
@@ -126,7 +128,7 @@ This object holds the details required to establish connections with a Neo4j dat
126128
Closing a driver will immediately shut down all connections in the pool.
127129

128130
.. autoclass:: neo4j.AsyncDriver()
129-
:members: session, encrypted, close
131+
:members: session, encrypted, close, verify_connectivity, get_server_info
130132

131133

132134
.. _async-driver-configuration-ref:

neo4j/_async/driver.py

Lines changed: 55 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
SessionConfig,
3232
WorkspaceConfig,
3333
)
34+
from ..exceptions import (
35+
ServiceUnavailable,
36+
SessionExpired,
37+
)
3438
from ..meta import (
3539
deprecation_warn,
3640
experimental,
@@ -236,9 +240,11 @@ class AsyncDriver:
236240
#: Flag if the driver has been closed
237241
_closed = False
238242

239-
def __init__(self, pool):
243+
def __init__(self, pool, default_workspace_config):
240244
assert pool is not None
245+
assert default_workspace_config is not None
241246
self._pool = pool
247+
self._default_workspace_config = default_workspace_config
242248

243249
async def __aenter__(self):
244250
return self
@@ -285,17 +291,56 @@ async def close(self):
285291
await self._pool.close()
286292
self._closed = True
287293

288-
@experimental("The configuration may change in the future.")
294+
# TODO: 6.0 - remove config argument
289295
async def verify_connectivity(self, **config):
290-
""" This verifies if the driver can connect to a remote server or a cluster
291-
by establishing a network connection with the remote and possibly exchanging
292-
a few data before closing the connection. It throws exception if fails to connect.
296+
"""Verify that the driver can establish a connection to the server.
297+
298+
This verifies if the driver can establish a reading connection to a
299+
remote server or a cluster. Some data will be exchanged.
293300
294-
Use the exception to further understand the cause of the connectivity problem.
301+
.. note::
302+
Even if this method raises an exception, the driver still needs to
303+
be closed via :meth:`close` to free up all resources.
295304
296-
Note: Even if this method throws an exception, the driver still need to be closed via close() to free up all resources.
305+
:raises DriverError: if the driver cannot connect to the remote.
306+
Use the exception to further understand the cause of the
307+
connectivity problem.
308+
309+
.. versionchanged:: 5.0 the config parameters will be removed in
310+
version 6 0. It has no effect starting in version 5.0.
297311
"""
298-
raise NotImplementedError
312+
if config:
313+
deprecation_warn(
314+
"verify_connectivity() will not accept any configuration "
315+
"parameters starting with version 6.0."
316+
)
317+
318+
await self.get_server_info()
319+
320+
async def get_server_info(self):
321+
"""Get information about the connected Neo4j server.
322+
323+
Try to establish a working read connection to the remote server or a
324+
member of a cluster and exchange some data. Then return the contacted
325+
server's information.
326+
327+
In a cluster, there is no guarantee about which server will be
328+
contacted.
329+
330+
.. note::
331+
Even if this method raises an exception, the driver still needs to
332+
be closed via :meth:`close` to free up all resources.
333+
334+
:rtype: ServerInfo
335+
336+
:raises DriverError: if the driver cannot connect to the remote.
337+
Use the exception to further understand the cause of the
338+
connectivity problem.
339+
340+
.. versionadded:: 5.0
341+
"""
342+
async with self.session() as session:
343+
return await session._get_server_info()
299344

300345
@experimental("Feature support query, based on Bolt Protocol Version and Neo4j Server Version will change in the future.")
301346
async def supports_multi_db(self):
@@ -339,7 +384,7 @@ def open(cls, target, *, auth=None, **config):
339384

340385
def __init__(self, pool, default_workspace_config):
341386
_Direct.__init__(self, pool.address)
342-
AsyncDriver.__init__(self, pool)
387+
AsyncDriver.__init__(self, pool, default_workspace_config)
343388
self._default_workspace_config = default_workspace_config
344389

345390
def session(self, **config):
@@ -354,17 +399,6 @@ def session(self, **config):
354399
SessionConfig.consume(config) # Consume the config
355400
return AsyncSession(self._pool, session_config)
356401

357-
@experimental("The configuration may change in the future.")
358-
async def verify_connectivity(self, **config):
359-
server_agent = None
360-
config["fetch_size"] = -1
361-
async with self.session(**config) as session:
362-
result = await session.run("RETURN 1 AS x")
363-
value = await result.single().value()
364-
summary = await result.consume()
365-
server_agent = summary.server.agent
366-
return server_agent
367-
368402

369403
class AsyncNeo4jDriver(_Routing, AsyncDriver):
370404
""":class:`.AsyncNeo4jDriver` is instantiated for ``neo4j`` URIs. The
@@ -387,45 +421,10 @@ def open(cls, *targets, auth=None, routing_context=None, **config):
387421

388422
def __init__(self, pool, default_workspace_config):
389423
_Routing.__init__(self, pool.get_default_database_initial_router_addresses())
390-
AsyncDriver.__init__(self, pool)
391-
self._default_workspace_config = default_workspace_config
424+
AsyncDriver.__init__(self, pool, default_workspace_config)
392425

393426
def session(self, **config):
394427
from .work import AsyncSession
395428
session_config = SessionConfig(self._default_workspace_config, config)
396429
SessionConfig.consume(config) # Consume the config
397430
return AsyncSession(self._pool, session_config)
398-
399-
@experimental("The configuration may change in the future.")
400-
async def verify_connectivity(self, **config):
401-
"""
402-
:raise ServiceUnavailable: raised if the server does not support routing or if routing support is broken.
403-
"""
404-
# TODO: Improve and update Stub Test Server to be able to test.
405-
return await self._verify_routing_connectivity()
406-
407-
async def _verify_routing_connectivity(self):
408-
from ..exceptions import (
409-
Neo4jError,
410-
ServiceUnavailable,
411-
SessionExpired,
412-
)
413-
414-
table = self._pool.get_routing_table_for_default_database()
415-
routing_info = {}
416-
for ix in list(table.routers):
417-
try:
418-
routing_info[ix] = await self._pool.fetch_routing_info(
419-
address=table.routers[0],
420-
database=self._default_workspace_config.database,
421-
imp_user=self._default_workspace_config.impersonated_user,
422-
bookmarks=None,
423-
timeout=self._default_workspace_config
424-
.connection_acquisition_timeout
425-
)
426-
except (ServiceUnavailable, SessionExpired, Neo4jError):
427-
routing_info[ix] = None
428-
for key, val in routing_info.items():
429-
if val is not None:
430-
return routing_info
431-
raise ServiceUnavailable("Could not connect to any routing servers.")

neo4j/_async/io/_bolt.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ class AsyncBolt:
7272
# The socket
7373
in_use = False
7474

75+
# When the connection was last put back into the pool
76+
idle_since = float("-inf")
77+
7578
# The socket
7679
_closed = False
7780

@@ -104,6 +107,7 @@ def __init__(self, unresolved_address, sock, max_connection_lifetime, *,
104107
self._max_connection_lifetime = max_connection_lifetime
105108
self._creation_timestamp = perf_counter()
106109
self.routing_context = routing_context
110+
self.idle_since = perf_counter()
107111

108112
# Determine the user agent
109113
if user_agent:
@@ -568,5 +572,15 @@ def closed(self):
568572
def defunct(self):
569573
pass
570574

575+
def is_idle_for(self, timeout):
576+
"""Check if connection has been idle for at least the given timeout.
577+
578+
:param timeout: timeout in seconds
579+
:type timeout: float
580+
581+
:rtype: bool
582+
"""
583+
return perf_counter() - self.idle_since > timeout
584+
571585

572586
AsyncBoltSocket.Bolt = AsyncBolt

0 commit comments

Comments
 (0)