Skip to content

Commit 165d783

Browse files
authored
New timeout config options [ADR 006] (#745)
amends #675 to fully implement ADR 006, therefore introducing two new driver config options `session_connection_timeout` and `update_routing_table_timeout`. Related fixes in this PR: * Fix pool._acquire It should retry finding an idle connection after being woken up Further small improvements: * Extend concurrent pool tests by pre-populating the pool * Fix missing `await` in `Bolt.ping` * Fix overwriting socket wrapper's close mothod * Fix ITs with Neo4j 5.0+: WAIT for async DB management queries
1 parent 6fe4ea7 commit 165d783

29 files changed

+887
-387
lines changed

docs/source/api.rst

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ Driver Configuration
160160

161161
Additional configuration can be provided via the :class:`neo4j.Driver` constructor.
162162

163-
163+
+ :ref:`session-connection-timeout-ref`
164+
+ :ref:`update-routing-table-timeout-ref`
164165
+ :ref:`connection-acquisition-timeout-ref`
165166
+ :ref:`connection-timeout-ref`
166167
+ :ref:`encrypted-ref`
@@ -175,12 +176,63 @@ Additional configuration can be provided via the :class:`neo4j.Driver` construct
175176
+ :ref:`user-agent-ref`
176177

177178

179+
.. _session-connection-timeout-ref:
180+
181+
``session_connection_timeout``
182+
------------------------------
183+
The maximum amount of time in seconds the session will wait when trying to
184+
establish a usable read/write connection to the remote host.
185+
This encompasses *everything* that needs to happen for this, including,
186+
if necessary, updating the routing table, fetching a connection from the pool,
187+
and, if necessary fully establishing a new connection with the reader/writer.
188+
189+
Since this process may involve updating the routing table, acquiring a
190+
connection from the pool, or establishing a new connection, it should be chosen
191+
larger than :ref:`update-routing-table-timeout-ref`,
192+
:ref:`connection-acquisition-timeout-ref`, and :ref:`connection-timeout-ref`.
193+
194+
:Type: ``float``
195+
:Default: ``120.0``
196+
197+
.. versionadded:: 4.4.5
198+
199+
.. versionchanged:: 5.0
200+
201+
The default value was changed from ``float("inf")`` to ``120.0``.
202+
203+
204+
.. _update-routing-table-timeout-ref:
205+
206+
``update_routing_table_timeout``
207+
--------------------------------
208+
The maximum amount of time in seconds the driver will attempt to fetch a new
209+
routing table. This encompasses *everything* that needs to happen for this,
210+
including fetching connections from the pool, performing handshakes, and
211+
requesting and receiving a fresh routing table.
212+
213+
Since this process may involve acquiring a connection from the pool, or
214+
establishing a new connection, it should be chosen larger than
215+
:ref:`connection-acquisition-timeout-ref` and :ref:`connection-timeout-ref`.
216+
217+
This setting only has an effect for :ref:`neo4j-driver-ref`, but not for
218+
:ref:`bolt-driver-ref` as it does no routing at all.
219+
220+
:Type: ``float``
221+
:Default: ``90.0``
222+
223+
.. versionadded:: 4.4.5
224+
225+
178226
.. _connection-acquisition-timeout-ref:
179227

180228
``connection_acquisition_timeout``
181229
----------------------------------
182-
The maximum amount of time in seconds a session will wait when requesting a connection from the connection pool.
183-
Since the process of acquiring a connection may involve creating a new connection, ensure that the value of this configuration is higher than the configured :ref:`connection-timeout-ref`.
230+
The maximum amount of time in seconds the driver will wait to either acquire an
231+
idle connection from the pool (including potential liveness checks) or create a
232+
new connection when the pool is not full and all existing connection are in use.
233+
234+
Since this process may involve opening a new connection including handshakes,
235+
it should be chosen larger than :ref:`connection-timeout-ref`.
184236

185237
:Type: ``float``
186238
:Default: ``60.0``
@@ -190,7 +242,11 @@ Since the process of acquiring a connection may involve creating a new connectio
190242

191243
``connection_timeout``
192244
----------------------
193-
The maximum amount of time in seconds to wait for a TCP connection to be established.
245+
The maximum amount of time in seconds to wait for a TCP connection to be
246+
established.
247+
248+
This *does not* include any handshake(s), or authentication required before the
249+
connection can be used to perform database related work.
194250

195251
:Type: ``float``
196252
:Default: ``30.0``
@@ -224,7 +280,6 @@ Specify whether TCP keep-alive should be enabled.
224280

225281
``max_connection_lifetime``
226282
---------------------------
227-
228283
The maximum duration in seconds that the driver will keep a connection for before being removed from the pool.
229284

230285
:Type: ``float``

neo4j/_async/io/_bolt.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ async def ping(cls, address, *, timeout=None, **config):
264264
except (ServiceUnavailable, SessionExpired, BoltHandshakeError):
265265
return None
266266
else:
267-
AsyncBoltSocket.close_socket(s)
267+
await AsyncBoltSocket.close_socket(s)
268268
return protocol_version
269269

270270
@classmethod
@@ -357,11 +357,6 @@ def time_remaining():
357357
connection.socket.set_deadline(time_remaining())
358358
try:
359359
await connection.hello()
360-
except SocketDeadlineExceeded as e:
361-
# connection._defunct = True
362-
raise ServiceUnavailable(
363-
"Timeout during initial handshake occurred"
364-
) from e
365360
finally:
366361
connection.socket.set_deadline(None)
367362
except Exception:
@@ -484,7 +479,7 @@ async def reset(self):
484479

485480
@abc.abstractmethod
486481
def goodbye(self):
487-
"""Append a GOODBYE message to the outgoing queued."""
482+
"""Append a GOODBYE message to the outgoing queue."""
488483
pass
489484

490485
def _append(self, signature, fields=(), response=None):
@@ -594,7 +589,7 @@ async def _set_defunct(self, message, error=None, silent=False):
594589
direct_driver = isinstance(self.pool, AsyncBoltPool)
595590

596591
if error:
597-
log.debug("[#%04X] %s", self.socket.getsockname()[1], error)
592+
log.debug("[#%04X] %r", self.socket.getsockname()[1], error)
598593
log.error(message)
599594
# We were attempting to receive data but the connection
600595
# has unexpectedly terminated. So, we need to close the

neo4j/_async/io/_common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from struct import pack as struct_pack
2424

2525
from ..._async_compat.util import AsyncUtil
26+
from ..._exceptions import SocketDeadlineExceeded
2627
from ...exceptions import (
2728
Neo4jError,
2829
ServiceUnavailable,
@@ -70,7 +71,7 @@ async def _yield_messages(self, sock):
7071
# Reset for new message
7172
unpacker.reset()
7273

73-
except (OSError, socket.timeout) as error:
74+
except (OSError, socket.timeout, SocketDeadlineExceeded) as error:
7475
await AsyncUtil.callback(self.on_error, error)
7576

7677
async def pop(self):

0 commit comments

Comments
 (0)