From 5b57f7e5055da224b81d2e212a99cfbf29d5248d Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Tue, 12 Mar 2019 09:45:30 +0100 Subject: [PATCH 1/7] Adding Session run tx with retries --- .../neo4j/driver/internal/NetworkSession.java | 5 + .../internal/reactive/InternalRxSession.java | 31 ++--- .../retry/ExponentialBackoffRetryLogic.java | 55 +++++++++ .../driver/internal/retry/RetryLogic.java | 4 + .../reactive/InternalRxSessionTest.java | 113 ++++++++++++++++-- .../ExponentialBackoffRetryLogicTest.java | 59 +++++++++ 6 files changed, 240 insertions(+), 27 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java index 6977b357ff..cd5c3b1e29 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java @@ -443,6 +443,11 @@ public CompletionStage runRx( Statement statement, Tran return newResultCursorStage; } + public RetryLogic retryLogic() + { + return retryLogic; + } + private CompletionStage run( Statement statement, TransactionConfig config, boolean waitForRunResponse ) { CompletionStage newResultCursorStage = diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java index 0948599c11..e2858c5f33 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java @@ -25,18 +25,17 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.internal.ExplicitTransaction; import org.neo4j.driver.internal.NetworkSession; +import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxSession; import org.neo4j.driver.reactive.RxTransaction; import org.neo4j.driver.reactive.RxTransactionWork; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.exceptions.TransientException; import static org.neo4j.driver.internal.reactive.RxUtils.createEmptyPublisher; import static org.neo4j.driver.internal.reactive.RxUtils.createMono; @@ -106,20 +105,14 @@ public Publisher writeTransaction( RxTransactionWork> work, private Publisher runTransaction( AccessMode mode, RxTransactionWork> work, TransactionConfig config ) { // TODO read and write - Publisher publisher = beginTransaction( config ); - Flux txExecutor = Mono.from( publisher ).flatMapMany( tx -> Flux.from( work.execute( tx ) ).flatMap( t -> Mono.create( sink -> sink.success( t ) ), - throwable -> Mono.from( tx.rollback() ).then( Mono.error( throwable ) ), // TODO chain errors from rollback to throwable - () -> Mono.from( tx.commit() ).then( Mono.empty() ) ) ); - return txExecutor.retry( throwable -> { - if ( throwable instanceof TransientException ) - { - return true; - } - else - { - return false; - } - } ); // TODO retry + Publisher publisher = beginTransaction( /*mode,*/ config ); + Flux txResult = Mono.from( publisher ) + .flatMapMany( tx -> Flux.from( work.execute( tx ) ) + .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ) // if failed then we rollback and rethrow the error + .concatWith( tx.commit() ) // if succeeded then we commit. + ); + + return session.retryLogic().retryRx( txResult ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java index d48f653f19..7ccf7c06f6 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java +++ b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java @@ -20,7 +20,13 @@ import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutorGroup; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.context.Context; +import reactor.util.function.Tuples; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -28,6 +34,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.function.Function; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.Futures; @@ -128,6 +135,12 @@ public CompletionStage retryAsync( Supplier> work ) return resultFuture; } + @Override + public Publisher retryRx( Publisher work ) + { + return Flux.from( work ).retryWhen( retryRxCondition() ); + } + protected boolean canRetryOn( Throwable error ) { return error instanceof SessionExpiredException || @@ -135,6 +148,48 @@ protected boolean canRetryOn( Throwable error ) isTransientError( error ); } + private Function,Publisher> retryRxCondition() + { + return errorCurrentAttempt -> errorCurrentAttempt.flatMap( e -> Mono.subscriberContext().map( ctx -> Tuples.of( e, ctx ) ) ).flatMap( t2 -> { + Throwable lastError = t2.getT1(); + Context ctx = t2.getT2(); + + List errors = ctx.getOrDefault( "errors", null ); + + long startTime = ctx.getOrDefault( "startTime", -1L ); + long nextDelayMs = ctx.getOrDefault( "nextDelayMs", initialRetryDelayMs ); + + if( !canRetryOn( lastError ) ) + { + addSuppressed( lastError, errors ); + return Mono.error( lastError ); + } + + long currentTime = clock.millis(); + if ( startTime == -1 ) + { + startTime = currentTime; + } + + long delayWithJitterMs = computeDelayWithJitter( nextDelayMs ); + if ( delayWithJitterMs < maxRetryTimeMs ) + { + log.warn( "Transaction failed and will be retried in " + delayWithJitterMs + "ms", lastError ); + + nextDelayMs = (long) (nextDelayMs * multiplier); + errors = recordError( lastError, errors ); + + return Mono.just( ctx.put( "errors", errors ).put( "startTime", startTime ).put( "nextDelayMs", nextDelayMs ) ) + .delayElement( Duration.ofMillis( delayWithJitterMs ) ); + } + else + { + addSuppressed( lastError, errors ); + return Mono.error( lastError ); + } + } ); + } + private void executeWorkInEventLoop( CompletableFuture resultFuture, Supplier> work ) { // this is the very first time we execute given work diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/RetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/RetryLogic.java index d5ba6956f7..332626d1d3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/retry/RetryLogic.java +++ b/driver/src/main/java/org/neo4j/driver/internal/retry/RetryLogic.java @@ -18,6 +18,8 @@ */ package org.neo4j.driver.internal.retry; +import org.reactivestreams.Publisher; + import java.util.concurrent.CompletionStage; import java.util.function.Supplier; @@ -26,4 +28,6 @@ public interface RetryLogic T retry( Supplier work ); CompletionStage retryAsync( Supplier> work ); + + Publisher retryRx( Publisher work ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java index 7b8d97281c..99e6344910 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java @@ -22,29 +22,30 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Stream; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.ExplicitTransaction; import org.neo4j.driver.internal.InternalRecord; import org.neo4j.driver.internal.NetworkSession; -import org.neo4j.driver.internal.reactive.InternalRxResult; -import org.neo4j.driver.internal.reactive.InternalRxSession; +import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.util.FixedRetryLogic; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.value.IntegerValue; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxSession; import org.neo4j.driver.reactive.RxTransaction; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; -import java.util.function.Function; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -54,12 +55,13 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.util.Futures.completedWithNull; import static org.neo4j.driver.TransactionConfig.empty; import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.internal.util.Futures.completedWithNull; class InternalRxSessionTest { @@ -86,6 +88,16 @@ private static Stream>> allBeginTxMe ); } + private static Stream>> allRunTxMethods() + { + return Stream.of( + rxSession -> rxSession.readTransaction( tx -> Flux.just( "a" ) ), + rxSession -> rxSession.writeTransaction( tx -> Flux.just( "a" ) ), + rxSession -> rxSession.readTransaction( tx -> Flux.just( "a" ), empty() ), + rxSession -> rxSession.writeTransaction( tx -> Flux.just( "a" ), empty() ) + ); + } + @ParameterizedTest @MethodSource( "allSessionRunMethods" ) void shouldDelegateRun( Function runReturnOne ) throws Throwable @@ -178,6 +190,91 @@ void shouldReleaseConnectionIfFailedToBeginTx( Function> runTx ) throws Throwable + { + // Given + NetworkSession session = mock( NetworkSession.class ); + ExplicitTransaction tx = mock( ExplicitTransaction.class ); + when( tx.commitAsync() ).thenReturn( completedWithNull() ); + when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); + + when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.retryLogic() ).thenReturn( new FixedRetryLogic( 1 ) ); + InternalRxSession rxSession = new InternalRxSession( session ); + + // When + Publisher strings = runTx.apply( rxSession ); + StepVerifier.create( Flux.from( strings ) ).expectNext( "a" ).verifyComplete(); + + // Then + verify( session ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( tx ).commitAsync(); + } + + @Test + void shouldRetryOnError() throws Throwable + { + // Given + int retryCount = 2; + NetworkSession session = mock( NetworkSession.class ); + ExplicitTransaction tx = mock( ExplicitTransaction.class ); + when( tx.commitAsync() ).thenReturn( completedWithNull() ); + when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); + + when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.retryLogic() ).thenReturn( new FixedRetryLogic( retryCount ) ); + InternalRxSession rxSession = new InternalRxSession( session ); + + // When + Publisher strings = rxSession.readTransaction( t -> + Flux.just( "a" ).then( Mono.error( new RuntimeException( "Errored" ) ) ) ); + StepVerifier.create( Flux.from( strings ) ) + // we lost the "a"s too as the user only see the last failure + .expectError( RuntimeException.class ) + .verify(); + + // Then + verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( tx, times( retryCount + 1 ) ).rollbackAsync(); + } + + @Test + void shouldObtainResultIfRetrySucceed() throws Throwable + { + // Given + int retryCount = 2; + NetworkSession session = mock( NetworkSession.class ); + ExplicitTransaction tx = mock( ExplicitTransaction.class ); + when( tx.commitAsync() ).thenReturn( completedWithNull() ); + when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); + + when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.retryLogic() ).thenReturn( new FixedRetryLogic( retryCount ) ); + InternalRxSession rxSession = new InternalRxSession( session ); + + // When + AtomicInteger count = new AtomicInteger(); + Publisher strings = rxSession.readTransaction( t -> { + // we fail for the first few retries, and then success on the last run. + if ( count.getAndIncrement() == retryCount ) + { + return Flux.just( "a" ); + } + else + { + return Flux.just( "a" ).then( Mono.error( new RuntimeException( "Errored" ) ) ); + } + } ); + StepVerifier.create( Flux.from( strings ) ).expectNext( "a" ).verifyComplete(); + + // Then + verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( tx, times( retryCount ) ).rollbackAsync(); + verify( tx ).commitAsync(); + } + @Test void shouldDelegateBookmark() throws Throwable { diff --git a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java index 51727cfe8a..1e870fab20 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java @@ -20,7 +20,11 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; +import reactor.util.function.Tuple2; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletionStage; @@ -36,9 +40,14 @@ import static java.lang.Long.MAX_VALUE; import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -741,6 +750,56 @@ public CompletionStage get() ); } + @Test + void shouldRetryWithBackOff() { + Exception exception = new TransientException( "Unknown", "Retry this error." ); + List elapsedList = new ArrayList<>(); + + ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0, + null, Clock.SYSTEM, DEV_NULL_LOGGING ); + + Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); + StepVerifier.withVirtualTime( () -> // This test uses a virtual time. So do not panic if you saw this test runs faster than it should be. + Flux.from( retryLogic.retryRx( source ) ) + .elapsed() + .doOnNext( elapsed -> { if ( elapsed.getT2() == 0 ) elapsedList.add( elapsed.getT1() ); } ) + .map( Tuple2::getT2 ) ) + .thenAwait( Duration.ofSeconds( 2 ) ) + .expectNext( 0, 1 ) // first run + .expectNext( 0, 1, 0, 1, 0, 1, 0, 1 ) //4 retry attempts + .verifyErrorSatisfies( e -> assertThat( e, equalTo( exception ) ) ); + + assertThat( elapsedList.size(), equalTo( 5 ) ); + assertThat( elapsedList, contains( 0L, 100L, 200L, 400L, 800L ) ); + } + + @Test + void shouldRetryWithRandomBackOff() { + List elapsedList = new ArrayList<>(); + Exception exception = new TransientException( "Unknown", "Retry this error." ); + + ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0.1, + null, Clock.SYSTEM, DEV_NULL_LOGGING ); + + Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); + StepVerifier.withVirtualTime( () -> // This test uses a virtual time. So do not panic if you saw this test runs faster than it should be. + Flux.from( retryLogic.retryRx( source ) ) + .elapsed() + .doOnNext( elapsed -> { if ( elapsed.getT2() == 0 ) elapsedList.add( elapsed.getT1() ); } ) + .map( Tuple2::getT2 ) ) + .thenAwait( Duration.ofSeconds( 2 ) ) + .expectNext( 0, 1 ) // first run + .expectNext( 0, 1, 0, 1, 0, 1, 0, 1 ) // 4 retry attempts + .verifyErrorSatisfies( e -> assertThat( e, equalTo( exception ) ) ); + + assertThat( elapsedList.size(), equalTo( 5 ) ); + assertThat( elapsedList.get( 0 ), equalTo( 0L ) ); + assertThat( elapsedList.get( 1 ), allOf( greaterThanOrEqualTo( 90L ), lessThanOrEqualTo( 110L ) ) ); + assertThat( elapsedList.get( 2 ), allOf( greaterThanOrEqualTo( 180L ), lessThanOrEqualTo( 220L ) ) ); + assertThat( elapsedList.get( 3 ), allOf( greaterThanOrEqualTo( 260L ), lessThanOrEqualTo( 440L ) ) ); + assertThat( elapsedList.get( 4 ), allOf( greaterThanOrEqualTo( 720L ), lessThanOrEqualTo( 880L ) ) ); + } + private static void retry( ExponentialBackoffRetryLogic retryLogic, final int times ) { retryLogic.retry( new Supplier() From 8a4303797b14e7cd6fc8b80364ca835eaba87d80 Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Mon, 1 Apr 2019 17:46:05 +0200 Subject: [PATCH 2/7] Split Sessions and tx. All sessions are now built on top of NetworkSession and all tx are built on top of ExplictTx. --- .../main/java/org/neo4j/driver/Session.java | 15 - .../internal/AbstractStatementRunner.java | 35 +- .../internal/DirectConnectionProvider.java | 2 +- .../neo4j/driver/internal/DriverFactory.java | 6 +- .../neo4j/driver/internal/InternalDriver.java | 12 +- .../driver/internal/InternalSession.java | 190 +++++++ .../driver/internal/InternalTransaction.java | 73 +++ .../neo4j/driver/internal/SessionFactory.java | 2 + .../driver/internal/SessionFactoryImpl.java | 5 +- .../async/AsyncAbstractStatementRunner.java | 58 +++ .../AsyncStatementResultCursor.java | 4 +- .../internal/async/ExplicitTransaction.java | 53 ++ .../internal/async/InternalAsyncSession.java | 216 ++++++++ .../async/InternalAsyncTransaction.java | 62 +++ .../InternalExplicitTransaction.java} | 90 ++-- .../InternalNetworkSession.java} | 479 ++++-------------- .../LeakLoggingNetworkSession.java | 11 +- .../driver/internal/async/NetworkSession.java | 54 ++ .../internal/async/ResultCursorsHolder.java | 2 +- .../{ => connection}/BoltProtocolUtil.java | 2 +- .../{ => connection}/BootstrapFactory.java | 2 +- .../{ => connection}/ChannelAttributes.java | 2 +- .../ChannelConnectedListener.java | 6 +- .../{ => connection}/ChannelConnector.java | 2 +- .../ChannelConnectorImpl.java | 2 +- .../ChannelPipelineBuilder.java | 2 +- .../ChannelPipelineBuilderImpl.java | 2 +- .../{ => connection}/DecoratedConnection.java | 2 +- .../{ => connection}/DirectConnection.java | 4 +- .../EventLoopGroupFactory.java | 2 +- .../HandshakeCompletedListener.java | 2 +- .../{ => connection}/HandshakeHandler.java | 6 +- .../NettyChannelInitializer.java | 8 +- .../{ => connection}/RoutingConnection.java | 2 +- .../async/inbound/ChannelErrorHandler.java | 4 +- .../async/inbound/InboundMessageHandler.java | 2 +- .../outbound/ChunkAwareByteBufOutput.java | 6 +- .../outbound/OutboundMessageHandler.java | 2 +- .../async/pool/ConnectionPoolImpl.java | 4 +- .../async/pool/NettyChannelHealthChecker.java | 6 +- .../internal/async/pool/NettyChannelPool.java | 2 +- .../async/pool/NettyChannelTracker.java | 2 +- .../cluster/RoutingProcedureRunner.java | 2 +- .../cluster/loadbalancing/LoadBalancer.java | 4 +- .../cursor/AsyncResultCursorOnlyFactory.java | 4 +- .../cursor/InternalStatementResultCursor.java | 2 +- .../InternalStatementResultCursorFactory.java | 4 +- .../cursor/RxStatementResultCursor.java | 2 +- .../cursor/StatementResultCursorFactory.java | 2 +- .../ChannelReleasingResetResponseHandler.java | 2 +- .../handlers/HelloResponseHandler.java | 4 +- .../handlers/InitResponseHandler.java | 2 +- .../internal/handlers/PullHandlers.java | 4 +- .../TransactionPullAllResponseHandler.java | 6 +- .../pulln/TransactionPullResponseHandler.java | 6 +- .../logging/ChannelActivityLogger.java | 2 +- .../internal/messaging/BoltProtocol.java | 18 +- .../internal/messaging/v1/BoltProtocolV1.java | 8 +- .../internal/messaging/v3/BoltProtocolV3.java | 8 +- .../internal/messaging/v4/BoltProtocolV4.java | 12 +- .../internal/metrics/MetricsListener.java | 2 +- .../internal/reactive/InternalRxResult.java | 5 +- .../internal/reactive/InternalRxSession.java | 31 +- .../reactive/InternalRxTransaction.java | 22 +- .../neo4j/driver/internal/util/Futures.java | 2 +- .../java/org/neo4j/driver/ParametersTest.java | 11 +- ...essionAsyncIT.java => AsyncSessionIT.java} | 2 +- ...onAsyncIT.java => AsyncTransactionIT.java} | 3 +- .../neo4j/driver/integration/BookmarkIT.java | 12 - .../integration/CausalClusteringIT.java | 26 - .../integration/ConnectionHandlingIT.java | 2 +- .../integration/ExplicitTransactionIT.java | 156 +++--- .../driver/integration/SessionBoltV3IT.java | 88 ++-- ...etworkSessionIT.java => SessionMixIT.java} | 94 +++- .../integration/TransactionBoltV3IT.java | 21 +- .../integration/UnsupportedBoltV3IT.java | 18 +- .../DirectConnectionProviderTest.java | 2 +- .../driver/internal/DriverFactoryTest.java | 6 +- ...sionTest.java => InternalSessionTest.java} | 433 ++++++---------- .../internal/InternalStatementResultTest.java | 1 + .../internal/InternalTransactionTest.java | 162 ++++++ .../internal/RoutingDriverBoltKitTest.java | 24 - .../internal/SessionFactoryImplTest.java | 20 +- .../AsyncStatementResultCursorTest.java | 16 +- .../async/InternalAsyncSessionTest.java | 395 +++++++++++++++ .../async/InternalAsyncTransactionTest.java | 155 ++++++ .../InternalExplicitTransactionTest.java} | 106 ++-- .../async/InternalNetworkSessionTest.java | 459 +++++++++++++++++ ...eakLoggingInternalNetworkSessionTest.java} | 17 +- .../async/ResultCursorsHolderTest.java | 1 - .../BoltProtocolUtilTest.java | 14 +- .../ChannelAttributesTest.java | 34 +- .../ChannelConnectedListenerTest.java | 4 +- .../ChannelConnectorImplIT.java | 10 +- .../ChannelErrorHandlerTest.java | 6 +- .../ChannelPipelineBuilderImplTest.java | 2 +- .../DecoratedConnectionTest.java | 4 +- .../DirectConnectionTest.java | 6 +- .../EventLoopGroupFactoryTest.java | 2 +- .../HandshakeCompletedListenerTest.java | 8 +- .../HandshakeHandlerTest.java | 16 +- .../NettyChannelInitializerTest.java | 8 +- .../RoutingConnectionTest.java | 4 +- .../inbound/InboundMessageHandlerTest.java | 2 +- .../outbound/OutboundMessageHandlerTest.java | 2 +- .../async/pool/ConnectionPoolImplIT.java | 6 +- .../pool/NettyChannelHealthCheckerTest.java | 6 +- .../async/pool/NettyChannelPoolIT.java | 4 +- .../async/pool/NettyChannelTrackerTest.java | 6 +- .../loadbalancing/LoadBalancerTest.java | 2 +- .../AsyncResultCursorOnlyFactoryTest.java | 4 +- ...ernalStatementResultCursorFactoryTest.java | 4 +- .../cursor/RxStatementResultCursorTest.java | 2 +- ...nnelReleasingResetResponseHandlerTest.java | 2 +- .../handlers/HelloResponseHandlerTest.java | 6 +- .../handlers/InitResponseHandlerTest.java | 4 +- ...TransactionPullAllResponseHandlerTest.java | 8 +- .../TransactionPullResponseHandlerTest.java | 6 +- .../logging/ChannelActivityLoggerTest.java | 2 +- .../internal/messaging/BoltProtocolTest.java | 2 +- .../internal/messaging/MessageFormatTest.java | 8 +- .../messaging/v1/BoltProtocolV1Test.java | 18 +- .../messaging/v3/BoltProtocolV3Test.java | 20 +- .../messaging/v4/BoltProtocolV4Test.java | 6 +- .../reactive/InternalRxResultTest.java | 4 +- .../reactive/InternalRxSessionTest.java | 27 +- .../reactive/InternalRxTransactionTest.java | 12 +- .../DriverFactoryWithOneEventLoopThread.java | 2 +- .../driver/internal/util/FuturesTest.java | 2 +- .../util/MessageRecordingDriverFactory.java | 8 +- ...pelineBuilderWithFailingMessageFormat.java | 4 +- .../util/io/ChannelTrackingConnector.java | 2 +- .../util/io/ChannelTrackingDriverFactory.java | 4 +- ...DriverFactoryWithFailingMessageFormat.java | 4 +- .../neo4j/driver/util/DriverExtension.java | 69 +++ .../neo4j/driver/util/SessionExtension.java | 109 +--- .../java/org/neo4j/driver/util/TestUtil.java | 262 +++++++++- 137 files changed, 3106 insertions(+), 1468 deletions(-) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/InternalSession.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/InternalTransaction.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/AsyncAbstractStatementRunner.java rename driver/src/main/java/org/neo4j/driver/internal/{ => async}/AsyncStatementResultCursor.java (97%) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncSession.java create mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncTransaction.java rename driver/src/main/java/org/neo4j/driver/internal/{ExplicitTransaction.java => async/InternalExplicitTransaction.java} (82%) rename driver/src/main/java/org/neo4j/driver/internal/{NetworkSession.java => async/InternalNetworkSession.java} (51%) rename driver/src/main/java/org/neo4j/driver/internal/{ => async}/LeakLoggingNetworkSession.java (87%) create mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/BoltProtocolUtil.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/BootstrapFactory.java (96%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelAttributes.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelConnectedListener.java (92%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelConnector.java (94%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelConnectorImpl.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelPipelineBuilder.java (94%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/ChannelPipelineBuilderImpl.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/DecoratedConnection.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/DirectConnection.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/EventLoopGroupFactory.java (99%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/HandshakeCompletedListener.java (97%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/HandshakeHandler.java (96%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/NettyChannelInitializer.java (90%) rename driver/src/main/java/org/neo4j/driver/internal/async/{ => connection}/RoutingConnection.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/{reactive => }/cursor/AsyncResultCursorOnlyFactory.java (96%) rename driver/src/main/java/org/neo4j/driver/internal/{reactive => }/cursor/InternalStatementResultCursor.java (94%) rename driver/src/main/java/org/neo4j/driver/internal/{reactive => }/cursor/InternalStatementResultCursorFactory.java (96%) rename driver/src/main/java/org/neo4j/driver/internal/{reactive => }/cursor/RxStatementResultCursor.java (98%) rename driver/src/main/java/org/neo4j/driver/internal/{reactive => }/cursor/StatementResultCursorFactory.java (94%) rename driver/src/test/java/org/neo4j/driver/integration/{SessionAsyncIT.java => AsyncSessionIT.java} (99%) rename driver/src/test/java/org/neo4j/driver/integration/{TransactionAsyncIT.java => AsyncTransactionIT.java} (99%) rename driver/src/test/java/org/neo4j/driver/integration/{NetworkSessionIT.java => SessionMixIT.java} (62%) rename driver/src/test/java/org/neo4j/driver/internal/{messaging/v2/NetworkSessionTest.java => InternalSessionTest.java} (60%) create mode 100644 driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java rename driver/src/test/java/org/neo4j/driver/internal/{ => async}/AsyncStatementResultCursorTest.java (99%) create mode 100644 driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java rename driver/src/test/java/org/neo4j/driver/internal/{messaging/v2/ExplicitTransactionTest.java => async/InternalExplicitTransactionTest.java} (73%) create mode 100644 driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java rename driver/src/test/java/org/neo4j/driver/internal/{LeakLoggingNetworkSessionTest.java => async/LeakLoggingInternalNetworkSessionTest.java} (93%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/BoltProtocolUtilTest.java (80%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/ChannelAttributesTest.java (76%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/ChannelConnectedListenerTest.java (95%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/ChannelConnectorImplIT.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/ChannelErrorHandlerTest.java (95%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/ChannelPipelineBuilderImplTest.java (97%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/DecoratedConnectionTest.java (99%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/DirectConnectionTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/EventLoopGroupFactoryTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/HandshakeCompletedListenerTest.java (95%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/HandshakeHandlerTest.java (97%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/NettyChannelInitializerTest.java (95%) rename driver/src/test/java/org/neo4j/driver/internal/async/{ => connection}/RoutingConnectionTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{reactive => }/cursor/AsyncResultCursorOnlyFactoryTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{reactive => }/cursor/InternalStatementResultCursorFactoryTest.java (98%) rename driver/src/test/java/org/neo4j/driver/internal/{reactive => }/cursor/RxStatementResultCursorTest.java (99%) create mode 100644 driver/src/test/java/org/neo4j/driver/util/DriverExtension.java diff --git a/driver/src/main/java/org/neo4j/driver/Session.java b/driver/src/main/java/org/neo4j/driver/Session.java index 99cb719eb7..d65fbf3c23 100644 --- a/driver/src/main/java/org/neo4j/driver/Session.java +++ b/driver/src/main/java/org/neo4j/driver/Session.java @@ -19,7 +19,6 @@ package org.neo4j.driver; import java.util.Map; -import java.util.function.Consumer; import org.neo4j.driver.async.AsyncSession; import org.neo4j.driver.util.Resource; @@ -85,20 +84,6 @@ public interface Session extends Resource, StatementRunner */ Transaction beginTransaction( TransactionConfig config ); - /** - * Begin a new explicit {@linkplain Transaction transaction}, - * requiring that the server hosting is at least as up-to-date as the - * transaction referenced by the supplied bookmark. - * - * @param bookmark a reference to a previous transaction - * @return a new {@link Transaction} - * @deprecated This method is deprecated in favour of {@link Driver#session(Consumer)} that accepts an initial - * bookmark. Session will ensure that all nested transactions are chained with bookmarks to guarantee - * causal consistency. This method will be removed in the next major release. - */ - @Deprecated - Transaction beginTransaction( String bookmark ); - /** * Execute given unit of work in a {@link AccessMode#READ read} transaction. *

diff --git a/driver/src/main/java/org/neo4j/driver/internal/AbstractStatementRunner.java b/driver/src/main/java/org/neo4j/driver/internal/AbstractStatementRunner.java index 6ee3d75dff..59abbf0101 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/AbstractStatementRunner.java +++ b/driver/src/main/java/org/neo4j/driver/internal/AbstractStatementRunner.java @@ -19,22 +19,19 @@ package org.neo4j.driver.internal; import java.util.Map; -import java.util.concurrent.CompletionStage; -import org.neo4j.driver.async.AsyncStatementRunner; -import org.neo4j.driver.internal.types.InternalTypeSystem; -import org.neo4j.driver.internal.util.Extract; -import org.neo4j.driver.internal.value.MapValue; import org.neo4j.driver.Record; import org.neo4j.driver.Statement; import org.neo4j.driver.StatementResult; -import org.neo4j.driver.async.StatementResultCursor; import org.neo4j.driver.StatementRunner; import org.neo4j.driver.Value; import org.neo4j.driver.Values; +import org.neo4j.driver.internal.types.InternalTypeSystem; +import org.neo4j.driver.internal.util.Extract; +import org.neo4j.driver.internal.value.MapValue; import org.neo4j.driver.types.TypeSystem; -public abstract class AbstractStatementRunner implements StatementRunner, AsyncStatementRunner +public abstract class AbstractStatementRunner implements StatementRunner { @Override public final StatementResult run( String statementTemplate, Value parameters ) @@ -42,48 +39,24 @@ public final StatementResult run( String statementTemplate, Value parameters ) return run( new Statement( statementTemplate, parameters ) ); } - @Override - public final CompletionStage runAsync( String statementTemplate, Value parameters ) - { - return runAsync( new Statement( statementTemplate, parameters ) ); - } - @Override public final StatementResult run( String statementTemplate, Map statementParameters ) { return run( statementTemplate, parameters( statementParameters ) ); } - @Override - public final CompletionStage runAsync( String statementTemplate, Map statementParameters ) - { - return runAsync( statementTemplate, parameters( statementParameters ) ); - } - @Override public final StatementResult run( String statementTemplate, Record statementParameters ) { return run( statementTemplate, parameters( statementParameters ) ); } - @Override - public final CompletionStage runAsync( String statementTemplate, Record statementParameters ) - { - return runAsync( statementTemplate, parameters( statementParameters ) ); - } - @Override public final StatementResult run( String statementText ) { return run( statementText, Values.EmptyMap ); } - @Override - public final CompletionStage runAsync( String statementText ) - { - return runAsync( statementText, Values.EmptyMap ); - } - @Override public final TypeSystem typeSystem() { diff --git a/driver/src/main/java/org/neo4j/driver/internal/DirectConnectionProvider.java b/driver/src/main/java/org/neo4j/driver/internal/DirectConnectionProvider.java index dfdcccbbcc..329414c0b5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DirectConnectionProvider.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DirectConnectionProvider.java @@ -21,7 +21,7 @@ import java.util.concurrent.CompletionStage; import org.neo4j.driver.AccessMode; -import org.neo4j.driver.internal.async.DecoratedConnection; +import org.neo4j.driver.internal.async.connection.DecoratedConnection; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.spi.ConnectionProvider; diff --git a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java index e6872a6127..e83a167103 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java @@ -26,9 +26,9 @@ import java.net.URI; import java.security.GeneralSecurityException; -import org.neo4j.driver.internal.async.BootstrapFactory; -import org.neo4j.driver.internal.async.ChannelConnector; -import org.neo4j.driver.internal.async.ChannelConnectorImpl; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; +import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl; import org.neo4j.driver.internal.async.pool.PoolSettings; import org.neo4j.driver.internal.cluster.DnsResolver; diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java index b625a6bdbf..9880cf2da4 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java @@ -29,6 +29,8 @@ import org.neo4j.driver.Session; import org.neo4j.driver.SessionParametersTemplate; import org.neo4j.driver.async.AsyncSession; +import org.neo4j.driver.internal.async.InternalAsyncSession; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.metrics.MetricsProvider; import org.neo4j.driver.internal.reactive.InternalRxSession; import org.neo4j.driver.internal.security.SecurityPlan; @@ -57,7 +59,7 @@ public class InternalDriver implements Driver @Override public Session session() { - return newSession( SessionParameters.empty() ); + return new InternalSession( newSession( SessionParameters.empty() ) ); } @Override @@ -65,7 +67,7 @@ public Session session( Consumer templateConsumer ) { SessionParameters.Template template = SessionParameters.template(); templateConsumer.accept( template ); - return newSession( template.build() ); + return new InternalSession( newSession( template.build() ) ); } @Override @@ -85,7 +87,7 @@ public RxSession rxSession( Consumer templateConsumer @Override public AsyncSession asyncSession() { - return newSession( SessionParameters.empty() ); + return new InternalAsyncSession( newSession( SessionParameters.empty() ) ); } @Override @@ -93,7 +95,7 @@ public AsyncSession asyncSession( Consumer templateCo { SessionParameters.Template template = SessionParameters.template(); templateConsumer.accept( template ); - return newSession( template.build() ); + return new InternalAsyncSession( newSession( template.build() ) ); } @Override @@ -148,7 +150,7 @@ private static RuntimeException driverCloseException() return new IllegalStateException( "This driver instance has already been closed" ); } - private NetworkSession newSession( SessionParameters parameters ) + public NetworkSession newSession( SessionParameters parameters ) { assertOpen(); NetworkSession session = sessionFactory.newInstance( parameters ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java b/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java new file mode 100644 index 0000000000..5c937c8fa4 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalSession.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal; + +import java.util.Map; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Session; +import org.neo4j.driver.Statement; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.TransactionWork; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.Futures; + +import static java.util.Collections.emptyMap; + +public class InternalSession extends AbstractStatementRunner implements Session +{ + private final NetworkSession session; + + public InternalSession( NetworkSession session ) + { + this.session = session; + } + + @Override + public StatementResult run( Statement statement ) + { + return run( statement, TransactionConfig.empty() ); + } + + @Override + public StatementResult run( String statement, TransactionConfig config ) + { + return run( statement, emptyMap(), config ); + } + + @Override + public StatementResult run( String statement, Map parameters, TransactionConfig config ) + { + return run( new Statement( statement, parameters ), config ); + } + + @Override + public StatementResult run( Statement statement, TransactionConfig config ) + { + StatementResultCursor cursor = Futures.blockingGet( session.runAsync( statement, config, false ), + () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while running query in session" ) ); + + // query executed, it is safe to obtain a connection in a blocking way + Connection connection = Futures.getNow( session.connectionAsync() ); + return new InternalStatementResult( connection, cursor ); + } + + @Override + public boolean isOpen() + { + return session.isOpen(); + } + + @Override + public void close() + { + Futures.blockingGet( session.closeAsync(), () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while closing the session" ) ); + } + + @Override + public Transaction beginTransaction() + { + return beginTransaction( TransactionConfig.empty() ); + } + + @Override + public Transaction beginTransaction( TransactionConfig config ) + { + ExplicitTransaction tx = Futures.blockingGet( session.beginTransactionAsync( config ), + () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while starting a transaction" ) ); + return new InternalTransaction( tx ); + } + + @Override + public T readTransaction( TransactionWork work ) + { + return readTransaction( work, TransactionConfig.empty() ); + } + + @Override + public T readTransaction( TransactionWork work, TransactionConfig config ) + { + return transaction( AccessMode.READ, work, config ); + } + + @Override + public T writeTransaction( TransactionWork work ) + { + return writeTransaction( work, TransactionConfig.empty() ); + } + + @Override + public T writeTransaction( TransactionWork work, TransactionConfig config ) + { + return transaction( AccessMode.WRITE, work, config ); + } + + @Override + public String lastBookmark() + { + return session.lastBookmark(); + } + + @Override + @SuppressWarnings( "deprecation" ) + public void reset() + { + Futures.blockingGet( session.resetAsync(), () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while resetting the session" ) ); + } + + private T transaction( AccessMode mode, TransactionWork work, TransactionConfig config ) + { + // use different code path compared to async so that work is executed in the caller thread + // caller thread will also be the one who sleeps between retries; + // it is unsafe to execute retries in the event loop threads because this can cause a deadlock + // event loop thread will bock and wait for itself to read some data + return session.retryLogic().retry( () -> { + try ( Transaction tx = beginTransaction( mode, config ) ) + { + try + { + T result = work.execute( tx ); + tx.success(); + return result; + } + catch ( Throwable t ) + { + // mark transaction for failure if the given unit of work threw exception + // this will override any success marks that were made by the unit of work + tx.failure(); + throw t; + } + } + } ); + } + + private Transaction beginTransaction( AccessMode mode, TransactionConfig config ) + { + ExplicitTransaction tx = Futures.blockingGet( session.beginTransactionAsync( mode, config ), + () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while starting a transaction" ) ); + return new InternalTransaction( tx ); + } + + private void terminateConnectionOnThreadInterrupt( String reason ) + { + // try to get current connection if it has been acquired + Connection connection = null; + try + { + connection = Futures.getNow( session.connectionAsync() ); + } + catch ( Throwable ignore ) + { + // ignore errors because handing interruptions is best effort + } + + if ( connection != null ) + { + connection.terminateAndRelease( reason ); + } + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/InternalTransaction.java new file mode 100644 index 0000000000..60d26b6947 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalTransaction.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal; + +import org.neo4j.driver.Statement; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.util.Futures; + +public class InternalTransaction extends AbstractStatementRunner implements Transaction +{ + private final ExplicitTransaction tx; + public InternalTransaction( ExplicitTransaction tx ) + { + this.tx = tx; + } + + @Override + public void success() + { + tx.success(); + } + + @Override + public void failure() + { + tx.failure(); + } + + @Override + public void close() + { + Futures.blockingGet( tx.closeAsync(), + () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while closing the transaction" ) ); + } + + @Override + public StatementResult run( Statement statement ) + { + StatementResultCursor cursor = Futures.blockingGet( tx.runAsync( statement, false ), + () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while running query in transaction" ) ); + return new InternalStatementResult( tx.connection(), cursor ); + } + + @Override + public boolean isOpen() + { + return tx.isOpen(); + } + + private void terminateConnectionOnThreadInterrupt( String reason ) + { + tx.connection().terminateAndRelease( reason ); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/SessionFactory.java b/driver/src/main/java/org/neo4j/driver/internal/SessionFactory.java index d2f2657bd8..2863980644 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/SessionFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/SessionFactory.java @@ -20,6 +20,8 @@ import java.util.concurrent.CompletionStage; +import org.neo4j.driver.internal.async.NetworkSession; + public interface SessionFactory { NetworkSession newInstance( SessionParameters parameters ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java index 5cb16520d7..acecfdd640 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java @@ -23,6 +23,9 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Config; import org.neo4j.driver.Logging; +import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.ConnectionProvider; @@ -77,6 +80,6 @@ private NetworkSession createSession( ConnectionProvider connectionProvider, Ret { return leakedSessionsLoggingEnabled ? new LeakLoggingNetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ) - : new NetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ); + : new InternalNetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/AsyncAbstractStatementRunner.java b/driver/src/main/java/org/neo4j/driver/internal/async/AsyncAbstractStatementRunner.java new file mode 100644 index 0000000000..d2a7df9ea4 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/AsyncAbstractStatementRunner.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import java.util.Map; +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.Record; +import org.neo4j.driver.Statement; +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; +import org.neo4j.driver.async.AsyncStatementRunner; +import org.neo4j.driver.async.StatementResultCursor; + +import static org.neo4j.driver.internal.AbstractStatementRunner.parameters; + +public abstract class AsyncAbstractStatementRunner implements AsyncStatementRunner +{ + @Override + public final CompletionStage runAsync( String statementTemplate, Value parameters ) + { + return runAsync( new Statement( statementTemplate, parameters ) ); + } + + @Override + public final CompletionStage runAsync( String statementTemplate, Map statementParameters ) + { + return runAsync( statementTemplate, parameters( statementParameters ) ); + } + + @Override + public final CompletionStage runAsync( String statementTemplate, Record statementParameters ) + { + return runAsync( statementTemplate, parameters( statementParameters ) ); + } + + @Override + public final CompletionStage runAsync( String statementText ) + { + return runAsync( statementText, Values.EmptyMap ); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/AsyncStatementResultCursor.java b/driver/src/main/java/org/neo4j/driver/internal/async/AsyncStatementResultCursor.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/AsyncStatementResultCursor.java rename to driver/src/main/java/org/neo4j/driver/internal/async/AsyncStatementResultCursor.java index 9b306aa207..dd82c5f2cd 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/AsyncStatementResultCursor.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/AsyncStatementResultCursor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -27,7 +27,7 @@ import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.util.Futures; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; import org.neo4j.driver.Record; import org.neo4j.driver.exceptions.NoSuchRecordException; import org.neo4j.driver.summary.ResultSummary; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java new file mode 100644 index 0000000000..8765188393 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.spi.Connection; + +public interface ExplicitTransaction +{ + CompletionStage beginAsync( Bookmarks initialBookmarks, TransactionConfig config ); + + CompletionStage runAsync( Statement statement, boolean waitForRunResponse ); + + CompletionStage runRx( Statement statement ); + + void success(); + + void failure(); + + CompletionStage commitAsync(); + + CompletionStage rollbackAsync(); + + boolean isOpen(); + + CompletionStage closeAsync(); + + void markTerminated(); + + Connection connection(); +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncSession.java new file mode 100644 index 0000000000..7dc5837fba --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncSession.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.AsyncSession; +import org.neo4j.driver.async.AsyncTransaction; +import org.neo4j.driver.async.AsyncTransactionWork; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.internal.util.Futures; + +import static java.util.Collections.emptyMap; +import static org.neo4j.driver.internal.util.Futures.completedWithNull; +import static org.neo4j.driver.internal.util.Futures.failedFuture; + +public class InternalAsyncSession extends AsyncAbstractStatementRunner implements AsyncSession +{ + private final NetworkSession session; + + public InternalAsyncSession( NetworkSession session ) + { + this.session = session; + } + + @Override + public CompletionStage runAsync( Statement statement ) + { + return runAsync( statement, TransactionConfig.empty() ); + } + + @Override + public CompletionStage runAsync( String statement, TransactionConfig config ) + { + return runAsync( statement, emptyMap(), config ); + } + + @Override + public CompletionStage runAsync( String statement, Map parameters, TransactionConfig config ) + { + return runAsync( new Statement( statement, parameters ), config ); + } + + @Override + public CompletionStage runAsync( Statement statement, TransactionConfig config ) + { + return session.runAsync( statement, config, true ); + } + + @Override + public CompletionStage closeAsync() + { + return session.closeAsync(); + } + + @Override + public CompletionStage beginTransactionAsync() + { + return beginTransactionAsync( TransactionConfig.empty() ); + } + + @Override + public CompletionStage beginTransactionAsync( TransactionConfig config ) + { + return session.beginTransactionAsync( config ).thenApply( InternalAsyncTransaction::new ); + } + + @Override + public CompletionStage readTransactionAsync( AsyncTransactionWork> work ) + { + return readTransactionAsync( work, TransactionConfig.empty() ); + } + + @Override + public CompletionStage readTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) + { + return transactionAsync( AccessMode.READ, work, config ); + } + + @Override + public CompletionStage writeTransactionAsync( AsyncTransactionWork> work ) + { + return writeTransactionAsync( work, TransactionConfig.empty() ); + } + + @Override + public CompletionStage writeTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) + { + return transactionAsync( AccessMode.WRITE, work, config ); + } + + @Override + public String lastBookmark() + { + return session.lastBookmark(); + } + + private CompletionStage transactionAsync( AccessMode mode, AsyncTransactionWork> work, TransactionConfig config ) + { + return session.retryLogic().retryAsync( () -> { + CompletableFuture resultFuture = new CompletableFuture<>(); + CompletionStage txFuture = session.beginTransactionAsync( mode, config ); + + txFuture.whenComplete( ( tx, completionError ) -> { + Throwable error = Futures.completionExceptionCause( completionError ); + if ( error != null ) + { + resultFuture.completeExceptionally( error ); + } + else + { + executeWork( resultFuture, tx, work ); + } + } ); + + return resultFuture; + } ); + } + + private void executeWork( CompletableFuture resultFuture, ExplicitTransaction tx, AsyncTransactionWork> work ) + { + CompletionStage workFuture = safeExecuteWork( tx, work ); + workFuture.whenComplete( ( result, completionError ) -> { + Throwable error = Futures.completionExceptionCause( completionError ); + if ( error != null ) + { + rollbackTxAfterFailedTransactionWork( tx, resultFuture, error ); + } + else + { + closeTxAfterSucceededTransactionWork( tx, resultFuture, result ); + } + } ); + } + + private CompletionStage safeExecuteWork( ExplicitTransaction tx, AsyncTransactionWork> work ) + { + // given work might fail in both async and sync way + // async failure will result in a failed future being returned + // sync failure will result in an exception being thrown + try + { + CompletionStage result = work.execute( new InternalAsyncTransaction( tx ) ); + + // protect from given transaction function returning null + return result == null ? completedWithNull() : result; + } + catch ( Throwable workError ) + { + // work threw an exception, wrap it in a future and proceed + return failedFuture( workError ); + } + } + + private void rollbackTxAfterFailedTransactionWork( ExplicitTransaction tx, CompletableFuture resultFuture, Throwable error ) + { + if ( tx.isOpen() ) + { + tx.rollbackAsync().whenComplete( ( ignore, rollbackError ) -> { + if ( rollbackError != null ) + { + error.addSuppressed( rollbackError ); + } + resultFuture.completeExceptionally( error ); + } ); + } + else + { + resultFuture.completeExceptionally( error ); + } + } + + private void closeTxAfterSucceededTransactionWork( ExplicitTransaction tx, CompletableFuture resultFuture, T result ) + { + if ( tx.isOpen() ) + { + tx.success(); + tx.closeAsync().whenComplete( ( ignore, completionError ) -> { + Throwable commitError = Futures.completionExceptionCause( completionError ); + if ( commitError != null ) + { + resultFuture.completeExceptionally( commitError ); + } + else + { + resultFuture.complete( result ); + } + } ); + } + else + { + resultFuture.complete( result ); + } + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncTransaction.java new file mode 100644 index 0000000000..75582f6bf4 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncTransaction.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.Statement; +import org.neo4j.driver.async.AsyncTransaction; +import org.neo4j.driver.async.StatementResultCursor; + +public class InternalAsyncTransaction extends AsyncAbstractStatementRunner implements AsyncTransaction +{ + private final ExplicitTransaction tx; + public InternalAsyncTransaction( ExplicitTransaction tx ) + { + this.tx = tx; + } + + @Override + public CompletionStage commitAsync() + { + return tx.commitAsync(); + } + + @Override + public CompletionStage rollbackAsync() + { + return tx.rollbackAsync(); + } + + @Override + public CompletionStage runAsync( Statement statement ) + { + return tx.runAsync( statement, true ); + } + + public void markTerminated() + { + tx.markTerminated(); + } + + public boolean isOpen() + { + return tx.isOpen(); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/ExplicitTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java similarity index 82% rename from driver/src/main/java/org/neo4j/driver/internal/ExplicitTransaction.java rename to driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java index 30efd30958..0a700ed948 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/ExplicitTransaction.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java @@ -16,31 +16,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; -import org.neo4j.driver.async.AsyncTransaction; -import org.neo4j.driver.internal.async.ResultCursorsHolder; -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.util.Futures; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; import org.neo4j.driver.Session; import org.neo4j.driver.Statement; -import org.neo4j.driver.StatementResult; -import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.Transaction; import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.StatementResultCursor; import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.BookmarksHolder; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.messaging.BoltProtocol; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.Futures; import static org.neo4j.driver.internal.util.Futures.completedWithNull; import static org.neo4j.driver.internal.util.Futures.failedFuture; -public class ExplicitTransaction extends AbstractStatementRunner implements Transaction, AsyncTransaction +public class InternalExplicitTransaction implements ExplicitTransaction { private enum State { @@ -73,7 +71,7 @@ private enum State private volatile State state = State.ACTIVE; - public ExplicitTransaction( Connection connection, BookmarksHolder bookmarksHolder ) + public InternalExplicitTransaction( Connection connection, BookmarksHolder bookmarksHolder ) { this.connection = connection; this.protocol = connection.protocol(); @@ -81,6 +79,7 @@ public ExplicitTransaction( Connection connection, BookmarksHolder bookmarksHold this.resultCursors = new ResultCursorsHolder(); } + @Override public CompletionStage beginAsync( Bookmarks initialBookmarks, TransactionConfig config ) { return protocol.beginTransaction( connection, initialBookmarks, config ) @@ -115,13 +114,7 @@ public void failure() } @Override - public void close() - { - Futures.blockingGet( closeAsync(), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while closing the transaction" ) ); - } - - CompletionStage closeAsync() + public CompletionStage closeAsync() { if ( state == State.MARKED_SUCCESS ) { @@ -176,29 +169,16 @@ else if ( state == State.ROLLED_BACK ) } @Override - public StatementResult run( Statement statement ) - { - StatementResultCursor cursor = Futures.blockingGet( run( statement, false ), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while running query in transaction" ) ); - return new InternalStatementResult( connection, cursor ); - } - - @Override - public CompletionStage runAsync( Statement statement ) - { - //noinspection unchecked - return (CompletionStage) run( statement, true ); - } - - private CompletionStage run( Statement statement, boolean waitForRunResponse ) + public CompletionStage runAsync( Statement statement, boolean waitForRunResponse ) { ensureCanRunQueries(); CompletionStage cursorStage = protocol.runInExplicitTransaction( connection, statement, this, waitForRunResponse ).asyncResult(); resultCursors.add( cursorStage ); - return cursorStage; + return cursorStage.thenApply( cursor -> cursor ); } + @Override public CompletionStage runRx( Statement statement ) { ensureCanRunQueries(); @@ -208,6 +188,24 @@ public CompletionStage runRx( Statement statement ) return cursorStage; } + @Override + public boolean isOpen() + { + return state != State.COMMITTED && state != State.ROLLED_BACK; + } + + @Override + public void markTerminated() + { + state = State.TERMINATED; + } + + @Override + public Connection connection() + { + return connection; + } + private void ensureCanRunQueries() { if ( state == State.COMMITTED ) @@ -221,26 +219,15 @@ else if ( state == State.ROLLED_BACK ) else if ( state == State.MARKED_FAILED ) { throw new ClientException( "Cannot run more statements in this transaction, it has been marked for failure. " + - "Please either rollback or close this transaction" ); + "Please either rollback or close this transaction" ); } else if ( state == State.TERMINATED ) { throw new ClientException( "Cannot run more statements in this transaction, " + - "it has either experienced an fatal error or was explicitly terminated" ); + "it has either experienced an fatal error or was explicitly terminated" ); } } - @Override - public boolean isOpen() - { - return state != State.COMMITTED && state != State.ROLLED_BACK; - } - - public void markTerminated() - { - state = State.TERMINATED; - } - private CompletionStage doCommitAsync() { if ( state == State.TERMINATED ) @@ -285,9 +272,4 @@ private void transactionClosed( boolean isCommitted ) } connection.release(); // release in background } - - private void terminateConnectionOnThreadInterrupt( String reason ) - { - connection.terminateAndRelease( reason ); - } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java similarity index 51% rename from driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java rename to driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java index cd5c3b1e29..ff1d30251d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java @@ -16,10 +16,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; -import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; @@ -27,32 +25,25 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import org.neo4j.driver.Session; import org.neo4j.driver.Statement; -import org.neo4j.driver.StatementResult; -import org.neo4j.driver.Transaction; import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.TransactionWork; -import org.neo4j.driver.async.AsyncSession; -import org.neo4j.driver.async.AsyncTransaction; -import org.neo4j.driver.async.AsyncTransactionWork; import org.neo4j.driver.async.StatementResultCursor; import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.BookmarksHolder; +import org.neo4j.driver.internal.FailableCursor; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.logging.PrefixedLogger; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.internal.util.Futures; -import static java.util.Collections.emptyMap; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.neo4j.driver.internal.util.Futures.completedWithNull; -import static org.neo4j.driver.internal.util.Futures.failedFuture; -public class NetworkSession extends AbstractStatementRunner implements Session, AsyncSession +public class InternalNetworkSession implements NetworkSession { private static final String LOG_NAME = "Session"; @@ -69,8 +60,8 @@ public class NetworkSession extends AbstractStatementRunner implements Session, private final AtomicBoolean open = new AtomicBoolean( true ); - public NetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, BookmarksHolder bookmarksHolder, - Logging logging ) + public InternalNetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, + BookmarksHolder bookmarksHolder, Logging logging ) { this.connectionProvider = connectionProvider; this.mode = mode; @@ -81,196 +72,65 @@ public NetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLo } @Override - public StatementResult run( Statement statement ) + public CompletionStage runAsync( Statement statement, TransactionConfig config, boolean waitForRunResponse ) { - return run( statement, TransactionConfig.empty() ); - } - - @Override - public StatementResult run( String statement, TransactionConfig config ) - { - return run( statement, emptyMap(), config ); - } - - @Override - public StatementResult run( String statement, Map parameters, TransactionConfig config ) - { - return run( new Statement( statement, parameters ), config ); - } - - @Override - public StatementResult run( Statement statement, TransactionConfig config ) - { - StatementResultCursor cursor = Futures.blockingGet( run( statement, config, false ), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while running query in session" ) ); - - // query executed, it is safe to obtain a connection in a blocking way - Connection connection = Futures.getNow( connectionStage ); - return new InternalStatementResult( connection, cursor ); - } - - @Override - public CompletionStage runAsync( Statement statement ) - { - return runAsync( statement, TransactionConfig.empty() ); - } + CompletionStage newResultCursorStage = + buildResultCursorFactory( statement, config, waitForRunResponse ).thenCompose( StatementResultCursorFactory::asyncResult ); - @Override - public CompletionStage runAsync( String statement, TransactionConfig config ) - { - return runAsync( statement, emptyMap(), config ); + resultCursorStage = newResultCursorStage.exceptionally( error -> null ); + return newResultCursorStage.thenApply( cursor -> cursor ); // convert the return type } @Override - public CompletionStage runAsync( String statement, Map parameters, TransactionConfig config ) + public CompletionStage runRx( Statement statement, TransactionConfig config ) { - return runAsync( new Statement( statement, parameters ), config ); - } + CompletionStage newResultCursorStage = + buildResultCursorFactory( statement, config, true ).thenCompose( StatementResultCursorFactory::rxResult ); - @Override - public CompletionStage runAsync( Statement statement, TransactionConfig config ) - { - //noinspection unchecked - return (CompletionStage) run( statement, config, true ); + resultCursorStage = newResultCursorStage.exceptionally( error -> null ); + return newResultCursorStage; } @Override - public boolean isOpen() + public CompletionStage beginTransactionAsync( TransactionConfig config ) { - return open.get(); + return this.beginTransactionAsync( mode, config ); } @Override - public void close() + public CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ) { - Futures.blockingGet( closeAsync(), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while closing the session" ) ); - } + ensureSessionIsOpen(); - @Override - public CompletionStage closeAsync() - { - if ( open.compareAndSet( true, false ) ) - { - return resultCursorStage.thenCompose( cursor -> - { - if ( cursor != null ) - { - // there exists a cursor with potentially unconsumed error, try to extract and propagate it - return cursor.failureAsync(); - } - // no result cursor exists so no error exists - return completedWithNull(); - } ).thenCompose( cursorError -> closeTransactionAndReleaseConnection().thenApply( txCloseError -> - { - // now we have cursor error, active transaction has been closed and connection has been released - // back to the pool; try to propagate cursor and transaction close errors, if any - CompletionException combinedError = Futures.combineErrors( cursorError, txCloseError ); - if ( combinedError != null ) + // create a chain that acquires connection and starts a transaction + CompletionStage newTransactionStage = ensureNoOpenTxBeforeStartingTx() + .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) + .thenCompose( connection -> { - throw combinedError; - } - return null; - } ) ); - } - return completedWithNull(); - } - - @Override - public Transaction beginTransaction() - { - return beginTransaction( TransactionConfig.empty() ); - } - - @Override - public Transaction beginTransaction( TransactionConfig config ) - { - return beginTransaction( mode, config ); - } - - @Deprecated - @Override - public Transaction beginTransaction( String bookmark ) - { - bookmarksHolder.setBookmarks( Bookmarks.from( bookmark ) ); - return beginTransaction(); - } - - @Override - public CompletionStage beginTransactionAsync() - { - return beginTransactionAsync( TransactionConfig.empty() ); - } - - @Override - public CompletionStage beginTransactionAsync( TransactionConfig config ) - { - //noinspection unchecked - return (CompletionStage) beginTransactionAsync( mode, config ); - } - - @Override - public T readTransaction( TransactionWork work ) - { - return readTransaction( work, TransactionConfig.empty() ); - } - - @Override - public T readTransaction( TransactionWork work, TransactionConfig config ) - { - return transaction( AccessMode.READ, work, config ); - } - - @Override - public CompletionStage readTransactionAsync( AsyncTransactionWork> work ) - { - return readTransactionAsync( work, TransactionConfig.empty() ); - } - - @Override - public CompletionStage readTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) - { - return transactionAsync( AccessMode.READ, work, config ); - } - - @Override - public T writeTransaction( TransactionWork work ) - { - return writeTransaction( work, TransactionConfig.empty() ); - } - - @Override - public T writeTransaction( TransactionWork work, TransactionConfig config ) - { - return transaction( AccessMode.WRITE, work, config ); - } + ExplicitTransaction tx = new InternalExplicitTransaction( connection, bookmarksHolder ); + return tx.beginAsync( bookmarksHolder.getBookmarks(), config ); + } ); - @Override - public CompletionStage writeTransactionAsync( AsyncTransactionWork> work ) - { - return writeTransactionAsync( work, TransactionConfig.empty() ); - } + // update the reference to the only known transaction + CompletionStage currentTransactionStage = transactionStage; - @Override - public CompletionStage writeTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) - { - return transactionAsync( AccessMode.WRITE, work, config ); - } + transactionStage = newTransactionStage + .exceptionally( error -> null ) // ignore errors from starting new transaction + .thenCompose( tx -> + { + if ( tx == null ) + { + // failed to begin new transaction, keep reference to the existing one + return currentTransactionStage; + } + // new transaction started, keep reference to it + return completedFuture( tx ); + } ); - @Override - public String lastBookmark() - { - return bookmarksHolder.lastBookmark(); + return newTransactionStage; } @Override - @SuppressWarnings( "deprecation" ) - public void reset() - { - Futures.blockingGet( resetAsync(), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while resetting the session" ) ); - } - public CompletionStage resetAsync() { return existingTransactionOrNull() @@ -293,168 +153,80 @@ public CompletionStage resetAsync() } ); } - CompletionStage currentConnectionIsOpen() - { - return connectionStage.handle( ( connection, error ) -> - error == null && // no acquisition error - connection != null && // some connection has actually been acquired - connection.isOpen() ); // and it's still open - } - - private T transaction( AccessMode mode, TransactionWork work, TransactionConfig config ) + @Override + public RetryLogic retryLogic() { - // use different code path compared to async so that work is executed in the caller thread - // caller thread will also be the one who sleeps between retries; - // it is unsafe to execute retries in the event loop threads because this can cause a deadlock - // event loop thread will bock and wait for itself to read some data - return retryLogic.retry( () -> - { - try ( Transaction tx = beginTransaction( mode, config ) ) - { - try - { - T result = work.execute( tx ); - tx.success(); - return result; - } - catch ( Throwable t ) - { - // mark transaction for failure if the given unit of work threw exception - // this will override any success marks that were made by the unit of work - tx.failure(); - throw t; - } - } - } ); + return retryLogic; } - private CompletionStage transactionAsync( AccessMode mode, AsyncTransactionWork> work, TransactionConfig config ) + @Override + public String lastBookmark() { - return retryLogic.retryAsync( () -> - { - CompletableFuture resultFuture = new CompletableFuture<>(); - CompletionStage txFuture = beginTransactionAsync( mode, config ); - - txFuture.whenComplete( ( tx, completionError ) -> - { - Throwable error = Futures.completionExceptionCause( completionError ); - if ( error != null ) - { - resultFuture.completeExceptionally( error ); - } - else - { - executeWork( resultFuture, tx, work ); - } - } ); - - return resultFuture; - } ); + return bookmarksHolder.lastBookmark(); } - private void executeWork( CompletableFuture resultFuture, ExplicitTransaction tx, - AsyncTransactionWork> work ) + @Override + public CompletionStage releaseConnectionAsync() { - CompletionStage workFuture = safeExecuteWork( tx, work ); - workFuture.whenComplete( ( result, completionError ) -> + return connectionStage.thenCompose( connection -> { - Throwable error = Futures.completionExceptionCause( completionError ); - if ( error != null ) - { - rollbackTxAfterFailedTransactionWork( tx, resultFuture, error ); - } - else + if ( connection != null ) { - closeTxAfterSucceededTransactionWork( tx, resultFuture, result ); + // there exists connection, try to release it back to the pool + return connection.release(); } + // no connection so return null + return completedWithNull(); } ); } - private CompletionStage safeExecuteWork( ExplicitTransaction tx, AsyncTransactionWork> work ) + @Override + public CompletionStage connectionAsync() { - // given work might fail in both async and sync way - // async failure will result in a failed future being returned - // sync failure will result in an exception being thrown - try - { - CompletionStage result = work.execute( tx ); - - // protect from given transaction function returning null - return result == null ? completedWithNull() : result; - } - catch ( Throwable workError ) - { - // work threw an exception, wrap it in a future and proceed - return failedFuture( workError ); - } + return connectionStage; } - private void rollbackTxAfterFailedTransactionWork( ExplicitTransaction tx, CompletableFuture resultFuture, - Throwable error ) + @Override + public boolean isOpen() { - if ( tx.isOpen() ) - { - tx.rollbackAsync().whenComplete( ( ignore, rollbackError ) -> - { - if ( rollbackError != null ) - { - error.addSuppressed( rollbackError ); - } - resultFuture.completeExceptionally( error ); - } ); - } - else - { - resultFuture.completeExceptionally( error ); - } + return open.get(); } - private void closeTxAfterSucceededTransactionWork( ExplicitTransaction tx, CompletableFuture resultFuture, - T result ) + @Override + public CompletionStage closeAsync() { - if ( tx.isOpen() ) + if ( open.compareAndSet( true, false ) ) { - tx.success(); - tx.closeAsync().whenComplete( ( ignore, completionError ) -> + return resultCursorStage.thenCompose( cursor -> { - Throwable commitError = Futures.completionExceptionCause( completionError ); - if ( commitError != null ) + if ( cursor != null ) { - resultFuture.completeExceptionally( commitError ); + // there exists a cursor with potentially unconsumed error, try to extract and propagate it + return cursor.failureAsync(); } - else + // no result cursor exists so no error exists + return completedWithNull(); + } ).thenCompose( cursorError -> closeTransactionAndReleaseConnection().thenApply( txCloseError -> + { + // now we have cursor error, active transaction has been closed and connection has been released + // back to the pool; try to propagate cursor and transaction close errors, if any + CompletionException combinedError = Futures.combineErrors( cursorError, txCloseError ); + if ( combinedError != null ) { - resultFuture.complete( result ); + throw combinedError; } - } ); - } - else - { - resultFuture.complete( result ); + return null; + } ) ); } + return completedWithNull(); } - public CompletionStage runRx( Statement statement, TransactionConfig config ) - { - CompletionStage newResultCursorStage = - buildResultCursorFactory( statement, config, true ).thenCompose( StatementResultCursorFactory::rxResult ); - - resultCursorStage = newResultCursorStage.exceptionally( error -> null ); - return newResultCursorStage; - } - - public RetryLogic retryLogic() - { - return retryLogic; - } - - private CompletionStage run( Statement statement, TransactionConfig config, boolean waitForRunResponse ) + protected CompletionStage currentConnectionIsOpen() { - CompletionStage newResultCursorStage = - buildResultCursorFactory( statement, config, waitForRunResponse ).thenCompose( StatementResultCursorFactory::asyncResult ); - - resultCursorStage = newResultCursorStage.exceptionally( error -> null ); - return newResultCursorStage; + return connectionStage.handle( ( connection, error ) -> + error == null && // no acquisition error + connection != null && // some connection has actually been acquired + connection.isOpen() ); // and it's still open } private CompletionStage buildResultCursorFactory( Statement statement, TransactionConfig config, boolean waitForRunResponse ) @@ -477,44 +249,6 @@ private CompletionStage buildResultCursorFactory( } ); } - private Transaction beginTransaction( AccessMode mode, TransactionConfig config ) - { - return Futures.blockingGet( beginTransactionAsync( mode, config ), - () -> terminateConnectionOnThreadInterrupt( "Thread interrupted while starting a transaction" ) ); - } - - private CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ) - { - ensureSessionIsOpen(); - - // create a chain that acquires connection and starts a transaction - CompletionStage newTransactionStage = ensureNoOpenTxBeforeStartingTx() - .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) - .thenCompose( connection -> - { - ExplicitTransaction tx = new ExplicitTransaction( connection, bookmarksHolder ); - return tx.beginAsync( bookmarksHolder.getBookmarks(), config ); - } ); - - // update the reference to the only known transaction - CompletionStage currentTransactionStage = transactionStage; - - transactionStage = newTransactionStage - .exceptionally( error -> null ) // ignore errors from starting new transaction - .thenCompose( tx -> - { - if ( tx == null ) - { - // failed to begin new transaction, keep reference to the existing one - return currentTransactionStage; - } - // new transaction started, keep reference to it - return completedFuture( tx ); - } ); - - return newTransactionStage; - } - private CompletionStage acquireConnection( String databaseName, AccessMode mode ) { CompletionStage currentConnectionStage = connectionStage; @@ -574,40 +308,7 @@ private CompletionStage closeTransactionAndReleaseConnection() return completedWithNull(); } ).thenCompose( txCloseError -> // then release the connection and propagate transaction close error, if any - releaseConnection().thenApply( ignore -> txCloseError ) ); - } - - public CompletionStage releaseConnection() - { - return connectionStage.thenCompose( connection -> - { - if ( connection != null ) - { - // there exists connection, try to release it back to the pool - return connection.release(); - } - // no connection so return null - return completedWithNull(); - } ); - } - - private void terminateConnectionOnThreadInterrupt( String reason ) - { - // try to get current connection if it has been acquired - Connection connection = null; - try - { - connection = Futures.getNow( connectionStage ); - } - catch ( Throwable ignore ) - { - // ignore errors because handing interruptions is best effort - } - - if ( connection != null ) - { - connection.terminateAndRelease( reason ); - } + releaseConnectionAsync().thenApply( ignore -> txCloseError ) ); } private CompletionStage ensureNoOpenTxBeforeRunningQuery() diff --git a/driver/src/main/java/org/neo4j/driver/internal/LeakLoggingNetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java similarity index 87% rename from driver/src/main/java/org/neo4j/driver/internal/LeakLoggingNetworkSession.java rename to driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java index 3a93085aaa..82e5914074 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/LeakLoggingNetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java @@ -16,21 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Logging; +import org.neo4j.driver.internal.BookmarksHolder; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.internal.util.Futures; -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.Logging; import static java.lang.System.lineSeparator; -class LeakLoggingNetworkSession extends NetworkSession +public class LeakLoggingNetworkSession extends InternalNetworkSession { private final String stackTrace; - LeakLoggingNetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, + public LeakLoggingNetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, BookmarksHolder bookmarksHolder, Logging logging ) { super( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java new file mode 100644 index 0000000000..e8c90e62e3 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.retry.RetryLogic; +import org.neo4j.driver.internal.spi.Connection; + +public interface NetworkSession +{ + CompletionStage runAsync( Statement statement, TransactionConfig config, boolean waitForRunResponse ); + + CompletionStage runRx( Statement statement, TransactionConfig config ); + + CompletionStage beginTransactionAsync( TransactionConfig config ); + + CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ); + + CompletionStage resetAsync(); + + RetryLogic retryLogic(); + + String lastBookmark(); + + CompletionStage releaseConnectionAsync(); + + CompletionStage connectionAsync(); + + boolean isOpen(); + + CompletionStage closeAsync(); +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ResultCursorsHolder.java b/driver/src/main/java/org/neo4j/driver/internal/async/ResultCursorsHolder.java index 2353b13013..319f576fac 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ResultCursorsHolder.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/ResultCursorsHolder.java @@ -38,7 +38,7 @@ public void add( CompletionStage cursorStage ) cursorStages.add( cursorStage ); } - public CompletionStage retrieveNotConsumedError() + CompletionStage retrieveNotConsumedError() { CompletableFuture[] failures = retrieveAllFailures(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/BoltProtocolUtil.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/async/BoltProtocolUtil.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java index 1e9001b37a..8602180966 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/BoltProtocolUtil.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.buffer.ByteBuf; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/BootstrapFactory.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BootstrapFactory.java similarity index 96% rename from driver/src/main/java/org/neo4j/driver/internal/async/BootstrapFactory.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/BootstrapFactory.java index 73d355d222..3262fba331 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/BootstrapFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BootstrapFactory.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelOption; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelAttributes.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelAttributes.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java index 628c99d57f..784f82b645 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelAttributes.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.Channel; import io.netty.util.AttributeKey; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectedListener.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListener.java similarity index 92% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectedListener.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListener.java index 164bd06f88..3a11804401 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectedListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListener.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -31,8 +31,8 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException; import static java.lang.String.format; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.handshakeBuf; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.handshakeString; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.handshakeBuf; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.handshakeString; public class ChannelConnectedListener implements ChannelFutureListener { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnector.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnector.java similarity index 94% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnector.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnector.java index aaff221c92..6ebf0b42d3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnector.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectorImpl.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectorImpl.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java index cbd257991c..76bde81a0b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelConnectorImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilder.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilder.java similarity index 94% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilder.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilder.java index 4fb537e678..9c7c2d9726 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilder.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilder.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelPipeline; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImpl.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImpl.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImpl.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImpl.java index ea7149e0a0..98535fdbef 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImpl.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelPipeline; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/DecoratedConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DecoratedConnection.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/async/DecoratedConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/DecoratedConnection.java index 5b52915795..29cacaed80 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/DecoratedConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DecoratedConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import java.util.concurrent.CompletionStage; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/DirectConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/async/DirectConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java index b50c5a1338..70820796d0 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/DirectConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.Channel; import io.netty.channel.pool.ChannelPool; @@ -40,7 +40,7 @@ import org.neo4j.driver.internal.util.ServerVersion; import static java.util.Collections.emptyMap; -import static org.neo4j.driver.internal.async.ChannelAttributes.setTerminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setTerminationReason; public class DirectConnection implements Connection { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/EventLoopGroupFactory.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactory.java similarity index 99% rename from driver/src/main/java/org/neo4j/driver/internal/async/EventLoopGroupFactory.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactory.java index c4fb71531e..2592f10ed3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/EventLoopGroupFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactory.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/HandshakeCompletedListener.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java similarity index 97% rename from driver/src/main/java/org/neo4j/driver/internal/async/HandshakeCompletedListener.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java index 72a14a90f9..a66c603b11 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/HandshakeCompletedListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/HandshakeHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeHandler.java similarity index 96% rename from driver/src/main/java/org/neo4j/driver/internal/async/HandshakeHandler.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeHandler.java index 637a0fe55d..cd1af387c7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/HandshakeHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeHandler.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -37,8 +37,8 @@ import org.neo4j.driver.exceptions.SecurityException; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.HTTP; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.NO_PROTOCOL_VERSION; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.HTTP; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.NO_PROTOCOL_VERSION; public class HandshakeHandler extends ReplayingDecoder { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NettyChannelInitializer.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializer.java similarity index 90% rename from driver/src/main/java/org/neo4j/driver/internal/async/NettyChannelInitializer.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializer.java index 859d3f5c9e..74ec34be5a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/NettyChannelInitializer.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; @@ -32,9 +32,9 @@ import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.Logging; -import static org.neo4j.driver.internal.async.ChannelAttributes.setCreationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setCreationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAddress; public class NettyChannelInitializer extends ChannelInitializer { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/RoutingConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/async/RoutingConnection.java rename to driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java index a6531629a5..16528529a3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/RoutingConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import java.util.concurrent.CompletionStage; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/inbound/ChannelErrorHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/inbound/ChannelErrorHandler.java index 3f1fc146c6..c80345df4d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/inbound/ChannelErrorHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/inbound/ChannelErrorHandler.java @@ -31,8 +31,8 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException; import static java.util.Objects.requireNonNull; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.terminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.terminationReason; public class ChannelErrorHandler extends ChannelInboundHandlerAdapter { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandler.java index 842e4373d3..5bd30ae175 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandler.java @@ -30,7 +30,7 @@ import static io.netty.buffer.ByteBufUtil.hexDump; import static java.util.Objects.requireNonNull; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; public class InboundMessageHandler extends SimpleChannelInboundHandler { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/outbound/ChunkAwareByteBufOutput.java b/driver/src/main/java/org/neo4j/driver/internal/async/outbound/ChunkAwareByteBufOutput.java index 79d31cb6be..d65d944ce1 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/outbound/ChunkAwareByteBufOutput.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/outbound/ChunkAwareByteBufOutput.java @@ -20,12 +20,12 @@ import io.netty.buffer.ByteBuf; -import org.neo4j.driver.internal.async.BoltProtocolUtil; +import org.neo4j.driver.internal.async.connection.BoltProtocolUtil; import org.neo4j.driver.internal.packstream.PackOutput; import static java.util.Objects.requireNonNull; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.CHUNK_HEADER_SIZE_BYTES; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.DEFAULT_MAX_OUTBOUND_CHUNK_SIZE_BYTES; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.CHUNK_HEADER_SIZE_BYTES; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.DEFAULT_MAX_OUTBOUND_CHUNK_SIZE_BYTES; public class ChunkAwareByteBufOutput implements PackOutput { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandler.java b/driver/src/main/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandler.java index 44b1e70436..97c189fd6f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandler.java @@ -25,7 +25,7 @@ import java.util.List; -import org.neo4j.driver.internal.async.BoltProtocolUtil; +import org.neo4j.driver.internal.async.connection.BoltProtocolUtil; import org.neo4j.driver.internal.logging.ChannelActivityLogger; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java b/driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java index 709c348c6e..8a1dd9be72 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java @@ -34,8 +34,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.ChannelConnector; -import org.neo4j.driver.internal.async.DirectConnection; +import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.async.connection.DirectConnection; import org.neo4j.driver.internal.metrics.ListenerEvent; import org.neo4j.driver.internal.metrics.MetricsListener; import org.neo4j.driver.internal.spi.Connection; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthChecker.java b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthChecker.java index 2d18fd998a..12fb6a465b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthChecker.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthChecker.java @@ -29,9 +29,9 @@ import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import static org.neo4j.driver.internal.async.ChannelAttributes.creationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.lastUsedTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.creationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.lastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; public class NettyChannelHealthChecker implements ChannelHealthChecker { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelPool.java b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelPool.java index 8ca45ff774..8168c09fda 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelPool.java @@ -25,7 +25,7 @@ import io.netty.channel.pool.FixedChannelPool; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.metrics.ListenerEvent; import static java.util.Objects.requireNonNull; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelTracker.java b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelTracker.java index d0f789dccc..949b604965 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelTracker.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelTracker.java @@ -36,7 +36,7 @@ import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress; public class NettyChannelTracker implements ChannelPoolHandler { diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java index 4b86a751d0..db688e9918 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java @@ -24,7 +24,7 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.async.DecoratedConnection; +import org.neo4j.driver.internal.async.connection.DecoratedConnection; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.util.ServerVersion; diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java index 466679f052..93adf8fd6b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java @@ -25,8 +25,8 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.RoutingErrorHandler; -import org.neo4j.driver.internal.async.DecoratedConnection; -import org.neo4j.driver.internal.async.RoutingConnection; +import org.neo4j.driver.internal.async.connection.DecoratedConnection; +import org.neo4j.driver.internal.async.connection.RoutingConnection; import org.neo4j.driver.internal.cluster.AddressSet; import org.neo4j.driver.internal.cluster.ClusterComposition; import org.neo4j.driver.internal.cluster.ClusterCompositionProvider; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactory.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactory.java similarity index 96% rename from driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactory.java rename to driver/src/main/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactory.java index 4a4f2d1b62..6d657afc7e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactory.java @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import java.util.concurrent.CompletionStage; -import org.neo4j.driver.internal.AsyncStatementResultCursor; +import org.neo4j.driver.internal.async.AsyncStatementResultCursor; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.Message; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursor.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursor.java similarity index 94% rename from driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursor.java rename to driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursor.java index e17ac81bbe..4560931daf 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursor.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import org.neo4j.driver.internal.FailableCursor; import org.neo4j.driver.async.StatementResultCursor; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactory.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactory.java similarity index 96% rename from driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactory.java rename to driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactory.java index 5ddf09941d..ab4fce646b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactory.java @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import java.util.concurrent.CompletionStage; -import org.neo4j.driver.internal.AsyncStatementResultCursor; +import org.neo4j.driver.internal.async.AsyncStatementResultCursor; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursor.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/RxStatementResultCursor.java similarity index 98% rename from driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursor.java rename to driver/src/main/java/org/neo4j/driver/internal/cursor/RxStatementResultCursor.java index 9eff8a906d..788d419b91 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursor.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/RxStatementResultCursor.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import org.reactivestreams.Subscription; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/StatementResultCursorFactory.java b/driver/src/main/java/org/neo4j/driver/internal/cursor/StatementResultCursorFactory.java similarity index 94% rename from driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/StatementResultCursorFactory.java rename to driver/src/main/java/org/neo4j/driver/internal/cursor/StatementResultCursorFactory.java index 6ab84663a9..200d362925 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/cursor/StatementResultCursorFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cursor/StatementResultCursorFactory.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import java.util.concurrent.CompletionStage; diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandler.java index 6d61bfb9f6..d96dfc4bf3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandler.java @@ -27,7 +27,7 @@ import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.util.Clock; -import static org.neo4j.driver.internal.async.ChannelAttributes.setLastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setLastUsedTimestamp; public class ChannelReleasingResetResponseHandler extends ResetResponseHandler { diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java index 13ca717886..ec088f9304 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java @@ -27,8 +27,8 @@ import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.Value; -import static org.neo4j.driver.internal.async.ChannelAttributes.setConnectionId; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion; import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion; public class HelloResponseHandler implements ResponseHandler diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/InitResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/InitResponseHandler.java index 4e6bb9dfcf..f03414bf3d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/InitResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/InitResponseHandler.java @@ -29,7 +29,7 @@ import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.Value; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion; import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion; public class InitResponseHandler implements ResponseHandler diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java index a3b6f2a120..fef1eac382 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java @@ -18,15 +18,15 @@ */ package org.neo4j.driver.internal.handlers; +import org.neo4j.driver.Statement; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; import org.neo4j.driver.internal.handlers.pulln.SessionPullResponseHandler; import org.neo4j.driver.internal.handlers.pulln.TransactionPullResponseHandler; import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.Statement; public class PullHandlers { diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandler.java index b9b7e8843e..c9ee224487 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandler.java @@ -20,11 +20,11 @@ import java.util.Map; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.util.MetadataExtractor; import org.neo4j.driver.Statement; import org.neo4j.driver.Value; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.MetadataExtractor; import static java.util.Objects.requireNonNull; diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandler.java index eb253abb99..5944d850cb 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandler.java @@ -20,12 +20,12 @@ import java.util.Map; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.Statement; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.async.ExplicitTransaction; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.MetadataExtractor; -import org.neo4j.driver.Statement; -import org.neo4j.driver.Value; import static java.util.Objects.requireNonNull; diff --git a/driver/src/main/java/org/neo4j/driver/internal/logging/ChannelActivityLogger.java b/driver/src/main/java/org/neo4j/driver/internal/logging/ChannelActivityLogger.java index 59f4dac4f2..d45b9e47a3 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/logging/ChannelActivityLogger.java +++ b/driver/src/main/java/org/neo4j/driver/internal/logging/ChannelActivityLogger.java @@ -21,7 +21,7 @@ import io.netty.channel.Channel; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java index 4cd63a1fbe..c34b1c0d82 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java @@ -24,23 +24,23 @@ import java.util.Map; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Session; +import org.neo4j.driver.Statement; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; -import org.neo4j.driver.Session; -import org.neo4j.driver.Statement; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import static org.neo4j.driver.internal.async.ChannelAttributes.protocolVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.protocolVersion; public interface BoltProtocol { diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java index add6cfcff5..b4d140c0e5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java @@ -31,7 +31,9 @@ import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.handlers.AbstractPullAllResponseHandler; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; @@ -46,15 +48,13 @@ import org.neo4j.driver.internal.messaging.request.InitMessage; import org.neo4j.driver.internal.messaging.request.PullAllMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.reactive.cursor.AsyncResultCursorOnlyFactory; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.util.MetadataExtractor; import static org.neo4j.driver.Values.ofValue; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.assertEmptyDatabaseName; public class BoltProtocolV1 implements BoltProtocol diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java index fa24cd708a..1fa439f571 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java @@ -30,7 +30,9 @@ import org.neo4j.driver.Value; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.handlers.AbstractPullAllResponseHandler; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; @@ -44,13 +46,11 @@ import org.neo4j.driver.internal.messaging.request.GoodbyeMessage; import org.neo4j.driver.internal.messaging.request.HelloMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; -import org.neo4j.driver.internal.reactive.cursor.AsyncResultCursorOnlyFactory; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.util.MetadataExtractor; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; import static org.neo4j.driver.internal.handlers.PullHandlers.newBoltV3PullAllHandler; import static org.neo4j.driver.internal.messaging.request.CommitMessage.COMMIT; import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.assertEmptyDatabaseName; diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4.java index 68f86ca5b4..9a2b33b20d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4.java @@ -18,11 +18,11 @@ */ package org.neo4j.driver.internal.messaging.v4; -import java.util.concurrent.CompletableFuture; - import org.neo4j.driver.Statement; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursorFactory; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.handlers.AbstractPullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; @@ -30,8 +30,6 @@ import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursorFactory; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.spi.Connection; import static org.neo4j.driver.internal.handlers.PullHandlers.newBoltV3PullAllHandler; @@ -52,9 +50,7 @@ public MessageFormat createMessageFormat() protected StatementResultCursorFactory buildResultCursorFactory( Connection connection, Statement statement, BookmarksHolder bookmarksHolder, ExplicitTransaction tx, RunWithMetadataMessage runMessage, boolean waitForRunResponse ) { - CompletableFuture runCompletedFuture = new CompletableFuture<>(); - - RunResponseHandler runHandler = new RunResponseHandler( runCompletedFuture, METADATA_EXTRACTOR ); + RunResponseHandler runHandler = new RunResponseHandler( METADATA_EXTRACTOR ); AbstractPullAllResponseHandler pullAllHandler = newBoltV3PullAllHandler( statement, runHandler, connection, bookmarksHolder, tx ); BasicPullResponseHandler pullHandler = newBoltV4PullHandler( statement, runHandler, connection, bookmarksHolder, tx ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/metrics/MetricsListener.java b/driver/src/main/java/org/neo4j/driver/internal/metrics/MetricsListener.java index 006a1e3434..0d5e9288c1 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/metrics/MetricsListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/metrics/MetricsListener.java @@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.DirectConnection; +import org.neo4j.driver.internal.async.connection.DirectConnection; import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl; import org.neo4j.driver.Config; diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxResult.java b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxResult.java index 7e136a56d5..d24fa9b45d 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxResult.java +++ b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxResult.java @@ -26,9 +26,9 @@ import java.util.function.Supplier; import org.neo4j.driver.Record; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.reactive.RxResult; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.summary.ResultSummary; public class InternalRxResult implements RxResult @@ -44,7 +44,8 @@ public InternalRxResult( Supplier> curs @Override public Publisher keys() { - return Flux.defer( () -> Mono.fromCompletionStage( getCursorFuture() ).flatMapIterable( RxStatementResultCursor::keys ).onErrorMap( Futures::completionExceptionCause ) ); + return Flux.defer( () -> Mono.fromCompletionStage( getCursorFuture() ) + .flatMapIterable( RxStatementResultCursor::keys ).onErrorMap( Futures::completionExceptionCause ) ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java index e2858c5f33..de1bf02abf 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java @@ -28,9 +28,8 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Statement; import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.NetworkSession; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxSession; @@ -67,7 +66,26 @@ public Publisher beginTransaction( TransactionConfig config ) session.beginTransactionAsync( config ).whenComplete( ( tx, completionError ) -> { if ( tx != null ) { - txFuture.complete( new InternalRxTransaction( (ExplicitTransaction) tx ) ); + txFuture.complete( new InternalRxTransaction( tx ) ); + } + else + { + releaseConnectionBeforeReturning( txFuture, completionError ); + } + } ); + return txFuture; + } ); + } + + private Publisher beginTransaction( AccessMode mode, TransactionConfig config ) + { + return createMono( () -> + { + CompletableFuture txFuture = new CompletableFuture<>(); + session.beginTransactionAsync( mode, config ).whenComplete( ( tx, completionError ) -> { + if ( tx != null ) + { + txFuture.complete( new InternalRxTransaction( tx ) ); } else { @@ -104,8 +122,7 @@ public Publisher writeTransaction( RxTransactionWork> work, private Publisher runTransaction( AccessMode mode, RxTransactionWork> work, TransactionConfig config ) { - // TODO read and write - Publisher publisher = beginTransaction( /*mode,*/ config ); + Publisher publisher = beginTransaction( mode, config ); Flux txResult = Mono.from( publisher ) .flatMapMany( tx -> Flux.from( work.execute( tx ) ) .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ) // if failed then we rollback and rethrow the error @@ -160,7 +177,7 @@ private void releaseConnectionBeforeReturning( CompletableFuture returnFu // The reason we need to release connection in session is that we do not have a `rxSession.close()`; // Otherwise, session.close shall handle everything for us. Throwable error = Futures.completionExceptionCause( completionError ); - session.releaseConnection().whenComplete( ( ignored, closeError ) -> + session.releaseConnectionAsync().whenComplete( ( ignored, closeError ) -> returnFuture.completeExceptionally( Futures.combineErrors( error, closeError ) ) ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxTransaction.java index d869601b81..7a5322a477 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxTransaction.java +++ b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxTransaction.java @@ -22,22 +22,22 @@ import java.util.concurrent.CompletableFuture; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.Statement; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxTransaction; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; -import org.neo4j.driver.Statement; import static org.neo4j.driver.internal.reactive.RxUtils.createEmptyPublisher; public class InternalRxTransaction extends AbstractRxStatementRunner implements RxTransaction { - private final ExplicitTransaction asyncTx; + private final ExplicitTransaction tx; - public InternalRxTransaction( ExplicitTransaction asyncTx ) + public InternalRxTransaction( ExplicitTransaction tx ) { - this.asyncTx = asyncTx; + this.tx = tx; } @Override @@ -45,7 +45,7 @@ public RxResult run( Statement statement ) { return new InternalRxResult( () -> { CompletableFuture cursorFuture = new CompletableFuture<>(); - asyncTx.runRx( statement ).whenComplete( ( cursor, completionError ) -> { + tx.runRx( statement ).whenComplete( ( cursor, completionError ) -> { if ( cursor != null ) { cursorFuture.complete( cursor ); @@ -54,9 +54,9 @@ public RxResult run( Statement statement ) { // We failed to create a result cursor so we cannot rely on result cursor to handle failure. // The logic here shall be the same as `TransactionPullResponseHandler#afterFailure` as that is where cursor handling failure - // This is optional as asyncTx still holds a reference to all cursor futures and they will be clean up properly in commit + // This is optional as tx still holds a reference to all cursor futures and they will be clean up properly in commit Throwable error = Futures.completionExceptionCause( completionError ); - asyncTx.markTerminated(); + tx.markTerminated(); cursorFuture.completeExceptionally( error ); } } ); @@ -81,11 +81,11 @@ private Publisher close( boolean commit ) return createEmptyPublisher( () -> { if ( commit ) { - return asyncTx.commitAsync(); + return tx.commitAsync(); } else { - return asyncTx.rollbackAsync(); + return tx.rollbackAsync(); } } ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/Futures.java b/driver/src/main/java/org/neo4j/driver/internal/util/Futures.java index d5d14b3165..e0a50d4a6b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/Futures.java +++ b/driver/src/main/java/org/neo4j/driver/internal/util/Futures.java @@ -26,7 +26,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; -import org.neo4j.driver.internal.async.EventLoopGroupFactory; +import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory; import static java.util.concurrent.CompletableFuture.completedFuture; diff --git a/driver/src/test/java/org/neo4j/driver/ParametersTest.java b/driver/src/test/java/org/neo4j/driver/ParametersTest.java index 7dff56e929..3b5793b9a7 100644 --- a/driver/src/test/java/org/neo4j/driver/ParametersTest.java +++ b/driver/src/test/java/org/neo4j/driver/ParametersTest.java @@ -24,12 +24,13 @@ import java.util.stream.Stream; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.DefaultBookmarksHolder; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.NetworkSession; +import org.neo4j.driver.internal.InternalSession; +import org.neo4j.driver.internal.async.InternalNetworkSession; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.ConnectionProvider; -import org.neo4j.driver.exceptions.ClientException; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -39,12 +40,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.mock; +import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue; import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue; import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue; -import static org.neo4j.driver.Values.parameters; class ParametersTest { @@ -107,6 +108,8 @@ private Session mockedSession() { ConnectionProvider provider = mock( ConnectionProvider.class ); RetryLogic retryLogic = mock( RetryLogic.class ); - return new NetworkSession( provider, retryLogic, ABSENT_DB_NAME, AccessMode.WRITE, new DefaultBookmarksHolder(), DEV_NULL_LOGGING ); + InternalNetworkSession session = + new InternalNetworkSession( provider, retryLogic, ABSENT_DB_NAME, AccessMode.WRITE, new DefaultBookmarksHolder(), DEV_NULL_LOGGING ); + return new InternalSession( session ); } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/SessionAsyncIT.java b/driver/src/test/java/org/neo4j/driver/integration/AsyncSessionIT.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/integration/SessionAsyncIT.java rename to driver/src/test/java/org/neo4j/driver/integration/AsyncSessionIT.java index 221328ad51..2c5bb64efe 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/SessionAsyncIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/AsyncSessionIT.java @@ -83,7 +83,7 @@ import static org.neo4j.driver.util.TestUtil.awaitAll; @ParallelizableIT -class SessionAsyncIT +class AsyncSessionIT { @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); diff --git a/driver/src/test/java/org/neo4j/driver/integration/TransactionAsyncIT.java b/driver/src/test/java/org/neo4j/driver/integration/AsyncTransactionIT.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/integration/TransactionAsyncIT.java rename to driver/src/test/java/org/neo4j/driver/integration/AsyncTransactionIT.java index beacf7d9bc..6faca07fab 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/TransactionAsyncIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/AsyncTransactionIT.java @@ -42,7 +42,6 @@ import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.NoSuchRecordException; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; import org.neo4j.driver.summary.ResultSummary; import org.neo4j.driver.summary.StatementType; import org.neo4j.driver.types.Node; @@ -69,7 +68,7 @@ import static org.neo4j.driver.util.TestUtil.await; @ParallelizableIT -class TransactionAsyncIT +class AsyncTransactionIT { @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); diff --git a/driver/src/test/java/org/neo4j/driver/integration/BookmarkIT.java b/driver/src/test/java/org/neo4j/driver/integration/BookmarkIT.java index a1b776ae8e..83e54d0665 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/BookmarkIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/BookmarkIT.java @@ -28,8 +28,6 @@ import org.neo4j.driver.Session; import org.neo4j.driver.Transaction; import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.TransientException; -import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; import org.neo4j.driver.util.ParallelizableIT; import org.neo4j.driver.util.SessionExtension; @@ -82,16 +80,6 @@ void shouldThrowForInvalidBookmark() } } - @SuppressWarnings( "deprecation" ) - @Test - void shouldThrowForUnreachableBookmark() - { - createNodeInTx( session ); - - TransientException e = assertThrows( TransientException.class, () -> session.beginTransaction( session.lastBookmark() + 42 ) ); - assertThat( e.getMessage(), startsWith( "Database not up to the requested version" ) ); - } - @Test void bookmarkRemainsAfterRolledBackTx() { diff --git a/driver/src/test/java/org/neo4j/driver/integration/CausalClusteringIT.java b/driver/src/test/java/org/neo4j/driver/integration/CausalClusteringIT.java index ddaa10fb0a..c38deecc1f 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/CausalClusteringIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/CausalClusteringIT.java @@ -56,7 +56,6 @@ import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; -import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.retry.RetrySettings; import org.neo4j.driver.internal.util.DisabledOnNeo4jWith; @@ -77,7 +76,6 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -317,30 +315,6 @@ void beginTransactionThrowsForInvalidBookmark() } } - @SuppressWarnings( "deprecation" ) - @Test - void beginTransactionThrowsForUnreachableBookmark() - { - ClusterMember leader = clusterRule.getCluster().leader(); - - try ( Driver driver = createDriver( leader.getBoltUri() ); - Session session = driver.session() ) - { - try ( Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE ()" ); - tx.success(); - } - - String bookmark = session.lastBookmark(); - assertNotNull( bookmark ); - String newBookmark = bookmark + "0"; - - TransientException e = assertThrows( TransientException.class, () -> session.beginTransaction( newBookmark ) ); - assertThat( e.getMessage(), startsWith( "Database not up to the requested version" ) ); - } - } - @Test void shouldHandleGracefulLeaderSwitch() throws Exception { diff --git a/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java b/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java index d85b9ac05f..863394a2ff 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java @@ -36,7 +36,7 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.DriverFactory; -import org.neo4j.driver.internal.async.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl; import org.neo4j.driver.internal.async.pool.PoolSettings; import org.neo4j.driver.internal.cluster.RoutingSettings; diff --git a/driver/src/test/java/org/neo4j/driver/integration/ExplicitTransactionIT.java b/driver/src/test/java/org/neo4j/driver/integration/ExplicitTransactionIT.java index 054ad28529..d51afb8bc2 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ExplicitTransactionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ExplicitTransactionIT.java @@ -31,15 +31,15 @@ import org.neo4j.driver.Config; import org.neo4j.driver.Driver; -import org.neo4j.driver.Session; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.async.AsyncSession; -import org.neo4j.driver.async.AsyncTransaction; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.async.StatementResultCursor; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.async.EventLoopGroupFactory; +import org.neo4j.driver.internal.InternalDriver; +import org.neo4j.driver.internal.SessionParameters; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.retry.RetrySettings; import org.neo4j.driver.internal.util.Clock; @@ -47,7 +47,6 @@ import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -57,13 +56,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; -import static org.neo4j.driver.internal.util.Matchers.blockingOperationInEventLoopError; import static org.neo4j.driver.util.TestUtil.await; -/** - * We leave the question whether we want to let {@link ExplicitTransaction} both implements blocking tx and async tx or not later. - * But as how it is right not, here are some tests for using mixes blocking and async API. - */ @ParallelizableIT class ExplicitTransactionIT @@ -71,12 +65,12 @@ class ExplicitTransactionIT @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); - private AsyncSession session; + private NetworkSession session; @BeforeEach void setUp() { - session = neo4j.driver().asyncSession(); + session = ((InternalDriver) neo4j.driver()).newSession( SessionParameters.empty() ); } @AfterEach @@ -85,35 +79,55 @@ void tearDown() session.closeAsync(); } + private ExplicitTransaction beginTransaction() + { + return beginTransaction( session ); + } + + private ExplicitTransaction beginTransaction( NetworkSession session ) + { + return await( session.beginTransactionAsync( TransactionConfig.empty() ) ); + } + + private StatementResultCursor sessionRun( NetworkSession session, Statement statement ) + { + return await( session.runAsync( statement, TransactionConfig.empty(), true ) ); + } + + private StatementResultCursor txRun( ExplicitTransaction tx, String statement ) + { + return await( tx.runAsync( new Statement( statement ), true ) ); + } + @Test void shouldDoNothingWhenCommittedSecondTime() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); assertNull( await( tx.commitAsync() ) ); assertTrue( tx.commitAsync().toCompletableFuture().isDone() ); - assertFalse( ((ExplicitTransaction) tx).isOpen() ); + assertFalse( tx.isOpen() ); } @Test void shouldFailToCommitAfterRollback() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); assertNull( await( tx.rollbackAsync() ) ); ClientException e = assertThrows( ClientException.class, () -> await( tx.commitAsync() ) ); assertEquals( "Can't commit, transaction has been rolled back", e.getMessage() ); - assertFalse( ((ExplicitTransaction) tx).isOpen() ); + assertFalse( tx.isOpen() ); } @Test void shouldFailToCommitAfterTermination() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); - ((ExplicitTransaction) tx).markTerminated(); + tx.markTerminated(); ClientException e = assertThrows( ClientException.class, () -> await( tx.commitAsync() ) ); assertThat( e.getMessage(), startsWith( "Transaction can't be committed" ) ); @@ -122,129 +136,89 @@ void shouldFailToCommitAfterTermination() @Test void shouldDoNothingWhenRolledBackSecondTime() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); assertNull( await( tx.rollbackAsync() ) ); assertTrue( tx.rollbackAsync().toCompletableFuture().isDone() ); - assertFalse( ((ExplicitTransaction) tx).isOpen() ); + assertFalse( tx.isOpen() ); } @Test void shouldFailToRollbackAfterCommit() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); assertNull( await( tx.commitAsync() ) ); ClientException e = assertThrows( ClientException.class, () -> await( tx.rollbackAsync() ) ); assertEquals( "Can't rollback, transaction has been committed", e.getMessage() ); - assertFalse( ((ExplicitTransaction) tx).isOpen() ); + assertFalse( tx.isOpen() ); } @Test void shouldRollbackAfterTermination() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); + ExplicitTransaction tx = beginTransaction(); - ((ExplicitTransaction) tx).markTerminated(); + tx.markTerminated(); assertNull( await( tx.rollbackAsync() ) ); - assertFalse( ((ExplicitTransaction) tx).isOpen() ); + assertFalse( tx.isOpen() ); } @Test void shouldFailToRunQueryWhenMarkedForFailure() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); - tx.runAsync( "CREATE (:MyLabel)" ); - ((ExplicitTransaction) tx).failure(); + ExplicitTransaction tx = beginTransaction(); + txRun( tx, "CREATE (:MyLabel)" ); + tx.failure(); - ClientException e = assertThrows( ClientException.class, () -> await( tx.runAsync( "CREATE (:MyOtherLabel)" ) ) ); + ClientException e = assertThrows( ClientException.class, () -> txRun( tx, "CREATE (:MyOtherLabel)" ) ); assertThat( e.getMessage(), startsWith( "Cannot run more statements in this transaction" ) ); } @Test void shouldFailToRunQueryWhenTerminated() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); - tx.runAsync( "CREATE (:MyLabel)" ); - ((ExplicitTransaction) tx).markTerminated(); + ExplicitTransaction tx = beginTransaction(); + txRun( tx, "CREATE (:MyLabel)" ); + tx.markTerminated(); - ClientException e = assertThrows( ClientException.class, () -> await( tx.runAsync( "CREATE (:MyOtherLabel)" ) ) ); + ClientException e = assertThrows( ClientException.class, () -> txRun( tx, "CREATE (:MyOtherLabel)" ) ); assertThat( e.getMessage(), startsWith( "Cannot run more statements in this transaction" ) ); } @Test void shouldAllowQueriesWhenMarkedForSuccess() { - AsyncTransaction tx = await( session.beginTransactionAsync() ); - tx.runAsync( "CREATE (:MyLabel)" ); + ExplicitTransaction tx = beginTransaction(); + txRun( tx, "CREATE (:MyLabel)" ); - ((ExplicitTransaction) tx).success(); + tx.success(); - tx.runAsync( "CREATE (:MyLabel)" ); + txRun( tx, "CREATE (:MyLabel)" ); assertNull( await( tx.commitAsync() ) ); - StatementResultCursor cursor = await( session.runAsync( "MATCH (n:MyLabel) RETURN count(n)" ) ); + StatementResultCursor cursor = sessionRun( session, new Statement( "MATCH (n:MyLabel) RETURN count(n)" ) ); assertEquals( 2, await( cursor.singleAsync() ).get( 0 ).asInt() ); } - @Test - void shouldFailToExecuteBlockingRunChainedWithAsyncTransaction() - { - CompletionStage result = session.beginTransactionAsync() - .thenApply( tx -> - { - if ( EventLoopGroupFactory.isEventLoopThread( Thread.currentThread() ) ) - { - IllegalStateException e = assertThrows( IllegalStateException.class, () -> ((ExplicitTransaction) tx).run( "CREATE ()" ) ); - assertThat( e, is( blockingOperationInEventLoopError() ) ); - } - return null; - } ); - - assertNull( await( result ) ); - } - - @Test - void shouldAllowUsingBlockingApiInCommonPoolWhenChaining() - { - CompletionStage txStage = session.beginTransactionAsync() - // move execution to ForkJoinPool.commonPool() - .thenApplyAsync( asyncTx -> - { - Transaction tx = (ExplicitTransaction) asyncTx; - tx.run( "UNWIND [1,1,2] AS x CREATE (:Node {id: x})" ); - tx.run( "CREATE (:Node {id: 42})" ); - tx.success(); - tx.close(); - return asyncTx; - } ); - - AsyncTransaction tx = await( txStage ); - - assertFalse( ((ExplicitTransaction) tx).isOpen() ); - assertEquals( 2, countNodes( 1 ) ); - assertEquals( 1, countNodes( 2 ) ); - assertEquals( 1, countNodes( 42 ) ); - } - @Test void shouldBePossibleToRunMoreTransactionsAfterOneIsTerminated() { - AsyncTransaction tx1 = await( session.beginTransactionAsync() ); - ((ExplicitTransaction) tx1).markTerminated(); + ExplicitTransaction tx1 = beginTransaction(); + tx1.markTerminated(); // commit should fail, make session forget about this transaction and release the connection to the pool ClientException e = assertThrows( ClientException.class, () -> await( tx1.commitAsync() ) ); assertThat( e.getMessage(), startsWith( "Transaction can't be committed" ) ); - await( session.beginTransactionAsync() - .thenCompose( tx -> tx.runAsync( "CREATE (:Node {id: 42})" ) + await( session.beginTransactionAsync( TransactionConfig.empty() ) + .thenCompose( tx -> tx.runAsync( new Statement( "CREATE (:Node {id: 42})" ), true ) .thenCompose( StatementResultCursor::consumeAsync ) .thenApply( ignore -> tx ) - ).thenCompose( AsyncTransaction::commitAsync ) ); + ).thenCompose( ExplicitTransaction::commitAsync ) ); assertEquals( 1, countNodes( 42 ) ); } @@ -263,7 +237,8 @@ void shouldPropagateRollbackFailureAfterFatalError() private int countNodes( Object id ) { - StatementResultCursor cursor = await( session.runAsync( "MATCH (n:Node {id: $id}) RETURN count(n)", parameters( "id", id ) ) ); + Statement statement = new Statement( "MATCH (n:Node {id: $id}) RETURN count(n)", parameters( "id", id ) ); + StatementResultCursor cursor = sessionRun( session, statement ); return await( cursor.singleAsync() ).get( 0 ).asInt(); } @@ -274,12 +249,12 @@ private void testCommitAndRollbackFailurePropagation( boolean commit ) try ( Driver driver = driverFactory.newInstance( neo4j.uri(), neo4j.authToken(), RoutingSettings.DEFAULT, RetrySettings.DEFAULT, config ) ) { - try ( Session session = driver.session() ) + NetworkSession session = ((InternalDriver) driver).newSession( SessionParameters.empty() ); { - Transaction tx = session.beginTransaction(); + ExplicitTransaction tx = beginTransaction( session ); // run query but do not consume the result - tx.run( "UNWIND range(0, 10000) AS x RETURN x + 1" ); + txRun( tx, "UNWIND range(0, 10000) AS x RETURN x + 1" ); IOException ioError = new IOException( "Connection reset by peer" ); for ( Channel channel : driverFactory.channels() ) @@ -290,8 +265,7 @@ private void testCommitAndRollbackFailurePropagation( boolean commit ) await( future ); } - AsyncTransaction asyncTx = (ExplicitTransaction) tx; - CompletionStage commitOrRollback = commit ? asyncTx.commitAsync() : asyncTx.rollbackAsync(); + CompletionStage commitOrRollback = commit ? tx.commitAsync() : tx.rollbackAsync(); // commit/rollback should fail and propagate the network error ServiceUnavailableException e = assertThrows( ServiceUnavailableException.class, () -> await( commitOrRollback ) ); diff --git a/driver/src/test/java/org/neo4j/driver/integration/SessionBoltV3IT.java b/driver/src/test/java/org/neo4j/driver/integration/SessionBoltV3IT.java index e3e5a319da..cddc6567e9 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/SessionBoltV3IT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/SessionBoltV3IT.java @@ -19,6 +19,7 @@ package org.neo4j.driver.integration; import io.netty.channel.Channel; +import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -29,6 +30,14 @@ import java.util.Map; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.AsyncSession; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.request.GoodbyeMessage; @@ -36,16 +45,9 @@ import org.neo4j.driver.internal.retry.RetrySettings; import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; import org.neo4j.driver.internal.util.MessageRecordingDriverFactory; -import org.neo4j.driver.Driver; -import org.neo4j.driver.Session; -import org.neo4j.driver.StatementResult; -import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.summary.ResultSummary; +import org.neo4j.driver.util.DriverExtension; import org.neo4j.driver.util.ParallelizableIT; -import org.neo4j.driver.util.SessionExtension; import static java.time.Duration.ofMillis; import static java.util.Arrays.asList; @@ -57,8 +59,8 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V3; import static org.neo4j.driver.Config.defaultConfig; +import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V3; import static org.neo4j.driver.util.TestUtil.await; @EnabledOnNeo4jWith( BOLT_V3 ) @@ -66,7 +68,7 @@ class SessionBoltV3IT { @RegisterExtension - static final SessionExtension session = new SessionExtension(); + static final DriverExtension driver = new DriverExtension(); @Test void shouldSetTransactionMetadata() @@ -81,7 +83,7 @@ void shouldSetTransactionMetadata() .build(); // call listTransactions procedure that should list itself with the specified metadata - StatementResult result = session.run( "CALL dbms.listTransactions()", config ); + StatementResult result = driver.session().run( "CALL dbms.listTransactions()", config ); Map receivedMetadata = result.single().get( "metaData" ).asMap(); assertEquals( metadata, receivedMetadata ); @@ -99,7 +101,8 @@ void shouldSetTransactionMetadataAsync() .build(); // call listTransactions procedure that should list itself with the specified metadata - CompletionStage> metadataFuture = session.runAsync( "CALL dbms.listTransactions()", config ) + CompletionStage> metadataFuture = driver.asyncSession() + .runAsync( "CALL dbms.listTransactions()", config ) .thenCompose( StatementResultCursor::singleAsync ) .thenApply( record -> record.get( "metaData" ).asMap() ); @@ -110,9 +113,10 @@ void shouldSetTransactionMetadataAsync() void shouldSetTransactionTimeout() { // create a dummy node + Session session = driver.session(); session.run( "CREATE (:Node)" ).consume(); - try ( Session otherSession = session.driver().session() ) + try ( Session otherSession = driver.driver().session() ) { try ( Transaction otherTx = otherSession.beginTransaction() ) { @@ -136,9 +140,10 @@ void shouldSetTransactionTimeout() void shouldSetTransactionTimeoutAsync() { // create a dummy node - session.run( "CREATE (:Node)" ).consume(); + AsyncSession asyncSession = driver.asyncSession(); + await( await( asyncSession.runAsync( "CREATE (:Node)" ) ).consumeAsync() ); - try ( Session otherSession = session.driver().session() ) + try ( Session otherSession = driver.driver().session() ) { try ( Transaction otherTx = otherSession.beginTransaction() ) { @@ -150,43 +155,44 @@ void shouldSetTransactionTimeoutAsync() .build(); // run a query in an auto-commit transaction with timeout and try to update the locked dummy node - CompletionStage resultFuture = session.runAsync( "MATCH (n:Node) SET n.prop = 2", config ) + CompletionStage resultFuture = asyncSession.runAsync( "MATCH (n:Node) SET n.prop = 2", config ) .thenCompose( StatementResultCursor::consumeAsync ); TransientException error = assertThrows( TransientException.class, () -> await( resultFuture ) ); - assertThat( error.getMessage(), containsString( "terminated" ) ); + MatcherAssert.assertThat( error.getMessage(), containsString( "terminated" ) ); } } } @Test - void shouldSetTransactionMetadataWithReadTransactionFunction() + void shouldSetTransactionMetadataWithAsyncReadTransactionFunction() { - testTransactionMetadataWithTransactionFunctions( true ); + testTransactionMetadataWithAsyncTransactionFunctions( true ); } @Test - void shouldSetTransactionMetadataWithWriteTransactionFunction() + void shouldSetTransactionMetadataWithAsyncWriteTransactionFunction() { - testTransactionMetadataWithTransactionFunctions( false ); + testTransactionMetadataWithAsyncTransactionFunctions( false ); } @Test - void shouldSetTransactionMetadataWithAsyncReadTransactionFunction() + void shouldSetTransactionMetadataWithReadTransactionFunction() { - testTransactionMetadataWithAsyncTransactionFunctions( true ); + testTransactionMetadataWithTransactionFunctions( true ); } @Test - void shouldSetTransactionMetadataWithAsyncWriteTransactionFunction() + void shouldSetTransactionMetadataWithWriteTransactionFunction() { - testTransactionMetadataWithAsyncTransactionFunctions( false ); + testTransactionMetadataWithTransactionFunctions( false ); } @Test void shouldUseBookmarksForAutoCommitTransactions() { + Session session = driver.session(); String initialBookmark = session.lastBookmark(); session.run( "CREATE ()" ).consume(); @@ -211,6 +217,7 @@ void shouldUseBookmarksForAutoCommitTransactions() @Test void shouldUseBookmarksForAutoCommitAndExplicitTransactions() { + Session session = driver.session(); String initialBookmark = session.lastBookmark(); try ( Transaction tx = session.beginTransaction() ) @@ -243,6 +250,7 @@ void shouldUseBookmarksForAutoCommitAndExplicitTransactions() @Test void shouldUseBookmarksForAutoCommitTransactionsAndTransactionFunctions() { + Session session = driver.session(); String initialBookmark = session.lastBookmark(); session.writeTransaction( tx -> tx.run( "CREATE ()" ) ); @@ -270,13 +278,13 @@ void shouldSendGoodbyeWhenClosingDriver() int txCount = 13; MessageRecordingDriverFactory driverFactory = new MessageRecordingDriverFactory(); - try ( Driver driver = driverFactory.newInstance( session.uri(), session.authToken(), RoutingSettings.DEFAULT, RetrySettings.DEFAULT, defaultConfig() ) ) + try ( Driver otherDriver = driverFactory.newInstance( driver.uri(), driver.authToken(), RoutingSettings.DEFAULT, RetrySettings.DEFAULT, defaultConfig() ) ) { List sessions = new ArrayList<>(); List txs = new ArrayList<>(); for ( int i = 0; i < txCount; i++ ) { - Session session = driver.session(); + Session session = otherDriver.session(); sessions.add( session ); Transaction tx = session.beginTransaction(); txs.add( tx ); @@ -305,8 +313,9 @@ void shouldSendGoodbyeWhenClosingDriver() } } - private static void testTransactionMetadataWithTransactionFunctions( boolean read ) + private static void testTransactionMetadataWithAsyncTransactionFunctions( boolean read ) { + AsyncSession asyncSession = driver.asyncSession(); Map metadata = new HashMap<>(); metadata.put( "foo", "bar" ); metadata.put( "baz", true ); @@ -317,16 +326,19 @@ private static void testTransactionMetadataWithTransactionFunctions( boolean rea .build(); // call listTransactions procedure that should list itself with the specified metadata - StatementResult result = read ? session.readTransaction( tx -> tx.run( "CALL dbms.listTransactions()" ), config ) - : session.writeTransaction( tx -> tx.run( "CALL dbms.listTransactions()" ), config ); + CompletionStage cursorFuture = + read ? asyncSession.readTransactionAsync( tx -> tx.runAsync( "CALL dbms.listTransactions()" ), config ) + : asyncSession.writeTransactionAsync( tx -> tx.runAsync( "CALL dbms.listTransactions()" ), config ); - Map receivedMetadata = result.single().get( "metaData" ).asMap(); + CompletionStage> metadataFuture = cursorFuture.thenCompose( StatementResultCursor::singleAsync ) + .thenApply( record -> record.get( "metaData" ).asMap() ); - assertEquals( metadata, receivedMetadata ); + assertEquals( metadata, await( metadataFuture ) ); } - private static void testTransactionMetadataWithAsyncTransactionFunctions( boolean read ) + private static void testTransactionMetadataWithTransactionFunctions( boolean read ) { + Session session = driver.session(); Map metadata = new HashMap<>(); metadata.put( "foo", "bar" ); metadata.put( "baz", true ); @@ -337,14 +349,12 @@ private static void testTransactionMetadataWithAsyncTransactionFunctions( boolea .build(); // call listTransactions procedure that should list itself with the specified metadata - CompletionStage cursorFuture = - read ? session.readTransactionAsync( tx -> tx.runAsync( "CALL dbms.listTransactions()" ), config ) - : session.writeTransactionAsync( tx -> tx.runAsync( "CALL dbms.listTransactions()" ), config ); + StatementResult result = read ? session.readTransaction( tx -> tx.run( "CALL dbms.listTransactions()" ), config ) + : session.writeTransaction( tx -> tx.run( "CALL dbms.listTransactions()" ), config ); - CompletionStage> metadataFuture = cursorFuture.thenCompose( StatementResultCursor::singleAsync ) - .thenApply( record -> record.get( "metaData" ).asMap() ); + Map receivedMetadata = result.single().get( "metaData" ).asMap(); - assertEquals( metadata, await( metadataFuture ) ); + assertEquals( metadata, receivedMetadata ); } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/NetworkSessionIT.java b/driver/src/test/java/org/neo4j/driver/integration/SessionMixIT.java similarity index 62% rename from driver/src/test/java/org/neo4j/driver/integration/NetworkSessionIT.java rename to driver/src/test/java/org/neo4j/driver/integration/SessionMixIT.java index c060bd1c70..70274c759f 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/NetworkSessionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/SessionMixIT.java @@ -28,12 +28,15 @@ import java.util.concurrent.CompletionStage; import org.neo4j.driver.Record; +import org.neo4j.driver.Session; +import org.neo4j.driver.Statement; import org.neo4j.driver.StatementResult; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.AsyncSession; +import org.neo4j.driver.async.AsyncTransaction; import org.neo4j.driver.async.AsyncTransactionWork; import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.NetworkSession; -import org.neo4j.driver.internal.async.EventLoopGroupFactory; +import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.types.Node; import org.neo4j.driver.util.DatabaseExtension; @@ -49,48 +52,77 @@ import static org.neo4j.driver.internal.util.Matchers.blockingOperationInEventLoopError; import static org.neo4j.driver.util.TestUtil.await; -/** - * We leave the question whether we want to let {@link NetworkSession} both implements blocking session and async session or not later. - * But as how it is right not, here are some tests for using mixes blocking and async API. - */ @ParallelizableIT -class NetworkSessionIT +class SessionMixIT { @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); - private NetworkSession session; + private AsyncSession asyncSession; + private Session session; @BeforeEach void setUp() { + asyncSession = newAsyncSession(); session = newSession(); } @AfterEach void tearDown() { - session.closeAsync(); + await( asyncSession.closeAsync() ); + session.close(); + } + + private AsyncSession newAsyncSession() + { + return neo4j.driver().asyncSession(); } - private NetworkSession newSession() + private Session newSession() { - return (NetworkSession) neo4j.driver().session(); + return neo4j.driver().session(); } @Test - void shouldBePossibleToMixRunAsyncAndBlockingSessionClose() + void shouldFailToExecuteBlockingRunChainedWithAsyncTransaction() { - long nodeCount = 5_000; + CompletionStage result = asyncSession.beginTransactionAsync( TransactionConfig.empty() ) + .thenApply( tx -> + { + if ( EventLoopGroupFactory.isEventLoopThread( Thread.currentThread() ) ) + { + IllegalStateException e = assertThrows( IllegalStateException.class, () -> session.run( "CREATE ()" ) ); + assertThat( e, is( blockingOperationInEventLoopError() ) ); + } + return null; + } ); - try ( NetworkSession session = newSession() ) - { - session.runAsync( "UNWIND range(1, " + nodeCount + ") AS x CREATE (n:AsyncNode {x: x}) RETURN n" ); - } + assertNull( await( result ) ); + } - assertEquals( nodeCount, countNodesByLabel( "AsyncNode" ) ); + @Test + void shouldAllowUsingBlockingApiInCommonPoolWhenChaining() + { + CompletionStage txStage = asyncSession.beginTransactionAsync() + // move execution to ForkJoinPool.commonPool() + .thenApplyAsync( tx -> + { + session.run( "UNWIND [1,1,2] AS x CREATE (:Node {id: x})" ).consume(); + session.run( "CREATE (:Node {id: 42})" ).consume(); + tx.commitAsync(); + return tx; + } ); + + await( txStage ); + + assertEquals( 2, countNodes( 1 ) ); + assertEquals( 1, countNodes( 2 ) ); + assertEquals( 1, countNodes( 42 ) ); } + @Test void shouldFailToExecuteBlockingRunInAsyncTransactionFunction() { @@ -98,21 +130,21 @@ void shouldFailToExecuteBlockingRunInAsyncTransactionFunction() if ( EventLoopGroupFactory.isEventLoopThread( Thread.currentThread() ) ) { IllegalStateException e = assertThrows( IllegalStateException.class, - () -> ((ExplicitTransaction) tx).run( "UNWIND range(1, 10000) AS x CREATE (n:AsyncNode {x: x}) RETURN n" ) ); + () -> session.run( "UNWIND range(1, 10000) AS x CREATE (n:AsyncNode {x: x}) RETURN n" ) ); assertThat( e, is( blockingOperationInEventLoopError() ) ); } return completedFuture( null ); }; - CompletionStage result = session.readTransactionAsync( completionStageTransactionWork ); + CompletionStage result = asyncSession.readTransactionAsync( completionStageTransactionWork ); assertNull( await( result ) ); } @Test void shouldFailToExecuteBlockingRunChainedWithAsyncRun() { - CompletionStage result = session.runAsync( "RETURN 1" ).thenCompose( StatementResultCursor::singleAsync ).thenApply( record -> { + CompletionStage result = asyncSession.runAsync( "RETURN 1" ).thenCompose( StatementResultCursor::singleAsync ).thenApply( record -> { if ( EventLoopGroupFactory.isEventLoopThread( Thread.currentThread() ) ) { IllegalStateException e = @@ -129,10 +161,11 @@ void shouldFailToExecuteBlockingRunChainedWithAsyncRun() @Test void shouldAllowBlockingOperationInCommonPoolWhenChaining() { - CompletionStage nodeStage = session.runAsync( "RETURN 42 AS value" ).thenCompose( StatementResultCursor::singleAsync ) + CompletionStage nodeStage = asyncSession.runAsync( "RETURN 42 AS value" ).thenCompose( StatementResultCursor::singleAsync ) // move execution to ForkJoinPool.commonPool() - .thenApplyAsync( record -> session.run( "CREATE (n:Node {value: $value}) RETURN n", record ) ).thenApply( StatementResult::single ).thenApply( - record -> record.get( 0 ).asNode() ); + .thenApplyAsync( record -> session.run( "CREATE (n:Node {value: $value}) RETURN n", record ) ) + .thenApply( StatementResult::single ) + .thenApply( record -> record.get( 0 ).asNode() ); Node node = await( nodeStage ); @@ -170,7 +203,7 @@ private void runNestedQuery( StatementResultCursor inputCursor, Record record, L long age = id * 10; CompletionStage response = - session.runAsync( "MATCH (p:Person {id: $id}) SET p.age = $age RETURN p", parameters( "id", id, "age", age ) ); + asyncSession.runAsync( "MATCH (p:Person {id: $id}) SET p.age = $age RETURN p", parameters( "id", id, "age", age ) ); response.whenComplete( ( cursor, error ) -> { if ( error != null ) @@ -188,9 +221,16 @@ private void runNestedQuery( StatementResultCursor inputCursor, Record record, L private long countNodesByLabel( String label ) { CompletionStage countStage = - session.runAsync( "MATCH (n:" + label + ") RETURN count(n)" ).thenCompose( StatementResultCursor::singleAsync ).thenApply( + asyncSession.runAsync( "MATCH (n:" + label + ") RETURN count(n)" ).thenCompose( StatementResultCursor::singleAsync ).thenApply( record -> record.get( 0 ).asLong() ); return await( countStage ); } + + private int countNodes( Object id ) + { + Statement statement = new Statement( "MATCH (n:Node {id: $id}) RETURN count(n)", parameters( "id", id ) ); + Record record = session.run( statement ).single(); + return record.get( 0 ).asInt(); + } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/TransactionBoltV3IT.java b/driver/src/test/java/org/neo4j/driver/integration/TransactionBoltV3IT.java index b550839375..61949664df 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/TransactionBoltV3IT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/TransactionBoltV3IT.java @@ -31,12 +31,13 @@ import org.neo4j.driver.Transaction; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.Value; +import org.neo4j.driver.async.AsyncSession; import org.neo4j.driver.async.AsyncTransaction; import org.neo4j.driver.async.StatementResultCursor; import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; +import org.neo4j.driver.util.DriverExtension; import org.neo4j.driver.util.ParallelizableIT; -import org.neo4j.driver.util.SessionExtension; import static java.time.Duration.ofMillis; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,7 +52,7 @@ class TransactionBoltV3IT { @RegisterExtension - static final SessionExtension session = new SessionExtension(); + static final DriverExtension driver = new DriverExtension(); @Test void shouldSetTransactionMetadata() @@ -65,7 +66,7 @@ void shouldSetTransactionMetadata() .withMetadata( metadata ) .build(); - try ( Transaction tx = session.beginTransaction( config ) ) + try ( Transaction tx = driver.session().beginTransaction( config ) ) { tx.run( "RETURN 1" ).consume(); @@ -84,7 +85,7 @@ void shouldSetTransactionMetadataAsync() .withMetadata( metadata ) .build(); - CompletionStage txFuture = session.beginTransactionAsync( config ) + CompletionStage txFuture = driver.asyncSession().beginTransactionAsync( config ) .thenCompose( tx -> tx.runAsync( "RETURN 1" ) .thenCompose( StatementResultCursor::consumeAsync ) .thenApply( ignore -> tx ) ); @@ -104,9 +105,10 @@ void shouldSetTransactionMetadataAsync() void shouldSetTransactionTimeout() { // create a dummy node + Session session = driver.session(); session.run( "CREATE (:Node)" ).consume(); - try ( Session otherSession = session.driver().session() ) + try ( Session otherSession = driver.driver().session() ) { try ( Transaction otherTx = otherSession.beginTransaction() ) { @@ -136,9 +138,12 @@ void shouldSetTransactionTimeout() void shouldSetTransactionTimeoutAsync() { // create a dummy node + Session session = driver.session(); + AsyncSession asyncSession = driver.asyncSession(); + session.run( "CREATE (:Node)" ).consume(); - try ( Session otherSession = session.driver().session() ) + try ( Session otherSession = driver.driver().session() ) { try ( Transaction otherTx = otherSession.beginTransaction() ) { @@ -150,7 +155,7 @@ void shouldSetTransactionTimeoutAsync() .build(); // start a new transaction with timeout and try to update the locked dummy node - CompletionStage txCommitFuture = session.beginTransactionAsync( config ) + CompletionStage txCommitFuture = asyncSession.beginTransactionAsync( config ) .thenCompose( tx -> tx.runAsync( "MATCH (n:Node) SET n.prop = 2" ) .thenCompose( ignore -> tx.commitAsync() ) ); @@ -162,7 +167,7 @@ void shouldSetTransactionTimeoutAsync() private static void verifyTransactionMetadata( Map metadata ) { - try ( Session session = TransactionBoltV3IT.session.driver().session() ) + try ( Session session = driver.driver().session() ) { StatementResult result = session.run( "CALL dbms.listTransactions()" ); diff --git a/driver/src/test/java/org/neo4j/driver/integration/UnsupportedBoltV3IT.java b/driver/src/test/java/org/neo4j/driver/integration/UnsupportedBoltV3IT.java index 770ac5d057..f601b093dc 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/UnsupportedBoltV3IT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/UnsupportedBoltV3IT.java @@ -24,11 +24,11 @@ import java.util.concurrent.CompletionStage; -import org.neo4j.driver.internal.util.DisabledOnNeo4jWith; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.util.DisabledOnNeo4jWith; +import org.neo4j.driver.util.DriverExtension; import org.neo4j.driver.util.ParallelizableIT; -import org.neo4j.driver.util.SessionExtension; import static java.time.Duration.ofSeconds; import static java.util.Collections.singletonMap; @@ -43,7 +43,7 @@ class UnsupportedBoltV3IT { @RegisterExtension - static final SessionExtension session = new SessionExtension(); + static final DriverExtension driver = new DriverExtension(); private final TransactionConfig txConfig = TransactionConfig.builder() .withTimeout( ofSeconds( 4 ) ) @@ -53,37 +53,37 @@ class UnsupportedBoltV3IT @Test void shouldNotSupportAutoCommitQueriesWithTransactionConfig() { - assertTxConfigNotSupported( () -> session.run( "RETURN 42", txConfig ) ); + assertTxConfigNotSupported( () -> driver.session().run( "RETURN 42", txConfig ) ); } @Test void shouldNotSupportAsyncAutoCommitQueriesWithTransactionConfig() { - assertTxConfigNotSupported( session.runAsync( "RETURN 42", txConfig ) ); + assertTxConfigNotSupported( driver.asyncSession().runAsync( "RETURN 42", txConfig ) ); } @Test void shouldNotSupportTransactionFunctionsWithTransactionConfig() { - assertTxConfigNotSupported( () -> session.readTransaction( tx -> tx.run( "RETURN 42" ), txConfig ) ); + assertTxConfigNotSupported( () -> driver.session().readTransaction( tx -> tx.run( "RETURN 42" ), txConfig ) ); } @Test void shouldNotSupportAsyncTransactionFunctionsWithTransactionConfig() { - assertTxConfigNotSupported( session.readTransactionAsync( tx -> tx.runAsync( "RETURN 42" ), txConfig ) ); + assertTxConfigNotSupported( driver.asyncSession().readTransactionAsync( tx -> tx.runAsync( "RETURN 42" ), txConfig ) ); } @Test void shouldNotSupportExplicitTransactionsWithTransactionConfig() { - assertTxConfigNotSupported( () -> session.beginTransaction( txConfig ) ); + assertTxConfigNotSupported( () -> driver.session().beginTransaction( txConfig ) ); } @Test void shouldNotSupportAsyncExplicitTransactionsWithTransactionConfig() { - assertTxConfigNotSupported( session.beginTransactionAsync( txConfig ) ); + assertTxConfigNotSupported( driver.asyncSession().beginTransactionAsync( txConfig ) ); } /** diff --git a/driver/src/test/java/org/neo4j/driver/internal/DirectConnectionProviderTest.java b/driver/src/test/java/org/neo4j/driver/internal/DirectConnectionProviderTest.java index 01c8bcc425..37ac1632ae 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DirectConnectionProviderTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DirectConnectionProviderTest.java @@ -27,7 +27,7 @@ import java.util.stream.Stream; import org.neo4j.driver.AccessMode; -import org.neo4j.driver.internal.async.DecoratedConnection; +import org.neo4j.driver.internal.async.connection.DecoratedConnection; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; diff --git a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java index 1f46343098..6280bde69d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java @@ -32,7 +32,9 @@ import org.neo4j.driver.Config; import org.neo4j.driver.Driver; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.internal.async.BootstrapFactory; +import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancer; import org.neo4j.driver.internal.metrics.InternalMetricsProvider; @@ -106,7 +108,7 @@ void usesStandardSessionFactoryWhenNothingConfigured( String uri ) createDriver( uri, factory, config ); SessionFactory capturedFactory = factory.capturedSessionFactory; - assertThat( capturedFactory.newInstance( empty() ), instanceOf( NetworkSession.class ) ); + assertThat( capturedFactory.newInstance( empty() ), instanceOf( InternalNetworkSession.class ) ); } @ParameterizedTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/NetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalSessionTest.java similarity index 60% rename from driver/src/test/java/org/neo4j/driver/internal/messaging/v2/NetworkSessionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/InternalSessionTest.java index 4a7d32240a..057154fe36 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/NetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalSessionTest.java @@ -16,41 +16,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.messaging.v2; +package org.neo4j.driver.internal; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InOrder; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.mockito.verification.VerificationMode; -import java.util.Map; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Stream; -import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.Bookmarks; -import org.neo4j.driver.internal.DefaultBookmarksHolder; -import org.neo4j.driver.internal.NetworkSession; -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.request.PullAllMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.retry.RetryLogic; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.ConnectionProvider; -import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.internal.util.FixedRetryLogic; -import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.AccessMode; import org.neo4j.driver.Session; +import org.neo4j.driver.Statement; +import org.neo4j.driver.StatementResult; import org.neo4j.driver.Transaction; +import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.TransactionWork; import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.internal.messaging.BoltProtocol; +import org.neo4j.driver.internal.messaging.request.CommitMessage; +import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.retry.RetryLogic; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.util.FixedRetryLogic; +import org.neo4j.driver.internal.value.IntegerValue; +import org.neo4j.driver.summary.ResultSummary; -import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -62,11 +64,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -75,43 +74,103 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; -import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; -import static org.neo4j.driver.internal.messaging.v2.ExplicitTransactionTest.connectionMock; -import static org.neo4j.driver.internal.util.Futures.completedWithNull; -import static org.neo4j.driver.internal.util.Futures.failedFuture; import static org.neo4j.driver.AccessMode.READ; import static org.neo4j.driver.AccessMode.WRITE; -import static org.neo4j.driver.util.TestUtil.await; -import static org.neo4j.driver.util.TestUtil.runMessageWithStatementMatcher; - -class NetworkSessionTest +import static org.neo4j.driver.TransactionConfig.empty; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; +import static org.neo4j.driver.internal.util.Futures.failedFuture; +import static org.neo4j.driver.util.TestUtil.connectionMock; +import static org.neo4j.driver.util.TestUtil.newSession; +import static org.neo4j.driver.util.TestUtil.setupFailingBegin; +import static org.neo4j.driver.util.TestUtil.setupFailingCommit; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyBeginTx; +import static org.neo4j.driver.util.TestUtil.verifyCommitTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; + +class InternalSessionTest { private Connection connection; private ConnectionProvider connectionProvider; - private NetworkSession session; + private InternalSession session; @BeforeEach void setUp() { - connection = connectionMock(); - when( connection.release() ).thenReturn( completedWithNull() ); - when( connection.reset() ).thenReturn( completedWithNull() ); - when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT ); - when( connection.serverVersion() ).thenReturn( ServerVersion.v3_2_0 ); - when( connection.protocol() ).thenReturn( BoltProtocolV2.INSTANCE ); + connection = connectionMock( BoltProtocolV4.INSTANCE ); connectionProvider = mock( ConnectionProvider.class ); - when( connectionProvider.acquireConnection( eq( ABSENT_DB_NAME ), any( AccessMode.class ) ) ) + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) .thenReturn( completedFuture( connection ) ); - session = newSession( connectionProvider, READ ); + session = new InternalSession( newSession( connectionProvider ) ); } - @Test - void shouldFlushOnRun() + private static Stream> allSessionRunMethods() { - session.run( "RETURN 1" ); + return Stream.of( + session -> session.run( "RETURN 1" ), + session -> session.run( "RETURN $x", parameters( "x", 1 ) ), + session -> session.run( "RETURN $x", singletonMap( "x", 1 ) ), + session -> session.run( "RETURN $x", + new InternalRecord( singletonList( "x" ), new Value[]{new IntegerValue( 1 )} ) ), + session -> session.run( new Statement( "RETURN $x", parameters( "x", 1 ) ) ), + session -> session.run( new Statement( "RETURN $x", parameters( "x", 1 ) ), empty() ), + session -> session.run( "RETURN $x", singletonMap( "x", 1 ), empty() ), + session -> session.run( "RETURN 1", empty() ) + ); + } + + private static Stream> allBeginTxMethods() + { + return Stream.of( + session -> session.beginTransaction(), + session -> session.beginTransaction( TransactionConfig.empty() ) + ); + } + + private static Stream> allRunTxMethods() + { + return Stream.of( + session -> session.readTransaction( tx -> "a" ), + session -> session.writeTransaction( tx -> "a" ), + session -> session.readTransaction( tx -> "a", empty() ), + session -> session.writeTransaction( tx -> "a", empty() ) + ); + } + + @ParameterizedTest + @MethodSource( "allSessionRunMethods" ) + void shouldFlushOnRun( Function runReturnOne ) throws Throwable + { + setupSuccessfulRunAndPull( connection ); + + StatementResult result = runReturnOne.apply( session ); + ResultSummary summary = result.summary(); + + verifyRunAndPull( connection, summary.statement().text() ); + } + + @ParameterizedTest + @MethodSource( "allBeginTxMethods" ) + void shouldBeginTx( Function beginTx ) throws Throwable + { + Transaction tx = beginTx.apply( session ); + + verifyBeginTx( connection ); + assertNotNull( tx ); + } - verify( connection ).writeAndFlush( eq( new RunMessage( "RETURN 1" ) ), any(), any(), any() ); + @ParameterizedTest + @MethodSource( "allRunTxMethods" ) + void runTxShouldBeginTxAndCloseTx( Function runTx ) throws Throwable + { + String string = runTx.apply( session ); + + verifyBeginTx( connection ); + verify( connection ).writeAndFlush( any( CommitMessage.class ), any() ); + verify( connection ).release(); + assertThat( string, equalTo( "a" ) ); } @Test @@ -157,7 +216,7 @@ void shouldBeAbleToUseSessionAgainWhenTransactionIsClosed() session.run( "RETURN 1" ); // Then - verify( connection ).writeAndFlush( eq( new RunMessage( "RETURN 1" ) ), any(), any(), any() ); + verifyRunAndPull( connection, "RETURN 1" ); } @Test @@ -169,15 +228,12 @@ void shouldNotCloseAlreadyClosedSession() session.close(); session.close(); - verifyRollbackTx( connection, times( 1 ) ); + verifyRollbackTx( connection ); } @Test void runThrowsWhenSessionIsClosed() { - ConnectionProvider connectionProvider = mock( ConnectionProvider.class, RETURNS_MOCKS ); - NetworkSession session = newSession( connectionProvider, READ ); - session.close(); Exception e = assertThrows( Exception.class, () -> session.run( "CREATE ()" ) ); @@ -188,29 +244,22 @@ void runThrowsWhenSessionIsClosed() @Test void acquiresNewConnectionForRun() { - ConnectionProvider connectionProvider = mock( ConnectionProvider.class ); - Connection connection = connectionMock(); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ).thenReturn( completedFuture( connection ) ); - NetworkSession session = newSession( connectionProvider, READ ); - session.run( "RETURN 1" ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, READ ); - verify( connection ).writeAndFlush( eq( new RunMessage( "RETURN 1" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); } @Test void releasesOpenConnectionUsedForRunWhenSessionIsClosed() { String query = "RETURN 1"; - setupSuccessfulPullAll( query ); + setupSuccessfulRunAndPull( connection, query ); session.run( query ); - - await( session.closeAsync() ); + session.close(); InOrder inOrder = inOrder( connection ); - inOrder.verify( connection ).writeAndFlush( eq( new RunMessage( "RETURN 1" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); + inOrder.verify( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any(), any( PullMessage.class ), any() ); inOrder.verify( connection, atLeastOnce() ).release(); } @@ -218,9 +267,6 @@ void releasesOpenConnectionUsedForRunWhenSessionIsClosed() @Test void resetDoesNothingWhenNoTransactionAndNoConnection() { - ConnectionProvider connectionProvider = mock( ConnectionProvider.class ); - NetworkSession session = newSession( connectionProvider, READ ); - session.reset(); verify( connectionProvider, never() ).acquireConnection( any( String.class ), any( AccessMode.class ) ); @@ -229,9 +275,6 @@ void resetDoesNothingWhenNoTransactionAndNoConnection() @Test void closeWithoutConnection() { - ConnectionProvider connectionProvider = mock( ConnectionProvider.class ); - NetworkSession session = newSession( connectionProvider, READ ); - session.close(); verify( connectionProvider, never() ).acquireConnection( any( String.class ), any( AccessMode.class ) ); @@ -243,7 +286,7 @@ void acquiresNewConnectionForBeginTx() Transaction tx = session.beginTransaction(); assertNotNull( tx ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, READ ); + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); } @Test @@ -251,7 +294,7 @@ void updatesBookmarkWhenTxIsClosed() { Bookmarks bookmarkAfterCommit = Bookmarks.from( "TheBookmark" ); - BoltProtocol protocol = spy( BoltProtocolV2.INSTANCE ); + BoltProtocol protocol = spy( BoltProtocolV4.INSTANCE ); doReturn( completedFuture( bookmarkAfterCommit ) ).when( protocol ).commitTransaction( any( Connection.class ) ); when( connection.protocol() ).thenReturn( protocol ); @@ -268,13 +311,13 @@ void updatesBookmarkWhenTxIsClosed() void releasesConnectionWhenTxIsClosed() { String query = "RETURN 42"; - setupSuccessfulPullAll( query ); + setupSuccessfulRunAndPull( connection, query ); Transaction tx = session.beginTransaction(); tx.run( query ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, READ ); - verify( connection ).writeAndFlush( eq( new RunMessage( query ) ), any(), any(), any() ); + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyRunAndPull( connection, query ); tx.close(); verify( connection ).release(); @@ -284,18 +327,7 @@ void releasesConnectionWhenTxIsClosed() void bookmarkIsPropagatedFromSession() { Bookmarks bookmarks = Bookmarks.from( "Bookmarks" ); - NetworkSession session = newSession( connectionProvider, READ, bookmarks ); - - Transaction tx = session.beginTransaction(); - assertNotNull( tx ); - verifyBeginTx( connection, bookmarks ); - } - - @Test - void bookmarkIsPropagatedInBeginTransaction() - { - Bookmarks bookmarks = Bookmarks.from( "Bookmarks" ); - NetworkSession session = newSession( connectionProvider, READ, bookmarks ); + Session session = new InternalSession( newSession( connectionProvider, bookmarks ) ); Transaction tx = session.beginTransaction(); assertNotNull( tx ); @@ -308,9 +340,9 @@ void bookmarkIsPropagatedBetweenTransactions() Bookmarks bookmarks1 = Bookmarks.from( "Bookmark1" ); Bookmarks bookmarks2 = Bookmarks.from( "Bookmark2" ); - NetworkSession session = newSession( connectionProvider, READ ); + Session session = new InternalSession( newSession( connectionProvider) ); - BoltProtocol protocol = spy( BoltProtocolV2.INSTANCE ); + BoltProtocol protocol = spy( BoltProtocolV4.INSTANCE ); doReturn( completedFuture( bookmarks1 ), completedFuture( bookmarks2 ) ).when( protocol ).commitTransaction( any( Connection.class ) ); when( connection.protocol() ).thenReturn( protocol ); @@ -332,34 +364,23 @@ void bookmarkIsPropagatedBetweenTransactions() @Test void accessModeUsedToAcquireConnections() { - NetworkSession session1 = newSession( connectionProvider, READ ); + Session session1 = new InternalSession( newSession( connectionProvider, READ ) ); session1.beginTransaction(); verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, READ ); - NetworkSession session2 = newSession( connectionProvider, WRITE ); + Session session2 = new InternalSession( newSession( connectionProvider, WRITE ) ); session2.beginTransaction(); verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, WRITE ); } - - @Test void testPassingNoBookmarkShouldRetainBookmark() { - NetworkSession session = newSession( connectionProvider, READ, Bookmarks.from( "X" ) ); + Session session = new InternalSession( newSession( connectionProvider, Bookmarks.from( "X" ) ) ); session.beginTransaction(); assertThat( session.lastBookmark(), equalTo( "X" ) ); } - - @SuppressWarnings( "deprecation" ) - @Test - void testPassingNullBookmarkShouldRetainBookmark() - { - NetworkSession session = newSession( connectionProvider, READ, Bookmarks.from( "X" ) ); - session.beginTransaction( (String) null ); - assertThat( session.lastBookmark(), equalTo( "X" ) ); - } - + @Test void acquiresReadConnectionForReadTxInReadSession() { @@ -471,7 +492,6 @@ void writeTxRetriedUntilFailureWhenTxCloseThrows() @Test void connectionShouldBeResetAfterSessionReset() { - NetworkSession session = newSession( connectionProvider, READ ); session.run( "RETURN 1" ); verify( connection, never() ).reset(); @@ -485,7 +505,6 @@ void connectionShouldBeResetAfterSessionReset() @Test void shouldHaveNullLastBookmarkInitially() { - NetworkSession session = newSession( mock( ConnectionProvider.class ), READ ); assertNull( session.lastBookmark() ); } @@ -493,7 +512,7 @@ void shouldHaveNullLastBookmarkInitially() void shouldDoNothingWhenClosingWithoutAcquiredConnection() { RuntimeException error = new RuntimeException( "Hi" ); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ).thenReturn( failedFuture( error ) ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ).thenReturn( failedFuture( error ) ); Exception e = assertThrows( Exception.class, () -> session.run( "RETURN 1" ) ); assertEquals( error, e ); @@ -505,7 +524,7 @@ void shouldDoNothingWhenClosingWithoutAcquiredConnection() void shouldRunAfterRunFailureToAcquireConnection() { RuntimeException error = new RuntimeException( "Hi" ); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ) + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) .thenReturn( failedFuture( error ) ).thenReturn( completedFuture( connection ) ); Exception e = assertThrows( Exception.class, () -> session.run( "RETURN 1" ) ); @@ -513,54 +532,54 @@ void shouldRunAfterRunFailureToAcquireConnection() session.run( "RETURN 2" ); - verify( connectionProvider, times( 2 ) ).acquireConnection( ABSENT_DB_NAME, READ ); - verifyRunAndFlush( connection, "RETURN 2", times( 1 ) ); + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyRunAndPull( connection, "RETURN 2" ); } @Test void shouldRunAfterBeginTxFailureOnBookmark() { RuntimeException error = new RuntimeException( "Hi" ); - Connection connection1 = connectionMock(); + Connection connection1 = connectionMock( BoltProtocolV4.INSTANCE ); setupFailingBegin( connection1, error ); - Connection connection2 = connectionMock(); + Connection connection2 = connectionMock( BoltProtocolV4.INSTANCE ); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ) + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) .thenReturn( completedFuture( connection1 ) ).thenReturn( completedFuture( connection2 ) ); Bookmarks bookmarks = Bookmarks.from( "neo4j:bookmark:v1:tx42" ); - NetworkSession session = newSession( connectionProvider, READ, bookmarks ); + Session session = new InternalSession( newSession( connectionProvider, bookmarks ) ); Exception e = assertThrows( Exception.class, session::beginTransaction ); assertEquals( error, e ); session.run( "RETURN 2" ); - verify( connectionProvider, times( 2 ) ).acquireConnection( ABSENT_DB_NAME, READ ); + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); verifyBeginTx( connection1, bookmarks ); - verifyRunAndFlush( connection2, "RETURN 2", times( 1 ) ); + verifyRunAndPull( connection2, "RETURN 2" ); } @Test void shouldBeginTxAfterBeginTxFailureOnBookmark() { RuntimeException error = new RuntimeException( "Hi" ); - Connection connection1 = connectionMock(); + Connection connection1 = connectionMock( BoltProtocolV4.INSTANCE ); setupFailingBegin( connection1, error ); - Connection connection2 = connectionMock(); + Connection connection2 = connectionMock( BoltProtocolV4.INSTANCE ); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ) + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) .thenReturn( completedFuture( connection1 ) ).thenReturn( completedFuture( connection2 ) ); Bookmarks bookmarks = Bookmarks.from( "neo4j:bookmark:v1:tx42" ); - NetworkSession session = newSession( connectionProvider, READ, bookmarks ); + Session session = new InternalSession( newSession( connectionProvider, bookmarks ) ); Exception e = assertThrows( Exception.class, session::beginTransaction ); assertEquals( error, e ); session.beginTransaction(); - verify( connectionProvider, times( 2 ) ).acquireConnection( ABSENT_DB_NAME, READ ); + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); verifyBeginTx( connection1, bookmarks ); verifyBeginTx( connection2, bookmarks ); } @@ -569,7 +588,7 @@ void shouldBeginTxAfterBeginTxFailureOnBookmark() void shouldBeginTxAfterRunFailureToAcquireConnection() { RuntimeException error = new RuntimeException( "Hi" ); - when( connectionProvider.acquireConnection( ABSENT_DB_NAME, READ ) ) + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) .thenReturn( failedFuture( error ) ).thenReturn( completedFuture( connection ) ); Exception e = assertThrows( Exception.class, () -> session.run( "RETURN 1" ) ); @@ -577,14 +596,13 @@ void shouldBeginTxAfterRunFailureToAcquireConnection() session.beginTransaction(); - verify( connectionProvider, times( 2 ) ).acquireConnection( ABSENT_DB_NAME, READ ); - verifyBeginTx( connection, times( 1 ) ); + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyBeginTx( connection ); } @Test void shouldMarkTransactionAsTerminatedAndThenResetConnectionOnReset() { - NetworkSession session = newSession( connectionProvider, READ ); Transaction tx = session.beginTransaction(); assertTrue( tx.isOpen() ); @@ -595,64 +613,27 @@ void shouldMarkTransactionAsTerminatedAndThenResetConnectionOnReset() verify( connection ).reset(); } - @Test - void shouldNotAllowStartingMultipleTransactions() - { - NetworkSession session = newSession( connectionProvider, READ ); - - Transaction tx = session.beginTransaction(); - assertNotNull( tx ); - - for ( int i = 0; i < 5; i++ ) - { - ClientException e = assertThrows( ClientException.class, session::beginTransaction ); - assertThat( e.getMessage(), - containsString( "You cannot begin a transaction on a session with an open transaction" ) ); - } - } - - @Test - void shouldAllowStartingTransactionAfterCurrentOneIsClosed() - { - NetworkSession session = newSession( connectionProvider, READ ); - - Transaction tx = session.beginTransaction(); - assertNotNull( tx ); - - ClientException e = assertThrows( ClientException.class, session::beginTransaction ); - assertThat( e.getMessage(), - containsString( "You cannot begin a transaction on a session with an open transaction" ) ); - - tx.close(); - - assertNotNull( session.beginTransaction() ); - } - private void testConnectionAcquisition( AccessMode sessionMode, AccessMode transactionMode ) { - NetworkSession session = newSession( connectionProvider, sessionMode ); + Session session = new InternalSession( newSession( connectionProvider, sessionMode ) ); TxWork work = new TxWork( 42 ); int result = executeTransaction( session, transactionMode, work ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, transactionMode ); - verifyBeginTx( connection, times( 1 ) ); - verifyCommitTx( connection, times( 1 ) ); + verify( connectionProvider ).acquireConnection( any( String.class ), eq( transactionMode ) ); + verifyBeginTx( connection ); + verifyCommitTx( connection ); assertEquals( 42, result ); } private void testTxCommitOrRollback( AccessMode transactionMode, final boolean commit ) { - NetworkSession session = newSession( connectionProvider, WRITE ); + Session session = new InternalSession( newSession( connectionProvider, WRITE ) ); TransactionWork work = tx -> { - if ( commit ) - { - tx.success(); - } - else + if ( !commit ) { tx.failure(); } @@ -661,16 +642,16 @@ private void testTxCommitOrRollback( AccessMode transactionMode, final boolean c int result = executeTransaction( session, transactionMode, work ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, transactionMode ); - verifyBeginTx( connection, times( 1 ) ); + verify( connectionProvider ).acquireConnection( any( String.class ), eq( transactionMode ) ); + verifyBeginTx( connection ); if ( commit ) { - verifyCommitTx( connection, times( 1 ) ); + verifyCommitTx( connection ); verifyRollbackTx( connection, never() ); } else { - verifyRollbackTx( connection, times( 1 ) ); + verifyRollbackTx( connection ); verifyCommitTx( connection, never() ); } assertEquals( 4242, result ); @@ -678,8 +659,6 @@ private void testTxCommitOrRollback( AccessMode transactionMode, final boolean c private void testTxRollbackWhenThrows( AccessMode transactionMode ) { - NetworkSession session = newSession( connectionProvider, WRITE ); - final RuntimeException error = new IllegalStateException( "Oh!" ); TransactionWork work = tx -> { @@ -689,9 +668,9 @@ private void testTxRollbackWhenThrows( AccessMode transactionMode ) Exception e = assertThrows( Exception.class, () -> executeTransaction( session, transactionMode, work ) ); assertEquals( error, e ); - verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, transactionMode ); - verifyBeginTx( connection, times( 1 ) ); - verifyRollbackTx( connection, times( 1 ) ); + verify( connectionProvider ).acquireConnection( any( String.class ), eq( transactionMode ) ); + verifyBeginTx( connection ); + verifyRollbackTx( connection ); } private void testTxIsRetriedUntilSuccessWhenFunctionThrows( AccessMode mode ) @@ -700,14 +679,14 @@ private void testTxIsRetriedUntilSuccessWhenFunctionThrows( AccessMode mode ) int retries = failures + 1; RetryLogic retryLogic = new FixedRetryLogic( retries ); - NetworkSession session = newSession( connectionProvider, retryLogic ); + Session session = new InternalSession( newSession( connectionProvider, retryLogic ) ); TxWork work = spy( new TxWork( 42, failures, new SessionExpiredException( "" ) ) ); int answer = executeTransaction( session, mode, work ); assertEquals( 42, answer ); verifyInvocationCount( work, failures + 1 ); - verifyCommitTx( connection, times( 1 ) ); + verifyCommitTx( connection ); verifyRollbackTx( connection, times( failures ) ); } @@ -718,7 +697,7 @@ private void testTxIsRetriedUntilSuccessWhenCommitThrows( AccessMode mode ) RetryLogic retryLogic = new FixedRetryLogic( retries ); setupFailingCommit( connection, failures ); - NetworkSession session = newSession( connectionProvider, retryLogic ); + Session session = new InternalSession( newSession( connectionProvider, retryLogic ) ); TxWork work = spy( new TxWork( 43 ) ); int answer = executeTransaction( session, mode, work ); @@ -734,7 +713,7 @@ private void testTxIsRetriedUntilFailureWhenFunctionThrows( AccessMode mode ) int retries = failures - 1; RetryLogic retryLogic = new FixedRetryLogic( retries ); - NetworkSession session = newSession( connectionProvider, retryLogic ); + Session session = new InternalSession( newSession( connectionProvider, retryLogic ) ); TxWork work = spy( new TxWork( 42, failures, new SessionExpiredException( "Oh!" ) ) ); @@ -754,7 +733,7 @@ private void testTxIsRetriedUntilFailureWhenCommitFails( AccessMode mode ) RetryLogic retryLogic = new FixedRetryLogic( retries ); setupFailingCommit( connection, failures ); - NetworkSession session = newSession( connectionProvider, retryLogic ); + Session session = new InternalSession( newSession( connectionProvider, retryLogic ) ); TxWork work = spy( new TxWork( 42 ) ); @@ -781,109 +760,11 @@ else if ( mode == WRITE ) } } - private static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode ) - { - return newSession( connectionProvider, mode, Bookmarks.empty() ); - } - - private static NetworkSession newSession( ConnectionProvider connectionProvider, RetryLogic retryLogic ) - { - return newSession( connectionProvider, WRITE, retryLogic, Bookmarks.empty() ); - } - - private static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, - Bookmarks bookmarks ) - { - return newSession( connectionProvider, mode, new FixedRetryLogic( 0 ), bookmarks ); - } - - private static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, - RetryLogic retryLogic, Bookmarks bookmarks ) - { - return new NetworkSession( connectionProvider, retryLogic, ABSENT_DB_NAME, mode, new DefaultBookmarksHolder( bookmarks ), DEV_NULL_LOGGING ); - } - private static void verifyInvocationCount( TransactionWork workSpy, int expectedInvocationCount ) { verify( workSpy, times( expectedInvocationCount ) ).execute( any( Transaction.class ) ); } - private static void verifyBeginTx( Connection connectionMock, VerificationMode mode ) - { - verify( connectionMock, mode ).write( eq( new RunMessage( "BEGIN" ) ), any(), any(), any() ); - } - - private static void verifyBeginTx( Connection connectionMock, Bookmarks bookmarks ) - { - if ( bookmarks.isEmpty() ) - { - verify( connectionMock ).write( eq( new RunMessage( "BEGIN" ) ), any(), any(), any() ); - } - else - { - Map params = bookmarks.asBeginTransactionParameters(); - verify( connectionMock ).writeAndFlush( eq( new RunMessage( "BEGIN", params ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - } - } - - private static void verifyCommitTx( Connection connectionMock, VerificationMode mode ) - { - verifyRunAndFlush( connectionMock, "COMMIT", mode ); - } - - private static void verifyRollbackTx( Connection connectionMock, VerificationMode mode ) - { - verifyRunAndFlush( connectionMock, "ROLLBACK", mode ); - } - - private static void verifyRunAndFlush( Connection connectionMock, String statement, VerificationMode mode ) - { - verify( connectionMock, mode ).writeAndFlush( eq( new RunMessage( statement ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - } - - private static void setupFailingCommit( Connection connection, int times ) - { - doAnswer( new Answer() - { - int invoked; - - @Override - public Void answer( InvocationOnMock invocation ) - { - ResponseHandler handler = invocation.getArgument( 3 ); - if ( invoked++ < times ) - { - handler.onFailure( new ServiceUnavailableException( "" ) ); - } - else - { - handler.onSuccess( emptyMap() ); - } - return null; - } - } ).when( connection ).writeAndFlush( eq( new RunMessage( "COMMIT" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - } - - private static void setupFailingBegin( Connection connection, Throwable error ) - { - doAnswer( invocation -> - { - ResponseHandler handler = invocation.getArgument( 3 ); - handler.onFailure( error ); - return null; - } ).when( connection ).writeAndFlush( argThat( runMessageWithStatementMatcher( "BEGIN" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - } - - private void setupSuccessfulPullAll( String query ) - { - doAnswer( invocation -> - { - ResponseHandler pullAllHandler = invocation.getArgument( 3 ); - pullAllHandler.onSuccess( emptyMap() ); - return null; - } ).when( connection ).writeAndFlush( eq( new RunMessage( query ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - } - private static class TxWork implements TransactionWork { final int result; diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalStatementResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalStatementResultTest.java index ff721a6e57..086ef8a366 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/InternalStatementResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalStatementResultTest.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import org.neo4j.driver.internal.async.AsyncStatementResultCursor; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.handlers.SessionPullAllResponseHandler; diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java new file mode 100644 index 0000000000..60ee1493e6 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalTransactionTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.function.Function; +import java.util.stream.Stream; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.value.IntegerValue; +import org.neo4j.driver.summary.ResultSummary; + +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.util.TestUtil.connectionMock; +import static org.neo4j.driver.util.TestUtil.newSession; +import static org.neo4j.driver.util.TestUtil.setupFailingCommit; +import static org.neo4j.driver.util.TestUtil.setupFailingRollback; +import static org.neo4j.driver.util.TestUtil.setupFailingRun; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyCommitTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; + +class InternalTransactionTest +{ + private Connection connection; + private Transaction tx; + + @BeforeEach + void setUp() + { + connection = connectionMock( BoltProtocolV4.INSTANCE ); + ConnectionProvider connectionProvider = mock( ConnectionProvider.class ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection ) ); + InternalSession session = new InternalSession( newSession( connectionProvider ) ); + tx = session.beginTransaction(); + } + + private static Stream> allSessionRunMethods() + { + return Stream.of( + tx -> tx.run( "RETURN 1" ), + tx -> tx.run( "RETURN $x", parameters( "x", 1 ) ), + tx -> tx.run( "RETURN $x", singletonMap( "x", 1 ) ), + tx -> tx.run( "RETURN $x", + new InternalRecord( singletonList( "x" ), new Value[]{new IntegerValue( 1 )} ) ), + tx -> tx.run( new Statement( "RETURN $x", parameters( "x", 1 ) ) ) + ); + } + + @ParameterizedTest + @MethodSource( "allSessionRunMethods" ) + void shouldFlushOnRun( Function runReturnOne ) throws Throwable + { + setupSuccessfulRunAndPull( connection ); + + StatementResult result = runReturnOne.apply( tx ); + ResultSummary summary = result.summary(); + + verifyRunAndPull( connection, summary.statement().text() ); + } + + @Test + void shouldCommit() throws Throwable + { + tx.success(); + tx.close(); + + verifyCommitTx( connection ); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldRollbackByDefault() throws Throwable + { + tx.close(); + + verifyRollbackTx( connection ); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldRollback() throws Throwable + { + tx.failure(); + tx.close(); + + verifyRollbackTx( connection ); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldRollbackWhenFailedRun() throws Throwable + { + setupFailingRun( connection, new RuntimeException( "Bang!" ) ); + assertThrows( RuntimeException.class, () -> tx.run( "RETURN 1" ).consume() ); + + tx.success(); + tx.close(); + + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldReleaseConnectionWhenFailedToCommit() throws Throwable + { + setupFailingCommit( connection ); + tx.success(); + assertThrows( Exception.class, () -> tx.close() ); + + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldReleaseConnectionWhenFailedToRollback() throws Throwable + { + setupFailingRollback( connection ); + assertThrows( Exception.class, () -> tx.close() ); + + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/RoutingDriverBoltKitTest.java b/driver/src/test/java/org/neo4j/driver/internal/RoutingDriverBoltKitTest.java index 9e681a3d8c..5fa6af31d3 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/RoutingDriverBoltKitTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/RoutingDriverBoltKitTest.java @@ -553,30 +553,6 @@ void shouldHandleLeaderSwitchWhenWritingInTransaction() assertThat( server.exitStatus(), equalTo( 0 ) ); } - @Test - @SuppressWarnings( "deprecation" ) - void shouldSendAndReceiveBookmark() throws Exception - { - StubServer router = StubServer.start( "acquire_endpoints.script", 9001 ); - StubServer writer = StubServer.start( "write_tx_with_bookmarks.script", 9007 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", config ); - Session session = driver.session() ) - { - // intentionally test deprecated API - try ( Transaction tx = session.beginTransaction( "OldBookmark" ) ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.success(); - } - - assertEquals( "NewBookmark", session.lastBookmark() ); - } - - assertThat( router.exitStatus(), equalTo( 0 ) ); - assertThat( writer.exitStatus(), equalTo( 0 ) ); - } - @Test void shouldSendInitialBookmark() throws Exception { diff --git a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java index 430470ff8e..0631aed0bb 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java @@ -20,11 +20,13 @@ import org.junit.jupiter.api.Test; -import org.neo4j.driver.internal.util.FixedRetryLogic; -import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.AccessMode; import org.neo4j.driver.Config; -import org.neo4j.driver.Session; +import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; +import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.util.FixedRetryLogic; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.junit.MatcherAssert.assertThat; @@ -40,11 +42,11 @@ void createsNetworkSessions() Config config = Config.builder().withLogging( DEV_NULL_LOGGING ).build(); SessionFactory factory = newSessionFactory( config ); - Session readSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.READ ).build() ); - assertThat( readSession, instanceOf( NetworkSession.class ) ); + NetworkSession readSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.READ ).build() ); + assertThat( readSession, instanceOf( InternalNetworkSession.class ) ); - Session writeSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.WRITE ).build() ); - assertThat( writeSession, instanceOf( NetworkSession.class ) ); + NetworkSession writeSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.WRITE ).build() ); + assertThat( writeSession, instanceOf( InternalNetworkSession.class ) ); } @Test @@ -53,10 +55,10 @@ void createsLeakLoggingNetworkSessions() Config config = Config.builder().withLogging( DEV_NULL_LOGGING ).withLeakedSessionsLogging().build(); SessionFactory factory = newSessionFactory( config ); - Session readSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.READ ).build() ); + NetworkSession readSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.READ ).build() ); assertThat( readSession, instanceOf( LeakLoggingNetworkSession.class ) ); - Session writeSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.WRITE ).build() ); + NetworkSession writeSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.WRITE ).build() ); assertThat( writeSession, instanceOf( LeakLoggingNetworkSession.class ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/AsyncStatementResultCursorTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncStatementResultCursorTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/AsyncStatementResultCursorTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/AsyncStatementResultCursorTest.java index 5bbfa42fd2..1bf48c77f1 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/AsyncStatementResultCursorTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncStatementResultCursorTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; import org.junit.jupiter.api.Test; @@ -27,6 +27,12 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import org.neo4j.driver.Record; +import org.neo4j.driver.Statement; +import org.neo4j.driver.exceptions.NoSuchRecordException; +import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.internal.BoltServerAddress; +import org.neo4j.driver.internal.InternalRecord; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; @@ -34,10 +40,6 @@ import org.neo4j.driver.internal.summary.InternalServerInfo; import org.neo4j.driver.internal.summary.InternalSummaryCounters; import org.neo4j.driver.internal.util.ServerVersion; -import org.neo4j.driver.Record; -import org.neo4j.driver.Statement; -import org.neo4j.driver.exceptions.NoSuchRecordException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.summary.ResultSummary; import org.neo4j.driver.summary.StatementType; @@ -55,10 +57,10 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.util.Futures.completedWithNull; -import static org.neo4j.driver.internal.util.Futures.failedFuture; import static org.neo4j.driver.Values.value; import static org.neo4j.driver.Values.values; +import static org.neo4j.driver.internal.util.Futures.completedWithNull; +import static org.neo4j.driver.internal.util.Futures.failedFuture; import static org.neo4j.driver.util.TestUtil.await; class AsyncStatementResultCursorTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java new file mode 100644 index 0000000000..6eab9dc858 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncSessionTest.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import org.hamcrest.junit.MatcherAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; +import org.neo4j.driver.async.AsyncSession; +import org.neo4j.driver.async.AsyncTransaction; +import org.neo4j.driver.async.AsyncTransactionWork; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.InternalRecord; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.retry.RetryLogic; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.util.FixedRetryLogic; +import org.neo4j.driver.internal.value.IntegerValue; + +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.AccessMode.READ; +import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.TransactionConfig.empty; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.connectionMock; +import static org.neo4j.driver.util.TestUtil.newSession; +import static org.neo4j.driver.util.TestUtil.setupFailingCommit; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyBeginTx; +import static org.neo4j.driver.util.TestUtil.verifyCommitTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; + +class InternalAsyncSessionTest +{ + private Connection connection; + private ConnectionProvider connectionProvider; + private AsyncSession asyncSession; + private NetworkSession session; + + @BeforeEach + void setUp() + { + connection = connectionMock( BoltProtocolV4.INSTANCE ); + connectionProvider = mock( ConnectionProvider.class ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection ) ); + session = newSession( connectionProvider ); + asyncSession = new InternalAsyncSession( session ); + } + + private static Stream>> allSessionRunMethods() + { + return Stream.of( + session -> session.runAsync( "RETURN 1" ), + session -> session.runAsync( "RETURN $x", parameters( "x", 1 ) ), + session -> session.runAsync( "RETURN $x", singletonMap( "x", 1 ) ), + session -> session.runAsync( "RETURN $x", + new InternalRecord( singletonList( "x" ), new Value[]{new IntegerValue( 1 )} ) ), + session -> session.runAsync( new Statement( "RETURN $x", parameters( "x", 1 ) ) ), + session -> session.runAsync( new Statement( "RETURN $x", parameters( "x", 1 ) ), empty() ), + session -> session.runAsync( "RETURN $x", singletonMap( "x", 1 ), empty() ), + session -> session.runAsync( "RETURN 1", empty() ) + ); + } + + private static Stream>> allBeginTxMethods() + { + return Stream.of( + session -> session.beginTransactionAsync(), + session -> session.beginTransactionAsync( TransactionConfig.empty() ) + ); + } + + private static Stream>> allRunTxMethods() + { + return Stream.of( + session -> session.readTransactionAsync( tx -> completedFuture( "a" ) ), + session -> session.writeTransactionAsync( tx -> completedFuture( "a" ) ), + session -> session.readTransactionAsync( tx -> completedFuture( "a" ), empty() ), + session -> session.writeTransactionAsync( tx -> completedFuture( "a" ), empty() ) + ); + } + + @ParameterizedTest + @MethodSource( "allSessionRunMethods" ) + void shouldFlushOnRun( Function> runReturnOne ) throws Throwable + { + setupSuccessfulRunAndPull( connection ); + + StatementResultCursor cursor = await( runReturnOne.apply( asyncSession ) ); + + verifyRunAndPull( connection, await( cursor.summaryAsync() ).statement().text() ); + } + + @ParameterizedTest + @MethodSource( "allBeginTxMethods" ) + void shouldDelegateBeginTx( Function> beginTx ) throws Throwable + { + AsyncTransaction tx = await( beginTx.apply( asyncSession ) ); + + verifyBeginTx( connection ); + assertNotNull( tx ); + } + + @ParameterizedTest + @MethodSource( "allRunTxMethods" ) + void txRunShouldBeginAndCommitTx( Function> runTx ) throws Throwable + { + String string = await( runTx.apply( asyncSession ) ); + + verifyBeginTx( connection ); + verifyCommitTx( connection ); + verify( connection ).release(); + assertThat( string, equalTo( "a" ) ); + } + + + @Test + void rollsBackReadTxWhenFunctionThrows() + { + testTxRollbackWhenThrows( READ ); + } + + @Test + void rollsBackWriteTxWhenFunctionThrows() + { + testTxRollbackWhenThrows( WRITE ); + } + + @Test + void readTxRetriedUntilSuccessWhenFunctionThrows() + { + testTxIsRetriedUntilSuccessWhenFunctionThrows( READ ); + } + + @Test + void writeTxRetriedUntilSuccessWhenFunctionThrows() + { + testTxIsRetriedUntilSuccessWhenFunctionThrows( WRITE ); + } + + @Test + void readTxRetriedUntilSuccessWhenTxCloseThrows() + { + testTxIsRetriedUntilSuccessWhenCommitThrows( READ ); + } + + @Test + void writeTxRetriedUntilSuccessWhenTxCloseThrows() + { + testTxIsRetriedUntilSuccessWhenCommitThrows( WRITE ); + } + + @Test + void readTxRetriedUntilFailureWhenFunctionThrows() + { + testTxIsRetriedUntilFailureWhenFunctionThrows( READ ); + } + + @Test + void writeTxRetriedUntilFailureWhenFunctionThrows() + { + testTxIsRetriedUntilFailureWhenFunctionThrows( WRITE ); + } + + @Test + void readTxRetriedUntilFailureWhenTxCloseThrows() + { + testTxIsRetriedUntilFailureWhenCommitFails( READ ); + } + + @Test + void writeTxRetriedUntilFailureWhenTxCloseThrows() + { + testTxIsRetriedUntilFailureWhenCommitFails( WRITE ); + } + + + @Test + void shouldCloseSession() throws Throwable + { + await ( asyncSession.closeAsync() ); + assertFalse( this.session.isOpen() ); + } + + @Test + void shouldReturnBookmark() throws Throwable + { + session = newSession( connectionProvider, Bookmarks.from( "Bookmark1" ) ); + asyncSession = new InternalAsyncSession( session ); + + assertThat( asyncSession.lastBookmark(), equalTo( session.lastBookmark() )); + } + + private void testTxRollbackWhenThrows( AccessMode transactionMode ) + { + final RuntimeException error = new IllegalStateException( "Oh!" ); + AsyncTransactionWork> work = tx -> + { + throw error; + }; + + Exception e = assertThrows( Exception.class, () -> executeTransaction( asyncSession, transactionMode, work ) ); + assertEquals( error, e ); + + verify( connectionProvider ).acquireConnection( any( String.class ), eq( transactionMode ) ); + verifyBeginTx( connection ); + verifyRollbackTx( connection ); + } + + private void testTxIsRetriedUntilSuccessWhenFunctionThrows( AccessMode mode ) + { + int failures = 12; + int retries = failures + 1; + + RetryLogic retryLogic = new FixedRetryLogic( retries ); + session = newSession( connectionProvider, retryLogic ); + asyncSession = new InternalAsyncSession( session ); + + TxWork work = spy( new TxWork( 42, failures, new SessionExpiredException( "" ) ) ); + int answer = executeTransaction( asyncSession, mode, work ); + + assertEquals( 42, answer ); + verifyInvocationCount( work, failures + 1 ); + verifyCommitTx( connection ); + verifyRollbackTx( connection, times( failures ) ); + } + + private void testTxIsRetriedUntilSuccessWhenCommitThrows( AccessMode mode ) + { + int failures = 13; + int retries = failures + 1; + + RetryLogic retryLogic = new FixedRetryLogic( retries ); + setupFailingCommit( connection, failures ); + session = newSession( connectionProvider, retryLogic ); + asyncSession = new InternalAsyncSession( session ); + + TxWork work = spy( new TxWork( 43 ) ); + int answer = executeTransaction( asyncSession, mode, work ); + + assertEquals( 43, answer ); + verifyInvocationCount( work, failures + 1 ); + verifyCommitTx( connection, times( retries ) ); + } + + private void testTxIsRetriedUntilFailureWhenFunctionThrows( AccessMode mode ) + { + int failures = 14; + int retries = failures - 1; + + RetryLogic retryLogic = new FixedRetryLogic( retries ); + session = newSession( connectionProvider, retryLogic ); + asyncSession = new InternalAsyncSession( session ); + + TxWork work = spy( new TxWork( 42, failures, new SessionExpiredException( "Oh!" ) ) ); + + Exception e = assertThrows( Exception.class, () -> executeTransaction( asyncSession, mode, work ) ); + + MatcherAssert.assertThat( e, instanceOf( SessionExpiredException.class ) ); + assertEquals( "Oh!", e.getMessage() ); + verifyInvocationCount( work, failures ); + verifyCommitTx( connection, never() ); + verifyRollbackTx( connection, times( failures ) ); + } + + private void testTxIsRetriedUntilFailureWhenCommitFails( AccessMode mode ) + { + int failures = 17; + int retries = failures - 1; + + RetryLogic retryLogic = new FixedRetryLogic( retries ); + setupFailingCommit( connection, failures ); + session = newSession( connectionProvider, retryLogic ); + asyncSession = new InternalAsyncSession( session ); + + TxWork work = spy( new TxWork( 42 ) ); + + Exception e = assertThrows( Exception.class, () -> executeTransaction( asyncSession, mode, work ) ); + + MatcherAssert.assertThat( e, instanceOf( ServiceUnavailableException.class ) ); + verifyInvocationCount( work, failures ); + verifyCommitTx( connection, times( failures ) ); + } + + private static T executeTransaction( AsyncSession session, AccessMode mode, AsyncTransactionWork> work ) + { + if ( mode == READ ) + { + return await( session.readTransactionAsync( work ) ); + } + else if ( mode == WRITE ) + { + return await( session.writeTransactionAsync( work ) ); + } + else + { + throw new IllegalArgumentException( "Unknown mode " + mode ); + } + } + + private static void verifyInvocationCount( AsyncTransactionWork workSpy, int expectedInvocationCount ) + { + verify( workSpy, times( expectedInvocationCount ) ).execute( any( AsyncTransaction.class ) ); + } + + private static class TxWork implements AsyncTransactionWork> + { + final int result; + final int timesToThrow; + final Supplier errorSupplier; + + int invoked; + + @SuppressWarnings( "unchecked" ) + TxWork( int result ) + { + this( result, 0, (Supplier) null ); + } + + TxWork( int result, int timesToThrow, final RuntimeException error ) + { + this.result = result; + this.timesToThrow = timesToThrow; + this.errorSupplier = () -> error; + } + + TxWork( int result, int timesToThrow, Supplier errorSupplier ) + { + this.result = result; + this.timesToThrow = timesToThrow; + this.errorSupplier = errorSupplier; + } + + @Override + public CompletionStage execute( AsyncTransaction tx ) + { + if ( timesToThrow > 0 && invoked++ < timesToThrow ) + { + throw errorSupplier.get(); + } + return completedFuture( result ); + } + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java new file mode 100644 index 0000000000..fca3c84275 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalAsyncTransactionTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.Value; +import org.neo4j.driver.async.AsyncTransaction; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.InternalRecord; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.value.IntegerValue; +import org.neo4j.driver.summary.ResultSummary; + +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.connectionMock; +import static org.neo4j.driver.util.TestUtil.newSession; +import static org.neo4j.driver.util.TestUtil.setupFailingCommit; +import static org.neo4j.driver.util.TestUtil.setupFailingRollback; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyCommitTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; + +class InternalAsyncTransactionTest +{ + private Connection connection; + private InternalAsyncTransaction tx; + + @BeforeEach + void setUp() + { + connection = connectionMock( BoltProtocolV4.INSTANCE ); + ConnectionProvider connectionProvider = mock( ConnectionProvider.class ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection ) ); + InternalAsyncSession session = new InternalAsyncSession( newSession( connectionProvider ) ); + tx = (InternalAsyncTransaction) await( session.beginTransactionAsync() ); + } + + private static Stream>> allSessionRunMethods() + { + return Stream.of( + tx -> tx.runAsync( "RETURN 1" ), + tx -> tx.runAsync( "RETURN $x", parameters( "x", 1 ) ), + tx -> tx.runAsync( "RETURN $x", singletonMap( "x", 1 ) ), + tx -> tx.runAsync( "RETURN $x", + new InternalRecord( singletonList( "x" ), new Value[]{new IntegerValue( 1 )} ) ), + tx -> tx.runAsync( new Statement( "RETURN $x", parameters( "x", 1 ) ) ) + ); + } + + @ParameterizedTest + @MethodSource( "allSessionRunMethods" ) + void shouldFlushOnRun( Function> runReturnOne ) throws Throwable + { + setupSuccessfulRunAndPull( connection ); + + StatementResultCursor result = await( runReturnOne.apply( tx ) ); + ResultSummary summary = await( result.summaryAsync() ); + + verifyRunAndPull( connection, summary.statement().text() ); + } + + @Test + void shouldCommit() throws Throwable + { + await( tx.commitAsync() ); + + verifyCommitTx( connection ); + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldRollback() throws Throwable + { + await( tx.rollbackAsync() ); + + verifyRollbackTx( connection ); + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldRollbackWhenFailedRun() throws Throwable + { + tx.markTerminated(); + ClientException clientException = assertThrows( ClientException.class, () -> await( tx.commitAsync() ) ); + + assertThat( clientException.getMessage(), containsString( "It has been rolled back either because of an error or explicit termination" ) ); + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldReleaseConnectionWhenFailedToCommit() throws Throwable + { + setupFailingCommit( connection ); + assertThrows( Exception.class, () -> await( tx.commitAsync() ) ); + + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } + + @Test + void shouldReleaseConnectionWhenFailedToRollback() throws Throwable + { + setupFailingRollback( connection ); + assertThrows( Exception.class, () -> await( tx.rollbackAsync() ) ); + + verify( connection ).release(); + assertFalse( tx.isOpen() ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/ExplicitTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java similarity index 73% rename from driver/src/test/java/org/neo4j/driver/internal/messaging/v2/ExplicitTransactionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java index d62a912039..ea3fdaa6a1 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/ExplicitTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java @@ -16,24 +16,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.messaging.v2; +package org.neo4j.driver.internal.async; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InOrder; import java.util.function.Consumer; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.DefaultBookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; import org.neo4j.driver.internal.messaging.request.PullAllMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.util.TestUtil; import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,16 +46,49 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.connectionMock; import static org.neo4j.driver.util.TestUtil.runMessageWithStatementMatcher; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRun; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyRun; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; -class ExplicitTransactionTest +class InternalExplicitTransactionTest { + @ParameterizedTest + @ValueSource( strings = {"true", "false"} ) + void shouldFlushOnRunAsync( boolean waitForResponse ) + { + // Given + Connection connection = connectionMock( BoltProtocolV4.INSTANCE ); + ExplicitTransaction tx = beginTx( connection ); + setupSuccessfulRunAndPull( connection ); + + // When + await( tx.runAsync( new Statement( "RETURN 1" ), waitForResponse ) ); + + // Then + verifyRunAndPull( connection, "RETURN 1" ); + } + + @Test + void shouldFlushOnRunRx() + { + // Given + Connection connection = connectionMock( BoltProtocolV4.INSTANCE ); + ExplicitTransaction tx = beginTx( connection ); + setupSuccessfulRun( connection ); + + // When + await( tx.runRx( new Statement( "RETURN 1" ) ) ); + + // Then + verifyRun( connection, "RETURN 1" ); + } + @Test void shouldRollbackOnImplicitFailure() { @@ -63,7 +97,7 @@ void shouldRollbackOnImplicitFailure() ExplicitTransaction tx = beginTx( connection ); // When - tx.close(); + await( tx.closeAsync() ); // Then InOrder order = inOrder( connection ); @@ -82,7 +116,7 @@ void shouldRollbackOnExplicitFailure() // When tx.failure(); tx.success(); // even if success is called after the failure call! - tx.close(); + await( tx.closeAsync() ); // Then InOrder order = inOrder( connection ); @@ -100,7 +134,7 @@ void shouldCommitOnSuccess() // When tx.success(); - tx.close(); + await( tx.closeAsync() ); // Then InOrder order = inOrder( connection ); @@ -136,7 +170,7 @@ void shouldFlushWhenBookmarkGiven() @Test void shouldBeOpenAfterConstruction() { - Transaction tx = beginTx( connectionMock() ); + ExplicitTransaction tx = beginTx( connectionMock() ); assertTrue( tx.isOpen() ); } @@ -144,7 +178,7 @@ void shouldBeOpenAfterConstruction() @Test void shouldBeOpenWhenMarkedForSuccess() { - Transaction tx = beginTx( connectionMock() ); + ExplicitTransaction tx = beginTx( connectionMock() ); tx.success(); @@ -154,7 +188,7 @@ void shouldBeOpenWhenMarkedForSuccess() @Test void shouldBeOpenWhenMarkedForFailure() { - Transaction tx = beginTx( connectionMock() ); + ExplicitTransaction tx = beginTx( connectionMock() ); tx.failure(); @@ -174,10 +208,10 @@ void shouldBeClosedWhenMarkedAsTerminated() @Test void shouldBeClosedAfterCommit() { - Transaction tx = beginTx( connectionMock() ); + ExplicitTransaction tx = beginTx( connectionMock() ); tx.success(); - tx.close(); + await( tx.closeAsync() ); assertFalse( tx.isOpen() ); } @@ -185,10 +219,10 @@ void shouldBeClosedAfterCommit() @Test void shouldBeClosedAfterRollback() { - Transaction tx = beginTx( connectionMock() ); + ExplicitTransaction tx = beginTx( connectionMock() ); tx.failure(); - tx.close(); + await( tx.closeAsync() ); assertFalse( tx.isOpen() ); } @@ -199,7 +233,7 @@ void shouldBeClosedWhenMarkedTerminatedAndClosed() ExplicitTransaction tx = beginTx( connectionMock() ); tx.markTerminated(); - tx.close(); + await( tx.closeAsync() ); assertFalse( tx.isOpen() ); } @@ -209,7 +243,7 @@ void shouldReleaseConnectionWhenBeginFails() { RuntimeException error = new RuntimeException( "Wrong bookmark!" ); Connection connection = connectionWithBegin( handler -> handler.onFailure( error ) ); - ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); Bookmarks bookmarks = Bookmarks.from( "SomeBookmark" ); TransactionConfig txConfig = TransactionConfig.empty(); @@ -224,7 +258,7 @@ void shouldReleaseConnectionWhenBeginFails() void shouldNotReleaseConnectionWhenBeginSucceeds() { Connection connection = connectionWithBegin( handler -> handler.onSuccess( emptyMap() ) ); - ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); Bookmarks bookmarks = Bookmarks.from( "SomeBookmark" ); TransactionConfig txConfig = TransactionConfig.empty(); @@ -238,7 +272,7 @@ void shouldNotReleaseConnectionWhenBeginSucceeds() void shouldReleaseConnectionWhenTerminatedAndCommitted() { Connection connection = connectionMock(); - ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); tx.markTerminated(); @@ -252,7 +286,7 @@ void shouldReleaseConnectionWhenTerminatedAndCommitted() void shouldReleaseConnectionWhenTerminatedAndRolledBack() { Connection connection = connectionMock(); - ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); tx.markTerminated(); await( tx.rollbackAsync() ); @@ -260,6 +294,17 @@ void shouldReleaseConnectionWhenTerminatedAndRolledBack() verify( connection ).release(); } + @Test + void shouldReleaseConnectionWhenClose() throws Throwable + { + Connection connection = connectionMock(); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + + await( tx.closeAsync() ); + + verify( connection ).release(); + } + private static ExplicitTransaction beginTx( Connection connection ) { return beginTx( connection, Bookmarks.empty() ); @@ -267,20 +312,13 @@ private static ExplicitTransaction beginTx( Connection connection ) private static ExplicitTransaction beginTx( Connection connection, Bookmarks initialBookmarks ) { - ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); return await( tx.beginAsync( initialBookmarks, TransactionConfig.empty() ) ); } - static Connection connectionMock() - { - return TestUtil.connectionMock( BoltProtocolV2.INSTANCE ); - } - private static Connection connectionWithBegin( Consumer beginBehaviour ) { - Connection connection = mock( Connection.class ); - when( connection.protocol() ).thenReturn( BoltProtocolV2.INSTANCE ); - when( connection.databaseName() ).thenReturn( ABSENT_DB_NAME ); + Connection connection = connectionMock(); doAnswer( invocation -> { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java new file mode 100644 index 0000000000..fe3c3d44eb --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InOrder; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.messaging.BoltProtocol; +import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; + +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.junit.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.AccessMode.READ; +import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; +import static org.neo4j.driver.internal.util.Futures.failedFuture; +import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.connectionMock; +import static org.neo4j.driver.util.TestUtil.newSession; +import static org.neo4j.driver.util.TestUtil.setupFailingBegin; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRun; +import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; +import static org.neo4j.driver.util.TestUtil.verifyBeginTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; +import static org.neo4j.driver.util.TestUtil.verifyRun; +import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; + +class InternalNetworkSessionTest +{ + private Connection connection; + private ConnectionProvider connectionProvider; + private InternalNetworkSession session; + + @BeforeEach + void setUp() + { + connection = connectionMock( BoltProtocolV4.INSTANCE ); + connectionProvider = mock( ConnectionProvider.class ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection ) ); + session = newSession( connectionProvider ); + } + + @ParameterizedTest + @ValueSource( strings = {"true", "false"} ) + void shouldFlushOnRunAsync( boolean waitForResponse ) + { + setupSuccessfulRunAndPull( connection ); + await( session.runAsync( new Statement( "RETURN 1" ), TransactionConfig.empty(), waitForResponse ) ); + + verifyRunAndPull( connection, "RETURN 1" ); + } + + @Test + void shouldFlushOnRunRx() + { + setupSuccessfulRun( connection ); + await( session.runRx( new Statement( "RETURN 1" ), TransactionConfig.empty() ) ); + + verifyRun( connection, "RETURN 1" ); + } + + @Test + void shouldNotAllowNewTxWhileOneIsRunning() + { + // Given + beginTransaction( session ); + + // Expect + assertThrows( ClientException.class, () -> beginTransaction( session ) ); + } + + @Test + void shouldBeAbleToOpenTxAfterPreviousIsClosed() + { + // Given + await( beginTransaction( session ).closeAsync() ); + + // When + ExplicitTransaction tx = beginTransaction( session ); + + // Then we should've gotten a transaction object back + assertNotNull( tx ); + verifyRollbackTx( connection ); + } + + @Test + void shouldNotBeAbleToUseSessionWhileOngoingTransaction() + { + // Given + beginTransaction( session ); + + // Expect + assertThrows( ClientException.class, () -> run( session, "RETURN 1" ) ); + } + + @Test + void shouldBeAbleToUseSessionAgainWhenTransactionIsClosed() + { + // Given + await ( beginTransaction( session ).closeAsync() ); + + // When + run( session, "RETURN 1" ); + + // Then + verifyRunAndPull( connection, "RETURN 1" ); + } + + @Test + void shouldNotCloseAlreadyClosedSession() + { + beginTransaction( session ); + + close( session ); + close( session ); + close( session ); + + verifyRollbackTx( connection ); + } + + @Test + void runThrowsWhenSessionIsClosed() + { + close( session ); + + Exception e = assertThrows( Exception.class, () -> run( session, "CREATE ()" ) ); + assertThat( e, instanceOf( ClientException.class ) ); + assertThat( e.getMessage(), containsString( "session is already closed" ) ); + } + + @Test + void acquiresNewConnectionForRun() + { + run( session, "RETURN 1" ); + + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + } + + @Test + void releasesOpenConnectionUsedForRunWhenSessionIsClosed() + { + String query = "RETURN 1"; + setupSuccessfulRunAndPull( connection, query ); + + run( session, query ); + + close( session ); + + InOrder inOrder = inOrder( connection ); + inOrder.verify( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any(), any( PullMessage.class ), any() ); + inOrder.verify( connection, atLeastOnce() ).release(); + } + + @SuppressWarnings( "deprecation" ) + @Test + void resetDoesNothingWhenNoTransactionAndNoConnection() + { + await( session.resetAsync() ); + + verify( connectionProvider, never() ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + } + + @Test + void closeWithoutConnection() + { + NetworkSession session = newSession( connectionProvider ); + + close( session ); + + verify( connectionProvider, never() ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + } + + @Test + void acquiresNewConnectionForBeginTx() + { + ExplicitTransaction tx = beginTransaction( session ); + + assertNotNull( tx ); + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + } + + @Test + void updatesBookmarkWhenTxIsClosed() + { + Bookmarks bookmarkAfterCommit = Bookmarks.from( "TheBookmark" ); + + BoltProtocol protocol = spy( BoltProtocolV4.INSTANCE ); + doReturn( completedFuture( bookmarkAfterCommit ) ).when( protocol ).commitTransaction( any( Connection.class ) ); + + when( connection.protocol() ).thenReturn( protocol ); + + ExplicitTransaction tx = beginTransaction( session ); + assertNull( session.lastBookmark() ); + + tx.success(); + await( tx.closeAsync() ); + assertEquals( "TheBookmark", session.lastBookmark() ); + } + + @Test + void releasesConnectionWhenTxIsClosed() + { + String query = "RETURN 42"; + setupSuccessfulRunAndPull( connection, query ); + + ExplicitTransaction tx = beginTransaction( session ); + await( tx.runAsync( new Statement( query ), false ) ); + + verify( connectionProvider ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyRunAndPull( connection, query ); + + await( tx.closeAsync() ); + verify( connection ).release(); + } + + @Test + void bookmarkIsPropagatedFromSession() + { + Bookmarks bookmarks = Bookmarks.from( "Bookmarks" ); + NetworkSession session = newSession( connectionProvider, bookmarks ); + + ExplicitTransaction tx = beginTransaction( session ); + assertNotNull( tx ); + verifyBeginTx( connection, bookmarks ); + } + + @Test + void bookmarkIsPropagatedBetweenTransactions() + { + Bookmarks bookmarks1 = Bookmarks.from( "Bookmark1" ); + Bookmarks bookmarks2 = Bookmarks.from( "Bookmark2" ); + + NetworkSession session = newSession( connectionProvider ); + + BoltProtocol protocol = spy( BoltProtocolV4.INSTANCE ); + doReturn( completedFuture( bookmarks1 ), completedFuture( bookmarks2 ) ).when( protocol ).commitTransaction( any( Connection.class ) ); + + when( connection.protocol() ).thenReturn( protocol ); + + ExplicitTransaction tx1 = beginTransaction( session ); + tx1.success(); + await( tx1.closeAsync() ); + assertEquals( bookmarks1, Bookmarks.from( session.lastBookmark() ) ); + + ExplicitTransaction tx2 = beginTransaction( session ); + verifyBeginTx( connection, bookmarks1 ); + tx2.success(); + await( tx2.closeAsync() ); + + assertEquals( bookmarks2, Bookmarks.from( session.lastBookmark() ) ); + } + + @Test + void accessModeUsedToAcquireConnections() + { + NetworkSession session1 = newSession( connectionProvider, READ ); + beginTransaction( session1 ); + verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, READ ); + + NetworkSession session2 = newSession( connectionProvider, WRITE ); + beginTransaction( session2 ); + verify( connectionProvider ).acquireConnection( ABSENT_DB_NAME, WRITE ); + } + + @Test + void testPassingNoBookmarkShouldRetainBookmark() + { + NetworkSession session = newSession( connectionProvider, Bookmarks.from( "X" ) ); + beginTransaction( session ); + assertThat( session.lastBookmark(), equalTo( "X" ) ); + } + + @Test + void connectionShouldBeResetAfterSessionReset() + { + run( session, "RETURN 1" ); + + verify( connection, never() ).reset(); + verify( connection, never() ).release(); + + await( session.resetAsync() ); + verify( connection ).reset(); + verify( connection, never() ).release(); + } + + @Test + void shouldHaveNullLastBookmarkInitially() + { + assertNull( session.lastBookmark() ); + } + + @Test + void shouldDoNothingWhenClosingWithoutAcquiredConnection() + { + RuntimeException error = new RuntimeException( "Hi" ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ).thenReturn( failedFuture( error ) ); + + Exception e = assertThrows( Exception.class, () -> run( session, "RETURN 1" ) ); + assertEquals( error, e ); + + close( session ); + } + + @Test + void shouldRunAfterRunFailureToAcquireConnection() + { + RuntimeException error = new RuntimeException( "Hi" ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( failedFuture( error ) ).thenReturn( completedFuture( connection ) ); + + Exception e = assertThrows( Exception.class, () -> run( session,"RETURN 1" ) ); + assertEquals( error, e ); + + run( session, "RETURN 2" ); + + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyRunAndPull( connection, "RETURN 2" ); + } + + @Test + void shouldRunAfterBeginTxFailureOnBookmark() + { + RuntimeException error = new RuntimeException( "Hi" ); + Connection connection1 = connectionMock( BoltProtocolV4.INSTANCE ); + setupFailingBegin( connection1, error ); + Connection connection2 = connectionMock( BoltProtocolV4.INSTANCE ); + + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection1 ) ).thenReturn( completedFuture( connection2 ) ); + + Bookmarks bookmarks = Bookmarks.from( "neo4j:bookmark:v1:tx42" ); + NetworkSession session = newSession( connectionProvider, bookmarks ); + + Exception e = assertThrows( Exception.class, () -> beginTransaction( session ) ); + assertEquals( error, e ); + + run( session, "RETURN 2" ); + + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyBeginTx( connection1, bookmarks ); + verifyRunAndPull( connection2, "RETURN 2" ); + } + + @Test + void shouldBeginTxAfterBeginTxFailureOnBookmark() + { + RuntimeException error = new RuntimeException( "Hi" ); + Connection connection1 = connectionMock( BoltProtocolV4.INSTANCE ); + setupFailingBegin( connection1, error ); + Connection connection2 = connectionMock( BoltProtocolV4.INSTANCE ); + + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( completedFuture( connection1 ) ).thenReturn( completedFuture( connection2 ) ); + + Bookmarks bookmarks = Bookmarks.from( "neo4j:bookmark:v1:tx42" ); + NetworkSession session = newSession( connectionProvider, bookmarks ); + + Exception e = assertThrows( Exception.class, () -> beginTransaction( session ) ); + assertEquals( error, e ); + + beginTransaction( session ); + + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyBeginTx( connection1, bookmarks ); + verifyBeginTx( connection2, bookmarks ); + } + + @Test + void shouldBeginTxAfterRunFailureToAcquireConnection() + { + RuntimeException error = new RuntimeException( "Hi" ); + when( connectionProvider.acquireConnection( any( String.class ), any( AccessMode.class ) ) ) + .thenReturn( failedFuture( error ) ).thenReturn( completedFuture( connection ) ); + + Exception e = assertThrows( Exception.class, () -> run( session, "RETURN 1" ) ); + assertEquals( error, e ); + + beginTransaction( session ); + + verify( connectionProvider, times( 2 ) ).acquireConnection( any( String.class ), any( AccessMode.class ) ); + verifyBeginTx( connection ); + } + + @Test + void shouldMarkTransactionAsTerminatedAndThenResetConnectionOnReset() + { + ExplicitTransaction tx = beginTransaction( session ); + + assertTrue( tx.isOpen() ); + verify( connection, never() ).reset(); + + await( session.resetAsync() ); + + verify( connection ).reset(); + } + + private static StatementResultCursor run( NetworkSession session, String statement ) + { + return await( session.runAsync( new Statement( statement ), TransactionConfig.empty(), false ) ); + } + + private static ExplicitTransaction beginTransaction( NetworkSession session ) + { + return await( session.beginTransactionAsync( TransactionConfig.empty() ) ); + } + + private static void close( NetworkSession session ) + { + await( session.closeAsync() ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/LeakLoggingNetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java similarity index 93% rename from driver/src/test/java/org/neo4j/driver/internal/LeakLoggingNetworkSessionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java index c2d5569e54..5d06ba5b36 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/LeakLoggingNetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal; +package org.neo4j.driver.internal.async; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -24,13 +24,14 @@ import java.lang.reflect.Method; -import org.neo4j.driver.internal.util.FixedRetryLogic; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.AccessMode; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; -import org.neo4j.driver.Session; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.internal.DefaultBookmarksHolder; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.util.FixedRetryLogic; import org.neo4j.driver.util.TestUtil; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -47,7 +48,7 @@ import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.util.TestUtil.DEFAULT_TEST_PROTOCOL; -class LeakLoggingNetworkSessionTest +class LeakLoggingInternalNetworkSessionTest { @Test void logsNothingDuringFinalizationIfClosed() throws Exception @@ -70,7 +71,7 @@ void logsMessageWithStacktraceDuringFinalizationIfLeaked( TestInfo testInfo ) th when( logging.getLog( anyString() ) ).thenReturn( log ); LeakLoggingNetworkSession session = newSession( logging, true ); // begin transaction to make session obtain a connection - session.beginTransaction(); + session.beginTransactionAsync( TransactionConfig.empty() ); finalize( session ); @@ -87,7 +88,7 @@ void logsMessageWithStacktraceDuringFinalizationIfLeaked( TestInfo testInfo ) th ); } - private static void finalize( Session session ) throws Exception + private static void finalize( NetworkSession session ) throws Exception { Method finalizeMethod = session.getClass().getDeclaredMethod( "finalize" ); finalizeMethod.setAccessible( true ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ResultCursorsHolderTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/ResultCursorsHolderTest.java index e2fea8953d..24dfac964a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ResultCursorsHolderTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/ResultCursorsHolderTest.java @@ -25,7 +25,6 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeoutException; -import org.neo4j.driver.internal.AsyncStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import static java.util.concurrent.CompletableFuture.completedFuture; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/BoltProtocolUtilTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java similarity index 80% rename from driver/src/test/java/org/neo4j/driver/internal/async/BoltProtocolUtilTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java index 70b6bf1014..76bb5434c7 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/BoltProtocolUtilTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -28,12 +28,12 @@ import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.BOLT_MAGIC_PREAMBLE; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.handshakeBuf; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.handshakeString; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.writeChunkHeader; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.writeEmptyChunkHeader; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.writeMessageBoundary; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.BOLT_MAGIC_PREAMBLE; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.handshakeBuf; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.handshakeString; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.writeChunkHeader; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.writeEmptyChunkHeader; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.writeMessageBoundary; import static org.neo4j.driver.util.TestUtil.assertByteBufContains; class BoltProtocolUtilTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelAttributesTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java similarity index 76% rename from driver/src/test/java/org/neo4j/driver/internal/async/ChannelAttributesTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java index c870332c8c..b7e6d2de8f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelAttributesTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.Test; @@ -29,22 +29,22 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; -import static org.neo4j.driver.internal.async.ChannelAttributes.connectionId; -import static org.neo4j.driver.internal.async.ChannelAttributes.creationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.lastUsedTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.protocolVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverAddress; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setConnectionId; -import static org.neo4j.driver.internal.async.ChannelAttributes.setCreationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.setLastUsedTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setProtocolVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerAddress; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setTerminationReason; -import static org.neo4j.driver.internal.async.ChannelAttributes.terminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.connectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.creationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.lastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.protocolVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setCreationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setLastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setTerminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.terminationReason; import static org.neo4j.driver.internal.util.ServerVersion.version; class ChannelAttributesTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListenerTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectedListenerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListenerTest.java index f3c9a0d889..f45f316ad2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectedListenerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; @@ -32,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.neo4j.driver.internal.BoltServerAddress.LOCAL_DEFAULT; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.handshakeBuf; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.handshakeBuf; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.util.TestUtil.await; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectorImplIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImplIT.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectorImplIT.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImplIT.java index 504238a6c4..53aabadafc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelConnectorImplIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImplIT.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; @@ -36,15 +36,15 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.exceptions.AuthenticationException; +import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.async.inbound.ConnectTimeoutHandler; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.FakeClock; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.exceptions.AuthenticationException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelErrorHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelErrorHandlerTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/async/ChannelErrorHandlerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelErrorHandlerTest.java index 6e252f41df..ed2d1c2288 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelErrorHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelErrorHandlerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; @@ -36,8 +36,8 @@ import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setTerminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setTerminationReason; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; class ChannelErrorHandlerTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java similarity index 97% rename from driver/src/test/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImplTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java index b8c1262bbf..cddb0bcf2f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/ChannelPipelineBuilderImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelHandler; import io.netty.channel.embedded.EmbeddedChannel; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/DecoratedConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DecoratedConnectionTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/async/DecoratedConnectionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/DecoratedConnectionTest.java index 5e9f6d0bdf..db0ae23915 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/DecoratedConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DecoratedConnectionTest.java @@ -16,20 +16,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; +import org.neo4j.driver.AccessMode; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.ServerVersion; -import org.neo4j.driver.AccessMode; import org.neo4j.driver.net.ServerAddress; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/DirectConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/async/DirectConnectionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java index 16df5f9c0d..08a65be48a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/DirectConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.Channel; import io.netty.channel.DefaultEventLoop; @@ -56,8 +56,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.terminationReason; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.terminationReason; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; import static org.neo4j.driver.internal.messaging.request.ResetMessage.RESET; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/EventLoopGroupFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactoryTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/async/EventLoopGroupFactoryTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactoryTest.java index 044571e6a5..bd48766610 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/EventLoopGroupFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/EventLoopGroupFactoryTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/HandshakeCompletedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/async/HandshakeCompletedListenerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java index fce29ba32e..0db2e11191 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/HandshakeCompletedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.handlers.HelloResponseHandler; import org.neo4j.driver.internal.handlers.InitResponseHandler; @@ -37,7 +38,6 @@ import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.Value; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -45,9 +45,9 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setProtocolVersion; import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; import static org.neo4j.driver.util.TestUtil.await; class HandshakeCompletedListenerTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/HandshakeHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java similarity index 97% rename from driver/src/test/java/org/neo4j/driver/internal/async/HandshakeHandlerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java index 39df7ffd54..26ae04c27c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/HandshakeHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; @@ -29,6 +29,10 @@ import java.io.IOException; import javax.net.ssl.SSLHandshakeException; +import org.neo4j.driver.Logging; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.exceptions.SecurityException; +import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.async.inbound.ChunkDecoder; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.async.inbound.InboundMessageHandler; @@ -40,10 +44,6 @@ import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v2.MessageFormatV2; import org.neo4j.driver.internal.util.ErrorUtil; -import org.neo4j.driver.Logging; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import static io.netty.buffer.Unpooled.copyInt; import static org.hamcrest.Matchers.instanceOf; @@ -53,9 +53,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.HTTP; -import static org.neo4j.driver.internal.async.BoltProtocolUtil.NO_PROTOCOL_VERSION; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.HTTP; +import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.NO_PROTOCOL_VERSION; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.util.TestUtil.await; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/NettyChannelInitializerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializerTest.java similarity index 95% rename from driver/src/test/java/org/neo4j/driver/internal/async/NettyChannelInitializerTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializerTest.java index 70f95073a0..9b2923502a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/NettyChannelInitializerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/NettyChannelInitializerTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.ssl.SslHandler; @@ -45,9 +45,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.neo4j.driver.internal.BoltServerAddress.LOCAL_DEFAULT; -import static org.neo4j.driver.internal.async.ChannelAttributes.creationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.creationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; class NettyChannelInitializerTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/RoutingConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/async/RoutingConnectionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java index 6b7e4ea8d2..1f728c5673 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/RoutingConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.async; +package org.neo4j.driver.internal.async.connection; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -31,9 +31,9 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.neo4j.driver.AccessMode.READ; import static org.neo4j.driver.internal.messaging.request.DiscardAllMessage.DISCARD_ALL; import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; -import static org.neo4j.driver.AccessMode.READ; class RoutingConnectionTest { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java index fecc7ad89f..faa9609c4a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java @@ -29,7 +29,7 @@ import java.util.HashMap; import java.util.Map; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.util.messaging.KnowledgeableMessageFormat; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.MessageFormat.Reader; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java index 5fa8190fcf..85541e1068 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java @@ -29,7 +29,7 @@ import java.util.HashMap; import java.util.Map; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.ChannelErrorHandler; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.messaging.Message; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java index b200c67944..cdcdd433b2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java @@ -33,9 +33,9 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; -import org.neo4j.driver.internal.async.BootstrapFactory; -import org.neo4j.driver.internal.async.ChannelConnector; -import org.neo4j.driver.internal.async.ChannelConnectorImpl; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; +import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.FakeClock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthCheckerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthCheckerTest.java index fca3592202..397abfc8cb 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthCheckerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelHealthCheckerTest.java @@ -35,9 +35,9 @@ import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.neo4j.driver.internal.async.ChannelAttributes.setCreationTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.setLastUsedTimestamp; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setCreationTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setLastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_CONNECTION_ACQUISITION_TIMEOUT; import static org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST; import static org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_MAX_CONNECTION_POOL_SIZE; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java index 7eeddc2899..af97f883d0 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java @@ -34,8 +34,8 @@ import java.util.concurrent.TimeoutException; import org.neo4j.driver.internal.ConnectionSettings; -import org.neo4j.driver.internal.async.BootstrapFactory; -import org.neo4j.driver.internal.async.ChannelConnectorImpl; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; +import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.FakeClock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelTrackerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelTrackerTest.java index a2e65f8c33..e326836a0c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelTrackerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelTrackerTest.java @@ -37,9 +37,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setProtocolVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setServerAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAddress; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.metrics.InternalAbstractMetrics.DEV_NULL_METRICS; diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java index 7a78f6e91d..38ca2bbc6c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java @@ -30,7 +30,7 @@ import java.util.Set; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.DecoratedConnection; +import org.neo4j.driver.internal.async.connection.DecoratedConnection; import org.neo4j.driver.internal.cluster.AddressSet; import org.neo4j.driver.internal.cluster.ClusterComposition; import org.neo4j.driver.internal.cluster.ClusterRoutingTable; diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactoryTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactoryTest.java rename to driver/src/test/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactoryTest.java index fbdad3395f..7f6df1aaa4 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/AsyncResultCursorOnlyFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cursor/AsyncResultCursorOnlyFactoryTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -27,7 +27,7 @@ import java.util.concurrent.CompletionStage; import java.util.stream.Stream; -import org.neo4j.driver.internal.AsyncStatementResultCursor; +import org.neo4j.driver.internal.async.AsyncStatementResultCursor; import org.neo4j.driver.internal.handlers.AbstractPullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.Message; diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactoryTest.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactoryTest.java rename to driver/src/test/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactoryTest.java index 97630de10b..0bf373d570 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/InternalStatementResultCursorFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cursor/InternalStatementResultCursorFactoryTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -27,7 +27,7 @@ import java.util.concurrent.CompletionStage; import java.util.stream.Stream; -import org.neo4j.driver.internal.AsyncStatementResultCursor; +import org.neo4j.driver.internal.async.AsyncStatementResultCursor; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursorTest.java b/driver/src/test/java/org/neo4j/driver/internal/cursor/RxStatementResultCursorTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursorTest.java rename to driver/src/test/java/org/neo4j/driver/internal/cursor/RxStatementResultCursorTest.java index 9372d9f978..a302781a51 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/cursor/RxStatementResultCursorTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cursor/RxStatementResultCursorTest.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.reactive.cursor; +package org.neo4j.driver.internal.cursor; import org.junit.jupiter.api.Test; diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandlerTest.java index 9529529871..601df886cb 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/ChannelReleasingResetResponseHandlerTest.java @@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.async.ChannelAttributes.lastUsedTimestamp; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.lastUsedTimestamp; class ChannelReleasingResetResponseHandlerTest { diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java index 09f3eae845..74b13915d7 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java @@ -43,9 +43,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.neo4j.driver.internal.async.ChannelAttributes.connectionId; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.connectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.outbound.OutboundMessageHandler.NAME; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.Values.value; diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java index 7581348757..1a5665ed87 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java @@ -46,8 +46,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.neo4j.driver.internal.async.ChannelAttributes.serverVersion; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.outbound.OutboundMessageHandler.NAME; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.Values.value; diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandlerTest.java index fc5b7ea992..898c12d0bc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullAllResponseHandlerTest.java @@ -23,15 +23,15 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; -import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.Statement; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.exceptions.TransientException; +import org.neo4j.driver.internal.BoltServerAddress; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.ServerVersion; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandlerTest.java index d6407a598f..a0b5c0e324 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/TransactionPullResponseHandlerTest.java @@ -21,12 +21,12 @@ import java.util.Collections; import java.util.function.BiConsumer; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.Record; +import org.neo4j.driver.Statement; +import org.neo4j.driver.internal.async.ExplicitTransaction; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.Record; -import org.neo4j.driver.Statement; import org.neo4j.driver.summary.ResultSummary; import static org.hamcrest.CoreMatchers.equalTo; diff --git a/driver/src/test/java/org/neo4j/driver/internal/logging/ChannelActivityLoggerTest.java b/driver/src/test/java/org/neo4j/driver/internal/logging/ChannelActivityLoggerTest.java index c561d4e40b..47587d4adc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/logging/ChannelActivityLoggerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/logging/ChannelActivityLoggerTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.Logging; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java index 6606746337..cde78822a6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java @@ -30,7 +30,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.neo4j.driver.internal.async.ChannelAttributes.setProtocolVersion; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; class BoltProtocolTest { diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java index 67e64c3005..e12bb2001e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java @@ -29,8 +29,8 @@ import java.util.List; import java.util.Map; -import org.neo4j.driver.internal.async.BoltProtocolUtil; -import org.neo4j.driver.internal.async.ChannelPipelineBuilderImpl; +import org.neo4j.driver.internal.async.connection.BoltProtocolUtil; +import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.async.outbound.ChunkAwareByteBufOutput; import org.neo4j.driver.internal.messaging.request.InitMessage; @@ -54,8 +54,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue; import static org.neo4j.driver.internal.util.ValueFactory.emptyPathValue; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java index 50de0af24d..0ebab73ccc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java @@ -31,11 +31,17 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.Logging; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; import org.neo4j.driver.internal.handlers.NoOpResponseHandler; @@ -51,12 +57,6 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.Futures; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; -import org.neo4j.driver.Logging; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; @@ -76,9 +76,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.internal.util.Futures.blockingGet; -import static org.neo4j.driver.Values.value; import static org.neo4j.driver.util.TestUtil.await; import static org.neo4j.driver.util.TestUtil.connectionMock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java index b7f452a6e4..68b21d4f22 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java @@ -32,13 +32,19 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Logging; +import org.neo4j.driver.Statement; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; import org.neo4j.driver.internal.DefaultBookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; -import org.neo4j.driver.internal.async.ChannelAttributes; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; import org.neo4j.driver.internal.handlers.NoOpResponseHandler; @@ -57,12 +63,6 @@ import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.Logging; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; import static java.time.Duration.ofSeconds; import static java.util.Collections.emptyMap; @@ -83,10 +83,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; -import static org.neo4j.driver.internal.util.ServerVersion.v3_5_0; import static org.neo4j.driver.AccessMode.WRITE; import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; +import static org.neo4j.driver.internal.util.ServerVersion.v3_5_0; import static org.neo4j.driver.util.TestUtil.await; import static org.neo4j.driver.util.TestUtil.connectionMock; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java index 3a791ee58a..1b95c0f5ac 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java @@ -28,7 +28,9 @@ import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.BookmarksHolder; import org.neo4j.driver.internal.DefaultBookmarksHolder; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.NoOpResponseHandler; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; @@ -39,8 +41,6 @@ import org.neo4j.driver.internal.messaging.request.PullMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3Test; -import org.neo4j.driver.internal.reactive.cursor.InternalStatementResultCursor; -import org.neo4j.driver.internal.reactive.cursor.StatementResultCursorFactory; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java index a4a3b0def1..73bae7427c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxResultTest.java @@ -34,7 +34,7 @@ import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.internal.reactive.util.ListBasedPullHandler; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.Record; import org.neo4j.driver.summary.ResultSummary; @@ -215,7 +215,7 @@ private InternalRxResult newRxResult( Throwable error ) { return new InternalRxResult( () -> { // now we successfully run - return failedFuture( new CompletionException(error) ); + return failedFuture( new CompletionException( error ) ); } ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java index 99e6344910..e4f5cde24d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxSessionTest.java @@ -33,13 +33,14 @@ import java.util.function.Function; import java.util.stream.Stream; +import org.neo4j.driver.AccessMode; import org.neo4j.driver.Statement; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.Value; -import org.neo4j.driver.internal.ExplicitTransaction; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.NetworkSession; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.FixedRetryLogic; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.value.IntegerValue; @@ -130,7 +131,7 @@ void shouldReleaseConnectionIfFailedToRun( Function runRetur // Run failed with error when( session.runRx( any( Statement.class ), any( TransactionConfig.class ) ) ).thenReturn( Futures.failedFuture( error ) ); - when( session.releaseConnection() ).thenReturn( Futures.completedWithNull() ); + when( session.releaseConnectionAsync() ).thenReturn( Futures.completedWithNull() ); InternalRxSession rxSession = new InternalRxSession( session ); @@ -143,7 +144,7 @@ void shouldReleaseConnectionIfFailedToRun( Function runRetur verify( session ).runRx( any( Statement.class ), any( TransactionConfig.class ) ); RuntimeException t = assertThrows( CompletionException.class, () -> Futures.getNow( cursorFuture ) ); assertThat( t.getCause(), equalTo( error ) ); - verify( session ).releaseConnection(); + verify( session ).releaseConnectionAsync(); } @ParameterizedTest @@ -175,7 +176,7 @@ void shouldReleaseConnectionIfFailedToBeginTx( Function Futures.getNow( txFuture ) ); assertThat( t.getCause(), equalTo( error ) ); - verify( session ).releaseConnection(); + verify( session ).releaseConnectionAsync(); } @ParameterizedTest @@ -200,7 +201,7 @@ void shouldDelegateRunTx( Function> runTx ) throws T when( tx.commitAsync() ).thenReturn( completedWithNull() ); when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); - when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); when( session.retryLogic() ).thenReturn( new FixedRetryLogic( 1 ) ); InternalRxSession rxSession = new InternalRxSession( session ); @@ -209,7 +210,7 @@ void shouldDelegateRunTx( Function> runTx ) throws T StepVerifier.create( Flux.from( strings ) ).expectNext( "a" ).verifyComplete(); // Then - verify( session ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( session ).beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ); verify( tx ).commitAsync(); } @@ -223,7 +224,7 @@ void shouldRetryOnError() throws Throwable when( tx.commitAsync() ).thenReturn( completedWithNull() ); when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); - when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); when( session.retryLogic() ).thenReturn( new FixedRetryLogic( retryCount ) ); InternalRxSession rxSession = new InternalRxSession( session ); @@ -236,7 +237,7 @@ void shouldRetryOnError() throws Throwable .verify(); // Then - verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ); verify( tx, times( retryCount + 1 ) ).rollbackAsync(); } @@ -250,7 +251,7 @@ void shouldObtainResultIfRetrySucceed() throws Throwable when( tx.commitAsync() ).thenReturn( completedWithNull() ); when( tx.rollbackAsync() ).thenReturn( completedWithNull() ); - when( session.beginTransactionAsync( any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); + when( session.beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ) ).thenReturn( completedFuture( tx ) ); when( session.retryLogic() ).thenReturn( new FixedRetryLogic( retryCount ) ); InternalRxSession rxSession = new InternalRxSession( session ); @@ -270,7 +271,7 @@ void shouldObtainResultIfRetrySucceed() throws Throwable StepVerifier.create( Flux.from( strings ) ).expectNext( "a" ).verifyComplete(); // Then - verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( TransactionConfig.class ) ); + verify( session, times( retryCount + 1 ) ).beginTransactionAsync( any( AccessMode.class ), any( TransactionConfig.class ) ); verify( tx, times( retryCount ) ).rollbackAsync(); verify( tx ).commitAsync(); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxTransactionTest.java index 848ca16aa6..cd4d5a736f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/reactive/InternalRxTransactionTest.java @@ -26,20 +26,18 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.function.Function; import java.util.stream.Stream; -import org.neo4j.driver.internal.ExplicitTransaction; +import org.neo4j.driver.Statement; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.InternalRecord; -import org.neo4j.driver.internal.reactive.InternalRxResult; -import org.neo4j.driver.internal.reactive.InternalRxTransaction; +import org.neo4j.driver.internal.async.ExplicitTransaction; +import org.neo4j.driver.internal.cursor.RxStatementResultCursor; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.value.IntegerValue; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxTransaction; -import org.neo4j.driver.internal.reactive.cursor.RxStatementResultCursor; -import org.neo4j.driver.Statement; -import org.neo4j.driver.Value; -import java.util.function.Function; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/DriverFactoryWithOneEventLoopThread.java b/driver/src/test/java/org/neo4j/driver/internal/util/DriverFactoryWithOneEventLoopThread.java index caa7f76cd2..a592f814b3 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/DriverFactoryWithOneEventLoopThread.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/DriverFactoryWithOneEventLoopThread.java @@ -23,7 +23,7 @@ import java.net.URI; import org.neo4j.driver.internal.DriverFactory; -import org.neo4j.driver.internal.async.BootstrapFactory; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.retry.RetrySettings; import org.neo4j.driver.AuthToken; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/FuturesTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/FuturesTest.java index 6ba3f38bb9..6e0b907ea8 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/FuturesTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/FuturesTest.java @@ -34,7 +34,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.neo4j.driver.internal.async.EventLoopGroupFactory; +import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory; import static org.hamcrest.Matchers.is; import static org.hamcrest.junit.MatcherAssert.assertThat; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java index 0a66bf78ca..ab5239e14a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java @@ -30,10 +30,10 @@ import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.DriverFactory; -import org.neo4j.driver.internal.async.ChannelConnector; -import org.neo4j.driver.internal.async.ChannelConnectorImpl; -import org.neo4j.driver.internal.async.ChannelPipelineBuilder; -import org.neo4j.driver.internal.async.ChannelPipelineBuilderImpl; +import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; +import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilder; +import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelPipelineBuilderWithFailingMessageFormat.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelPipelineBuilderWithFailingMessageFormat.java index b6014d3a6e..3f88983f60 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelPipelineBuilderWithFailingMessageFormat.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelPipelineBuilderWithFailingMessageFormat.java @@ -20,8 +20,8 @@ import io.netty.channel.ChannelPipeline; -import org.neo4j.driver.internal.async.ChannelPipelineBuilder; -import org.neo4j.driver.internal.async.ChannelPipelineBuilderImpl; +import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilder; +import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.util.FailingMessageFormat; import org.neo4j.driver.Logging; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingConnector.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingConnector.java index 7072d325ee..1282db02a9 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingConnector.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingConnector.java @@ -25,7 +25,7 @@ import java.util.List; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnector; public class ChannelTrackingConnector implements ChannelConnector { diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java index 13386d2037..252483c4ba 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java @@ -27,8 +27,8 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; -import org.neo4j.driver.internal.async.BootstrapFactory; -import org.neo4j.driver.internal.async.ChannelConnector; +import org.neo4j.driver.internal.async.connection.BootstrapFactory; +import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.metrics.MetricsProvider; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java index 60f3e0c2db..703f3e2081 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java @@ -19,8 +19,8 @@ package org.neo4j.driver.internal.util.io; import org.neo4j.driver.internal.ConnectionSettings; -import org.neo4j.driver.internal.async.ChannelConnector; -import org.neo4j.driver.internal.async.ChannelConnectorImpl; +import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.FailingMessageFormat; diff --git a/driver/src/test/java/org/neo4j/driver/util/DriverExtension.java b/driver/src/test/java/org/neo4j/driver/util/DriverExtension.java new file mode 100644 index 0000000000..2ada7da286 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/util/DriverExtension.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.util; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import org.neo4j.driver.Session; +import org.neo4j.driver.async.AsyncSession; + +import static org.neo4j.driver.util.TestUtil.await; + +/** + * A little utility for integration testing, this provides tests with sessions they can work with. + * If you want more direct control, have a look at {@link DatabaseExtension} instead. + */ +public class DriverExtension extends DatabaseExtension implements BeforeEachCallback, AfterEachCallback +{ + private AsyncSession asyncSession; + private Session session; + + public AsyncSession asyncSession() + { + return asyncSession; + } + + public Session session() + { + return session; + } + + @Override + public void beforeEach( ExtensionContext context ) throws Exception + { + super.beforeEach( context ); + asyncSession = driver().asyncSession(); + session = driver().session(); + } + + @Override + public void afterEach( ExtensionContext context ) + { + if ( asyncSession != null ) + { + await( asyncSession.closeAsync() ); + } + if ( session != null ) + { + session.close(); + } + } +} diff --git a/driver/src/test/java/org/neo4j/driver/util/SessionExtension.java b/driver/src/test/java/org/neo4j/driver/util/SessionExtension.java index eda0e49009..e06a10da13 100644 --- a/driver/src/test/java/org/neo4j/driver/util/SessionExtension.java +++ b/driver/src/test/java/org/neo4j/driver/util/SessionExtension.java @@ -23,7 +23,6 @@ import org.junit.jupiter.api.extension.ExtensionContext; import java.util.Map; -import java.util.concurrent.CompletionStage; import org.neo4j.driver.Record; import org.neo4j.driver.Session; @@ -33,26 +32,21 @@ import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.TransactionWork; import org.neo4j.driver.Value; -import org.neo4j.driver.async.AsyncSession; -import org.neo4j.driver.async.AsyncTransaction; -import org.neo4j.driver.async.AsyncTransactionWork; -import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.internal.NetworkSession; import org.neo4j.driver.types.TypeSystem; /** * A little utility for integration testing, this provides tests with a session they can work with. * If you want more direct control, have a look at {@link DatabaseExtension} instead. */ -public class SessionExtension extends DatabaseExtension implements Session, AsyncSession, BeforeEachCallback, AfterEachCallback +public class SessionExtension extends DatabaseExtension implements Session, BeforeEachCallback, AfterEachCallback { - private NetworkSession realSession; + private Session realSession; @Override public void beforeEach( ExtensionContext context ) throws Exception { super.beforeEach( context ); - realSession = (NetworkSession) driver().session(); // TODO This shall only return a blocking driver + realSession = driver().session(); } @Override @@ -76,18 +70,6 @@ public void close() throw new UnsupportedOperationException( "Disallowed on this test session" ); } - @Override - public CompletionStage closeAsync() - { - throw new UnsupportedOperationException( "Disallowed on this test session" ); - } - - @Override - public CompletionStage runAsync( String statement, Map params ) - { - return realSession.runAsync( statement, params ); - } - @Override public Transaction beginTransaction() { @@ -100,25 +82,6 @@ public Transaction beginTransaction( TransactionConfig config ) return realSession.beginTransaction( config ); } - @Deprecated - @Override - public Transaction beginTransaction( String bookmark ) - { - return realSession.beginTransaction( bookmark ); - } - - @Override - public CompletionStage beginTransactionAsync() - { - return realSession.beginTransactionAsync(); - } - - @Override - public CompletionStage beginTransactionAsync( TransactionConfig config ) - { - return realSession.beginTransactionAsync( config ); - } - @Override public T readTransaction( TransactionWork work ) { @@ -131,18 +94,6 @@ public T readTransaction( TransactionWork work, TransactionConfig config return realSession.readTransaction( work, config ); } - @Override - public CompletionStage readTransactionAsync( AsyncTransactionWork> work ) - { - return realSession.readTransactionAsync( work ); - } - - @Override - public CompletionStage readTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) - { - return realSession.readTransactionAsync( work, config ); - } - @Override public T writeTransaction( TransactionWork work ) { @@ -155,18 +106,6 @@ public T writeTransaction( TransactionWork work, TransactionConfig config return realSession.writeTransaction( work, config ); } - @Override - public CompletionStage writeTransactionAsync( AsyncTransactionWork> work ) - { - return realSession.writeTransactionAsync( work ); - } - - @Override - public CompletionStage writeTransactionAsync( AsyncTransactionWork> work, TransactionConfig config ) - { - return realSession.writeTransactionAsync( work, config ); - } - @Override public String lastBookmark() { @@ -192,48 +131,24 @@ public StatementResult run( String statementText, Value parameters ) return realSession.run( statementText, parameters ); } - @Override - public CompletionStage runAsync( String statementText, Value parameters ) - { - return realSession.runAsync( statementText, parameters ); - } - @Override public StatementResult run( String statementText, Record parameters ) { return realSession.run( statementText, parameters ); } - @Override - public CompletionStage runAsync( String statementTemplate, Record statementParameters ) - { - return realSession.runAsync( statementTemplate, statementParameters ); - } - @Override public StatementResult run( String statementTemplate ) { return realSession.run( statementTemplate ); } - @Override - public CompletionStage runAsync( String statementTemplate ) - { - return realSession.runAsync( statementTemplate ); - } - @Override public StatementResult run( Statement statement ) { return realSession.run( statement.text(), statement.parameters() ); } - @Override - public CompletionStage runAsync( Statement statement ) - { - return realSession.runAsync( statement ); - } - @Override public StatementResult run( String statement, TransactionConfig config ) { @@ -252,24 +167,6 @@ public StatementResult run( Statement statement, TransactionConfig config ) return realSession.run( statement, config ); } - @Override - public CompletionStage runAsync( String statement, TransactionConfig config ) - { - return realSession.runAsync( statement, config ); - } - - @Override - public CompletionStage runAsync( String statement, Map parameters, TransactionConfig config ) - { - return realSession.runAsync( statement, parameters, config ); - } - - @Override - public CompletionStage runAsync( Statement statement, TransactionConfig config ) - { - return realSession.runAsync( statement, config ); - } - @Override public TypeSystem typeSystem() { diff --git a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java index e6b8a34fe0..96ae058d17 100644 --- a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java +++ b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java @@ -21,6 +21,9 @@ import io.netty.buffer.ByteBuf; import io.netty.util.internal.PlatformDependent; import org.mockito.ArgumentMatcher; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.mockito.verification.VerificationMode; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,22 +43,36 @@ import java.util.concurrent.TimeoutException; import java.util.function.BooleanSupplier; +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.async.EventLoopGroupFactory; +import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.DefaultBookmarksHolder; +import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory; +import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; +import org.neo4j.driver.internal.handlers.NoOpResponseHandler; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.request.BeginMessage; import org.neo4j.driver.internal.messaging.request.CommitMessage; +import org.neo4j.driver.internal.messaging.request.PullMessage; import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; +import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; +import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.internal.spi.ResponseHandler; +import org.neo4j.driver.internal.util.FixedRetryLogic; import org.neo4j.driver.internal.util.ServerVersion; -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.Driver; -import org.neo4j.driver.Session; -import org.neo4j.driver.StatementResult; import static java.util.Collections.emptyMap; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -66,11 +83,17 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.internal.Bookmarks.empty; +import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; +import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; +import static org.neo4j.driver.internal.util.Futures.completedWithNull; public final class TestUtil { @@ -212,6 +235,202 @@ public static String cleanDb( Driver driver ) } } + public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, Bookmarks x ) + { + return newSession( connectionProvider, WRITE, x ); + } + + private static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, Bookmarks x ) + { + return newSession( connectionProvider, mode, new FixedRetryLogic( 0 ), x ); + } + + public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode ) + { + return newSession( connectionProvider, mode, empty() ); + } + + public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, RetryLogic logic ) + { + return newSession( connectionProvider, WRITE, logic, empty() ); + } + + public static InternalNetworkSession newSession( ConnectionProvider connectionProvider ) + { + return newSession( connectionProvider, WRITE, empty() ); + } + + public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, + RetryLogic retryLogic, Bookmarks bookmarks ) + { + return new InternalNetworkSession( connectionProvider, retryLogic, ABSENT_DB_NAME, mode, new DefaultBookmarksHolder( bookmarks ), DEV_NULL_LOGGING ); + } + + public static void verifyRun( Connection connection, String query ) + { + verify( connection ).writeAndFlush( argThat( runWithMetaMessageWithStatementMatcher( query ) ), any() ); + } + + public static void verifyRunAndPull( Connection connection, String query ) + { + verify( connection ).writeAndFlush( argThat( runWithMetaMessageWithStatementMatcher( query ) ), any(), any( PullMessage.class ), any() ); + } + + public static void verifyCommitTx( Connection connection, VerificationMode mode ) + { + verify( connection, mode ).writeAndFlush( any( CommitMessage.class ), any() ); + } + + public static void verifyCommitTx( Connection connection ) + { + verifyCommitTx( connection, times( 1 ) ); + } + + public static void verifyRollbackTx( Connection connection, VerificationMode mode ) + { + verify( connection, mode ).writeAndFlush( any( RollbackMessage.class ), any() ); + } + + public static void verifyRollbackTx( Connection connection ) + { + verifyRollbackTx( connection, times( 1 ) ); + } + + public static void verifyBeginTx( Connection connectionMock ) + { + verifyBeginTx( connectionMock, Bookmarks.empty() ); + } + + public static void verifyBeginTx( Connection connectionMock, Bookmarks bookmarks ) + { + if ( bookmarks.isEmpty() ) + { + verify( connectionMock ).write( any( BeginMessage.class ), eq( NoOpResponseHandler.INSTANCE ) ); + } + else + { + verify( connectionMock ).writeAndFlush( any( BeginMessage.class ), any( BeginTxResponseHandler.class ) ); + } + } + + public static void setupFailingRun( Connection connection, Throwable error ) + { + doAnswer( invocation -> + { + ResponseHandler runHandler = invocation.getArgument( 1 ); + runHandler.onFailure( error ); + ResponseHandler pullHandler = invocation.getArgument( 3 ); + pullHandler.onFailure( error ); + return null; + } ).when( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any(), any( PullMessage.class ), any() ); + } + + public static void setupFailingBegin( Connection connection, Throwable error ) + { + // with bookmarks + doAnswer( invocation -> + { + ResponseHandler handler = invocation.getArgument( 1 ); + handler.onFailure( error ); + return null; + } ).when( connection ).writeAndFlush( any( BeginMessage.class ), any( BeginTxResponseHandler.class ) ); + } + + public static void setupFailingCommit( Connection connection ) + { + setupFailingCommit( connection, 1 ); + } + + public static void setupFailingCommit( Connection connection, int times ) + { + doAnswer( new Answer() + { + int invoked; + + @Override + public Void answer( InvocationOnMock invocation ) + { + ResponseHandler handler = invocation.getArgument( 1 ); + if ( invoked++ < times ) + { + handler.onFailure( new ServiceUnavailableException( "" ) ); + } + else + { + handler.onSuccess( emptyMap() ); + } + return null; + } + } ).when( connection ).writeAndFlush( any( CommitMessage.class ), any() ); + } + + public static void setupFailingRollback( Connection connection ) + { + setupFailingRollback( connection, 1 ); + } + + public static void setupFailingRollback( Connection connection, int times ) + { + doAnswer( new Answer() + { + int invoked; + + @Override + public Void answer( InvocationOnMock invocation ) + { + ResponseHandler handler = invocation.getArgument( 1 ); + if ( invoked++ < times ) + { + handler.onFailure( new ServiceUnavailableException( "" ) ); + } + else + { + handler.onSuccess( emptyMap() ); + } + return null; + } + } ).when( connection ).writeAndFlush( any( RollbackMessage.class ), any() ); + } + + public static void setupSuccessfulRunAndPull( Connection connection ) + { + doAnswer( invocation -> + { + ResponseHandler runHandler = invocation.getArgument( 1 ); + runHandler.onSuccess( emptyMap() ); + ResponseHandler pullHandler = invocation.getArgument( 3 ); + pullHandler.onSuccess( emptyMap() ); + return null; + } ).when( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any(), any( PullMessage.class ), any() ); + } + + public static void setupSuccessfulRun( Connection connection ) + { + doAnswer( invocation -> + { + ResponseHandler runHandler = invocation.getArgument( 1 ); + runHandler.onSuccess( emptyMap() ); + return null; + } ).when( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any() ); + } + + public static void setupSuccessfulRunAndPull( Connection connection, String query ) + { + doAnswer( invocation -> + { + ResponseHandler runHandler = invocation.getArgument( 1 ); + runHandler.onSuccess( emptyMap() ); + ResponseHandler pullHandler = invocation.getArgument( 3 ); + pullHandler.onSuccess( emptyMap() ); + return null; + } ).when( connection ).writeAndFlush( argThat( runWithMetaMessageWithStatementMatcher( query ) ), any(), any( PullMessage.class ), any() ); + } + + public static Connection connectionMock() + { + return connectionMock( BoltProtocolV2.INSTANCE ); + } + public static Connection connectionMock( BoltProtocol protocol ) { return connectionMock( WRITE, protocol ); @@ -235,12 +454,25 @@ public static Connection connectionMock( String databaseName, AccessMode mode, B when( connection.protocol() ).thenReturn( protocol ); when( connection.mode() ).thenReturn( mode ); when( connection.databaseName() ).thenReturn( databaseName ); - setupSuccessfulPullAll( connection, "COMMIT" ); - setupSuccessfulPullAll( connection, "ROLLBACK" ); - setupSuccessfulPullAll( connection, "BEGIN" ); - setupSuccessResponse( connection, CommitMessage.class ); - setupSuccessResponse( connection, RollbackMessage.class ); - setupSuccessResponse( connection, BeginMessage.class ); + int version = protocol.version(); + if ( version == BoltProtocolV1.VERSION || version == BoltProtocolV2.VERSION ) + { + setupSuccessfulPullAll( connection, "COMMIT" ); + setupSuccessfulPullAll( connection, "ROLLBACK" ); + setupSuccessfulPullAll( connection, "BEGIN" ); + } + else if ( version == BoltProtocolV3.VERSION || version == BoltProtocolV4.VERSION ) + { + setupSuccessResponse( connection, CommitMessage.class ); + setupSuccessResponse( connection, RollbackMessage.class ); + setupSuccessResponse( connection, BeginMessage.class ); + when( connection.release() ).thenReturn( completedWithNull() ); + when( connection.reset() ).thenReturn( completedWithNull() ); + } + else + { + throw new IllegalArgumentException( "Unsupported bolt protocol version: " + version ); + } return connection; } @@ -332,6 +564,12 @@ public static ArgumentMatcher runMessageWithStatementMatcher( String st return message -> message instanceof RunMessage && Objects.equals( statement, ((RunMessage) message).statement() ); } + public static ArgumentMatcher runWithMetaMessageWithStatementMatcher( String statement ) + { + return message -> message instanceof RunWithMetadataMessage && Objects.equals( statement, ((RunWithMetadataMessage) message).statement() ); + } + + private static void setupSuccessfulPullAll( Connection connection, String statement ) { doAnswer( invocation -> From 5fa8ca626695265609f7042aec392e0299af69fa Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Fri, 5 Apr 2019 15:14:58 +0200 Subject: [PATCH 3/7] Refined the tx run methods using Flux.usingWhen Added more IT for tx run with retries. --- .../internal/reactive/InternalRxSession.java | 11 +- .../integration/reactive/RxSessionIT.java | 161 +++++++++++++++++- .../integration/reactive/RxTransactionIT.java | 119 +++++-------- examples/pom.xml | 4 + 4 files changed, 211 insertions(+), 84 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java index de1bf02abf..fb1fdb4649 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/reactive/InternalRxSession.java @@ -20,7 +20,6 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -122,14 +121,8 @@ public Publisher writeTransaction( RxTransactionWork> work, private Publisher runTransaction( AccessMode mode, RxTransactionWork> work, TransactionConfig config ) { - Publisher publisher = beginTransaction( mode, config ); - Flux txResult = Mono.from( publisher ) - .flatMapMany( tx -> Flux.from( work.execute( tx ) ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ) // if failed then we rollback and rethrow the error - .concatWith( tx.commit() ) // if succeeded then we commit. - ); - - return session.retryLogic().retryRx( txResult ); + Flux repeatableWork = Flux.usingWhen( beginTransaction( mode, config ), work::execute, RxTransaction::commit, RxTransaction::rollback ); + return session.retryLogic().retryRx( repeatableWork ); } @Override diff --git a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java index 1302894e8f..6830781661 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java @@ -20,16 +20,33 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.util.Arrays; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +import org.neo4j.driver.Session; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.exceptions.DatabaseException; +import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; import org.neo4j.driver.reactive.RxResult; import org.neo4j.driver.reactive.RxSession; -import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.reactive.RxTransaction; +import org.neo4j.driver.reactive.RxTransactionWork; import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; +import static java.util.Collections.emptyIterator; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V4; @@ -74,4 +91,146 @@ void shouldBeAbleToReuseSessionAfterFailure() assertEquals( record.get("1").asLong(), 1L ); } ).expectComplete().verify(); } + + @Test + void shouldRunAsyncTransactionWithoutRetries() + { + RxSession session = neo4j.driver().rxSession(); + InvocationTrackingWork work = new InvocationTrackingWork( "CREATE (:Apa) RETURN 42" ); + Publisher publisher = session.writeTransaction( work ); + + StepVerifier.create( publisher ).expectNext( 42 ).verifyComplete(); + + assertEquals( 1, work.invocationCount() ); + assertEquals( 1, countNodesByLabel( "Apa" ) ); + } + + @Test + void shouldRunAsyncTransactionWithRetriesOnAsyncFailures() + { + RxSession session = neo4j.driver().rxSession(); + InvocationTrackingWork work = new InvocationTrackingWork( "CREATE (:Node) RETURN 24" ).withAsyncFailures( + new ServiceUnavailableException( "Oh!" ), + new SessionExpiredException( "Ah!" ), + new TransientException( "Code", "Message" ) ); + + Publisher publisher = session.writeTransaction( work ); + StepVerifier.create( publisher ).expectNext( 24 ).verifyComplete(); + + assertEquals( 4, work.invocationCount() ); + assertEquals( 1, countNodesByLabel( "Node" ) ); + } + + @Test + void shouldRunAsyncTransactionWithRetriesOnSyncFailures() + { + RxSession session = neo4j.driver().rxSession(); + InvocationTrackingWork work = new InvocationTrackingWork( "CREATE (:Test) RETURN 12" ).withSyncFailures( + new TransientException( "Oh!", "Deadlock!" ), + new ServiceUnavailableException( "Oh! Network Failure" ) ); + + Publisher publisher = session.writeTransaction( work ); + StepVerifier.create( publisher ).expectNext( 12 ).verifyComplete(); + + assertEquals( 3, work.invocationCount() ); + assertEquals( 1, countNodesByLabel( "Test" ) ); + } + + @Test + void shouldRunAsyncTransactionThatCanNotBeRetried() + { + RxSession session = neo4j.driver().rxSession(); + InvocationTrackingWork work = new InvocationTrackingWork( "UNWIND [10, 5, 0] AS x CREATE (:Hi) RETURN 10/x" ); + Publisher publisher = session.writeTransaction( work ); + + StepVerifier.create( publisher ) + .expectNext( 1 ).expectNext( 2 ) + .expectErrorSatisfies( error -> assertThat( error, instanceOf( ClientException.class ) ) ) + .verify(); + + assertEquals( 1, work.invocationCount() ); + assertEquals( 0, countNodesByLabel( "Hi" ) ); + } + + @Test + void shouldRunAsyncTransactionThatCanNotBeRetriedAfterATransientFailure() + { + RxSession session = neo4j.driver().rxSession(); + // first throw TransientException directly from work, retry can happen afterwards + // then return a future failed with DatabaseException, retry can't happen afterwards + InvocationTrackingWork work = new InvocationTrackingWork( "CREATE (:Person) RETURN 1" ) + .withSyncFailures( new TransientException( "Oh!", "Deadlock!" ) ) + .withAsyncFailures( new DatabaseException( "Oh!", "OutOfMemory!" ) ); + Publisher publisher = session.writeTransaction( work ); + + StepVerifier.create( publisher ) + .expectErrorSatisfies( e -> { + assertThat( e, instanceOf( DatabaseException.class ) ); + assertEquals( 1, e.getSuppressed().length ); + assertThat( e.getSuppressed()[0], instanceOf( TransientException.class ) ); + } ) + .verify(); + + assertEquals( 2, work.invocationCount() ); + assertEquals( 0, countNodesByLabel( "Person" ) ); + } + + private long countNodesByLabel( String label ) + { + try ( Session session = neo4j.driver().session() ) + { + StatementResult result = session.run( "MATCH (n:" + label + ") RETURN count(n)" ); + return result.single().get( 0 ).asLong(); + } + } + + private static class InvocationTrackingWork implements RxTransactionWork> + { + final String query; + final AtomicInteger invocationCount; + + Iterator asyncFailures = emptyIterator(); + Iterator syncFailures = emptyIterator(); + + InvocationTrackingWork( String query ) + { + this.query = query; + this.invocationCount = new AtomicInteger(); + } + + InvocationTrackingWork withAsyncFailures( RuntimeException... failures ) + { + asyncFailures = Arrays.asList( failures ).iterator(); + return this; + } + + InvocationTrackingWork withSyncFailures( RuntimeException... failures ) + { + syncFailures = Arrays.asList( failures ).iterator(); + return this; + } + + int invocationCount() + { + return invocationCount.get(); + } + + @Override + public Publisher execute( RxTransaction tx ) + { + invocationCount.incrementAndGet(); + + if ( syncFailures.hasNext() ) + { + throw syncFailures.next(); + } + + if ( asyncFailures.hasNext() ) + { + return Mono.error( asyncFailures.next() ); + } + + return Flux.from( tx.run( query ).records() ).map( r -> r.get( 0 ).asInt() ); + } + } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java index d051b6d916..b2e1ead44f 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java @@ -38,15 +38,15 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; -import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; -import org.neo4j.driver.reactive.RxResult; -import org.neo4j.driver.reactive.RxSession; -import org.neo4j.driver.reactive.RxTransaction; import org.neo4j.driver.Record; import org.neo4j.driver.Statement; import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.driver.internal.util.EnabledOnNeo4jWith; +import org.neo4j.driver.reactive.RxResult; +import org.neo4j.driver.reactive.RxSession; +import org.neo4j.driver.reactive.RxTransaction; import org.neo4j.driver.summary.ResultSummary; import org.neo4j.driver.summary.StatementType; import org.neo4j.driver.types.Node; @@ -67,11 +67,11 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.util.Iterables.single; import static org.neo4j.driver.internal.util.Matchers.containsResultAvailableAfterAndResultConsumedAfter; import static org.neo4j.driver.internal.util.Matchers.syntaxError; import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V4; -import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.util.TestUtil.await; @EnabledOnNeo4jWith( BOLT_V4 ) @@ -118,14 +118,10 @@ void shouldBePossibleToRollbackEmptyTx() @Test void shouldBePossibleToRunSingleStatementAndCommit() { - - Flux ids = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( "CREATE (n:Node {id: 42}) RETURN n" ); - return Flux.from( result.records() ) - .map( record -> record.get( 0 ).asNode().get( "id" ).asInt() ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - } ); + Flux ids = Flux.usingWhen( session.beginTransaction(), + tx -> Flux.from( tx.run( "CREATE (n:Node {id: 42}) RETURN n" ).records() ) + .map( record -> record.get( 0 ).asNode().get( "id" ).asInt() ), + RxTransaction::commit, RxTransaction::rollback ); StepVerifier.create( ids ).expectNext( 42 ).verifyComplete(); assertEquals( 1, countNodes( 42 ) ); @@ -309,13 +305,9 @@ void shouldFailToRollbackWhenCommitted() @Test void shouldAllowRollbackAfterFailedCommit() { - Flux records = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( "WRONG" ); - return Flux.from( result.records() ) - .concatWith( tx.commit() ) // if we completed without error then we commit - .onErrorResume( error -> Mono.from( tx.rollback() ) // otherwise we rollback and then return the original error - .then( Mono.error( error ) ) ); - } ); + Flux records = Flux.usingWhen( session.beginTransaction(), + tx -> Flux.from( tx.run( "WRONG" ).records() ), + RxTransaction::commit, RxTransaction::rollback ); StepVerifier.create( records ).verifyErrorSatisfies( error -> assertThat( error.getMessage(), containsString( "Invalid input" ) ) ); @@ -465,17 +457,12 @@ void shouldFailForEachWhenActionFails() { RuntimeException e = new RuntimeException(); - Flux records = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( "RETURN 'Hi!'" ); - return Flux.from( result.records() ) - .doOnNext( record -> { throw e; } ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - } ); + Flux records = Flux.usingWhen( session.beginTransaction(), + tx -> Flux.from( tx.run( "RETURN 'Hi!'" ).records() ).doOnNext( record -> { throw e; } ), + RxTransaction::commit, + RxTransaction::rollback ); - StepVerifier.create( records ).expectErrorSatisfies( error -> { - assertEquals( e, error ); - } ).verify(); + StepVerifier.create( records ).expectErrorSatisfies( error -> assertEquals( e, error ) ).verify(); } @Test @@ -516,14 +503,9 @@ void shouldFailWhenListTransformationFunctionFails() { RuntimeException e = new RuntimeException(); - Flux records = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( "RETURN 'Hi!'" ); - return Flux.from( result.records() ) - .map( record -> { throw e; } ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - - } ); + Flux records = Flux.usingWhen( session.beginTransaction(), + tx -> Flux.from( tx.run( "RETURN 'Hi!'" ).records() ).map( record -> { throw e; } ), + RxTransaction::commit, RxTransaction::rollback ); StepVerifier.create( records ).expectErrorSatisfies( error -> { assertEquals( e, error ); @@ -715,12 +697,10 @@ void shouldUpdateSessionBookmarkAfterCommit() { String bookmarkBefore = session.lastBookmark(); - RxTransaction tx = await( Mono.from( session.beginTransaction() ) ); - RxResult result = tx.run( "CREATE (:MyNode)" ); - await( Flux.from( result.records() ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ) ); - + await( Flux.usingWhen( session.beginTransaction(), + tx -> tx.run( "CREATE (:MyNode)" ).records(), + RxTransaction::commit, + RxTransaction::rollback ) ); String bookmarkAfter = session.lastBookmark(); @@ -822,19 +802,17 @@ void shouldHandleNestedQueries() throws Throwable { int size = 12555; - Flux nodeIds = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( "UNWIND range(1, $size) AS x RETURN x", Collections.singletonMap( "size", size ) ); - return Flux.from( result.records() ) - .limitRate( 20 ) // batch size - .flatMap( record -> { + Flux nodeIds = Flux.usingWhen( session.beginTransaction(), + tx -> { + RxResult result = tx.run( "UNWIND range(1, $size) AS x RETURN x", Collections.singletonMap( "size", size ) ); + return Flux.from( result.records() ).limitRate( 20 ).flatMap( record -> { int x = record.get( "x" ).asInt(); RxResult innerResult = tx.run( "CREATE (n:Node {id: $x}) RETURN n.id", Collections.singletonMap( "x", x ) ); return innerResult.records(); - } ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ) - .map( record -> record.get( 0 ).asInt() ); - } ); + } ).map( record -> record.get( 0 ).asInt() ); + }, + RxTransaction::commit, RxTransaction::rollback + ); StepVerifier.create( nodeIds ).expectNextCount( size ).verifyComplete(); } @@ -847,7 +825,8 @@ private int countNodes( Object id ) private void testForEach( String query, int expectedSeenRecords ) { - Flux summary = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { + + Flux summary = Flux.usingWhen( session.beginTransaction(), tx -> { RxResult result = tx.run( query ); AtomicInteger recordsSeen = new AtomicInteger(); return Flux.from( result.records() ) @@ -858,10 +837,8 @@ private void testForEach( String query, int expectedSeenRecords ) assertEquals( query, s.statement().text() ); assertEquals( emptyMap(), s.statement().parameters().asMap() ); assertEquals( expectedSeenRecords, recordsSeen.get() ); - } ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - } ); + } ); + }, RxTransaction::commit, RxTransaction::rollback ); StepVerifier.create( summary ).expectNextCount( 1 ).verifyComplete(); // we indeed get a summary. } @@ -870,13 +847,10 @@ private void testList( String query, List expectedList ) { List actualList = new ArrayList<>(); - Flux> records = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( query ); - return Flux.from( result.records() ) - .collectList() - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - } ); + Flux> records = Flux.usingWhen( session.beginTransaction(), + tx -> Flux.from( tx.run( query ).records() ).collectList(), + RxTransaction::commit, + RxTransaction::rollback ); StepVerifier.create( records.single() ).consumeNextWith( allRecords -> { for ( Record record : allRecords ) @@ -890,13 +864,11 @@ private void testList( String query, List expectedList ) private void testConsume( String query ) { - Flux summary = Mono.from( session.beginTransaction() ).flatMapMany( tx -> { - RxResult result = tx.run( query ); - return Mono.from( result.summary() ) - .concatWith( tx.commit() ) - .onErrorResume( error -> Mono.from( tx.rollback() ).then( Mono.error( error ) ) ); - - } ); + Flux summary = Flux.usingWhen( session.beginTransaction(), tx -> + tx.run( query ).summary(), + RxTransaction::commit, + RxTransaction::rollback + ); StepVerifier.create( summary.single() ).consumeNextWith( Assertions::assertNotNull ).verifyComplete(); } @@ -935,7 +907,6 @@ private void assertCanCommit( RxTransaction tx ) private void assertCanRollback( RxTransaction tx ) { assertThat( await( tx.rollback() ), equalTo( emptyList() ) ); - System.out.println("rolled back"); } private static Stream commit() diff --git a/examples/pom.xml b/examples/pom.xml index 864b2e9696..fb54589307 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -41,6 +41,10 @@ org.hamcrest hamcrest-junit + + org.mockito + mockito-core + org.junit.jupiter junit-jupiter From a87bf792e63f035dbc4fd3fed94e6e2b4874397f Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Tue, 9 Apr 2019 16:00:22 +0200 Subject: [PATCH 4/7] Added missing API docs for reactive API --- .../driver/async/AsyncTransactionWork.java | 4 +- .../org/neo4j/driver/reactive/RxResult.java | 14 +-- .../org/neo4j/driver/reactive/RxSession.java | 87 ++++++++++++++++++- .../driver/reactive/RxTransactionWork.java | 14 +++ 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/async/AsyncTransactionWork.java b/driver/src/main/java/org/neo4j/driver/async/AsyncTransactionWork.java index 838fcd6a81..456c9b0af2 100644 --- a/driver/src/main/java/org/neo4j/driver/async/AsyncTransactionWork.java +++ b/driver/src/main/java/org/neo4j/driver/async/AsyncTransactionWork.java @@ -18,10 +18,8 @@ */ package org.neo4j.driver.async; -import org.neo4j.driver.Transaction; - /** - * Callback that executes operations against a given {@link Transaction}. + * Callback that executes operations against a given {@link AsyncTransaction}. * To be used with {@link AsyncSession#readTransactionAsync(AsyncTransactionWork)} and * {@link AsyncSession#writeTransactionAsync(AsyncTransactionWork)} (AsyncTransactionWork)} methods. * diff --git a/driver/src/main/java/org/neo4j/driver/reactive/RxResult.java b/driver/src/main/java/org/neo4j/driver/reactive/RxResult.java index 231f0aae1c..2ccf52a9ee 100644 --- a/driver/src/main/java/org/neo4j/driver/reactive/RxResult.java +++ b/driver/src/main/java/org/neo4j/driver/reactive/RxResult.java @@ -46,8 +46,8 @@ public interface RxResult * When this publisher is {@linkplain Publisher#subscribe(Subscriber) subscribed}, the query statement is sent to the server and get executed. * This method does not start the record streaming nor publish query execution error. * To retrieve the execution result, either {@link #records()} or {@link #summary()} can be used. - * {@link #records()} starts record streaming and report query execution error. - * {@link #summary()} skips record streaming and directly report query execution error. + * {@link #records()} starts record streaming and reports query execution error. + * {@link #summary()} skips record streaming and directly reports query execution error. *

* Consuming of execution result ensures the resources (such as network connections) used by this result is freed correctly. * Consuming the keys without consuming the execution result will result in resource leak. @@ -66,7 +66,7 @@ public interface RxResult *

* When the record publisher is {@linkplain Publisher#subscribe(Subscriber) subscribed}, * the query statement is executed and the query result is streamed back as a record stream followed by a result summary. - * This record publisher publishes all records in the result and signal the completion. + * This record publisher publishes all records in the result and signals the completion. * However before completion or error reporting if any, a cleanup of result resources such as network connection will be carried out automatically. *

* Therefore the {@link Subscriber} of this record publisher shall wait for the termination signal (complete or error) @@ -77,13 +77,13 @@ public interface RxResult * But it will not cancel the query execution. * As a result, a termination signal (complete or error) will still be sent to the {@link Subscriber} after the query execution is finished. *

- * The record publishing event by default runs in Netty IO thread, as a result no blocking operation is allowed in this thread. + * The record publishing event by default runs in an Network IO thread, as a result no blocking operation is allowed in this thread. * Otherwise network IO might be blocked by application logic. *

* This publisher can only be subscribed by one {@link Subscriber} once. *

- * If this publisher is subscribed after {@link #keys()}, then the publish of records is carried out once each record arrives. - * If this publisher is subscribed after {@link #summary()}, then the publish of records has been cancelled + * If this publisher is subscribed after {@link #keys()}, then the publish of records is carried out after the arrival of keys. + * If this publisher is subscribed after {@link #summary()}, then the publish of records is already cancelled * and an empty publisher of zero record will be return. * @return a cold unicast publisher of records. */ @@ -94,7 +94,7 @@ public interface RxResult *

* {@linkplain Publisher#subscribe(Subscriber) Subscribing} the summary publisher results in the execution of the query followed by the result summary returned. * The summary publisher cancels record publishing if not yet subscribed and directly streams back the summary on query execution completion. - * As a result, the invocation of {@link #records()} after this method, would receive a empty publisher. + * As a result, the invocation of {@link #records()} after this method, would receive an empty publisher. *

* If subscribed after {@link #keys()}, then the result summary will be published after the query execution without streaming any record to client. * If subscribed after {@link #records()}, then the result summary will be published after the query execution and the streaming of records. diff --git a/driver/src/main/java/org/neo4j/driver/reactive/RxSession.java b/driver/src/main/java/org/neo4j/driver/reactive/RxSession.java index 2879860eae..9c04f060f0 100644 --- a/driver/src/main/java/org/neo4j/driver/reactive/RxSession.java +++ b/driver/src/main/java/org/neo4j/driver/reactive/RxSession.java @@ -21,7 +21,9 @@ import org.reactivestreams.Publisher; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.neo4j.driver.AccessMode; import org.neo4j.driver.Session; import org.neo4j.driver.Statement; import org.neo4j.driver.TransactionConfig; @@ -43,7 +45,7 @@ public interface RxSession extends RxStatementRunner * maintain multiple concurrent transactions, use multiple concurrent * sessions. *

- * It by default is executed in Netty IO thread, as a result no blocking operation is allowed in this thread. + * It by default is executed in a Network IO thread, as a result no blocking operation is allowed in this thread. * * @return a new {@link RxTransaction} */ @@ -52,22 +54,99 @@ public interface RxSession extends RxStatementRunner /** * Begin a new explicit {@linkplain RxTransaction transaction} with the specified {@link TransactionConfig configuration}. * At most one transaction may exist in a session at any point in time. To - * maintain multiple concurrent transactions, use multiple concurrent - * sessions. + * maintain multiple concurrent transactions, use multiple concurrent sessions. *

- * It by default is executed in Netty IO thread, as a result no blocking operation is allowed in this thread. + * It by default is executed in a Network IO thread, as a result no blocking operation is allowed in this thread. * * @param config configuration for the new transaction. * @return a new {@link RxTransaction} */ Publisher beginTransaction( TransactionConfig config ); + /** + * Execute given unit of reactive work in a {@link AccessMode#READ read} reactive transaction. +

+ * Transaction will automatically be committed unless given unit of work fails or + * {@link RxTransaction#commit() transaction commit} fails. + * It will also not be committed if explicitly rolled back via {@link RxTransaction#rollback()}. + *

+ * Returned publisher and given {@link RxTransactionWork} is completed/executed by an IO thread which should never block. + * Otherwise IO operations on this and potentially other network connections might deadlock. + * Please do not chain blocking operations like {@link CompletableFuture#get()} on the returned publisher and do not use them inside the + * {@link RxTransactionWork}. + * + * @param work the {@link RxTransactionWork} to be applied to a new read transaction. + * Operation executed by the given work must NOT include any blocking operation. + * @param the return type of the given unit of work. + * @return a {@link Publisher publisher} completed with the same result as returned by the given unit of work. + * publisher can be completed exceptionally if given work or commit fails. + * + */ Publisher readTransaction( RxTransactionWork> work ); + /** + * Execute given unit of reactive work in a {@link AccessMode#READ read} reactive transaction with + * the specified {@link TransactionConfig configuration}. +

+ * Transaction will automatically be committed unless given unit of work fails or + * {@link RxTransaction#commit() transaction commit} fails. + * It will also not be committed if explicitly rolled back via {@link RxTransaction#rollback()}. + *

+ * Returned publisher and given {@link RxTransactionWork} is completed/executed by an IO thread which should never block. + * Otherwise IO operations on this and potentially other network connections might deadlock. + * Please do not chain blocking operations like {@link CompletableFuture#get()} on the returned publisher and do not use them inside the + * {@link RxTransactionWork}. + * + * @param work the {@link RxTransactionWork} to be applied to a new read transaction. + * Operation executed by the given work must NOT include any blocking operation. + * @param the return type of the given unit of work. + * @return a {@link Publisher publisher} completed with the same result as returned by the given unit of work. + * publisher can be completed exceptionally if given work or commit fails. + * + */ Publisher readTransaction( RxTransactionWork> work, TransactionConfig config ); + /** + * Execute given unit of reactive work in a {@link AccessMode#WRITE write} reactive transaction. +

+ * Transaction will automatically be committed unless given unit of work fails or + * {@link RxTransaction#commit() transaction commit} fails. + * It will also not be committed if explicitly rolled back via {@link RxTransaction#rollback()}. + *

+ * Returned publisher and given {@link RxTransactionWork} is completed/executed by an IO thread which should never block. + * Otherwise IO operations on this and potentially other network connections might deadlock. + * Please do not chain blocking operations like {@link CompletableFuture#get()} on the returned publisher and do not use them inside the + * {@link RxTransactionWork}. + * + * @param work the {@link RxTransactionWork} to be applied to a new read transaction. + * Operation executed by the given work must NOT include any blocking operation. + * @param the return type of the given unit of work. + * @return a {@link Publisher publisher} completed with the same result as returned by the given unit of work. + * publisher can be completed exceptionally if given work or commit fails. + * + */ Publisher writeTransaction( RxTransactionWork> work ); + /** + * Execute given unit of reactive work in a {@link AccessMode#WRITE write} reactive transaction with + * the specified {@link TransactionConfig configuration}. +

+ * Transaction will automatically be committed unless given unit of work fails or + * {@link RxTransaction#commit() transaction commit} fails. + * It will also not be committed if explicitly rolled back via {@link RxTransaction#rollback()}. + *

+ * Returned publisher and given {@link RxTransactionWork} is completed/executed by an IO thread which should never block. + * Otherwise IO operations on this and potentially other network connections might deadlock. + * Please do not chain blocking operations like {@link CompletableFuture#get()} on the returned publisher and do not use them inside the + * {@link RxTransactionWork}. + * + * @param work the {@link RxTransactionWork} to be applied to a new read transaction. + * Operation executed by the given work must NOT include any blocking operation. + * @param the return type of the given unit of work. + * @return a {@link Publisher publisher} completed with the same result as returned by the given unit of work. + * publisher can be completed exceptionally if given work or commit fails. + * + */ Publisher writeTransaction( RxTransactionWork> work, TransactionConfig config ); /** diff --git a/driver/src/main/java/org/neo4j/driver/reactive/RxTransactionWork.java b/driver/src/main/java/org/neo4j/driver/reactive/RxTransactionWork.java index 9cda2a246d..449607c7b3 100644 --- a/driver/src/main/java/org/neo4j/driver/reactive/RxTransactionWork.java +++ b/driver/src/main/java/org/neo4j/driver/reactive/RxTransactionWork.java @@ -18,7 +18,21 @@ */ package org.neo4j.driver.reactive; +/** + * Callback that executes operations against a given {@link RxTransaction}. + * To be used with {@link RxSession#readTransaction(RxTransactionWork)} and + * {@link RxSession#writeTransaction(RxTransactionWork)} methods. + * + * @param the return type of this work. + * @since 2.0 + */ public interface RxTransactionWork { + /** + * Executes all given operations against the same transaction. + * + * @param tx the transaction to use. + * @return some result object or {@code null} if none. + */ T execute( RxTransaction tx ); } From fb45ba64dc00e86615521177e3d9ec3d7a739b86 Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Tue, 9 Apr 2019 15:31:18 +0200 Subject: [PATCH 5/7] Schedule reactive retries in Netty event loop thread pool. By default, Project Reactor schedules delayed operations on a parallel scheduler, which is created lazily when needed. We can avoid creating such an extra resource by schedule the delayed work on our existing netty event loop thread pool. This behaviour is exactly the same as our existing async retries. Added tests to verify that the Parallel scheduler is not created. --- .../retry/ExponentialBackoffRetryLogic.java | 12 +++-- .../integration/reactive/RxSessionIT.java | 17 ++++++ .../ExponentialBackoffRetryLogicTest.java | 53 ++++++++----------- .../ImmediateSchedulingEventExecutor.java | 4 +- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java index 7ccf7c06f6..c078a78feb 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java +++ b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java @@ -23,6 +23,7 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; import reactor.util.context.Context; import reactor.util.function.Tuples; @@ -33,16 +34,16 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.function.Function; +import java.util.function.Supplier; -import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.exceptions.TransientException; +import org.neo4j.driver.internal.util.Clock; +import org.neo4j.driver.internal.util.Futures; import static java.util.concurrent.TimeUnit.SECONDS; @@ -179,12 +180,15 @@ private Function,Publisher> retryRxCondition() nextDelayMs = (long) (nextDelayMs * multiplier); errors = recordError( lastError, errors ); + // retry on netty event loop thread + EventExecutor eventExecutor = eventExecutorGroup.next(); return Mono.just( ctx.put( "errors", errors ).put( "startTime", startTime ).put( "nextDelayMs", nextDelayMs ) ) - .delayElement( Duration.ofMillis( delayWithJitterMs ) ); + .delayElement( Duration.ofMillis( delayWithJitterMs ), Schedulers.fromExecutorService( eventExecutor ) ); } else { addSuppressed( lastError, errors ); + return Mono.error( lastError ); } } ); diff --git a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java index 6830781661..0f5531a802 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxSessionIT.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Iterator; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.neo4j.driver.Session; @@ -46,6 +47,8 @@ import static java.util.Collections.emptyIterator; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V4; @@ -119,6 +122,7 @@ void shouldRunAsyncTransactionWithRetriesOnAsyncFailures() assertEquals( 4, work.invocationCount() ); assertEquals( 1, countNodesByLabel( "Node" ) ); + assertNoParallelScheduler(); } @Test @@ -134,6 +138,7 @@ void shouldRunAsyncTransactionWithRetriesOnSyncFailures() assertEquals( 3, work.invocationCount() ); assertEquals( 1, countNodesByLabel( "Test" ) ); + assertNoParallelScheduler(); } @Test @@ -150,6 +155,7 @@ void shouldRunAsyncTransactionThatCanNotBeRetried() assertEquals( 1, work.invocationCount() ); assertEquals( 0, countNodesByLabel( "Hi" ) ); + assertNoParallelScheduler(); } @Test @@ -173,6 +179,17 @@ void shouldRunAsyncTransactionThatCanNotBeRetriedAfterATransientFailure() assertEquals( 2, work.invocationCount() ); assertEquals( 0, countNodesByLabel( "Person" ) ); + assertNoParallelScheduler(); + } + + private void assertNoParallelScheduler() + { + Set threadSet = Thread.getAllStackTraces().keySet(); + for ( Thread t : threadSet ) + { + String name = t.getName(); + assertThat( name, not( startsWith( "parallel" ) ) ); + } } private long countNodesByLabel( String label ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java index 1e870fab20..d395152a84 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java @@ -22,21 +22,19 @@ import org.mockito.ArgumentCaptor; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; -import reactor.util.function.Tuple2; -import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.exceptions.TransientException; +import org.neo4j.driver.internal.util.Clock; +import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; import static java.lang.Long.MAX_VALUE; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -751,53 +749,44 @@ public CompletionStage get() } @Test - void shouldRetryWithBackOff() { + void shouldRetryWithBackOff() + { Exception exception = new TransientException( "Unknown", "Retry this error." ); - List elapsedList = new ArrayList<>(); - ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0, - null, Clock.SYSTEM, DEV_NULL_LOGGING ); + eventExecutor, Clock.SYSTEM, DEV_NULL_LOGGING ); Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); - StepVerifier.withVirtualTime( () -> // This test uses a virtual time. So do not panic if you saw this test runs faster than it should be. - Flux.from( retryLogic.retryRx( source ) ) - .elapsed() - .doOnNext( elapsed -> { if ( elapsed.getT2() == 0 ) elapsedList.add( elapsed.getT1() ); } ) - .map( Tuple2::getT2 ) ) - .thenAwait( Duration.ofSeconds( 2 ) ) + Flux retriedSource = Flux.from( retryLogic.retryRx( source ) ); + StepVerifier.create( retriedSource ) .expectNext( 0, 1 ) // first run .expectNext( 0, 1, 0, 1, 0, 1, 0, 1 ) //4 retry attempts .verifyErrorSatisfies( e -> assertThat( e, equalTo( exception ) ) ); - assertThat( elapsedList.size(), equalTo( 5 ) ); - assertThat( elapsedList, contains( 0L, 100L, 200L, 400L, 800L ) ); + List delays = eventExecutor.scheduleDelays(); + assertThat( delays.size(), equalTo( 4 ) ); + assertThat( delays, contains( 100L, 200L, 400L, 800L ) ); } @Test - void shouldRetryWithRandomBackOff() { - List elapsedList = new ArrayList<>(); + void shouldRetryWithRandomBackOff() + { Exception exception = new TransientException( "Unknown", "Retry this error." ); - ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0.1, - null, Clock.SYSTEM, DEV_NULL_LOGGING ); + eventExecutor, Clock.SYSTEM, DEV_NULL_LOGGING ); Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); - StepVerifier.withVirtualTime( () -> // This test uses a virtual time. So do not panic if you saw this test runs faster than it should be. - Flux.from( retryLogic.retryRx( source ) ) - .elapsed() - .doOnNext( elapsed -> { if ( elapsed.getT2() == 0 ) elapsedList.add( elapsed.getT1() ); } ) - .map( Tuple2::getT2 ) ) - .thenAwait( Duration.ofSeconds( 2 ) ) + Flux retriedSource = Flux.from( retryLogic.retryRx( source ) ); + StepVerifier.create( retriedSource ) .expectNext( 0, 1 ) // first run .expectNext( 0, 1, 0, 1, 0, 1, 0, 1 ) // 4 retry attempts .verifyErrorSatisfies( e -> assertThat( e, equalTo( exception ) ) ); - assertThat( elapsedList.size(), equalTo( 5 ) ); - assertThat( elapsedList.get( 0 ), equalTo( 0L ) ); - assertThat( elapsedList.get( 1 ), allOf( greaterThanOrEqualTo( 90L ), lessThanOrEqualTo( 110L ) ) ); - assertThat( elapsedList.get( 2 ), allOf( greaterThanOrEqualTo( 180L ), lessThanOrEqualTo( 220L ) ) ); - assertThat( elapsedList.get( 3 ), allOf( greaterThanOrEqualTo( 260L ), lessThanOrEqualTo( 440L ) ) ); - assertThat( elapsedList.get( 4 ), allOf( greaterThanOrEqualTo( 720L ), lessThanOrEqualTo( 880L ) ) ); + List delays = eventExecutor.scheduleDelays(); + assertThat( delays.size(), equalTo( 4 ) ); + assertThat( delays.get( 0 ), allOf( greaterThanOrEqualTo( 90L ), lessThanOrEqualTo( 110L ) ) ); + assertThat( delays.get( 1 ), allOf( greaterThanOrEqualTo( 180L ), lessThanOrEqualTo( 220L ) ) ); + assertThat( delays.get( 2 ), allOf( greaterThanOrEqualTo( 260L ), lessThanOrEqualTo( 440L ) ) ); + assertThat( delays.get( 3 ), allOf( greaterThanOrEqualTo( 720L ), lessThanOrEqualTo( 880L ) ) ); } private static void retry( ExponentialBackoffRetryLogic retryLogic, final int times ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/ImmediateSchedulingEventExecutor.java b/driver/src/test/java/org/neo4j/driver/internal/util/ImmediateSchedulingEventExecutor.java index c5edc335df..0df9a26eae 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/ImmediateSchedulingEventExecutor.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/ImmediateSchedulingEventExecutor.java @@ -180,7 +180,9 @@ public ScheduledFuture schedule( Runnable command, long delay, TimeUnit unit @Override public ScheduledFuture schedule( Callable callable, long delay, TimeUnit unit ) { - throw new UnsupportedOperationException(); + scheduleDelays.add( unit.toMillis( delay ) ); + delegate.submit( callable ); + return mock( ScheduledFuture.class ); } @Override From a8b03060b64d677947b949f62bd468252eb9b23c Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Wed, 10 Apr 2019 15:58:48 +0200 Subject: [PATCH 6/7] More tests and fixed a bug in original code where the maxRetryTime is not used correctly. --- .../retry/ExponentialBackoffRetryLogic.java | 7 +- .../ExponentialBackoffRetryLogicTest.java | 300 +++++++++++++++++- 2 files changed, 297 insertions(+), 10 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java index c078a78feb..6791d66244 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java +++ b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java @@ -172,10 +172,11 @@ private Function,Publisher> retryRxCondition() startTime = currentTime; } - long delayWithJitterMs = computeDelayWithJitter( nextDelayMs ); - if ( delayWithJitterMs < maxRetryTimeMs ) + long elapsedTime = currentTime - startTime; + if ( elapsedTime < maxRetryTimeMs ) { - log.warn( "Transaction failed and will be retried in " + delayWithJitterMs + "ms", lastError ); + long delayWithJitterMs = computeDelayWithJitter( nextDelayMs ); + log.warn( "Reactive transaction failed and is scheduled to retry in " + delayWithJitterMs + "ms", lastError ); nextDelayMs = (long) (nextDelayMs * multiplier); errors = recordError( lastError, errors ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java index d395152a84..236d951c96 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java @@ -19,14 +19,25 @@ package org.neo4j.driver.internal.retry; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; +import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoSink; import reactor.test.StepVerifier; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Stream; import org.neo4j.driver.Logger; import org.neo4j.driver.Logging; @@ -140,6 +151,24 @@ void nextDelayCalculatedAccordingToMultiplierAsync() assertEquals( delaysWithoutJitter( initialDelay, multiplier, retries ), eventExecutor.scheduleDelays() ); } + @Test + void nextDelayCalculatedAccordingToMultiplierRx() + { + String result = "The Result"; + int retries = 14; + int initialDelay = 1; + int multiplier = 2; + int noJitter = 0; + + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( MAX_VALUE, initialDelay, multiplier, noJitter, + Clock.SYSTEM ); + + Mono single = Flux.from( retryRx( retryLogic, retries, result ) ).single(); + + assertEquals( result, await( single ) ); + assertEquals( delaysWithoutJitter( initialDelay, multiplier, retries ), eventExecutor.scheduleDelays() ); + } + @Test void nextDelayCalculatedAccordingToJitter() throws Exception { @@ -180,6 +209,27 @@ void nextDelayCalculatedAccordingToJitterAsync() assertDelaysApproximatelyEqual( delaysWithoutJitter, scheduleDelays, jitterFactor ); } + @Test + void nextDelayCalculatedAccordingToJitterRx() + { + String result = "The Result"; + int retries = 24; + double jitterFactor = 0.2; + int initialDelay = 1; + int multiplier = 2; + + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( MAX_VALUE, initialDelay, multiplier, jitterFactor, + mock( Clock.class ) ); + + Mono single = Flux.from( retryRx( retryLogic, retries, result ) ).single(); + assertEquals( result, await( single ) ); + + List scheduleDelays = eventExecutor.scheduleDelays(); + List delaysWithoutJitter = delaysWithoutJitter( initialDelay, multiplier, retries ); + + assertDelaysApproximatelyEqual( delaysWithoutJitter, scheduleDelays, jitterFactor ); + } + @Test void doesNotRetryWhenMaxRetryTimeExceeded() throws Exception { @@ -237,6 +287,35 @@ void doesNotRetryWhenMaxRetryTimeExceededAsync() verify( workMock, times( 3 ) ).get(); } + @Test + void doesNotRetryWhenMaxRetryTimeExceededRx() + { + long retryStart = Clock.SYSTEM.millis(); + int initialDelay = 100; + int multiplier = 2; + long maxRetryTimeMs = 45; + Clock clock = mock( Clock.class ); + when( clock.millis() ).thenReturn( retryStart ) + .thenReturn( retryStart + maxRetryTimeMs - 5 ) + .thenReturn( retryStart + maxRetryTimeMs + 7 ); + + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( maxRetryTimeMs, initialDelay, multiplier, 0, clock ); + + SessionExpiredException error = sessionExpired(); + AtomicInteger executionCount = new AtomicInteger(); + Publisher publisher = retryLogic.retryRx( Mono.error( error ).doOnTerminate( executionCount::getAndIncrement ) ); + + Exception e = assertThrows( Exception.class, () -> await( publisher ) ); + assertEquals( error, e ); + + List scheduleDelays = eventExecutor.scheduleDelays(); + assertEquals( 2, scheduleDelays.size() ); + assertEquals( initialDelay, scheduleDelays.get( 0 ).intValue() ); + assertEquals( initialDelay * multiplier, scheduleDelays.get( 1 ).intValue() ); + + assertThat( executionCount.get(), equalTo( 3 ) ); + } + @Test void sleepsOnServiceUnavailableException() throws Exception { @@ -262,7 +341,7 @@ void schedulesRetryOnServiceUnavailableExceptionAsync() ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 42, 1, 0, clock ); Supplier> workMock = newWorkMock(); - SessionExpiredException error = sessionExpired(); + ServiceUnavailableException error = serviceUnavailable(); when( workMock.get() ).thenReturn( failedFuture( error ) ).thenReturn( completedFuture( result ) ); assertEquals( result, await( retryLogic.retryAsync( workMock ) ) ); @@ -450,6 +529,40 @@ void doesNotRetryOnTransactionLockClientStoppedErrorAsync() assertEquals( 0, eventExecutor.scheduleDelays().size() ); } + @ParameterizedTest + @MethodSource( "canBeRetriedErrors" ) + void schedulesRetryOnErrorRx( Exception error ) + { + String result = "The Result"; + Clock clock = mock( Clock.class ); + + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 4242, 1, 0, clock ); + + Publisher publisher = createMono( result, error ); + Mono single = Flux.from( retryLogic.retryRx( publisher ) ).single(); + + assertEquals( result, await( single ) ); + + List scheduleDelays = eventExecutor.scheduleDelays(); + assertEquals( 1, scheduleDelays.size() ); + assertEquals( 4242, scheduleDelays.get( 0 ).intValue() ); + } + + @ParameterizedTest + @MethodSource( "cannotBeRetriedErrors" ) + void scheduleNoRetryOnErrorRx( Exception error ) + { + Clock clock = mock( Clock.class ); + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 10, 1, 1, clock ); + + Mono single = Flux.from( retryLogic.retryRx( Mono.error( error ) ) ).single(); + + Exception e = assertThrows( Exception.class, () -> await( single ) ); + assertEquals( error, e ); + + assertEquals( 0, eventExecutor.scheduleDelays().size() ); + } + @Test void throwsWhenSleepInterrupted() throws Exception { @@ -546,6 +659,37 @@ void collectsSuppressedErrorsAsync() assertEquals( initialDelay * multiplier * multiplier, scheduleDelays.get( 2 ).intValue() ); } + @Test + void collectsSuppressedErrorsRx() throws Exception + { + long maxRetryTime = 20; + int initialDelay = 15; + int multiplier = 2; + Clock clock = mock( Clock.class ); + when( clock.millis() ).thenReturn( 0L ).thenReturn( 10L ).thenReturn( 15L ).thenReturn( 25L ); + ExponentialBackoffRetryLogic logic = newRetryLogic( maxRetryTime, initialDelay, multiplier, 0, clock ); + + SessionExpiredException error1 = sessionExpired(); + SessionExpiredException error2 = sessionExpired(); + ServiceUnavailableException error3 = serviceUnavailable(); + TransientException error4 = transientException(); + Mono mono = createMono( "A result", error1, error2, error3, error4 ); + + StepVerifier.create( logic.retryRx( mono ) ).expectErrorSatisfies( e -> { + assertEquals( error4, e ); + Throwable[] suppressed = e.getSuppressed(); + assertEquals( 3, suppressed.length ); + assertEquals( error1, suppressed[0] ); + assertEquals( error2, suppressed[1] ); + assertEquals( error3, suppressed[2] ); + } ).verify(); + + List scheduleDelays = eventExecutor.scheduleDelays(); + assertEquals( 3, scheduleDelays.size() ); + assertEquals( initialDelay, scheduleDelays.get( 0 ).intValue() ); + assertEquals( initialDelay * multiplier, scheduleDelays.get( 1 ).intValue() ); + assertEquals( initialDelay * multiplier * multiplier, scheduleDelays.get( 2 ).intValue() ); } + @Test void doesNotCollectSuppressedErrorsWhenSameErrorIsThrown() throws Exception { @@ -598,6 +742,26 @@ void doesNotCollectSuppressedErrorsWhenSameErrorIsThrownAsync() assertEquals( initialDelay * multiplier, scheduleDelays.get( 1 ).intValue() ); } + @Test + void doesNotCollectSuppressedErrorsWhenSameErrorIsThrownRx() + { + long maxRetryTime = 20; + int initialDelay = 15; + int multiplier = 2; + Clock clock = mock( Clock.class ); + when( clock.millis() ).thenReturn( 0L ).thenReturn( 10L ).thenReturn( 25L ); + + ExponentialBackoffRetryLogic retryLogic = newRetryLogic( maxRetryTime, initialDelay, multiplier, 0, clock ); + + SessionExpiredException error = sessionExpired(); + StepVerifier.create( retryLogic.retryRx( Mono.error( error ) ) ).expectErrorSatisfies( e -> assertEquals( error, e ) ).verify(); + + List scheduleDelays = eventExecutor.scheduleDelays(); + assertEquals( 2, scheduleDelays.size() ); + assertEquals( initialDelay, scheduleDelays.get( 0 ).intValue() ); + assertEquals( initialDelay * multiplier, scheduleDelays.get( 1 ).intValue() ); + } + @Test void eachRetryIsLogged() { @@ -638,6 +802,27 @@ void eachRetryIsLoggedAsync() ); } + @Test + void eachRetryIsLoggedRx() + { + String result = "The Result"; + int retries = 9; + Clock clock = mock( Clock.class ); + Logging logging = mock( Logging.class ); + Logger logger = mock( Logger.class ); + when( logging.getLog( anyString() ) ).thenReturn( logger ); + + ExponentialBackoffRetryLogic logic = new ExponentialBackoffRetryLogic( RetrySettings.DEFAULT, eventExecutor, + clock, logging ); + + assertEquals( result, await( Flux.from( retryRx( logic, retries, result ) ).single() ) ); + + verify( logger, times( retries ) ).warn( + startsWith( "Reactive transaction failed and is scheduled to retry" ), + any( ServiceUnavailableException.class ) + ); + } + @Test void nothingIsLoggedOnFatalFailure() { @@ -672,6 +857,22 @@ void nothingIsLoggedOnFatalFailureAsync() verifyZeroInteractions( logger ); } + @Test + void nothingIsLoggedOnFatalFailureRx() + { + Logging logging = mock( Logging.class ); + Logger logger = mock( Logger.class ); + when( logging.getLog( anyString() ) ).thenReturn( logger ); + ExponentialBackoffRetryLogic logic = new ExponentialBackoffRetryLogic( RetrySettings.DEFAULT, eventExecutor, + mock( Clock.class ), logging ); + + Publisher retryRx = logic.retryRx( Mono.error( new RuntimeException( "Fatal rx" ) ) ); + RuntimeException error = assertThrows( RuntimeException.class, () -> await( retryRx ) ); + + assertEquals( "Fatal rx", error.getMessage() ); + verifyZeroInteractions( logger ); + } + @Test void correctNumberOfRetiesAreLoggedOnFailure() { @@ -749,11 +950,44 @@ public CompletionStage get() } @Test - void shouldRetryWithBackOff() + void correctNumberOfRetiesAreLoggedOnFailureRx() + { + Clock clock = mock( Clock.class ); + Logging logging = mock( Logging.class ); + Logger logger = mock( Logger.class ); + when( logging.getLog( anyString() ) ).thenReturn( logger ); + RetrySettings settings = RetrySettings.DEFAULT; + RetryLogic logic = new ExponentialBackoffRetryLogic( settings, eventExecutor, clock, logging ); + + AtomicBoolean invoked = new AtomicBoolean( false ); + SessionExpiredException error = assertThrows( SessionExpiredException.class, () -> + await( logic.retryRx( Mono.create( e -> { + if ( invoked.get() ) + { + // move clock forward to stop retries + when( clock.millis() ).thenReturn( settings.maxRetryTimeMs() + 42 ); + } + else + { + invoked.set( true ); + } + e.error( new SessionExpiredException( "Session no longer valid" ) ); + } ) ) ) ); + assertEquals( "Session no longer valid", error.getMessage() ); + verify( logger ).warn( + startsWith( "Reactive transaction failed and is scheduled to retry" ), + any( SessionExpiredException.class ) + ); + } + + @Test + void shouldRetryWithBackOffRx() { Exception exception = new TransientException( "Unknown", "Retry this error." ); - ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0, - eventExecutor, Clock.SYSTEM, DEV_NULL_LOGGING ); + Clock clock = mock( Clock.class ); + when( clock.millis() ).thenReturn( 0L, 100L, 200L, 400L, 800L ); + ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 500, 100, 2, 0, + eventExecutor, clock, DEV_NULL_LOGGING ); Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); Flux retriedSource = Flux.from( retryLogic.retryRx( source ) ); @@ -768,11 +1002,13 @@ void shouldRetryWithBackOff() } @Test - void shouldRetryWithRandomBackOff() + void shouldRetryWithRandomBackOffRx() { Exception exception = new TransientException( "Unknown", "Retry this error." ); - ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 1000, 100, 2, 0.1, - eventExecutor, Clock.SYSTEM, DEV_NULL_LOGGING ); + Clock clock = mock( Clock.class ); + when( clock.millis() ).thenReturn( 0L, 100L, 200L, 400L, 800L ); + ExponentialBackoffRetryLogic retryLogic = new ExponentialBackoffRetryLogic( 500, 100, 2, 0.1, + eventExecutor, clock, DEV_NULL_LOGGING ); Flux source = Flux.concat( Flux.range( 0, 2 ), Flux.error( exception ) ); Flux retriedSource = Flux.from( retryLogic.retryRx( source ) ); @@ -827,6 +1063,22 @@ public CompletionStage get() } ); } + private Publisher retryRx( ExponentialBackoffRetryLogic retryLogic, int times, Object result ) + { + AtomicInteger invoked = new AtomicInteger(); + return retryLogic.retryRx( Mono.create( e -> { + if ( invoked.get() < times ) + { + invoked.getAndIncrement(); + e.error( serviceUnavailable() ); + } + else + { + e.success( result ); + } + } ) ); + } + private static List delaysWithoutJitter( long initialDelay, double multiplier, int count ) { List values = new ArrayList<>(); @@ -888,4 +1140,38 @@ private static void assertDelaysApproximatelyEqual( List expectedDelays, L assertThat( actualValue, closeTo( expectedValue, expectedValue * delta ) ); } } + + private Mono createMono( T result, Exception... errors ) + { + AtomicInteger executionCount = new AtomicInteger(); + Iterator iterator = Arrays.asList( errors ).iterator(); + return Mono.create( (Consumer>) e -> { + if ( iterator.hasNext() ) + { + e.error( iterator.next() ); + } + else + { + e.success( result ); + } + } ).doOnTerminate( executionCount::getAndIncrement ); + } + + private static Stream canBeRetriedErrors() + { + return Stream.of( + transientException(), + sessionExpired(), + serviceUnavailable() + ); + } + + private static Stream cannotBeRetriedErrors() + { + return Stream.of( + new IllegalStateException(), + new TransientException( "Neo.TransientError.Transaction.Terminated", "" ), + new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" ) + ); + } } From e36d4d9a0646b7ccfe4e1fa39779a377b15b848e Mon Sep 17 00:00:00 2001 From: Zhen Li Date: Mon, 15 Apr 2019 10:00:00 +0200 Subject: [PATCH 7/7] Removed unnecessary interfaces `NetworkSession` and `ExplicitTransaction`. And put implementation directly instead. --- .../driver/internal/SessionFactoryImpl.java | 5 +- .../internal/async/ExplicitTransaction.java | 235 +++++++++++- .../async/InternalExplicitTransaction.java | 275 -------------- .../async/InternalNetworkSession.java | 352 ------------------ .../async/LeakLoggingNetworkSession.java | 2 +- .../driver/internal/async/NetworkSession.java | 311 +++++++++++++++- .../java/org/neo4j/driver/ParametersTest.java | 6 +- .../driver/internal/DriverFactoryTest.java | 4 +- .../internal/SessionFactoryImplTest.java | 7 +- ...Test.java => ExplicitTransactionTest.java} | 14 +- ...ava => LeakLoggingNetworkSessionTest.java} | 2 +- ...ssionTest.java => NetworkSessionTest.java} | 4 +- .../java/org/neo4j/driver/util/TestUtil.java | 16 +- 13 files changed, 551 insertions(+), 682 deletions(-) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java rename driver/src/test/java/org/neo4j/driver/internal/async/{InternalExplicitTransactionTest.java => ExplicitTransactionTest.java} (93%) rename driver/src/test/java/org/neo4j/driver/internal/async/{LeakLoggingInternalNetworkSessionTest.java => LeakLoggingNetworkSessionTest.java} (99%) rename driver/src/test/java/org/neo4j/driver/internal/async/{InternalNetworkSessionTest.java => NetworkSessionTest.java} (99%) diff --git a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java index acecfdd640..37ee254375 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/SessionFactoryImpl.java @@ -23,9 +23,8 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Config; import org.neo4j.driver.Logging; -import org.neo4j.driver.internal.async.InternalNetworkSession; -import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.ConnectionProvider; @@ -80,6 +79,6 @@ private NetworkSession createSession( ConnectionProvider connectionProvider, Ret { return leakedSessionsLoggingEnabled ? new LeakLoggingNetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ) - : new InternalNetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ); + : new NetworkSession( connectionProvider, retryLogic, databaseName, mode, bookmarksHolder, logging ); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java index 8765188393..fc1e848240 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/ExplicitTransaction.java @@ -18,36 +18,247 @@ */ package org.neo4j.driver.internal.async; +import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.function.BiFunction; +import org.neo4j.driver.Session; import org.neo4j.driver.Statement; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.Bookmarks; +import org.neo4j.driver.internal.BookmarksHolder; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.util.Futures; -public interface ExplicitTransaction +import static org.neo4j.driver.internal.util.Futures.completedWithNull; +import static org.neo4j.driver.internal.util.Futures.failedFuture; + +public class ExplicitTransaction { - CompletionStage beginAsync( Bookmarks initialBookmarks, TransactionConfig config ); + private enum State + { + /** The transaction is running with no explicit success or failure marked */ + ACTIVE, + + /** Running, user marked for success, meaning it'll value committed */ + MARKED_SUCCESS, + + /** User marked as failed, meaning it'll be rolled back. */ + MARKED_FAILED, + + /** + * This transaction has been terminated either because of explicit {@link Session#reset()} or because of a + * fatal connection error. + */ + TERMINATED, + + /** This transaction has successfully committed */ + COMMITTED, + + /** This transaction has been rolled back */ + ROLLED_BACK + } + + private final Connection connection; + private final BoltProtocol protocol; + private final BookmarksHolder bookmarksHolder; + private final ResultCursorsHolder resultCursors; + + private volatile State state = State.ACTIVE; + + public ExplicitTransaction( Connection connection, BookmarksHolder bookmarksHolder ) + { + this.connection = connection; + this.protocol = connection.protocol(); + this.bookmarksHolder = bookmarksHolder; + this.resultCursors = new ResultCursorsHolder(); + } + + public CompletionStage beginAsync( Bookmarks initialBookmarks, TransactionConfig config ) + { + return protocol.beginTransaction( connection, initialBookmarks, config ) + .handle( ( ignore, beginError ) -> + { + if ( beginError != null ) + { + // release connection if begin failed, transaction can't be started + connection.release(); + throw Futures.asCompletionException( beginError ); + } + return this; + } ); + } + + public void success() + { + if ( state == State.ACTIVE ) + { + state = State.MARKED_SUCCESS; + } + } + + public void failure() + { + if ( state == State.ACTIVE || state == State.MARKED_SUCCESS ) + { + state = State.MARKED_FAILED; + } + } + + public CompletionStage closeAsync() + { + if ( state == State.MARKED_SUCCESS ) + { + return commitAsync(); + } + else if ( state != State.COMMITTED && state != State.ROLLED_BACK ) + { + return rollbackAsync(); + } + else + { + return completedWithNull(); + } + } + + public CompletionStage commitAsync() + { + if ( state == State.COMMITTED ) + { + return completedWithNull(); + } + else if ( state == State.ROLLED_BACK ) + { + return failedFuture( new ClientException( "Can't commit, transaction has been rolled back" ) ); + } + else + { + return resultCursors.retrieveNotConsumedError() + .thenCompose( error -> doCommitAsync().handle( handleCommitOrRollback( error ) ) ) + .whenComplete( ( ignore, error ) -> transactionClosed( error == null ) ); + } + } + + public CompletionStage rollbackAsync() + { + if ( state == State.COMMITTED ) + { + return failedFuture( new ClientException( "Can't rollback, transaction has been committed" ) ); + } + else if ( state == State.ROLLED_BACK ) + { + return completedWithNull(); + } + else + { + return resultCursors.retrieveNotConsumedError() + .thenCompose( error -> doRollbackAsync().handle( handleCommitOrRollback( error ) ) ) + .whenComplete( ( ignore, error ) -> transactionClosed( false ) ); + } + } - CompletionStage runAsync( Statement statement, boolean waitForRunResponse ); + public CompletionStage runAsync( Statement statement, boolean waitForRunResponse ) + { + ensureCanRunQueries(); + CompletionStage cursorStage = + protocol.runInExplicitTransaction( connection, statement, this, waitForRunResponse ).asyncResult(); + resultCursors.add( cursorStage ); + return cursorStage.thenApply( cursor -> cursor ); + } - CompletionStage runRx( Statement statement ); + public CompletionStage runRx( Statement statement ) + { + ensureCanRunQueries(); + CompletionStage cursorStage = + protocol.runInExplicitTransaction( connection, statement, this, false ).rxResult(); + resultCursors.add( cursorStage ); + return cursorStage; + } - void success(); + public boolean isOpen() + { + return state != State.COMMITTED && state != State.ROLLED_BACK; + } - void failure(); + public void markTerminated() + { + state = State.TERMINATED; + } - CompletionStage commitAsync(); + public Connection connection() + { + return connection; + } - CompletionStage rollbackAsync(); + private void ensureCanRunQueries() + { + if ( state == State.COMMITTED ) + { + throw new ClientException( "Cannot run more statements in this transaction, it has been committed" ); + } + else if ( state == State.ROLLED_BACK ) + { + throw new ClientException( "Cannot run more statements in this transaction, it has been rolled back" ); + } + else if ( state == State.MARKED_FAILED ) + { + throw new ClientException( "Cannot run more statements in this transaction, it has been marked for failure. " + + "Please either rollback or close this transaction" ); + } + else if ( state == State.TERMINATED ) + { + throw new ClientException( "Cannot run more statements in this transaction, " + + "it has either experienced an fatal error or was explicitly terminated" ); + } + } - boolean isOpen(); + private CompletionStage doCommitAsync() + { + if ( state == State.TERMINATED ) + { + return failedFuture( new ClientException( "Transaction can't be committed. " + + "It has been rolled back either because of an error or explicit termination" ) ); + } + return protocol.commitTransaction( connection ).thenAccept( bookmarksHolder::setBookmarks ); + } - CompletionStage closeAsync(); + private CompletionStage doRollbackAsync() + { + if ( state == State.TERMINATED ) + { + return completedWithNull(); + } + return protocol.rollbackTransaction( connection ); + } - void markTerminated(); + private static BiFunction handleCommitOrRollback( Throwable cursorFailure ) + { + return ( ignore, commitOrRollbackError ) -> + { + CompletionException combinedError = Futures.combineErrors( cursorFailure, commitOrRollbackError ); + if ( combinedError != null ) + { + throw combinedError; + } + return null; + }; + } - Connection connection(); + private void transactionClosed( boolean isCommitted ) + { + if ( isCommitted ) + { + state = State.COMMITTED; + } + else + { + state = State.ROLLED_BACK; + } + connection.release(); // release in background + } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java deleted file mode 100644 index 0a700ed948..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/async/InternalExplicitTransaction.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2002-2019 "Neo4j," - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.neo4j.driver.internal.async; - -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.function.BiFunction; - -import org.neo4j.driver.Session; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.Bookmarks; -import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; -import org.neo4j.driver.internal.cursor.RxStatementResultCursor; -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.util.Futures; - -import static org.neo4j.driver.internal.util.Futures.completedWithNull; -import static org.neo4j.driver.internal.util.Futures.failedFuture; - -public class InternalExplicitTransaction implements ExplicitTransaction -{ - private enum State - { - /** The transaction is running with no explicit success or failure marked */ - ACTIVE, - - /** Running, user marked for success, meaning it'll value committed */ - MARKED_SUCCESS, - - /** User marked as failed, meaning it'll be rolled back. */ - MARKED_FAILED, - - /** - * This transaction has been terminated either because of explicit {@link Session#reset()} or because of a - * fatal connection error. - */ - TERMINATED, - - /** This transaction has successfully committed */ - COMMITTED, - - /** This transaction has been rolled back */ - ROLLED_BACK - } - - private final Connection connection; - private final BoltProtocol protocol; - private final BookmarksHolder bookmarksHolder; - private final ResultCursorsHolder resultCursors; - - private volatile State state = State.ACTIVE; - - public InternalExplicitTransaction( Connection connection, BookmarksHolder bookmarksHolder ) - { - this.connection = connection; - this.protocol = connection.protocol(); - this.bookmarksHolder = bookmarksHolder; - this.resultCursors = new ResultCursorsHolder(); - } - - @Override - public CompletionStage beginAsync( Bookmarks initialBookmarks, TransactionConfig config ) - { - return protocol.beginTransaction( connection, initialBookmarks, config ) - .handle( ( ignore, beginError ) -> - { - if ( beginError != null ) - { - // release connection if begin failed, transaction can't be started - connection.release(); - throw Futures.asCompletionException( beginError ); - } - return this; - } ); - } - - @Override - public void success() - { - if ( state == State.ACTIVE ) - { - state = State.MARKED_SUCCESS; - } - } - - @Override - public void failure() - { - if ( state == State.ACTIVE || state == State.MARKED_SUCCESS ) - { - state = State.MARKED_FAILED; - } - } - - @Override - public CompletionStage closeAsync() - { - if ( state == State.MARKED_SUCCESS ) - { - return commitAsync(); - } - else if ( state != State.COMMITTED && state != State.ROLLED_BACK ) - { - return rollbackAsync(); - } - else - { - return completedWithNull(); - } - } - - @Override - public CompletionStage commitAsync() - { - if ( state == State.COMMITTED ) - { - return completedWithNull(); - } - else if ( state == State.ROLLED_BACK ) - { - return failedFuture( new ClientException( "Can't commit, transaction has been rolled back" ) ); - } - else - { - return resultCursors.retrieveNotConsumedError() - .thenCompose( error -> doCommitAsync().handle( handleCommitOrRollback( error ) ) ) - .whenComplete( ( ignore, error ) -> transactionClosed( error == null ) ); - } - } - - @Override - public CompletionStage rollbackAsync() - { - if ( state == State.COMMITTED ) - { - return failedFuture( new ClientException( "Can't rollback, transaction has been committed" ) ); - } - else if ( state == State.ROLLED_BACK ) - { - return completedWithNull(); - } - else - { - return resultCursors.retrieveNotConsumedError() - .thenCompose( error -> doRollbackAsync().handle( handleCommitOrRollback( error ) ) ) - .whenComplete( ( ignore, error ) -> transactionClosed( false ) ); - } - } - - @Override - public CompletionStage runAsync( Statement statement, boolean waitForRunResponse ) - { - ensureCanRunQueries(); - CompletionStage cursorStage = - protocol.runInExplicitTransaction( connection, statement, this, waitForRunResponse ).asyncResult(); - resultCursors.add( cursorStage ); - return cursorStage.thenApply( cursor -> cursor ); - } - - @Override - public CompletionStage runRx( Statement statement ) - { - ensureCanRunQueries(); - CompletionStage cursorStage = - protocol.runInExplicitTransaction( connection, statement, this, false ).rxResult(); - resultCursors.add( cursorStage ); - return cursorStage; - } - - @Override - public boolean isOpen() - { - return state != State.COMMITTED && state != State.ROLLED_BACK; - } - - @Override - public void markTerminated() - { - state = State.TERMINATED; - } - - @Override - public Connection connection() - { - return connection; - } - - private void ensureCanRunQueries() - { - if ( state == State.COMMITTED ) - { - throw new ClientException( "Cannot run more statements in this transaction, it has been committed" ); - } - else if ( state == State.ROLLED_BACK ) - { - throw new ClientException( "Cannot run more statements in this transaction, it has been rolled back" ); - } - else if ( state == State.MARKED_FAILED ) - { - throw new ClientException( "Cannot run more statements in this transaction, it has been marked for failure. " + - "Please either rollback or close this transaction" ); - } - else if ( state == State.TERMINATED ) - { - throw new ClientException( "Cannot run more statements in this transaction, " + - "it has either experienced an fatal error or was explicitly terminated" ); - } - } - - private CompletionStage doCommitAsync() - { - if ( state == State.TERMINATED ) - { - return failedFuture( new ClientException( "Transaction can't be committed. " + - "It has been rolled back either because of an error or explicit termination" ) ); - } - return protocol.commitTransaction( connection ).thenAccept( bookmarksHolder::setBookmarks ); - } - - private CompletionStage doRollbackAsync() - { - if ( state == State.TERMINATED ) - { - return completedWithNull(); - } - return protocol.rollbackTransaction( connection ); - } - - private static BiFunction handleCommitOrRollback( Throwable cursorFailure ) - { - return ( ignore, commitOrRollbackError ) -> - { - CompletionException combinedError = Futures.combineErrors( cursorFailure, commitOrRollbackError ); - if ( combinedError != null ) - { - throw combinedError; - } - return null; - }; - } - - private void transactionClosed( boolean isCommitted ) - { - if ( isCommitted ) - { - state = State.COMMITTED; - } - else - { - state = State.ROLLED_BACK; - } - connection.release(); // release in background - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java deleted file mode 100644 index ff1d30251d..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/async/InternalNetworkSession.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2002-2019 "Neo4j," - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.neo4j.driver.internal.async; - -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.Logger; -import org.neo4j.driver.Logging; -import org.neo4j.driver.Statement; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.async.StatementResultCursor; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.BookmarksHolder; -import org.neo4j.driver.internal.FailableCursor; -import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; -import org.neo4j.driver.internal.cursor.RxStatementResultCursor; -import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; -import org.neo4j.driver.internal.logging.PrefixedLogger; -import org.neo4j.driver.internal.retry.RetryLogic; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.ConnectionProvider; -import org.neo4j.driver.internal.util.Futures; - -import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.neo4j.driver.internal.util.Futures.completedWithNull; - -public class InternalNetworkSession implements NetworkSession -{ - private static final String LOG_NAME = "Session"; - - private final ConnectionProvider connectionProvider; - private final AccessMode mode; - private final String databaseName; - private final RetryLogic retryLogic; - protected final Logger logger; - - private final BookmarksHolder bookmarksHolder; - private volatile CompletionStage transactionStage = completedWithNull(); - private volatile CompletionStage connectionStage = completedWithNull(); - private volatile CompletionStage resultCursorStage = completedWithNull(); - - private final AtomicBoolean open = new AtomicBoolean( true ); - - public InternalNetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, - BookmarksHolder bookmarksHolder, Logging logging ) - { - this.connectionProvider = connectionProvider; - this.mode = mode; - this.retryLogic = retryLogic; - this.logger = new PrefixedLogger( "[" + hashCode() + "]", logging.getLog( LOG_NAME ) ); - this.bookmarksHolder = bookmarksHolder; - this.databaseName = databaseName; - } - - @Override - public CompletionStage runAsync( Statement statement, TransactionConfig config, boolean waitForRunResponse ) - { - CompletionStage newResultCursorStage = - buildResultCursorFactory( statement, config, waitForRunResponse ).thenCompose( StatementResultCursorFactory::asyncResult ); - - resultCursorStage = newResultCursorStage.exceptionally( error -> null ); - return newResultCursorStage.thenApply( cursor -> cursor ); // convert the return type - } - - @Override - public CompletionStage runRx( Statement statement, TransactionConfig config ) - { - CompletionStage newResultCursorStage = - buildResultCursorFactory( statement, config, true ).thenCompose( StatementResultCursorFactory::rxResult ); - - resultCursorStage = newResultCursorStage.exceptionally( error -> null ); - return newResultCursorStage; - } - - @Override - public CompletionStage beginTransactionAsync( TransactionConfig config ) - { - return this.beginTransactionAsync( mode, config ); - } - - @Override - public CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ) - { - ensureSessionIsOpen(); - - // create a chain that acquires connection and starts a transaction - CompletionStage newTransactionStage = ensureNoOpenTxBeforeStartingTx() - .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) - .thenCompose( connection -> - { - ExplicitTransaction tx = new InternalExplicitTransaction( connection, bookmarksHolder ); - return tx.beginAsync( bookmarksHolder.getBookmarks(), config ); - } ); - - // update the reference to the only known transaction - CompletionStage currentTransactionStage = transactionStage; - - transactionStage = newTransactionStage - .exceptionally( error -> null ) // ignore errors from starting new transaction - .thenCompose( tx -> - { - if ( tx == null ) - { - // failed to begin new transaction, keep reference to the existing one - return currentTransactionStage; - } - // new transaction started, keep reference to it - return completedFuture( tx ); - } ); - - return newTransactionStage; - } - - @Override - public CompletionStage resetAsync() - { - return existingTransactionOrNull() - .thenAccept( tx -> - { - if ( tx != null ) - { - tx.markTerminated(); - } - } ) - .thenCompose( ignore -> connectionStage ) - .thenCompose( connection -> - { - if ( connection != null ) - { - // there exists an active connection, send a RESET message over it - return connection.reset(); - } - return completedWithNull(); - } ); - } - - @Override - public RetryLogic retryLogic() - { - return retryLogic; - } - - @Override - public String lastBookmark() - { - return bookmarksHolder.lastBookmark(); - } - - @Override - public CompletionStage releaseConnectionAsync() - { - return connectionStage.thenCompose( connection -> - { - if ( connection != null ) - { - // there exists connection, try to release it back to the pool - return connection.release(); - } - // no connection so return null - return completedWithNull(); - } ); - } - - @Override - public CompletionStage connectionAsync() - { - return connectionStage; - } - - @Override - public boolean isOpen() - { - return open.get(); - } - - @Override - public CompletionStage closeAsync() - { - if ( open.compareAndSet( true, false ) ) - { - return resultCursorStage.thenCompose( cursor -> - { - if ( cursor != null ) - { - // there exists a cursor with potentially unconsumed error, try to extract and propagate it - return cursor.failureAsync(); - } - // no result cursor exists so no error exists - return completedWithNull(); - } ).thenCompose( cursorError -> closeTransactionAndReleaseConnection().thenApply( txCloseError -> - { - // now we have cursor error, active transaction has been closed and connection has been released - // back to the pool; try to propagate cursor and transaction close errors, if any - CompletionException combinedError = Futures.combineErrors( cursorError, txCloseError ); - if ( combinedError != null ) - { - throw combinedError; - } - return null; - } ) ); - } - return completedWithNull(); - } - - protected CompletionStage currentConnectionIsOpen() - { - return connectionStage.handle( ( connection, error ) -> - error == null && // no acquisition error - connection != null && // some connection has actually been acquired - connection.isOpen() ); // and it's still open - } - - private CompletionStage buildResultCursorFactory( Statement statement, TransactionConfig config, boolean waitForRunResponse ) - { - ensureSessionIsOpen(); - - return ensureNoOpenTxBeforeRunningQuery() - .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) - .thenCompose( connection -> { - try - { - StatementResultCursorFactory factory = connection.protocol() - .runInAutoCommitTransaction( connection, statement, bookmarksHolder, config, waitForRunResponse ); - return completedFuture( factory ); - } - catch ( Throwable e ) - { - return Futures.failedFuture( e ); - } - } ); - } - - private CompletionStage acquireConnection( String databaseName, AccessMode mode ) - { - CompletionStage currentConnectionStage = connectionStage; - - CompletionStage newConnectionStage = resultCursorStage.thenCompose( cursor -> - { - if ( cursor == null ) - { - return completedWithNull(); - } - // make sure previous result is fully consumed and connection is released back to the pool - return cursor.failureAsync(); - } ).thenCompose( error -> - { - if ( error == null ) - { - // there is no unconsumed error, so one of the following is true: - // 1) this is first time connection is acquired in this session - // 2) previous result has been successful and is fully consumed - // 3) previous result failed and error has been consumed - - // return existing connection, which should've been released back to the pool by now - return currentConnectionStage.exceptionally( ignore -> null ); - } - else - { - // there exists unconsumed error, re-throw it - throw new CompletionException( error ); - } - } ).thenCompose( existingConnection -> - { - if ( existingConnection != null && existingConnection.isOpen() ) - { - // there somehow is an existing open connection, this should not happen, just a precondition - throw new IllegalStateException( "Existing open connection detected" ); - } - return connectionProvider.acquireConnection( databaseName, mode ); - } ); - - connectionStage = newConnectionStage.exceptionally( error -> null ); - - return newConnectionStage; - } - - private CompletionStage closeTransactionAndReleaseConnection() - { - return existingTransactionOrNull().thenCompose( tx -> - { - if ( tx != null ) - { - // there exists an open transaction, let's close it and propagate the error, if any - return tx.closeAsync() - .thenApply( ignore -> (Throwable) null ) - .exceptionally( error -> error ); - } - // no open transaction so nothing to close - return completedWithNull(); - } ).thenCompose( txCloseError -> - // then release the connection and propagate transaction close error, if any - releaseConnectionAsync().thenApply( ignore -> txCloseError ) ); - } - - private CompletionStage ensureNoOpenTxBeforeRunningQuery() - { - return ensureNoOpenTx( "Statements cannot be run directly on a session with an open transaction; " + - "either run from within the transaction or use a different session." ); - } - - private CompletionStage ensureNoOpenTxBeforeStartingTx() - { - return ensureNoOpenTx( "You cannot begin a transaction on a session with an open transaction; " + - "either run from within the transaction or use a different session." ); - } - - private CompletionStage ensureNoOpenTx( String errorMessage ) - { - return existingTransactionOrNull().thenAccept( tx -> - { - if ( tx != null ) - { - throw new ClientException( errorMessage ); - } - } ); - } - - private CompletionStage existingTransactionOrNull() - { - return transactionStage - .exceptionally( error -> null ) // handle previous connection acquisition and tx begin failures - .thenApply( tx -> tx != null && tx.isOpen() ? tx : null ); - } - - private void ensureSessionIsOpen() - { - if ( !open.get() ) - { - throw new ClientException( - "No more interaction with this session are allowed as the current session is already closed. " ); - } - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java index 82e5914074..c50f013069 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java @@ -27,7 +27,7 @@ import static java.lang.System.lineSeparator; -public class LeakLoggingNetworkSession extends InternalNetworkSession +public class LeakLoggingNetworkSession extends NetworkSession { private final String stackTrace; diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java index e8c90e62e3..09c21d99e2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkSession.java @@ -18,37 +18,324 @@ */ package org.neo4j.driver.internal.async; +import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicBoolean; import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Logger; +import org.neo4j.driver.Logging; import org.neo4j.driver.Statement; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.async.StatementResultCursor; +import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.BookmarksHolder; +import org.neo4j.driver.internal.FailableCursor; +import org.neo4j.driver.internal.cursor.InternalStatementResultCursor; import org.neo4j.driver.internal.cursor.RxStatementResultCursor; +import org.neo4j.driver.internal.cursor.StatementResultCursorFactory; +import org.neo4j.driver.internal.logging.PrefixedLogger; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ConnectionProvider; +import org.neo4j.driver.internal.util.Futures; -public interface NetworkSession +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.neo4j.driver.internal.util.Futures.completedWithNull; + +public class NetworkSession { - CompletionStage runAsync( Statement statement, TransactionConfig config, boolean waitForRunResponse ); + private static final String LOG_NAME = "Session"; + + private final ConnectionProvider connectionProvider; + private final AccessMode mode; + private final String databaseName; + private final RetryLogic retryLogic; + protected final Logger logger; + + private final BookmarksHolder bookmarksHolder; + private volatile CompletionStage transactionStage = completedWithNull(); + private volatile CompletionStage connectionStage = completedWithNull(); + private volatile CompletionStage resultCursorStage = completedWithNull(); + + private final AtomicBoolean open = new AtomicBoolean( true ); + + public NetworkSession( ConnectionProvider connectionProvider, RetryLogic retryLogic, String databaseName, AccessMode mode, + BookmarksHolder bookmarksHolder, Logging logging ) + { + this.connectionProvider = connectionProvider; + this.mode = mode; + this.retryLogic = retryLogic; + this.logger = new PrefixedLogger( "[" + hashCode() + "]", logging.getLog( LOG_NAME ) ); + this.bookmarksHolder = bookmarksHolder; + this.databaseName = databaseName; + } + + public CompletionStage runAsync( Statement statement, TransactionConfig config, boolean waitForRunResponse ) + { + CompletionStage newResultCursorStage = + buildResultCursorFactory( statement, config, waitForRunResponse ).thenCompose( StatementResultCursorFactory::asyncResult ); + + resultCursorStage = newResultCursorStage.exceptionally( error -> null ); + return newResultCursorStage.thenApply( cursor -> cursor ); // convert the return type + } + + public CompletionStage runRx( Statement statement, TransactionConfig config ) + { + CompletionStage newResultCursorStage = + buildResultCursorFactory( statement, config, true ).thenCompose( StatementResultCursorFactory::rxResult ); + + resultCursorStage = newResultCursorStage.exceptionally( error -> null ); + return newResultCursorStage; + } + + public CompletionStage beginTransactionAsync( TransactionConfig config ) + { + return this.beginTransactionAsync( mode, config ); + } + + public CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ) + { + ensureSessionIsOpen(); + + // create a chain that acquires connection and starts a transaction + CompletionStage newTransactionStage = ensureNoOpenTxBeforeStartingTx() + .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) + .thenCompose( connection -> + { + ExplicitTransaction tx = new ExplicitTransaction( connection, bookmarksHolder ); + return tx.beginAsync( bookmarksHolder.getBookmarks(), config ); + } ); + + // update the reference to the only known transaction + CompletionStage currentTransactionStage = transactionStage; + + transactionStage = newTransactionStage + .exceptionally( error -> null ) // ignore errors from starting new transaction + .thenCompose( tx -> + { + if ( tx == null ) + { + // failed to begin new transaction, keep reference to the existing one + return currentTransactionStage; + } + // new transaction started, keep reference to it + return completedFuture( tx ); + } ); + + return newTransactionStage; + } + + public CompletionStage resetAsync() + { + return existingTransactionOrNull() + .thenAccept( tx -> + { + if ( tx != null ) + { + tx.markTerminated(); + } + } ) + .thenCompose( ignore -> connectionStage ) + .thenCompose( connection -> + { + if ( connection != null ) + { + // there exists an active connection, send a RESET message over it + return connection.reset(); + } + return completedWithNull(); + } ); + } + + public RetryLogic retryLogic() + { + return retryLogic; + } + + public String lastBookmark() + { + return bookmarksHolder.lastBookmark(); + } + + public CompletionStage releaseConnectionAsync() + { + return connectionStage.thenCompose( connection -> + { + if ( connection != null ) + { + // there exists connection, try to release it back to the pool + return connection.release(); + } + // no connection so return null + return completedWithNull(); + } ); + } + + public CompletionStage connectionAsync() + { + return connectionStage; + } + + public boolean isOpen() + { + return open.get(); + } + + public CompletionStage closeAsync() + { + if ( open.compareAndSet( true, false ) ) + { + return resultCursorStage.thenCompose( cursor -> + { + if ( cursor != null ) + { + // there exists a cursor with potentially unconsumed error, try to extract and propagate it + return cursor.failureAsync(); + } + // no result cursor exists so no error exists + return completedWithNull(); + } ).thenCompose( cursorError -> closeTransactionAndReleaseConnection().thenApply( txCloseError -> + { + // now we have cursor error, active transaction has been closed and connection has been released + // back to the pool; try to propagate cursor and transaction close errors, if any + CompletionException combinedError = Futures.combineErrors( cursorError, txCloseError ); + if ( combinedError != null ) + { + throw combinedError; + } + return null; + } ) ); + } + return completedWithNull(); + } + + protected CompletionStage currentConnectionIsOpen() + { + return connectionStage.handle( ( connection, error ) -> + error == null && // no acquisition error + connection != null && // some connection has actually been acquired + connection.isOpen() ); // and it's still open + } + + private CompletionStage buildResultCursorFactory( Statement statement, TransactionConfig config, boolean waitForRunResponse ) + { + ensureSessionIsOpen(); + + return ensureNoOpenTxBeforeRunningQuery() + .thenCompose( ignore -> acquireConnection( databaseName, mode ) ) + .thenCompose( connection -> { + try + { + StatementResultCursorFactory factory = connection.protocol() + .runInAutoCommitTransaction( connection, statement, bookmarksHolder, config, waitForRunResponse ); + return completedFuture( factory ); + } + catch ( Throwable e ) + { + return Futures.failedFuture( e ); + } + } ); + } + + private CompletionStage acquireConnection( String databaseName, AccessMode mode ) + { + CompletionStage currentConnectionStage = connectionStage; - CompletionStage runRx( Statement statement, TransactionConfig config ); + CompletionStage newConnectionStage = resultCursorStage.thenCompose( cursor -> + { + if ( cursor == null ) + { + return completedWithNull(); + } + // make sure previous result is fully consumed and connection is released back to the pool + return cursor.failureAsync(); + } ).thenCompose( error -> + { + if ( error == null ) + { + // there is no unconsumed error, so one of the following is true: + // 1) this is first time connection is acquired in this session + // 2) previous result has been successful and is fully consumed + // 3) previous result failed and error has been consumed - CompletionStage beginTransactionAsync( TransactionConfig config ); + // return existing connection, which should've been released back to the pool by now + return currentConnectionStage.exceptionally( ignore -> null ); + } + else + { + // there exists unconsumed error, re-throw it + throw new CompletionException( error ); + } + } ).thenCompose( existingConnection -> + { + if ( existingConnection != null && existingConnection.isOpen() ) + { + // there somehow is an existing open connection, this should not happen, just a precondition + throw new IllegalStateException( "Existing open connection detected" ); + } + return connectionProvider.acquireConnection( databaseName, mode ); + } ); - CompletionStage beginTransactionAsync( AccessMode mode, TransactionConfig config ); + connectionStage = newConnectionStage.exceptionally( error -> null ); - CompletionStage resetAsync(); + return newConnectionStage; + } - RetryLogic retryLogic(); + private CompletionStage closeTransactionAndReleaseConnection() + { + return existingTransactionOrNull().thenCompose( tx -> + { + if ( tx != null ) + { + // there exists an open transaction, let's close it and propagate the error, if any + return tx.closeAsync() + .thenApply( ignore -> (Throwable) null ) + .exceptionally( error -> error ); + } + // no open transaction so nothing to close + return completedWithNull(); + } ).thenCompose( txCloseError -> + // then release the connection and propagate transaction close error, if any + releaseConnectionAsync().thenApply( ignore -> txCloseError ) ); + } - String lastBookmark(); + private CompletionStage ensureNoOpenTxBeforeRunningQuery() + { + return ensureNoOpenTx( "Statements cannot be run directly on a session with an open transaction; " + + "either run from within the transaction or use a different session." ); + } - CompletionStage releaseConnectionAsync(); + private CompletionStage ensureNoOpenTxBeforeStartingTx() + { + return ensureNoOpenTx( "You cannot begin a transaction on a session with an open transaction; " + + "either run from within the transaction or use a different session." ); + } - CompletionStage connectionAsync(); + private CompletionStage ensureNoOpenTx( String errorMessage ) + { + return existingTransactionOrNull().thenAccept( tx -> + { + if ( tx != null ) + { + throw new ClientException( errorMessage ); + } + } ); + } - boolean isOpen(); + private CompletionStage existingTransactionOrNull() + { + return transactionStage + .exceptionally( error -> null ) // handle previous connection acquisition and tx begin failures + .thenApply( tx -> tx != null && tx.isOpen() ? tx : null ); + } - CompletionStage closeAsync(); + private void ensureSessionIsOpen() + { + if ( !open.get() ) + { + throw new ClientException( + "No more interaction with this session are allowed as the current session is already closed. " ); + } + } } diff --git a/driver/src/test/java/org/neo4j/driver/ParametersTest.java b/driver/src/test/java/org/neo4j/driver/ParametersTest.java index 3b5793b9a7..ead3036281 100644 --- a/driver/src/test/java/org/neo4j/driver/ParametersTest.java +++ b/driver/src/test/java/org/neo4j/driver/ParametersTest.java @@ -28,7 +28,7 @@ import org.neo4j.driver.internal.DefaultBookmarksHolder; import org.neo4j.driver.internal.InternalRecord; import org.neo4j.driver.internal.InternalSession; -import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.retry.RetryLogic; import org.neo4j.driver.internal.spi.ConnectionProvider; @@ -108,8 +108,8 @@ private Session mockedSession() { ConnectionProvider provider = mock( ConnectionProvider.class ); RetryLogic retryLogic = mock( RetryLogic.class ); - InternalNetworkSession session = - new InternalNetworkSession( provider, retryLogic, ABSENT_DB_NAME, AccessMode.WRITE, new DefaultBookmarksHolder(), DEV_NULL_LOGGING ); + NetworkSession session = + new NetworkSession( provider, retryLogic, ABSENT_DB_NAME, AccessMode.WRITE, new DefaultBookmarksHolder(), DEV_NULL_LOGGING ); return new InternalSession( session ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java index 6280bde69d..32f4924dba 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java @@ -32,7 +32,7 @@ import org.neo4j.driver.Config; import org.neo4j.driver.Driver; import org.neo4j.driver.exceptions.ServiceUnavailableException; -import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.cluster.RoutingSettings; @@ -108,7 +108,7 @@ void usesStandardSessionFactoryWhenNothingConfigured( String uri ) createDriver( uri, factory, config ); SessionFactory capturedFactory = factory.capturedSessionFactory; - assertThat( capturedFactory.newInstance( empty() ), instanceOf( InternalNetworkSession.class ) ); + assertThat( capturedFactory.newInstance( empty() ), instanceOf( NetworkSession.class ) ); } @ParameterizedTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java index 0631aed0bb..80c69515a6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/SessionFactoryImplTest.java @@ -22,9 +22,8 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Config; -import org.neo4j.driver.internal.async.InternalNetworkSession; -import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; +import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.internal.util.FixedRetryLogic; @@ -43,10 +42,10 @@ void createsNetworkSessions() SessionFactory factory = newSessionFactory( config ); NetworkSession readSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.READ ).build() ); - assertThat( readSession, instanceOf( InternalNetworkSession.class ) ); + assertThat( readSession, instanceOf( NetworkSession.class ) ); NetworkSession writeSession = factory.newInstance( template().withDefaultAccessMode( AccessMode.WRITE ).build() ); - assertThat( writeSession, instanceOf( InternalNetworkSession.class ) ); + assertThat( writeSession, instanceOf( NetworkSession.class ) ); } @Test diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/ExplicitTransactionTest.java similarity index 93% rename from driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/ExplicitTransactionTest.java index ea3fdaa6a1..d8906112be 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/InternalExplicitTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/ExplicitTransactionTest.java @@ -56,7 +56,7 @@ import static org.neo4j.driver.util.TestUtil.verifyRun; import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; -class InternalExplicitTransactionTest +class ExplicitTransactionTest { @ParameterizedTest @ValueSource( strings = {"true", "false"} ) @@ -243,7 +243,7 @@ void shouldReleaseConnectionWhenBeginFails() { RuntimeException error = new RuntimeException( "Wrong bookmark!" ); Connection connection = connectionWithBegin( handler -> handler.onFailure( error ) ); - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); Bookmarks bookmarks = Bookmarks.from( "SomeBookmark" ); TransactionConfig txConfig = TransactionConfig.empty(); @@ -258,7 +258,7 @@ void shouldReleaseConnectionWhenBeginFails() void shouldNotReleaseConnectionWhenBeginSucceeds() { Connection connection = connectionWithBegin( handler -> handler.onSuccess( emptyMap() ) ); - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); Bookmarks bookmarks = Bookmarks.from( "SomeBookmark" ); TransactionConfig txConfig = TransactionConfig.empty(); @@ -272,7 +272,7 @@ void shouldNotReleaseConnectionWhenBeginSucceeds() void shouldReleaseConnectionWhenTerminatedAndCommitted() { Connection connection = connectionMock(); - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); tx.markTerminated(); @@ -286,7 +286,7 @@ void shouldReleaseConnectionWhenTerminatedAndCommitted() void shouldReleaseConnectionWhenTerminatedAndRolledBack() { Connection connection = connectionMock(); - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); tx.markTerminated(); await( tx.rollbackAsync() ); @@ -298,7 +298,7 @@ void shouldReleaseConnectionWhenTerminatedAndRolledBack() void shouldReleaseConnectionWhenClose() throws Throwable { Connection connection = connectionMock(); - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); await( tx.closeAsync() ); @@ -312,7 +312,7 @@ private static ExplicitTransaction beginTx( Connection connection ) private static ExplicitTransaction beginTx( Connection connection, Bookmarks initialBookmarks ) { - ExplicitTransaction tx = new InternalExplicitTransaction( connection, new DefaultBookmarksHolder() ); + ExplicitTransaction tx = new ExplicitTransaction( connection, new DefaultBookmarksHolder() ); return await( tx.beginAsync( initialBookmarks, TransactionConfig.empty() ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java index 5d06ba5b36..00da69a766 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingInternalNetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSessionTest.java @@ -48,7 +48,7 @@ import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.ABSENT_DB_NAME; import static org.neo4j.driver.util.TestUtil.DEFAULT_TEST_PROTOCOL; -class LeakLoggingInternalNetworkSessionTest +class LeakLoggingNetworkSessionTest { @Test void logsNothingDuringFinalizationIfClosed() throws Exception diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java similarity index 99% rename from driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java rename to driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java index fe3c3d44eb..57771ce4d2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/InternalNetworkSessionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkSessionTest.java @@ -72,11 +72,11 @@ import static org.neo4j.driver.util.TestUtil.verifyRun; import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; -class InternalNetworkSessionTest +class NetworkSessionTest { private Connection connection; private ConnectionProvider connectionProvider; - private InternalNetworkSession session; + private NetworkSession session; @BeforeEach void setUp() diff --git a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java index 96ae058d17..cfdd01bf0a 100644 --- a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java +++ b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java @@ -51,7 +51,7 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.Bookmarks; import org.neo4j.driver.internal.DefaultBookmarksHolder; -import org.neo4j.driver.internal.async.InternalNetworkSession; +import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.async.connection.EventLoopGroupFactory; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.NoOpResponseHandler; @@ -235,35 +235,35 @@ public static String cleanDb( Driver driver ) } } - public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, Bookmarks x ) + public static NetworkSession newSession( ConnectionProvider connectionProvider, Bookmarks x ) { return newSession( connectionProvider, WRITE, x ); } - private static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, Bookmarks x ) + private static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, Bookmarks x ) { return newSession( connectionProvider, mode, new FixedRetryLogic( 0 ), x ); } - public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode ) + public static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode ) { return newSession( connectionProvider, mode, empty() ); } - public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, RetryLogic logic ) + public static NetworkSession newSession( ConnectionProvider connectionProvider, RetryLogic logic ) { return newSession( connectionProvider, WRITE, logic, empty() ); } - public static InternalNetworkSession newSession( ConnectionProvider connectionProvider ) + public static NetworkSession newSession( ConnectionProvider connectionProvider ) { return newSession( connectionProvider, WRITE, empty() ); } - public static InternalNetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, + public static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, RetryLogic retryLogic, Bookmarks bookmarks ) { - return new InternalNetworkSession( connectionProvider, retryLogic, ABSENT_DB_NAME, mode, new DefaultBookmarksHolder( bookmarks ), DEV_NULL_LOGGING ); + return new NetworkSession( connectionProvider, retryLogic, ABSENT_DB_NAME, mode, new DefaultBookmarksHolder( bookmarks ), DEV_NULL_LOGGING ); } public static void verifyRun( Connection connection, String query )