Skip to content

Commit 8227955

Browse files
committed
Add RetryableException interface
This interface is meant to indicate what exceptions are retryable.
1 parent 2b9a8f0 commit 8227955

File tree

9 files changed

+87
-50
lines changed

9 files changed

+87
-50
lines changed

driver/src/main/java/org/neo4j/driver/exceptions/AuthorizationExpiredException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
* <p>
2424
* Error code: Neo.ClientError.Security.AuthorizationExpired
2525
*/
26-
public class AuthorizationExpiredException extends SecurityException
26+
public class AuthorizationExpiredException extends SecurityException implements RetryableException
2727
{
2828
public static final String DESCRIPTION = "Authorization information kept on the server has expired, this connection is no longer valid.";
2929

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.exceptions;
20+
21+
/**
22+
* A marker interface indicating if a given exception is considered to be retryable.
23+
*/
24+
public interface RetryableException
25+
{
26+
}

driver/src/main/java/org/neo4j/driver/exceptions/ServiceUnavailableException.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020

2121
/**
2222
* An <em>ServiceUnavailableException</em> indicates that the driver cannot communicate with the cluster.
23+
*
2324
* @since 1.1
2425
*/
25-
public class ServiceUnavailableException extends Neo4jException
26+
public class ServiceUnavailableException extends Neo4jException implements RetryableException
2627
{
2728
public ServiceUnavailableException( String message )
2829
{
@@ -31,6 +32,6 @@ public ServiceUnavailableException( String message )
3132

3233
public ServiceUnavailableException( String message, Throwable throwable )
3334
{
34-
super( message, throwable);
35+
super( message, throwable );
3536
}
3637
}

driver/src/main/java/org/neo4j/driver/exceptions/SessionExpiredException.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
package org.neo4j.driver.exceptions;
2020

2121
/**
22-
* A <em>SessionExpiredException</em> indicates that the session can no longer satisfy the criteria under which it
23-
* was acquired, e.g. a server no longer accepts write requests. A new session needs to be acquired from the driver
24-
* and all actions taken on the expired session must be replayed.
22+
* A <em>SessionExpiredException</em> indicates that the session can no longer satisfy the criteria under which it was acquired, e.g. a server no longer accepts
23+
* write requests. A new session needs to be acquired from the driver and all actions taken on the expired session must be replayed.
24+
*
2525
* @since 1.1
2626
*/
27-
public class SessionExpiredException extends Neo4jException
27+
public class SessionExpiredException extends Neo4jException implements RetryableException
2828
{
29-
public SessionExpiredException( String message)
29+
public SessionExpiredException( String message )
3030
{
3131
super( message );
3232
}

driver/src/main/java/org/neo4j/driver/exceptions/TransientException.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
package org.neo4j.driver.exceptions;
2020

2121
/**
22-
* A <em>TransientException</em> signals a temporary fault that may be worked around by retrying.
23-
* The error code provided can be used to determine further detail for the problem.
22+
* A <em>TransientException</em> signals a temporary fault that may be worked around by retrying. The error code provided can be used to determine further
23+
* detail for the problem.
24+
*
2425
* @since 1.0
2526
*/
26-
public class TransientException extends Neo4jException
27+
public class TransientException extends Neo4jException implements RetryableException
2728
{
2829
public TransientException( String code, String message )
2930
{

driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,11 @@
3838

3939
import org.neo4j.driver.Logger;
4040
import org.neo4j.driver.Logging;
41-
import org.neo4j.driver.exceptions.AuthorizationExpiredException;
4241
import org.neo4j.driver.exceptions.ClientException;
4342
import org.neo4j.driver.exceptions.Neo4jException;
44-
import org.neo4j.driver.exceptions.ServiceUnavailableException;
45-
import org.neo4j.driver.exceptions.SessionExpiredException;
46-
import org.neo4j.driver.exceptions.TransientException;
43+
import org.neo4j.driver.exceptions.RetryableException;
4744
import org.neo4j.driver.internal.util.Clock;
4845
import org.neo4j.driver.internal.util.Futures;
49-
import org.neo4j.driver.util.Experimental;
5046

5147
import static java.util.concurrent.TimeUnit.SECONDS;
5248

@@ -148,14 +144,7 @@ public <T> Publisher<T> retryRx( Publisher<T> work )
148144

149145
protected boolean canRetryOn( Throwable error )
150146
{
151-
return isRetryable( error );
152-
}
153-
154-
@Experimental
155-
public static boolean isRetryable( Throwable error )
156-
{
157-
return error instanceof SessionExpiredException || error instanceof ServiceUnavailableException || error instanceof AuthorizationExpiredException ||
158-
isTransientError( error );
147+
return error instanceof RetryableException;
159148
}
160149

161150
/**
@@ -351,25 +340,6 @@ private void verifyAfterConstruction()
351340
}
352341
}
353342

354-
private static boolean isTransientError( Throwable error )
355-
{
356-
if ( error instanceof TransientException )
357-
{
358-
String code = ((TransientException) error).code();
359-
// Retries should not happen when transaction was explicitly terminated by the user.
360-
// Termination of transaction might result in two different error codes depending on where it was
361-
// terminated. These are really client errors but classification on the server is not entirely correct and
362-
// they are classified as transient.
363-
if ( "Neo.TransientError.Transaction.Terminated".equals( code ) ||
364-
"Neo.TransientError.Transaction.LockClientStopped".equals( code ) )
365-
{
366-
return false;
367-
}
368-
return true;
369-
}
370-
return false;
371-
}
372-
373343
private static List<Throwable> recordError( Throwable error, List<Throwable> errors )
374344
{
375345
if ( errors == null )

driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,20 @@ else if ( code.equalsIgnoreCase( "Neo.ClientError.Security.TokenExpired" ) )
100100
}
101101
}
102102
case "TransientError":
103-
return new TransientException( code, message );
103+
// Since 5.0 these 2 errors have been moved to ClientError class.
104+
// This mapping is required if driver is connection to earlier server versions.
105+
if ( "Neo.TransientError.Transaction.Terminated".equals( code ) )
106+
{
107+
return new ClientException( "Neo.ClientError.Transaction.Terminated", message );
108+
}
109+
else if ( "Neo.TransientError.Transaction.LockClientStopped".equals( code ) )
110+
{
111+
return new ClientException( "Neo.ClientError.Transaction.LockClientStopped", message );
112+
}
113+
else
114+
{
115+
return new TransientException( code, message );
116+
}
104117
default:
105118
return new DatabaseException( code, message );
106119
}

driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ void throwsWhenTransactionTerminatedError() throws Exception
471471
ExponentialBackoffRetryLogic logic = newRetryLogic( 1, 13, 1, 0, clock );
472472

473473
Supplier<Void> workMock = newWorkMock();
474-
TransientException error = new TransientException( "Neo.TransientError.Transaction.Terminated", "" );
474+
ClientException error = new ClientException( "Neo.ClientError.Transaction.Terminated", "" );
475475
when( workMock.get() ).thenThrow( error ).thenReturn( null );
476476

477477
Exception e = assertThrows( Exception.class, () -> logic.retry( workMock ) );
@@ -489,7 +489,7 @@ void doesNotRetryOnTransactionTerminatedErrorAsync()
489489
ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 13, 1, 0, clock );
490490

491491
Supplier<CompletionStage<Object>> workMock = newWorkMock();
492-
TransientException error = new TransientException( "Neo.TransientError.Transaction.Terminated", "" );
492+
ClientException error = new ClientException( "Neo.ClientError.Transaction.Terminated", "" );
493493
when( workMock.get() ).thenReturn( failedFuture( error ) );
494494

495495
Exception e = assertThrows( Exception.class, () -> await( retryLogic.retryAsync( workMock ) ) );
@@ -506,7 +506,7 @@ void throwsWhenTransactionLockClientStoppedError() throws Exception
506506
ExponentialBackoffRetryLogic logic = newRetryLogic( 1, 13, 1, 0, clock );
507507

508508
Supplier<Void> workMock = newWorkMock();
509-
TransientException error = new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" );
509+
ClientException error = new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" );
510510
when( workMock.get() ).thenThrow( error ).thenReturn( null );
511511

512512
Exception e = assertThrows( Exception.class, () -> logic.retry( workMock ) );
@@ -524,7 +524,7 @@ void doesNotRetryOnTransactionLockClientStoppedErrorAsync()
524524
ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 15, 1, 0, clock );
525525

526526
Supplier<CompletionStage<Object>> workMock = newWorkMock();
527-
TransientException error = new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" );
527+
ClientException error = new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" );
528528
when( workMock.get() ).thenReturn( failedFuture( error ) );
529529

530530
Exception e = assertThrows( Exception.class, () -> await( retryLogic.retryAsync( workMock ) ) );
@@ -1437,8 +1437,8 @@ private static Stream<Exception> cannotBeRetriedErrors()
14371437
{
14381438
return Stream.of(
14391439
new IllegalStateException(),
1440-
new TransientException( "Neo.TransientError.Transaction.Terminated", "" ),
1441-
new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" )
1440+
new ClientException( "Neo.ClientError.Transaction.Terminated", "" ),
1441+
new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" )
14421442
);
14431443
}
14441444
}

driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,30 @@ void shouldCreateTokenExpiredException()
189189
assertEquals( code, error.code() );
190190
assertEquals( message, error.getMessage() );
191191
}
192+
193+
@Test
194+
void shouldMapTransientTransactionTerminatedToClientException()
195+
{
196+
String code = "Neo.TransientError.Transaction.Terminated";
197+
String message = "message";
198+
199+
Neo4jException error = newNeo4jError( code, message );
200+
201+
assertThat( error, instanceOf( ClientException.class ) );
202+
assertEquals( "Neo.ClientError.Transaction.Terminated", error.code() );
203+
assertEquals( message, error.getMessage() );
204+
}
205+
206+
@Test
207+
void shouldMapTransientTransactionLockClientStoppedToClientException()
208+
{
209+
String code = "Neo.TransientError.Transaction.LockClientStopped";
210+
String message = "message";
211+
212+
Neo4jException error = newNeo4jError( code, message );
213+
214+
assertThat( error, instanceOf( ClientException.class ) );
215+
assertEquals( "Neo.ClientError.Transaction.LockClientStopped", error.code() );
216+
assertEquals( message, error.getMessage() );
217+
}
192218
}

0 commit comments

Comments
 (0)