Skip to content

Commit d48907c

Browse files
authored
Improve error reporting on routing discovery (#870)
Routing drivers (`neo4j[+s[sc]]://` scheme) retry fetching a routing table on many different errors that are considered not retryable in the context of transactions. This is to overall improve the driver's stability when connecting to clusters. However, this poses the risk of hiding user-input errors (e.g., selecting a database name that is invalid or doesn't exist). Hence, the driver blacklists a handful of selected error codes upon which the discovery process is terminated prematurely, raising the raw error to the user. We expand the list to include more errors: * `Neo.ClientError.Statement.TypeError`, e.g., when trying to impersonate an integer. * `Neo.ClientError.Statement.ArgumentError`, e.g., when trying to impersonate without the required permissions. * `Neo.ClientError.Request.Invalid`, e.g., when trying to select an integer database. Also adding tests for discovery retries and fast failure.
1 parent f53c358 commit d48907c

File tree

21 files changed

+332
-113
lines changed

21 files changed

+332
-113
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
See also https://github.com/neo4j/neo4j-python-driver/wiki for more details.
44

5+
## Version 5.4
6+
- Undocumented helper methods `Neo4jError.is_fatal_during_discovery` and
7+
`Neo4jError.invalidates_all_connections` have been deprecated and will be
8+
removed without replacement in version 6.0.
9+
10+
511
## Version 5.3
612
- Python 3.11 support added
713
- Removed undocumented, unused `neo4j.data.map_type`

src/neo4j/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@
150150

151151

152152
def __getattr__(name):
153-
# TODO 6.0 - remove this
153+
# TODO: 6.0 - remove this
154154
if name in (
155155
"log", "Config", "PoolConfig", "SessionConfig", "WorkspaceConfig"
156156
):

src/neo4j/_async/driver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def driver(cls, uri, *, auth=None, **config) -> AsyncDriver:
131131

132132
driver_type, security_type, parsed = parse_neo4j_uri(uri)
133133

134-
# TODO: 6.0 remove "trust" config option
134+
# TODO: 6.0 - remove "trust" config option
135135
if "trust" in config.keys():
136136
if config["trust"] not in (
137137
TRUST_ALL_CERTIFICATES,
@@ -166,7 +166,7 @@ def driver(cls, uri, *, auth=None, **config) -> AsyncDriver:
166166
or "ssl_context" in config.keys())):
167167
from ..exceptions import ConfigurationError
168168

169-
# TODO: 6.0 remove "trust" from error message
169+
# TODO: 6.0 - remove "trust" from error message
170170
raise ConfigurationError(
171171
'The config settings "encrypted", "trust", '
172172
'"trusted_certificates", and "ssl_context" can only be '

src/neo4j/_async/io/_bolt3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ async def _process_message(self, tag, fields):
377377
self.pool.on_write_failure(address=self.unresolved_address)
378378
raise
379379
except Neo4jError as e:
380-
if self.pool and e.invalidates_all_connections():
380+
if self.pool and e._invalidates_all_connections():
381381
await self.pool.mark_all_stale()
382382
raise
383383
else:

src/neo4j/_async/io/_bolt4.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ async def _process_message(self, tag, fields):
333333
self.pool.on_write_failure(address=self.unresolved_address)
334334
raise
335335
except Neo4jError as e:
336-
if self.pool and e.invalidates_all_connections():
336+
if self.pool and e._invalidates_all_connections():
337337
await self.pool.mark_all_stale()
338338
raise
339339
else:

src/neo4j/_async/io/_bolt5.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ async def _process_message(self, tag, fields):
324324
self.pool.on_write_failure(address=self.unresolved_address)
325325
raise
326326
except Neo4jError as e:
327-
if self.pool and e.invalidates_all_connections():
327+
if self.pool and e._invalidates_all_connections():
328328
await self.pool.mark_all_stale()
329329
raise
330330
else:

src/neo4j/_async/io/_pool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ async def fetch_routing_table(
538538
# checks if the code is an error that is caused by the client. In
539539
# this case there is no sense in trying to fetch a RT from another
540540
# router. Hence, the driver should fail fast during discovery.
541-
if e.is_fatal_during_discovery():
541+
if e._is_fatal_during_discovery():
542542
raise
543543
except (ServiceUnavailable, SessionExpired):
544544
pass

src/neo4j/_async/work/result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ async def to_df(
611611
612612
::
613613
614-
res = await tx.run("UNWIND range(1, 10) AS n RETURN n, n+1 as m")
614+
res = await tx.run("UNWIND range(1, 10) AS n RETURN n, n+1 AS m")
615615
df = await res.to_df()
616616
617617
for instance will return a DataFrame with two columns: ``n`` and ``m``

src/neo4j/_async/work/session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ async def get_two_tx(tx):
591591
READ_ACCESS, transaction_function, *args, **kwargs
592592
)
593593

594-
# TODO 6.0: Remove this method
594+
# TODO: 6.0 - Remove this method
595595
@deprecated("read_transaction has been renamed to execute_read")
596596
async def read_transaction(
597597
self,
@@ -673,7 +673,7 @@ async def create_node_tx(tx, name):
673673
WRITE_ACCESS, transaction_function, *args, **kwargs
674674
)
675675

676-
# TODO 6.0: Remove this method
676+
# TODO: 6.0 - Remove this method
677677
@deprecated("write_transaction has been renamed to execute_write")
678678
async def write_transaction(
679679
self,

src/neo4j/_sync/driver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def driver(cls, uri, *, auth=None, **config) -> Driver:
128128

129129
driver_type, security_type, parsed = parse_neo4j_uri(uri)
130130

131-
# TODO: 6.0 remove "trust" config option
131+
# TODO: 6.0 - remove "trust" config option
132132
if "trust" in config.keys():
133133
if config["trust"] not in (
134134
TRUST_ALL_CERTIFICATES,
@@ -163,7 +163,7 @@ def driver(cls, uri, *, auth=None, **config) -> Driver:
163163
or "ssl_context" in config.keys())):
164164
from ..exceptions import ConfigurationError
165165

166-
# TODO: 6.0 remove "trust" from error message
166+
# TODO: 6.0 - remove "trust" from error message
167167
raise ConfigurationError(
168168
'The config settings "encrypted", "trust", '
169169
'"trusted_certificates", and "ssl_context" can only be '

0 commit comments

Comments
 (0)