26
26
27
27
import org .neo4j .driver .internal .async .InternalStatementResultCursor ;
28
28
import org .neo4j .driver .internal .async .QueryRunner ;
29
- import org .neo4j .driver .internal .async .ResultCursorsHolder ;
30
29
import org .neo4j .driver .internal .logging .DelegatingLogger ;
31
30
import org .neo4j .driver .internal .retry .RetryLogic ;
32
31
import org .neo4j .driver .internal .spi .Connection ;
@@ -60,12 +59,12 @@ public class NetworkSession implements Session
60
59
private final ConnectionProvider connectionProvider ;
61
60
private final AccessMode mode ;
62
61
private final RetryLogic retryLogic ;
63
- private final ResultCursorsHolder resultCursors ;
64
62
protected final Logger logger ;
65
63
66
64
private volatile Bookmark bookmark = Bookmark .empty ();
67
65
private volatile CompletionStage <ExplicitTransaction > transactionStage = completedFuture ( null );
68
66
private volatile CompletionStage <Connection > connectionStage = completedFuture ( null );
67
+ private volatile CompletionStage <InternalStatementResultCursor > resultCursorStage = completedFuture ( null );
69
68
70
69
private final AtomicBoolean open = new AtomicBoolean ( true );
71
70
@@ -75,7 +74,6 @@ public NetworkSession( ConnectionProvider connectionProvider, AccessMode mode, R
75
74
this .connectionProvider = connectionProvider ;
76
75
this .mode = mode ;
77
76
this .retryLogic = retryLogic ;
78
- this .resultCursors = new ResultCursorsHolder ();
79
77
this .logger = new DelegatingLogger ( logging .getLog ( LOG_NAME ), String .valueOf ( hashCode () ) );
80
78
}
81
79
@@ -163,22 +161,28 @@ public CompletionStage<Void> closeAsync()
163
161
{
164
162
if ( open .compareAndSet ( true , false ) )
165
163
{
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
+ } ) );
182
186
}
183
187
return completedFuture ( null );
184
188
}
@@ -275,7 +279,7 @@ CompletionStage<Boolean> currentConnectionIsOpen()
275
279
return connectionStage .handle ( ( connection , error ) ->
276
280
error == null && // no acquisition error
277
281
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
279
283
}
280
284
281
285
private <T > T transaction ( AccessMode mode , TransactionWork <T > work )
@@ -412,7 +416,7 @@ private CompletionStage<InternalStatementResultCursor> runAsync( Statement state
412
416
{
413
417
ensureSessionIsOpen ();
414
418
415
- CompletionStage <InternalStatementResultCursor > cursorStage = ensureNoOpenTxBeforeRunningQuery ()
419
+ CompletionStage <InternalStatementResultCursor > newResultCursorStage = ensureNoOpenTxBeforeRunningQuery ()
416
420
.thenCompose ( ignore -> acquireConnection ( mode ) )
417
421
.thenCompose ( connection ->
418
422
{
@@ -426,8 +430,9 @@ private CompletionStage<InternalStatementResultCursor> runAsync( Statement state
426
430
}
427
431
} );
428
432
429
- resultCursors .add ( cursorStage );
430
- return cursorStage ;
433
+ resultCursorStage = newResultCursorStage .exceptionally ( error -> null );
434
+
435
+ return newResultCursorStage ;
431
436
}
432
437
433
438
private CompletionStage <ExplicitTransaction > beginTransactionAsync ( AccessMode mode )
@@ -447,28 +452,46 @@ private CompletionStage<ExplicitTransaction> beginTransactionAsync( AccessMode m
447
452
448
453
private CompletionStage <Connection > acquireConnection ( AccessMode mode )
449
454
{
450
- // memorize in local so same instance is transformed and used in callbacks
451
- CompletionStage <Connection > currentAsyncConnectionStage = connectionStage ;
455
+ CompletionStage <Connection > currentConnectionStage = connectionStage ;
452
456
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 );
470
493
471
- return connectionStage ;
494
+ return newConnectionStage ;
472
495
}
473
496
474
497
private CompletionStage <Void > releaseResources ()
0 commit comments