tinkerpop/blueprints — Issue #197: Getting TransactionFailureException, seems to be related to Transaction Buffers+Gremlin
Author: countvajhula
Created: 2011-12-20 21:43:01+00:00
State: open

Hello,
This one has been a real doosie, took me a while to nail down. The symptom of the bug is that, in some cases when you create a graph (from scratch, say) and then shut it down cleanly, the next time the graph is opened it fails on a call to clear() with the following exception:

```
Caused by: org.neo4j.graphdb.TransactionFailureException: Failed to mark transaction as rollback only.
at org.neo4j.kernel.TopLevelTransaction.failure(TopLevelTransaction.java:49)
at com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph.stopTransaction(Neo4jGraph.java:324)
at com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph.clear(Neo4jGraph.java:390)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:229)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at com.pilot.GraphDbOperator.clear(GraphDbOperator.groovy:190)
... 57 more
Caused by: java.lang.NullPointerException
at org.neo4j.kernel.TopLevelTransaction.failure(TopLevelTransaction.java:45)
... 69 more
```

It appears that some kind of corruption occurs only ~after~ the first run which subsequently causes the problem on the next run. If I call clear() at the end of the first run before calling shutdown(), the exception never occurs. It also appears to happen only when I do a Gremlin traversal involving retrieving the neighbors of some vertices and then adding an edge between them.

I've created a test app that reproduces this bug (GraphBugger.groovy).

Other+summarized features of the bug:
- it only shows up in versions after and including the Blueprints 1.0 graph stack -- Blueprints 1.0, Gremlin 1.3, Pipes 0.8
- running with Blueprints 0.9 stack (incl. CommitManager) does not give the exception
- running with different versions of Neo4j doesn't have any effect -- tried Neo4j 1.3, 1.4.1, and 1.5 -- bug behavior is similar for all versions
- the exception only appears after the graph is shutdown() and then reopened
- I was not able to reproduce the exception by just building a random graph of N vertices and randomly assigning any number of edges, and repeating this process over and over again. It only appears to happen when the Gremlin traversal involving retrieving neighbors of vertices is involved
- behavior is the same using Gremlin 1.4 (but the supplied test app is for <= Gremlin 1.3)

Here is the test app, GraphBugger.groovy:
```groovy
#!/usr/bin/env groovy

import com.tinkerpop.blueprints.*
import com.tinkerpop.blueprints.pgm.*
import com.tinkerpop.blueprints.pgm.impls.neo4j.*
import com.tinkerpop.blueprints.pgm.impls.orientdb.*
import com.tinkerpop.blueprints.pgm.util.*
import com.tinkerpop.blueprints.pgm.util.graphml.GraphMLReader
import java.lang.Math
import java.util.Random
import com.tinkerpop.gremlin.*

public class GraphBugger {

static {
Gremlin.load()
}

// Random number generator
private Random randGen

public GraphBugger() {
randGen = new Random() //initialize random number generator
}

public void addNVertices(Graph graph, long n) {
println "Adding ${n} vertices..."
// add n vertices
for (int i=0; i>> 1;
val = bits % n;
}
return val;
}

private Vertex selectRandomVertex(Graph graph, long numVertices) {
long randNum = nextLong(randGen, numVertices)
Vertex v1
int tries = 0
while (!v1 && tries<1) { //might be good to set #tries to 1
v1 = graph.getVertex(randNum)
tries++
}

return v1
}

// Gremlin traversals to return neighbors of a vertex (co-opted from the Pilot getNeighbors() function -- https://github.com/countvajhula/pilot/blob/master/src/main/groovy/com/pilot/GraphDbOperator.groovy )
public List getVertexNeighbors(Vertex v1, String alongEdge) {
List neighbors = []
List tempList = []
v1.bothE(alongEdge).bothV.filter { node -> !node.id.equals(v1.id) }.aggregate(neighbors) >> -1
//add current node back if there is a self-connecting edge
v1.outE(alongEdge).inV.filter { node -> node.id.equals(v1.id) }.aggregate(tempList) >> -1
if (tempList.size() > 0) {
neighbors += tempList
}

return neighbors
}

public void createRandomGraph() {

String graphUrl = './dbs/myneodb'
long numVertices = 140
long edgesPerVertex = 70

Graph graph = new Neo4jGraph(graphUrl)

graph.clear()

// automatic transactions via transaction buffer
graph.setMaxBufferSize(1000)

// create a random graph with some number of vertices and randomly added edges
addNVertices(graph, numVertices)
addRandomEdgesER(graph, edgesPerVertex)

// flush buffer
graph.stopTransaction(TransactionalGraph.Conclusion.SUCCESS)

println "Selecting random vertices and adding edges to their immediate neighbors..."
//get neighbors and add edges
for (int j=0; j<100; j++) {
Vertex v1 = selectRandomVertex(graph, numVertices)
if (v1) {
def neighbors = getVertexNeighbors(v1, 'knows')
//graph.getNeighbors(v1, null, 'knows')

for (neighbor in neighbors) {
graph.addEdge(null, v1, neighbor, 'hasAThingFor')
}
}
}
graph.stopTransaction(TransactionalGraph.Conclusion.SUCCESS)

graph.shutdown()
}

public static void main(String[] args) {
GraphBugger gb = new GraphBugger()
gb.createRandomGraph()

}
}
```

Let me know if you need more information. Thanks,
-Sid