Skip to content

Commit 520c65d

Browse files
committed
Close connection after result reading failure
When result fetching fails (cypher runtime error or network error) exception is thrown but it is still possible to maintain a reference to the result object. It'll just be empty after failure. It is not possible to fetch any other records after failure and the underlying pooled connection can be closed and returned to the pool. This commit makes result return its connection to the pool on failure. This connection (if healthy) can be used by subsequent sessions. Also added tests for connection handling.
1 parent 23e570a commit 520c65d

File tree

5 files changed

+513
-10
lines changed

5 files changed

+513
-10
lines changed

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545

4646
import static java.util.Collections.emptyList;
4747

48-
// todo: connection should be released if fetching from network fails, no need to sync...
4948
public class InternalStatementResult implements StatementResult
5049
{
5150
private final Connection connection;
@@ -289,7 +288,7 @@ public ResultSummary consume()
289288
{
290289
do
291290
{
292-
connection.receiveOne();
291+
receiveOne();
293292
recordBuffer.clear();
294293
}
295294
while ( !done );
@@ -303,7 +302,7 @@ public ResultSummary summary()
303302
{
304303
while( !done )
305304
{
306-
connection.receiveOne();
305+
receiveOne();
307306
}
308307

309308
return summary;
@@ -321,12 +320,28 @@ private boolean tryFetchNext()
321320
{
322321
if ( done )
323322
{
324-
resourcesHandler.onResultConsumed();
325323
return false;
326324
}
327-
connection.receiveOne();
325+
receiveOne();
328326
}
329327

330328
return true;
331329
}
330+
331+
private void receiveOne()
332+
{
333+
try
334+
{
335+
connection.receiveOne();
336+
}
337+
catch ( Throwable error )
338+
{
339+
resourcesHandler.onResultConsumed();
340+
throw error;
341+
}
342+
if ( done )
343+
{
344+
resourcesHandler.onResultConsumed();
345+
}
346+
}
332347
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ public StatementResult run( Statement statement )
9999

100100
public static StatementResult run( Connection connection, Statement statement, SessionResourcesHandler resourcesHandler )
101101
{
102-
InternalStatementResult cursor = new InternalStatementResult( connection, resourcesHandler, null, statement );
102+
InternalStatementResult result = new InternalStatementResult( connection, resourcesHandler, null, statement );
103103
connection.run( statement.text(), statement.parameters().asMap( Values.ofValue() ),
104-
cursor.runResponseCollector() );
105-
connection.pullAll( cursor.pullAllResponseCollector() );
104+
result.runResponseCollector() );
105+
connection.pullAll( result.pullAllResponseCollector() );
106106
connection.flush();
107-
return cursor;
107+
return result;
108108
}
109109

110110
@Override

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

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import static java.util.Arrays.asList;
4242
import static org.hamcrest.CoreMatchers.equalTo;
4343
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
44+
import static org.junit.Assert.assertEquals;
4445
import static org.junit.Assert.assertFalse;
4546
import static org.junit.Assert.assertNotNull;
4647
import static org.junit.Assert.assertNull;
@@ -49,6 +50,7 @@
4950
import static org.junit.Assert.fail;
5051
import static org.mockito.Mockito.doAnswer;
5152
import static org.mockito.Mockito.mock;
53+
import static org.mockito.Mockito.verify;
5254
import static org.neo4j.driver.v1.Records.column;
5355
import static org.neo4j.driver.v1.Values.ofString;
5456
import static org.neo4j.driver.v1.Values.value;
@@ -384,12 +386,77 @@ public void shouldNotPeekIntoTheFutureWhenResultIsEmpty()
384386
Record future = result.peek();
385387
}
386388

389+
@Test
390+
public void shouldNotifyResourcesHandlerWhenFetchedViaList()
391+
{
392+
SessionResourcesHandler resourcesHandler = mock( SessionResourcesHandler.class );
393+
StatementResult result = createResult( 10, resourcesHandler );
394+
395+
List<Record> records = result.list();
396+
assertEquals( 10, records.size() );
397+
398+
verify( resourcesHandler ).onResultConsumed();
399+
}
400+
401+
@Test
402+
public void shouldNotifyResourcesHandlerWhenFetchedViaSingle()
403+
{
404+
SessionResourcesHandler resourcesHandler = mock( SessionResourcesHandler.class );
405+
StatementResult result = createResult( 1, resourcesHandler );
406+
407+
Record record = result.single();
408+
assertEquals( "v1-1", record.get( "k1" ).asString() );
409+
410+
verify( resourcesHandler ).onResultConsumed();
411+
}
412+
413+
@Test
414+
public void shouldNotifyResourcesHandlerWhenFetchedViaIterator()
415+
{
416+
SessionResourcesHandler resourcesHandler = mock( SessionResourcesHandler.class );
417+
StatementResult result = createResult( 1, resourcesHandler );
418+
419+
while ( result.hasNext() )
420+
{
421+
assertNotNull( result.next() );
422+
}
423+
424+
verify( resourcesHandler ).onResultConsumed();
425+
}
426+
427+
@Test
428+
public void shouldNotifyResourcesHandlerWhenSummary()
429+
{
430+
SessionResourcesHandler resourcesHandler = mock( SessionResourcesHandler.class );
431+
StatementResult result = createResult( 10, resourcesHandler );
432+
433+
assertNotNull( result.summary() );
434+
435+
verify( resourcesHandler ).onResultConsumed();
436+
}
437+
438+
@Test
439+
public void shouldNotifyResourcesHandlerWhenConsumed()
440+
{
441+
SessionResourcesHandler resourcesHandler = mock( SessionResourcesHandler.class );
442+
StatementResult result = createResult( 5, resourcesHandler );
443+
444+
result.consume();
445+
446+
verify( resourcesHandler ).onResultConsumed();
447+
}
448+
387449
private StatementResult createResult( int numberOfRecords )
450+
{
451+
return createResult( numberOfRecords, SessionResourcesHandler.NO_OP );
452+
}
453+
454+
private StatementResult createResult( int numberOfRecords, SessionResourcesHandler resourcesHandler )
388455
{
389456
Connection connection = mock( Connection.class );
390457
String statement = "<unknown>";
391458

392-
final InternalStatementResult result = new InternalStatementResult( connection, SessionResourcesHandler.NO_OP, null,
459+
final InternalStatementResult result = new InternalStatementResult( connection, resourcesHandler, null,
393460
new Statement( statement ) );
394461

395462
// Each time the cursor calls `recieveOne`, we'll run one of these,

0 commit comments

Comments
 (0)