2015-09-21 84 views
5

我想在多線程環境(Java 8)中使用orientdb(v2.1.2),其中我更新多個線程內的頂點。我知道orientdb正在使用MVCC,因此這些操作可能會失敗並需要重新執行。在Java中的OrientDB併發圖操作

我寫了一個小單元測試,試圖通過等待線程內的循環障礙來引發這種情況。不幸的是,測試失敗,一個不起眼的異常,我不明白:

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) 

測試使用一個簡單的內存數據庫。我不知道爲什麼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; 
} 

的測試用例完整的源可以在這裏找到:

https://github.com/Jotschi/orientdb-concurrency-test/blob/master/src/test/java/com/gentics/test/orientdb/OrientDBTinkerpopMultithreadingTest.java#L18

一般來說,我會是演示如何在使用orientdb時應對MVCC衝突例子非常感謝嵌入式多線程Java環境。


更新:

我注意到,當我通過tx.getVertex(vertex.getId())我的線程中重新加載頂點問題不再occures(不是通過.reload()) 。當我將頂點對象引用傳遞給我的線程並在那裏使用它時,我得到各種錯誤。我認爲OrientVertex類不是線程安全的。

回答

4
  1. 你是對的全部圖形元素不是線程安全的。
  2. 當你創建邊緣時,你的異常的原因是,你在圖形數據庫的下面創建文檔,其中類別等於邊緣標籤。如果類不存在,則會自動提交事務並創建模式內的新類。當您同時添加邊並同時創建相同的類和結果時,每個類都映射到數據庫中的羣集(就像一個表),創建相同的羣集。因此,一個線程贏得其他失敗,並且已經創建了具有給定名稱的羣集。實際上,我建議您在運行時添加邊緣之前,先創建所有類的邊界標記(如果可能)。

還有一個建議。您應該考慮OrientGraph實例,就好像它是連接到服務器一樣。最好的用法如下:

  1. 設置池OrientGraphFactory交易之前
  2. 採集圖實例。
  3. 執行交易。
  4. 致電.shutdown(),請勿創建長時間存在的圖形實例。