2015-09-21 3 views
5

Ich versuche Orientdb (v2.1.2) in einer Multithread-Umgebung (Java 8) zu verwenden, wo ich einen Scheitelpunkt innerhalb von mehreren Threads aktualisieren. Mir ist bewusst, dass orientdb MVCC verwendet und daher diese Operationen möglicherweise fehlschlagen und erneut ausgeführt werden müssen.OrientDB gleichzeitige Graph-Operationen in Java

Ich schrieb einen kleinen Komponententest, der versucht, solche Situationen zu provozieren, indem er auf eine zyklische Barriere in den Threads i fork wartet. Leider versagt der Test mit einer obskuren Ausnahme, die ich nicht verstehe:

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) 

Der Test eine einfache In-Memory-Datenbank verwendet. Ich verstehe nicht, warum Orientdb einige Cluster Aktionen überprüft:

Cluster with id 11 already belongs to class testedge

Irgendwie dieses Problem erscheint nur, wenn ich versuche, zwei Kanten mit dem gleichen Etikett zu erstellen.

für ein Beispiel wäre ich sehr dankbar

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

Im Allgemeinen, die wie mit MVCC Konflikten zu bewältigen zeigt, wenn sie in einem mit Orientdb:

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; 
} 

die kompletten Quellen für den Testfall kann hier gefunden werden eingebettete Multithread-Java-Umgebung.


Update:

bemerkte ich, dass das Problem nicht mehr occures, wenn ich den Scheitelpunkt in meinem Thread über tx.getVertex (vertex.getId()) (nicht über .reload()) nachladen . Ich erhalte verschiedene Fehler, wenn ich den Vertex-Objektverweis nur an meinen Thread übergebe und ihn dort verwende. Ich nehme an, die OrientVertex-Klasse ist nicht threadsicher.

Antwort

4
  1. Sie haben Recht alle Grafikelemente sind nicht threadsicher.
  2. Ursache Ihrer Ausnahme folgt, wenn Sie Kante erstellen, Sie unterhalb der Grafikdatenbank erstellen Dokument mit Klasse, die gleich der Beschriftung der Kante ist. Wenn die Klasse nicht vorhanden ist, wird die Transaktion automatisch festgeschrieben und es wird eine neue Klasse innerhalb des Schemas erstellt. Jede Klasse wird dem Cluster in der Datenbank zugeordnet (es ist wie eine Tabelle), wenn Sie gleichzeitig Kanten hinzufügen und gleichzeitig die gleiche Klasse erstellen und das Ergebnis, dass derselbe Cluster erstellt wird. Ein Thread gewinnt also einen anderen Fehler mit der Ausnahme, dass der Cluster mit dem angegebenen Namen bereits erstellt wurde. Eigentlich schlage ich vor, dass Sie alle Klassen aka Labels von Kanten erstellen, wenn möglich, bevor Sie zur Laufzeit Kanten hinzufügen.

Ein weiterer Vorschlag. Sie sollten über die OrientGraph-Instanz nachdenken, als ob es sich um eine Verbindung zum Server handelt. Die beste Nutzung ist folgende:

  1. Setup-Pool in OrientGraphFactory
  2. Acquire Graph Instanz vor der Transaktion.
  3. Transaktion ausführen.
  4. Rufen Sie .shutdown(), erstellen Sie keine langlebige Grafik Instanzen.