Skip to content

Commit 996a473

Browse files
committed
Backport new connection timeout config options
Backport of #745
1 parent 8acff42 commit 996a473

19 files changed

+840
-389
lines changed

docs/source/api.rst

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ Driver Configuration
160160
Additional configuration can be provided via the :class:`neo4j.Driver` constructor.
161161

162162

163+
+ :ref:`session-connection-timeout-ref`
164+
+ :ref:`update-routing-table-timeout-ref`
163165
+ :ref:`connection-acquisition-timeout-ref`
164166
+ :ref:`connection-timeout-ref`
165167
+ :ref:`encrypted-ref`
@@ -172,12 +174,59 @@ Additional configuration can be provided via the :class:`neo4j.Driver` construct
172174
+ :ref:`user-agent-ref`
173175

174176

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

177222
``connection_acquisition_timeout``
178223
----------------------------------
179-
The maximum amount of time in seconds a session will wait when requesting a connection from the connection pool.
180-
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`.
224+
The maximum amount of time in seconds the driver will wait to either acquire an
225+
idle connection from the pool (including potential liveness checks) or create a
226+
new connection when the pool is not full and all existing connection are in use.
227+
228+
Since this process may involve opening a new connection including handshakes,
229+
it should be chosen larger than :ref:`connection-timeout-ref`.
181230

182231
:Type: ``float``
183232
:Default: ``60.0``
@@ -187,7 +236,11 @@ Since the process of acquiring a connection may involve creating a new connectio
187236

188237
``connection_timeout``
189238
----------------------
190-
The maximum amount of time in seconds to wait for a TCP connection to be established.
239+
The maximum amount of time in seconds to wait for a TCP connection to be
240+
established.
241+
242+
This *does not* include any handshake(s), or authentication required before the
243+
connection can be used to perform database related work.
191244

192245
:Type: ``float``
193246
:Default: ``30.0``

neo4j/_exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,7 @@ def transaction(self):
172172
class BoltProtocolError(BoltError):
173173
""" Raised when an unexpected or unsupported protocol event occurs.
174174
"""
175+
176+
177+
class SocketDeadlineExceeded(RuntimeError):
178+
"""Raised from sockets with deadlines when a timeout occurs."""

neo4j/_io/__init__.py

Whitespace-only changes.

neo4j/_io/deadline.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) "Neo4j"
2+
# Neo4j Sweden AB [https://neo4j.com]
3+
#
4+
# This file is part of Neo4j.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# https://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
19+
from contextlib import contextmanager
20+
from time import perf_counter
21+
22+
23+
class Deadline:
24+
def __init__(self, timeout):
25+
if timeout is None or timeout == float("inf"):
26+
self._deadline = float("inf")
27+
else:
28+
self._deadline = perf_counter() + timeout
29+
self._original_timeout = timeout
30+
31+
@property
32+
def original_timeout(self):
33+
return self._original_timeout
34+
35+
def expired(self):
36+
return self.to_timeout() == 0
37+
38+
def to_timeout(self):
39+
if self._deadline == float("inf"):
40+
return None
41+
timeout = self._deadline - perf_counter()
42+
return timeout if timeout > 0 else 0
43+
44+
def __eq__(self, other):
45+
if isinstance(other, Deadline):
46+
return self._deadline == other._deadline
47+
return NotImplemented
48+
49+
def __gt__(self, other):
50+
if isinstance(other, Deadline):
51+
return self._deadline > other._deadline
52+
return NotImplemented
53+
54+
def __ge__(self, other):
55+
if isinstance(other, Deadline):
56+
return self._deadline >= other._deadline
57+
return NotImplemented
58+
59+
def __lt__(self, other):
60+
if isinstance(other, Deadline):
61+
return self._deadline < other._deadline
62+
return NotImplemented
63+
64+
def __le__(self, other):
65+
if isinstance(other, Deadline):
66+
return self._deadline <= other._deadline
67+
return NotImplemented
68+
69+
@classmethod
70+
def from_timeout_or_deadline(cls, timeout):
71+
if isinstance(timeout, cls):
72+
return timeout
73+
return cls(timeout)
74+
75+
76+
merge_deadlines = min
77+
78+
79+
def merge_deadlines_and_timeouts(*deadline):
80+
deadlines = map(Deadline.from_timeout_or_deadline, deadline)
81+
return merge_deadlines(deadlines)
82+
83+
84+
@contextmanager
85+
def connection_deadline(connection, deadline):
86+
original_deadline = connection.socket.get_deadline()
87+
if deadline is None and original_deadline is not None:
88+
# nothing to do here
89+
yield
90+
return
91+
deadline = merge_deadlines(
92+
*(d for d in (deadline, original_deadline) if d is not None)
93+
)
94+
connection.socket.set_deadline(deadline)
95+
try:
96+
yield
97+
finally:
98+
connection.socket.set_deadline(original_deadline)

neo4j/conf.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ class PoolConfig(Config):
185185
connection_timeout = 30.0 # seconds
186186
# The maximum amount of time to wait for a TCP connection to be established.
187187

188+
#: Update Routing Table Timout
189+
update_routing_table_timeout = 90.0 # seconds
190+
# The maximum amount of time to wait for updating the routing table.
191+
# This includes everything necessary for this to happen.
192+
# Including opening sockets, requesting and receiving the routing table,
193+
# etc.
194+
188195
#: Trust
189196
trust = TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
190197
# Specify how to determine the authenticity of encryption certificates provided by the Neo4j instance on connection.
@@ -256,6 +263,12 @@ class WorkspaceConfig(Config):
256263
""" WorkSpace configuration.
257264
"""
258265

266+
#: Session Connection Timeout
267+
session_connection_timeout = 120.0 # seconds
268+
# The maximum amount of time to wait for a session to obtain a usable
269+
# read/write connection. This includes everything necessary for this to
270+
# happen. Including fetching routing tables, opening sockets, etc.
271+
259272
#: Connection Acquisition Timeout
260273
connection_acquisition_timeout = 60.0 # seconds
261274
# The maximum amount of time a session will wait when requesting a connection from the connection pool.

neo4j/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def __str__(self):
152152
class ClientError(Neo4jError):
153153
""" The Client sent a bad request - changing the request might yield a successful outcome.
154154
"""
155+
def __str__(self):
156+
return super(Neo4jError, self).__str__()
155157

156158

157159
class DatabaseError(Neo4jError):

0 commit comments

Comments
 (0)