2015-09-21 18 views
5

私は複数のスレッド内から頂点を更新するマルチスレッド環境(Java 8)でorientdb(v2.1.2)を使用しようとしています。私はorientdbがMVCCを使用していることを認識しているため、これらの操作は失敗し、再度実行する必要があります。OrientDBのJavaでの同時グラフ操作

私はスレッドiフォーク内の周期的な障壁を待ってこのような状況を引き起こそうとする小さなユニットテストを書いた。残念ながらテストは、私は理解していない無名の例外で失敗します。

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

を私は同じラベルを持つ2つのエッジを作成しようとすると、どういうわけか、この問題がのみ表示されます。

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. あなたの例外の理由は、エッジを作成するときに、グラフデータベースの下にエッジのラベルに等しいクラスを持つドキュメントを作成することです。 classが存在しない場合、トランザクションは自動的にコミットされ、スキーマ内の新しいクラスが作成されます。エッジを同時に追加すると同時に、同じクラスを作成し、結果として同じクラスターが作成されるとき、各クラスはデータベース内のクラスターにマップされます(テーブルのようなものです)。したがって、ある名前のクラスタがすでに作成されているという例外を除いて、あるスレッドが他のスレッドを勝ち取る。実際には、実行時にエッジを追加する前に、可能であればすべてのクラスを別名で作成することをお勧めします。

もう1つの提案。 OrientGraphインスタンスは、サーバーに接続しているかのように考える必要があります。最善の使用方法は以下の通りです:

  1. セットアッププールOrientGraphFactoryに
  2. 獲得グラフインスタンストランザクションの前に。
  3. トランザクションを実行します。
  4. .shutdown()を呼び出して、長いリビンググラフインスタンスを作成しないでください。
関連する問題