2015-09-21 9 views
5

Sto cercando di utilizzare orientdb (v2.1.2) in un ambiente con multithreading (Java 8) in cui aggiorno un vertice da più thread. Sono consapevole del fatto che orientdb utilizza MVCC e quindi tali operazioni potrebbero fallire e devono essere nuovamente eseguite.Operazioni con grafici concentri di OrientDB in Java

Ho scritto un test di una piccola unità che tenta di provocare tali situazioni attendendo su una barriera ciclica all'interno dei fili che si biforca. Purtroppo il test fallisce con un'eccezione oscura, che non capisco:

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) 

Il test utilizza un semplice database in-memory. Non capisco il motivo per cui OrientDB sta controllando alcune azioni a grappolo:

Cluster with id 11 already belongs to class testedge

In qualche modo questo problema appare solo quando si tenta di creare due bordi con la stessa etichetta.

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

Le fonti complete per la testcase può essere trovato qui:

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

In generale sarei molto grato per l'esempio che dimostra come affrontare i conflitti MVCC quando si utilizza OrientDB in un ambiente java multithreaded incorporato.


Aggiornamento:

ho notato che il problema non è più occures quando ricarico il vertice nel mio thread via tx.getVertex (vertex.getId()) (non tramite .reload()) . Ottengo vari errori quando passo il riferimento dell'oggetto vertice al mio thread e lo uso lì. Presumo che la classe OrientVertex non sia protetta da thread.

risposta

4
  1. Hai ragione, tutti gli elementi grafici non sono thread-safe.
  2. Il motivo dell'eccezione che segue quando crei lo spigolo, al di sotto del database del grafico, crea un documento con una classe che equivale all'etichetta del bordo. Se la classe è assente, la transazione viene eseguita automaticamente e viene creata una nuova classe all'interno dello schema. Ogni classe è mappata al cluster nel database (è come una tabella) quando si aggiungono gli spigoli contemporaneamente contemporaneamente si crea la stessa classe e come risultato viene creato lo stesso cluster. Quindi un thread vince altri fallisce con l'eccezione del cluster con un nome già creato. In realtà ti suggerisco di creare tutte le classi, ovvero le etichette di bordi, se possibile, prima di aggiungere bordi in fase di esecuzione.

Un altro suggerimento. Dovresti pensare all'istanza di OrientGraph come se fosse una connessione al server. Il miglior utilizzo è la seguente: piscina

  1. installazione in OrientGraphFactory
  2. esempio grafico Acquisire prima transazione.
  3. Eseguire la transazione.
  4. Chiama .shutdown(), non creare istanze di grafici a lunga durata.