I am trying to use orientdb (v2.1.2) in a multi-threaded environment (Java 8), where I am updating a vertex from multiple threads. I know that orientdb uses MVCC, and therefore these operations may fail and must be performed again.
I wrote a small unit test that tries to provoke such situations, expecting a cyclic barrier inside the threads that I fork. Unfortunately, the test fails with an unclear exception, which I do not understand:
Sep 21, 2015 3:00:24 PM com.orientechnologies.common.log.OLogManager log INFO: OrientDB auto-config DISKCACHE=10,427MB (heap=3,566MB os=16,042MB disk=31,720MB) Thread [0] running Thread [1] running Sep 21, 2015 3:00:24 PM com.orientechnologies.common.log.OLogManager log WARNING: {db=tinkerpop} Requested command 'create edge type 'testedge_1442840424480' as subclass of 'E'' must be executed outside active transaction: the transaction will be committed and reopen right after it. To avoid this behavior execute it outside a transaction Sep 21, 2015 3:00:24 PM com.orientechnologies.common.log.OLogManager log WARNING: {db=tinkerpop} Requested command 'create edge type 'testedge_1442840424480' as subclass of 'E'' must be executed outside active transaction: the transaction will be committed and reopen right after it. To avoid this behavior execute it outside a transaction Exception in thread "Thread-4" com.orientechnologies.orient.core.exception.OSchemaException: Cluster with id 11 already belongs to class testedge_1442840424480 at com.orientechnologies.orient.core.metadata.schema.OSchemaShared.checkClustersAreAbsent(OSchemaShared.java:1264) at com.orientechnologies.orient.core.metadata.schema.OSchemaShared.doCreateClass(OSchemaShared.java:983) at com.orientechnologies.orient.core.metadata.schema.OSchemaShared.createClass(OSchemaShared.java:415) at com.orientechnologies.orient.core.metadata.schema.OSchemaShared.createClass(OSchemaShared.java:400) at com.orientechnologies.orient.core.metadata.schema.OSchemaProxy.createClass(OSchemaProxy.java:100) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph$6.call(OrientBaseGraph.java:1387) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph$6.call(OrientBaseGraph.java:1384) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.executeOutsideTx(OrientBaseGraph.java:1739) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.createEdgeType(OrientBaseGraph.java:1384) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.createEdgeType(OrientBaseGraph.java:1368) at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.createEdgeType(OrientBaseGraph.java:1353) at com.tinkerpop.blueprints.impls.orient.OrientVertex.addEdge(OrientVertex.java:928) at com.tinkerpop.blueprints.impls.orient.OrientVertex.addEdge(OrientVertex.java:832) at com.gentics.test.orientdb.OrientDBTinkerpopMultithreadingTest.lambda$0(OrientDBTinkerpopMultithreadingTest.java:31) at com.gentics.test.orientdb.OrientDBTinkerpopMultithreadingTest$$Lambda$1/1446001495.run(Unknown Source) at java.lang.Thread.run(Thread.java:745)
The test uses a simple in-memory database. I do not understand why orientdb checks for some cluster actions:
Cluster with id 11 already belongs to class testedge
For some reason, this problem appears only when I try to create two edges with the same label.
private OrientGraphFactory factory = new OrientGraphFactory("memory:tinkerpop").setupPool(5, 20); @Test public void testConcurrentGraphModifications() throws InterruptedException { OrientGraph graph = factory.getTx(); Vertex v = graph.addVertex(null); graph.commit(); CyclicBarrier barrier = new CyclicBarrier(2); List<Thread> threads = new ArrayList<>(); // Spawn two threads for (int i = 0; i < 2; i++) { final int threadNo = i; threads.add(run(() -> { System.out.println("Running thread [" + threadNo + "]"); // Start a new transaction and modify vertex v OrientGraph tx = factory.getTx(); Vertex v2 = tx.addVertex(null); v.addEdge("testedge", v2); try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } tx.commit(); })); } // Wait for all spawned threads for (Thread thread : threads) { thread.join(); } } protected Thread run(Runnable runnable) { Thread thread = new Thread(runnable); thread.start(); return thread; }
In general, I would be very grateful for an example that demonstrates how to deal with MVCC conflicts when using orientdb in the embedded multi-threaded Java environment.
Update:
I noticed that the problem no longer occurs when I reload the vertex in my thread through tx.getVertex (vertex.getId ()) (not through.reload ()). I get various errors when I simply pass the vertex object reference to my stream and use it there. I assume that the OrientVertex class is not thread safe.