Едновременни графични операции на OrientDB в Java

Опитвам се да използвам orientdb (v2.1.2) в многонишкова среда (Java 8), където актуализирам връх от множество нишки. Наясно съм, че orientdb използва MVCC и по този начин тези операции може да се провалят и трябва да се изпълнят отново.

Написах малък модулен тест, който се опитва да провокира такива ситуации, като изчаква циклична бариера в нишките, които разклонявам. За съжаление тестът се проваля с неясно изключение, което не разбирам:

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

Като цяло ще бъда много благодарен за пример, който демонстрира как да се справяте с MVCC конфликти при използване на orientdb във вградена многонишкова среда на Java.


Актуализация:

Забелязах, че проблемът вече не се появява, когато презареждам върха в моята нишка чрез tx.getVertex(vertex.getId()) (не чрез .reload()). Получавам различни грешки, когато просто предам препратката към обекта на върха към моята нишка и я използвам там. Предполагам, че класът OrientVertex не е безопасен за нишки.


person Jotschi    schedule 21.09.2015    source източник


Отговори (1)


  1. Прав си всички графични елементи не са безопасни за нишки.
  2. Причината за вашето изключение е следната, когато създавате ръб, вие под графичната база данни създавате документ с клас, който е равен на етикета на ръба. Ако класът липсва, транзакцията се извършва автоматично и се създава нов клас вътре в схемата. Всеки клас е картографиран към клъстер в базата данни (той е като таблица), когато добавяте ръбове едновременно, вие в същото време създавате същия клас и в резултат се създава същият клъстер. Така една нишка печели, друга се проваля с изключение на този клъстер с дадено име, който вече е създаден. Всъщност ви предлагам да създадете всички класове, известни още като етикети на ръбове, ако е възможно, преди да добавите ръбове по време на изпълнение.

Още едно предложение. Трябва да мислите за екземпляра на OrientGraph като за връзка със сървъра. Най-добрата употреба е следната:

  1. Пул за настройка в OrientGraphFactory
  2. Придобийте екземпляр на графика преди транзакция.
  3. Изпълнение на транзакция.
  4. Извикайте .shutdown(), не създавайте дълготрайни графични екземпляри.
person Andrey Lomakin    schedule 02.10.2015