Question

I just spent ten minutes figuring out why my unit test working against the test database was taking around 25 seconds to finish a simple query. The reason was, apparently, that I had forgotten to wrap a call to ExecutionEngine#execute(String) in a Transaction. I didn't know that was supposed to be possible. Hmm.

My code looked something like this:

...

@Before
public void before() {
  db = new TestGraphDatabaseFactory().newImpermanentDatabase();
  /* create all the test nodes and rels */
}

@After
public void after() {
  db.shutdown();
}

@Test
public void testAllTheThings() {
  /* build the cypher query */
  Iterable<Map<String, Object>> result = engine.execute(cypher);
  /* assert all the things */
}

...

The confusing part here is that execute() returns at once and the following statements gets executed. But, the test stalls for about 25 seconds before successfully terminating.

Changing the statement to the following works as expected, without any lag:

...
try (Transaction transaction = db.beginTx()) {
  Iterable<Map<String, Object>> result = engine.execute(cypher);
}
...

What is actually happening here? Why is this possible to do?

Was it helpful?

Solution

I suspect that you are not exhausting the Iterator from the ExecutionResult returned by ExecutionEngine#execute(). If you do so, the test will finish quickly without needing to wrap execute() in a transaction.

Here's what I think is going on, although I haven't reproduced this in a debugger.

ExecutionEngine#execute() creates its own transaction, so it shouldn't be necessary to create your own. However all transactions must be closed. In this case the transaction state is handled by the ExecutionResult its methods as documented here. If you partially consume the results Iterator then the transaction is left in an open state.

When you call GraphDatabaseService#shutdown(), it tries to accommodate open transactions by waiting for them to complete before forcing them closed. The stall you see in your test is probably that timeout happening. Of course in this case it is not possible for the transaction to close during the wait because it is held by the same thread that is shutting the database down.

Because transactions are tied to threads it would be possible, in principle, for the GraphDatabaseService to detect this case and close the transaction without waiting. However it's probably rare in production code that the same thread is executing transactions and controlling the database, so the extra complexity would not be justified.

The simplest way to handle this is to always ensure that you exhaust the results iterators that are returned by the ExecutionEngine.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top