Java中的OrientDB并发图操作

Jot*_*chi 5 java orientdb

我试图在多线程环境(Java 8)中使用orientdb(v2.1.2),我在多个线程中更新顶点.我知道orientdb正在使用MVCC,因此这些操作可能会失败并且必须再次执行.

我写了一个小单元测试,试图通过等待我fork的线程中的循环障碍来激发这种情况.不幸的是,测试失败了一个模糊的异常,我不明白:

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)
Run Code Online (Sandbox Code Playgroud)

该测试使用的是简单的内存数据库.我不明白为什么orientdb正在检查一些集群操作:

Cluster with id 11 already belongs to class testedge

不知何故,只有当我尝试使用相同的标签创建两个边时才会出现此问题.

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;
}
Run Code Online (Sandbox Code Playgroud)

总的来说,我非常感谢一个演示如何在嵌入式多线程java环境中使用orientdb时处理MVCC冲突的示例.


更新:

我注意到当我通过tx.getVertex(vertex.getId())(而不是通过.reload())重新加载我的线程中的顶点时,问题不再发生.当我将顶点对象引用传递给我的线程并在那里使用它时,我得到了各种错误.我假设OrientVertex类不是线程安全的.

And*_*kin 4

  1. 你是对的,所有图形元素都不是线程安全的。
  2. 异常的原因如下:当您创建边缘时,您在图形数据库下面创建了类等于边缘标签的文档。如果类不存在,事务会自动提交,并在模式内创建新类。当您同时添加边时,每个类都会映射到数据库中的簇(它就像一个表),同时创建相同的类,从而创建相同的簇。因此,一个线程获胜,其他线程失败,但已创建具有给定名称的集群除外。实际上,我建议您在运行时添加边缘之前,如果可能的话,创建所有类,也称为边缘标签。

还有一个建议。您应该将 OrientGraph 实例视为与服务器的连接。最佳用法如下:

  1. OrientGraphFactory 中的设置池
  2. 在交易之前获取图实例。
  3. 执行交易。
  4. 调用.shutdown(),不要创建长期存在的图实例。