2626
2727import org .neo4j .driver .internal .async .InternalStatementResultCursor ;
2828import org .neo4j .driver .internal .async .QueryRunner ;
29- import org .neo4j .driver .internal .async .ResultCursorsHolder ;
3029import org .neo4j .driver .internal .logging .DelegatingLogger ;
3130import org .neo4j .driver .internal .retry .RetryLogic ;
3231import org .neo4j .driver .internal .spi .Connection ;
@@ -60,12 +59,12 @@ public class NetworkSession implements Session
6059 private final ConnectionProvider connectionProvider ;
6160 private final AccessMode mode ;
6261 private final RetryLogic retryLogic ;
63- private final ResultCursorsHolder resultCursors ;
6462 protected final Logger logger ;
6563
6664 private volatile Bookmark bookmark = Bookmark .empty ();
6765 private volatile CompletionStage <ExplicitTransaction > transactionStage = completedFuture ( null );
6866 private volatile CompletionStage <Connection > connectionStage = completedFuture ( null );
67+ private volatile CompletionStage <InternalStatementResultCursor > resultCursorStage = completedFuture ( null );
6968
7069 private final AtomicBoolean open = new AtomicBoolean ( true );
7170
@@ -75,7 +74,6 @@ public NetworkSession( ConnectionProvider connectionProvider, AccessMode mode, R
7574 this .connectionProvider = connectionProvider ;
7675 this .mode = mode ;
7776 this .retryLogic = retryLogic ;
78- this .resultCursors = new ResultCursorsHolder ();
7977 this .logger = new DelegatingLogger ( logging .getLog ( LOG_NAME ), String .valueOf ( hashCode () ) );
8078 }
8179
@@ -163,22 +161,28 @@ public CompletionStage<Void> closeAsync()
163161 {
164162 if ( open .compareAndSet ( true , false ) )
165163 {
166- return resultCursors .retrieveNotConsumedError ()
167- .thenCompose ( error -> releaseResources ().thenApply ( ignore ->
168- {
169- Throwable queryError = Futures .completionErrorCause ( error );
170- if ( queryError != null )
171- {
172- // connection has been acquired and there is an unconsumed error in result cursor
173- throw new CompletionException ( queryError );
174- }
175- else
176- {
177- // either connection acquisition failed or
178- // there are no unconsumed errors in the result cursor
179- return null ;
180- }
181- } ) );
164+ return resultCursorStage .thenCompose ( cursor ->
165+ {
166+ if ( cursor == null )
167+ {
168+ return completedFuture ( null );
169+ }
170+ return cursor .failureAsync ();
171+ } ).thenCompose ( error -> releaseResources ().thenApply ( ignore ->
172+ {
173+ Throwable queryError = Futures .completionErrorCause ( error );
174+ if ( queryError != null )
175+ {
176+ // connection has been acquired and there is an unconsumed error in result cursor
177+ throw new CompletionException ( queryError );
178+ }
179+ else
180+ {
181+ // either connection acquisition failed or
182+ // there are no unconsumed errors in the result cursor
183+ return null ;
184+ }
185+ } ) );
182186 }
183187 return completedFuture ( null );
184188 }
@@ -275,7 +279,7 @@ CompletionStage<Boolean> currentConnectionIsOpen()
275279 return connectionStage .handle ( ( connection , error ) ->
276280 error == null && // no acquisition error
277281 connection != null && // some connection has actually been acquired
278- connection .isInUse () ); // and it's still being used
282+ connection .isOpen () ); // and it's still open
279283 }
280284
281285 private <T > T transaction ( AccessMode mode , TransactionWork <T > work )
@@ -412,7 +416,7 @@ private CompletionStage<InternalStatementResultCursor> runAsync( Statement state
412416 {
413417 ensureSessionIsOpen ();
414418
415- CompletionStage <InternalStatementResultCursor > cursorStage = ensureNoOpenTxBeforeRunningQuery ()
419+ CompletionStage <InternalStatementResultCursor > newResultCursorStage = ensureNoOpenTxBeforeRunningQuery ()
416420 .thenCompose ( ignore -> acquireConnection ( mode ) )
417421 .thenCompose ( connection ->
418422 {
@@ -426,8 +430,9 @@ private CompletionStage<InternalStatementResultCursor> runAsync( Statement state
426430 }
427431 } );
428432
429- resultCursors .add ( cursorStage );
430- return cursorStage ;
433+ resultCursorStage = newResultCursorStage .exceptionally ( error -> null );
434+
435+ return newResultCursorStage ;
431436 }
432437
433438 private CompletionStage <ExplicitTransaction > beginTransactionAsync ( AccessMode mode )
@@ -447,28 +452,46 @@ private CompletionStage<ExplicitTransaction> beginTransactionAsync( AccessMode m
447452
448453 private CompletionStage <Connection > acquireConnection ( AccessMode mode )
449454 {
450- // memorize in local so same instance is transformed and used in callbacks
451- CompletionStage <Connection > currentAsyncConnectionStage = connectionStage ;
455+ CompletionStage <Connection > currentConnectionStage = connectionStage ;
452456
453- connectionStage = currentAsyncConnectionStage
454- .exceptionally ( error -> null ) // handle previous acquisition failures
455- .thenCompose ( connection ->
456- {
457- if ( connection != null && connection .tryMarkInUse () )
458- {
459- // previous acquisition attempt was successful and connection has not been released yet
460- // continue using same connection
461- return currentAsyncConnectionStage ;
462- }
463- else
464- {
465- // previous acquisition attempt failed or connection has been released
466- // acquire new connection
467- return connectionProvider .acquireConnection ( mode );
468- }
469- } );
457+ CompletionStage <Connection > newConnectionStage = resultCursorStage .thenCompose ( cursor ->
458+ {
459+ if ( cursor == null )
460+ {
461+ return completedFuture ( null );
462+ }
463+ // make sure previous result is fully consumed and connection is released back to the pool
464+ return cursor .failureAsync ();
465+ } ).thenCompose ( error ->
466+ {
467+ if ( error == null )
468+ {
469+ // there is no unconsumed error, so one of the following is true:
470+ // 1) this is first time connection is acquired in this session
471+ // 2) previous result has been successful and is fully consumed
472+ // 3) previous result failed and error has been consumed
473+
474+ // return existing connection, which should've been released back to the pool by now
475+ return currentConnectionStage .exceptionally ( ignore -> null );
476+ }
477+ else
478+ {
479+ // there exists unconsumed error, re-throw it
480+ throw new CompletionException ( error );
481+ }
482+ } ).thenCompose ( existingConnection ->
483+ {
484+ if ( existingConnection != null && existingConnection .isOpen () )
485+ {
486+ // there somehow is an existing open connection, this should not happen, just a precondition
487+ throw new IllegalStateException ( "Existing open connection detected" );
488+ }
489+ return connectionProvider .acquireConnection ( mode );
490+ } );
491+
492+ connectionStage = newConnectionStage .exceptionally ( error -> null );
470493
471- return connectionStage ;
494+ return newConnectionStage ;
472495 }
473496
474497 private CompletionStage <Void > releaseResources ()
0 commit comments