Skip to content

Commit 8351b4f

Browse files
committed
Correctly prohibit multiple transactions in session
Session can only have one active (not closed) transaction at a time. Exception is thrown when starting a transaction on a session that already has an open one. Situation when transaction is started multiple times on a session with an open transaction was not handled correctly. First failure overrode the reference to the last transaction kept in the session. Later attempts resulted in a different error saying that there already exists an open connection. This commit fixes the problem by making session keep current transaction reference even if new transaction failed to begin. So callers will receive consistent errors.
1 parent a150643 commit 8351b4f

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.neo4j.driver.v1.exceptions.ClientException;
4747
import org.neo4j.driver.v1.types.TypeSystem;
4848

49+
import static java.util.concurrent.CompletableFuture.completedFuture;
4950
import static org.neo4j.driver.internal.util.Futures.completedWithNull;
5051
import static org.neo4j.driver.internal.util.Futures.failedFuture;
5152
import static org.neo4j.driver.v1.Values.value;
@@ -436,15 +437,32 @@ private CompletionStage<ExplicitTransaction> beginTransactionAsync( AccessMode m
436437
{
437438
ensureSessionIsOpen();
438439

439-
transactionStage = ensureNoOpenTxBeforeStartingTx()
440+
// create a chain that acquires connection and starts a transaction
441+
CompletionStage<ExplicitTransaction> newTransactionStage = ensureNoOpenTxBeforeStartingTx()
440442
.thenCompose( ignore -> acquireConnection( mode ) )
441443
.thenCompose( connection ->
442444
{
443445
ExplicitTransaction tx = new ExplicitTransaction( connection, NetworkSession.this );
444446
return tx.beginAsync( bookmark );
445447
} );
446448

447-
return transactionStage;
449+
// update the reference to the only known transaction
450+
CompletionStage<ExplicitTransaction> currentTransactionStage = transactionStage;
451+
452+
transactionStage = newTransactionStage
453+
.exceptionally( error -> null ) // ignore errors from starting new transaction
454+
.thenCompose( tx ->
455+
{
456+
if ( tx == null )
457+
{
458+
// failed to begin new transaction, keep reference to the existing one
459+
return currentTransactionStage;
460+
}
461+
// new transaction started, keep reference to it
462+
return completedFuture( tx );
463+
} );
464+
465+
return newTransactionStage;
448466
}
449467

450468
private CompletionStage<Connection> acquireConnection( AccessMode mode )

driver/src/test/java/org/neo4j/driver/internal/NetworkSessionTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,53 @@ public void shouldMarkTransactionAsTerminatedAndThenReleaseConnectionOnReset()
681681
verify( connection ).release();
682682
}
683683

684+
@Test
685+
public void shouldNotAllowStartingMultipleTransactions()
686+
{
687+
NetworkSession session = newSession( connectionProvider, READ );
688+
689+
Transaction tx = session.beginTransaction();
690+
assertNotNull( tx );
691+
692+
for ( int i = 0; i < 5; i++ )
693+
{
694+
try
695+
{
696+
session.beginTransaction();
697+
fail( "Exception expected" );
698+
}
699+
catch ( ClientException e )
700+
{
701+
assertThat( e.getMessage(),
702+
containsString( "You cannot begin a transaction on a session with an open transaction" ) );
703+
}
704+
}
705+
}
706+
707+
@Test
708+
public void shouldAllowStartingTransactionAfterCurrentOneIsClosed()
709+
{
710+
NetworkSession session = newSession( connectionProvider, READ );
711+
712+
Transaction tx = session.beginTransaction();
713+
assertNotNull( tx );
714+
715+
try
716+
{
717+
session.beginTransaction();
718+
fail( "Exception expected" );
719+
}
720+
catch ( ClientException e )
721+
{
722+
assertThat( e.getMessage(),
723+
containsString( "You cannot begin a transaction on a session with an open transaction" ) );
724+
}
725+
726+
tx.close();
727+
728+
assertNotNull( session.beginTransaction() );
729+
}
730+
684731
private void testConnectionAcquisition( AccessMode sessionMode, AccessMode transactionMode )
685732
{
686733
NetworkSession session = newSession( connectionProvider, sessionMode );

driver/src/test/java/org/neo4j/driver/v1/integration/SessionIT.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,6 +1623,34 @@ public void shouldConsumeWithFailure()
16231623
}
16241624
}
16251625

1626+
@Test
1627+
public void shouldNotAllowStartingMultipleTransactions()
1628+
{
1629+
try ( Session session = neo4j.driver().session() )
1630+
{
1631+
Transaction tx = session.beginTransaction();
1632+
assertNotNull( tx );
1633+
1634+
for ( int i = 0; i < 3; i++ )
1635+
{
1636+
try
1637+
{
1638+
session.beginTransaction();
1639+
fail( "Exception expected" );
1640+
}
1641+
catch ( ClientException e )
1642+
{
1643+
assertThat( e.getMessage(),
1644+
containsString( "You cannot begin a transaction on a session with an open transaction" ) );
1645+
}
1646+
}
1647+
1648+
tx.close();
1649+
1650+
assertNotNull( session.beginTransaction() );
1651+
}
1652+
}
1653+
16261654
private void assumeServerIs31OrLater()
16271655
{
16281656
ServerVersion serverVersion = ServerVersion.version( neo4j.driver() );

0 commit comments

Comments
 (0)