Skip to content

Commit 4ed2bff

Browse files
Add new exception types and change to explicitly indetifying fast fail errors (#668)
* Add new exception types and change to explicitly idetifying fast fail errors * Add some missing fail fast types * Added unknown security exceptions * Fix TokenExpiredException reporting to testkit * Address review notes
1 parent 6906193 commit 4ed2bff

File tree

5 files changed

+200
-49
lines changed

5 files changed

+200
-49
lines changed

Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Exceptions/ExceptionManager.cs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,32 @@ internal static class ExceptionManager
2929
{
3030
private static Dictionary<Type, string> TypeMap { get; set; } = new Dictionary<Type, string>()
3131
{
32-
{ typeof(Neo4jException), "Neo4jError" },
33-
{ typeof(ClientException), "ClientError" },
34-
{ typeof(TransientException), "DriverError" }, //Should maybe Transient error, talk to Peter or Martin
35-
{ typeof(DatabaseException), "DatabaseError" },
36-
{ typeof(ServiceUnavailableException), "ServiceUnavailableError" },
37-
{ typeof(SessionExpiredException), "SessionExpiredError" },
38-
{ typeof(Driver.ProtocolException), "ProtocolError" },
39-
{ typeof(SecurityException), "SecurityError" },
40-
{ typeof(AuthenticationException), "AuthenticationError" },
41-
{ typeof(AuthorizationException), "AuthorizationExpired" },
42-
{ typeof(ValueTruncationException), "ValueTruncationError" },
43-
{ typeof(ValueOverflowException), "ValueOverflowError" },
44-
{ typeof(FatalDiscoveryException), "FatalDiscoveryError" },
45-
{ typeof(ResultConsumedException), "ResultConsumedError" },
46-
{ typeof(TransactionNestingException), "TransactionNestingException" },
47-
{ typeof(TokenExpiredException), "TokenExpiredError" },
48-
{ typeof(ConnectionReadTimeoutException), "ConnectionReadTimeoutError"},
49-
{ typeof(InvalidBookmarkException), "InvalidBookmarkError"},
50-
{ typeof(TransactionClosedException), "ClientError"},
51-
52-
{ typeof(NotSupportedException), "NotSupportedException" },
53-
54-
{ typeof(ArgumentException), "ArgumentError"}
32+
{ typeof(Neo4jException), "Neo4jError" },
33+
{ typeof(ClientException), "ClientError" },
34+
{ typeof(TransientException), "DriverError" }, //Should maybe Transient error, talk to Peter or Martin
35+
{ typeof(DatabaseException), "DatabaseError" },
36+
{ typeof(ServiceUnavailableException), "ServiceUnavailableError" },
37+
{ typeof(SessionExpiredException), "SessionExpiredError" },
38+
{ typeof(Driver.ProtocolException), "ProtocolError" },
39+
{ typeof(SecurityException), "SecurityError" },
40+
{ typeof(AuthenticationException), "AuthenticationError" },
41+
{ typeof(AuthorizationException), "AuthorizationExpired" },
42+
{ typeof(ValueTruncationException), "ValueTruncationError" },
43+
{ typeof(ValueOverflowException), "ValueOverflowError" },
44+
{ typeof(FatalDiscoveryException), "FatalDiscoveryError" },
45+
{ typeof(ResultConsumedException), "ResultConsumedError" },
46+
{ typeof(TransactionNestingException), "TransactionNestingException" },
47+
{ typeof(TokenExpiredException), "ClientError" },
48+
{ typeof(ConnectionReadTimeoutException), "ConnectionReadTimeoutError" },
49+
{ typeof(InvalidBookmarkException), "InvalidBookmarkError" },
50+
{ typeof(TransactionClosedException), "ClientError" },
51+
{ typeof(NotSupportedException), "NotSupportedException" },
52+
{ typeof(ArgumentException), "ArgumentError" },
53+
{ typeof(InvalidBookmarkMixtureException), "InvalidBookmarkMixtureError" },
54+
{ typeof(ArgumentErrorException), "ArgumentError" },
55+
{ typeof(TypeException), "TypeError" },
56+
{ typeof(ForbiddenException), "ForbiddenError" },
57+
{ typeof(UnknownSecurityException), "OtherSecurityException" }
5558
};
5659

5760

Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,7 @@ static class TestBlackList
116116
("test_temporal_types.TestDataTypes.test_date_time_cypher_created_tz_id",
117117
"No Antarctica/Troll mapping available."),
118118
("test_temporal_types.TestDataTypes.test_should_echo_all_timezone_ids",
119-
"EST/HST/MST not supported."),
120-
121-
(".test_should_fail_with_invalid_routing_request", "Needs to adjust fast failing discovery errors.")
119+
"EST/HST/MST not supported.")
122120
};
123121

124122
public static bool FindTest(string testName, out string reason)

Neo4j.Driver/Neo4j.Driver/Internal/Extensions/ErrorExtensions.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,27 @@ public static Neo4jException ParseServerException(string code, string message)
5555
{
5656
error = new InvalidBookmarkException(message);
5757
}
58+
else if (InvalidBookmarkMixtureException.IsInvalidBookmarkMixtureException(code))
59+
{
60+
error = new InvalidBookmarkMixtureException(message);
61+
}
62+
else if (ArgumentErrorException.IsArgumentErrorException(code))
63+
{
64+
error = new ArgumentErrorException(message);
65+
}
66+
else if (TypeException.IsTypeException(code))
67+
{
68+
error = new TypeException(message);
69+
}
70+
else if (ForbiddenException.IsForbiddenException(code))
71+
{
72+
error = new ForbiddenException(message);
73+
}
74+
// this one needs to come after it has checked all other possibilities
75+
else if (UnknownSecurityException.IsUnknownSecurityException(code))
76+
{
77+
return new UnknownSecurityException(message, code);
78+
}
5879
else
5980
{
6081
error = new ClientException(code, message);
@@ -127,4 +148,4 @@ public static ResultConsumedException NewResultConsumedException()
127148
"or the query runner where the result is created has already been closed.");
128149
}
129150
}
130-
}
151+
}

Neo4j.Driver/Neo4j.Driver/Internal/Routing/RoutingTableManager.cs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -291,31 +291,49 @@ await _discovery.DiscoverAsync(conn, database, impersonatedUser, bookmarks)
291291
router, database);
292292
}
293293
}
294-
catch (AuthorizationException e)
294+
catch (Exception ex)
295295
{
296-
_logger?.Warn(e, "Failed to update routing table from server '{0}' for database '{1}'.", router, database);
297-
}
298-
catch (SecurityException e)
299-
{
300-
_logger?.Error(e,
301-
"Failed to update routing table from server '{0}' for database '{1}' because of a security exception.",
302-
router, database);
303-
throw;
304-
}
305-
catch (ClientException e)
306-
{
307-
_logger?.Error(e,
308-
"Failed to update routing table from server '{0}' for database '{1}' because of a client exception.",
309-
router, database);
310-
throw;
311-
}
312-
catch (Exception e)
313-
{
314-
_logger?.Warn(e, "Failed to update routing table from server '{0}' for database '{1}'.", router, database);
296+
var failfast = IsFailFastException(ex);
297+
var logMsg = "Failed to update routing table from server '{0}' for database '{1}'.";
298+
if (ex is Neo4jException ne)
299+
{
300+
logMsg += " Error code: '{2}'";
301+
var code = ne.Code;
302+
if (failfast)
303+
{
304+
_logger?.Error(ex, logMsg, router, database, code);
305+
}
306+
else
307+
{
308+
_logger?.Warn(ex, logMsg, router, database, code);
309+
}
310+
}
311+
else
312+
{
313+
_logger?.Warn(ex, logMsg, router, database);
314+
}
315+
316+
if (failfast)
317+
{
318+
throw;
319+
}
315320
}
316321
}
317322

318323
return null;
319324
}
325+
326+
private static bool IsFailFastException(Exception ex)
327+
{
328+
return ex
329+
is FatalDiscoveryException // Neo.ClientError.Database.DatabaseNotFound
330+
or InvalidBookmarkException // Neo.ClientError.Transaction.InvalidBookmark
331+
or InvalidBookmarkMixtureException // Neo.ClientError.Transaction.InvalidBookmarkMixture
332+
or ArgumentErrorException // Neo.ClientError.Statement.ArgumentError
333+
or ProtocolException // Neo.ClientError.Request.Invalid and (special to .NET driver) Neo.ClientError.Request.InvalidFormat
334+
or TypeException // Neo.ClientError.Statement.TypeError
335+
or SecurityException // Neo.ClientError.Security.*
336+
and not AuthorizationException; // except Neo.ClientError.Security.AuthorizationExpired
337+
}
320338
}
321-
}
339+
}

Neo4j.Driver/Neo4j.Driver/Neo4jException.cs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ public AuthorizationException(string message) : base(ErrorCode, message)
426426
/// </summary>
427427
public class TokenExpiredException : SecurityException
428428
{
429-
private const string ErrorCode = "Neo.ClientError.Security.TokenExpiredException";
429+
private const string ErrorCode = "Neo.ClientError.Security.TokenExpired";
430430

431431
internal static bool IsTokenExpiredError(string code)
432432
{
@@ -464,6 +464,117 @@ public InvalidBookmarkException(string message) : base(ErrorCode, message)
464464
}
465465
}
466466

467+
/// <summary>
468+
/// The provided bookmark is invalid. To recover from this a new session needs to be created.
469+
/// </summary>
470+
public class InvalidBookmarkMixtureException : ClientException
471+
{
472+
private const string ErrorCode = "Neo.ClientError.Transaction.InvalidBookmarkMixture";
473+
474+
/// <summary>
475+
/// Create a new <see cref="InvalidBookmarkMixtureException" /> with an error message.
476+
/// </summary>
477+
/// <param name="message">The error message.</param>
478+
public InvalidBookmarkMixtureException(string message) : base(ErrorCode, message)
479+
{
480+
}
481+
482+
internal static bool IsInvalidBookmarkMixtureException(string code)
483+
{
484+
return string.Equals(code, ErrorCode);
485+
}
486+
}
487+
488+
/// <summary>
489+
/// A generic argument error has occurred. To recover from this a new session needs to be created.
490+
/// </summary>
491+
[DataContract]
492+
public class ArgumentErrorException : ClientException
493+
{
494+
private const string ErrorCode = "Neo.ClientError.Statement.ArgumentError";
495+
496+
/// <summary>
497+
/// Create a new <see cref="ArgumentErrorException" /> with an error message.
498+
/// </summary>
499+
/// <param name="message">The error message.</param>
500+
public ArgumentErrorException(string message) : base(ErrorCode, message)
501+
{
502+
}
503+
504+
internal static bool IsArgumentErrorException(string code)
505+
{
506+
return string.Equals(code, ErrorCode);
507+
}
508+
}
509+
510+
/// <summary>
511+
/// An error occurred related to data typing.
512+
/// </summary>
513+
[DataContract]
514+
public class TypeException : ClientException
515+
{
516+
private const string ErrorCode = "Neo.ClientError.Statement.TypeError";
517+
518+
/// <summary>
519+
/// Create a new <see cref="TypeException" /> with an error message.
520+
/// </summary>
521+
/// <param name="message">The error message.</param>
522+
public TypeException(string message) : base(ErrorCode, message)
523+
{
524+
}
525+
526+
internal static bool IsTypeException(string code)
527+
{
528+
return string.Equals(code, ErrorCode);
529+
}
530+
}
531+
532+
/// <summary>
533+
/// This operation is forbidden.
534+
/// </summary>
535+
[DataContract]
536+
public class ForbiddenException : SecurityException
537+
{
538+
private const string ErrorCode = "Neo.ClientError.Security.Forbidden";
539+
540+
/// <summary>
541+
/// Create a new <see cref="ForbiddenException" /> with an error message.
542+
/// </summary>
543+
/// <param name="message">The error message.</param>
544+
public ForbiddenException(string message) : base(ErrorCode, message)
545+
{
546+
}
547+
548+
internal static bool IsForbiddenException(string code)
549+
{
550+
return string.Equals(code, ErrorCode);
551+
}
552+
}
553+
554+
/// <summary>
555+
/// An unknown security error occurred.
556+
/// </summary>
557+
[DataContract]
558+
public class UnknownSecurityException : SecurityException
559+
{
560+
private const string ErrorCodePrefix= "Neo.ClientError.Security.";
561+
562+
/// <summary>
563+
/// Create a new <see cref="UnknownSecurityException" /> with an error message.
564+
/// </summary>
565+
/// <param name="message">The error message.</param>
566+
/// <param name="code">The error code.</param>
567+
public UnknownSecurityException(string message, string code) : base($"{ErrorCodePrefix}*", message)
568+
{
569+
Code = code;
570+
}
571+
572+
internal static bool IsUnknownSecurityException(string code)
573+
{
574+
return code.StartsWith(ErrorCodePrefix);
575+
}
576+
}
577+
467578
/// <summary>
468579
/// A value retrieved from the database needs to be truncated for this conversion to work, and will
469580
/// cause working with a modified data.

0 commit comments

Comments
 (0)