Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f7c0ce6

Browse files
Keep routing table entries on failed liveness check (#1221)
On failed liveness check (s. `liveness_check_timeout` configuration option), the driver will no longer remove the remote from the cached routing tables, but only close the connection under test. This aligns the driver with the other official Neo4j drivers.
1 parent 83f1fa1 commit f7c0ce6

File tree

18 files changed

+362
-46
lines changed

18 files changed

+362
-46
lines changed

‎CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ See also https://github.com/neo4j/neo4j-python-driver/wiki for a full changelog.
187187
- `ResultSummary.gql_status_objects`
188188
- `neo4j.GqlStatusObject`
189189
- (`neo4j.exceptions.GqlError`, `neo4j.exceptions.GqlErrorClassification`)
190+
- On failed liveness check (s. `liveness_check_timeout` configuration option), the driver will no longer remove the
191+
remote from the cached routing tables, but only close the connection under test.
192+
This aligns the driver with the other official Neo4j drivers.
190193

191194

192195
## Version 5.28

‎src/neo4j/_addressing.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ def port_number(self) -> int:
306306
pass
307307
raise error_cls(f"Unknown port value {self[1]!r}")
308308

309+
def __reduce__(self):
310+
return Address, (tuple(self),)
311+
309312

310313
class IPv4Address(Address):
311314
"""
@@ -352,12 +355,15 @@ def _host_name(self) -> str:
352355
def _unresolved(self) -> Address:
353356
return super().__new__(Address, (self._host_name, *self[1:]))
354357

355-
def __new__(cls, iterable, *, host_name: str) -> ResolvedAddress:
358+
def __new__(cls, iterable, host_name: str) -> ResolvedAddress:
356359
new = super().__new__(cls, iterable)
357360
new = t.cast(ResolvedAddress, new)
358361
new._unresolved_host_name = host_name
359362
return new
360363

364+
def __reduce__(self):
365+
return ResolvedAddress, (tuple(self), self._unresolved_host_name)
366+
361367

362368
class ResolvedIPv4Address(IPv4Address, ResolvedAddress):
363369
pass

‎src/neo4j/_async/io/_bolt.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class AsyncBolt:
126126
_closed = False
127127
_defunct = False
128128

129+
# Flag if the connection is currently performing a liveness check.
130+
_liveness_check = False
131+
129132
#: The pool of which this connection is a member
130133
pool = None
131134

@@ -758,6 +761,13 @@ async def reset(self, dehydration_hooks=None, hydration_hooks=None):
758761
type understood by packstream and are free to return anything.
759762
"""
760763

764+
async def liveness_check(self):
765+
self._liveness_check = True
766+
try:
767+
await self.reset()
768+
finally:
769+
self._liveness_check = False
770+
761771
@abc.abstractmethod
762772
def goodbye(self, dehydration_hooks=None, hydration_hooks=None):
763773
"""
@@ -934,7 +944,11 @@ async def _set_defunct(self, message, error=None, silent=False):
934944
# remove the connection from the pool, nor to try to close the
935945
# connection again.
936946
await self.close()
937-
if self.pool and not self._get_server_state_manager().failed():
947+
if (
948+
not self._liveness_check
949+
and self.pool
950+
and not self._get_server_state_manager().failed()
951+
):
938952
await self.pool.deactivate(address=self.unresolved_address)
939953

940954
# Iterate through the outstanding responses, and if any correspond

‎src/neo4j/_async/io/_pool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ async def health_check(connection_, deadline_):
355355
"[#%04X] _: <POOL> liveness check",
356356
connection_.local_port,
357357
)
358-
await connection_.reset()
358+
await connection_.liveness_check()
359359
except (OSError, ServiceUnavailable, SessionExpired):
360360
return False
361361
return True

‎src/neo4j/_routing.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,14 @@ def update(self, new_routing_table):
193193

194194
def servers(self):
195195
return set(self.routers) | set(self.writers) | set(self.readers)
196+
197+
def __eq__(self, other):
198+
if not isinstance(other, RoutingTable):
199+
return NotImplemented
200+
return (
201+
self.database == other.database
202+
and self.routers == other.routers
203+
and self.readers == other.readers
204+
and self.writers == other.writers
205+
and self.ttl == other.ttl
206+
)

‎src/neo4j/_sync/io/_bolt.py

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/neo4j/_sync/io/_pool.py

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎testkitbackend/test_config.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
"'neo4j.datatypes.test_temporal_types.TestDataTypes.test_should_echo_all_timezone_ids'":
1212
"test_subtest_skips.dt_conversion",
1313
"'neo4j.datatypes.test_temporal_types.TestDataTypes.test_date_time_cypher_created_tz_id'":
14-
"test_subtest_skips.tz_id",
15-
"stub\\.routing\\.test_routing_v[0-9x]+\\.RoutingV[0-9x]+\\.test_should_drop_connections_failing_liveness_check":
16-
"Liveness check error handling is not (yet) unified: https://github.com/neo-technology/drivers-adr/pull/83"
14+
"test_subtest_skips.tz_id"
1715
},
1816
"features": {
1917
"Feature:API:BookmarkManager": true,

‎tests/unit/async_/fixtures/fake_connection.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(self, *args, **kwargs):
5555
self.attach_mock(
5656
mock.AsyncMock(spec=AsyncAuthManager), "auth_manager"
5757
)
58+
self.attach_mock(mock.AsyncMock(), "liveness_check")
5859
self.unresolved_address = next(iter(args), "localhost")
5960

6061
self.callbacks = []

‎tests/unit/async_/io/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ async def send_message(self, tag, *fields):
110110
self._outbox.append_message(tag, fields, None)
111111
await self._outbox.flush()
112112

113+
def assert_no_more_messages(self):
114+
assert self._messages
115+
assert not self.recv_buffer
116+
113117

114118
class AsyncFakeSocketPair:
115119
def __init__(self, address, packer_cls=None, unpacker_cls=None):

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /