Skip to content

Commit 1530f30

Browse files
committed
Fix reactive transaction function retry logic to retry on relevant resource cleanup failures
This update fixes reactive transaction function retry logic and makes it retry when retryable errors occur during resource cleanup.
1 parent e0a4f6e commit 1530f30

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.neo4j.driver.Logging;
4141
import org.neo4j.driver.exceptions.AuthorizationExpiredException;
4242
import org.neo4j.driver.exceptions.ClientException;
43+
import org.neo4j.driver.exceptions.Neo4jException;
4344
import org.neo4j.driver.exceptions.ServiceUnavailableException;
4445
import org.neo4j.driver.exceptions.SessionExpiredException;
4546
import org.neo4j.driver.exceptions.TransientException;
@@ -180,6 +181,11 @@ private Retry exponentialBackoffRetryRx()
180181
contextView ->
181182
{
182183
Throwable throwable = retrySignal.failure();
184+
// Reactor usingWhen returns RuntimeException when resource cleanup fails
185+
if ( throwable instanceof RuntimeException && throwable.getCause() instanceof Neo4jException )
186+
{
187+
throwable = throwable.getCause();
188+
}
183189
Throwable error = extractPossibleTerminationCause( throwable );
184190

185191
List<Throwable> errors = contextView.getOrDefault( "errors", null );

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,29 @@ void doesRetryOnAuthorizationExpiredExceptionRx()
10081008
assertEquals( "Done", result );
10091009
}
10101010

1011+
@Test
1012+
void doesRetryOnAsyncResourceCleanupRuntimeExceptionRx()
1013+
{
1014+
Clock clock = mock( Clock.class );
1015+
Logging logging = mock( Logging.class );
1016+
Logger logger = mock( Logger.class );
1017+
when( logging.getLog( any( Class.class ) ) ).thenReturn( logger );
1018+
ExponentialBackoffRetryLogic logic = new ExponentialBackoffRetryLogic( RetrySettings.DEFAULT, eventExecutor, clock, logging );
1019+
1020+
AtomicBoolean exceptionThrown = new AtomicBoolean( false );
1021+
String result = await( Mono.from( logic.retryRx( Mono.fromSupplier( () ->
1022+
{
1023+
if ( exceptionThrown.compareAndSet( false, true ) )
1024+
{
1025+
throw new RuntimeException( "Async resource cleanup failed after",
1026+
authorizationExpiredException() );
1027+
}
1028+
return "Done";
1029+
} ) ) ) );
1030+
1031+
assertEquals( "Done", result );
1032+
}
1033+
10111034
@Test
10121035
void doesNotRetryOnRandomClientExceptionRx()
10131036
{

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ public class StartTest implements TestkitRequest
6666
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestSessionRun\\.test_discard_on_session_close_unfinished_result$",
6767
"Does not support partially consumed state" );
6868
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.NoRouting[^.]+\\.test_should_error_on_database_shutdown_using_tx_run$", "Session close throws error" );
69-
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.Routing[^.]+\\.test_should_retry_write_until_success_with_leader_shutdown_during_tx_using_tx_function$",
70-
"Commit failure leaks outside function" );
7169
REACTIVE_SKIP_PATTERN_TO_REASON.put(
7270
"^.*\\.Routing[^.]+\\.test_should_fail_when_reading_from_unexpectedly_interrupting_readers_on_run_using_tx_function$",
7371
"Rollback failures following commit failure" );
@@ -84,7 +82,6 @@ public class StartTest implements TestkitRequest
8482
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestDirectConnectionRecvTimeout\\..*$", skipMessage );
8583
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestRoutingConnectionRecvTimeout\\..*$", skipMessage );
8684
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.Routing[^.]+\\.test_should_successfully_acquire_rt_when_router_ip_changes$", skipMessage );
87-
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.Routing[^.]+\\.test_should_revert_to_initial_router_if_known_router_throws_protocol_errors$", skipMessage );
8885
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestRoutingConnectionRecvTimeout\\.test_timeout$", skipMessage );
8986
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestRoutingConnectionRecvTimeout\\.test_timeout_unmanaged_tx$", skipMessage );
9087
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.TestDisconnects\\.test_disconnect_session_on_tx_commit$", skipMessage );

0 commit comments

Comments
 (0)