Graph OConcurrentModificationException在添加边时

Graph OConcurrentModificationException在添加边时,graph,concurrency,orientdb,Graph,Concurrency,Orientdb,我们正在开发一个基于OrientDB图形OrientDB 2.1.3的系统。在应用程序中,我们有一个精简的pojo->graph持久层,它应该可以正常工作,但当有多个线程更新数据库时,我会遇到OConcurrentModificationException 下面是一个示例场景: 创建边为蓝色的产品顶点 同时,当创建产品1的事务处于打开状态时,将创建另一个产品顶点,并为蓝色添加一条边。 OConcurrentModificationException是在更新蓝色顶点的版本后引发的。请注意,我并没有

我们正在开发一个基于OrientDB图形OrientDB 2.1.3的系统。在应用程序中,我们有一个精简的pojo->graph持久层,它应该可以正常工作,但当有多个线程更新数据库时,我会遇到OConcurrentModificationException

下面是一个示例场景:

创建边为蓝色的产品顶点 同时,当创建产品1的事务处于打开状态时,将创建另一个产品顶点,并为蓝色添加一条边。 OConcurrentModificationException是在更新蓝色顶点的版本后引发的。请注意,我并没有试图保存或修改蓝色顶点本身的颜色。 据我所知,设置-DridBag.embeddedtosbtreebonsairthreshold=-1的文档应该可以帮助我避免问题,尽管它仍然不起作用

我错过了什么?我还能做些什么来避免这种情况吗

更新:

异常的堆栈跟踪:

Error on releasing database 'infogileorientdatabasetest' in pool
com.orientechnologies.orient.core.exception.OConcurrentModificationException: Cannot UPDATE the record #40:1 because the version is not the latest. Probably you are updating an old record or it has been modified by another user (db=v34 your=v33)
at com.orientechnologies.orient.core.conflict.OVersionRecordConflictStrategy.checkVersions(OVersionRecordConflictStrategy.java:55)
at com.orientechnologies.orient.core.conflict.OVersionRecordConflictStrategy.onUpdate(OVersionRecordConflictStrategy.java:42)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.checkAndIncrementVersion(OAbstractPaginatedStorage.java:2279)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.doUpdateRecord(OAbstractPaginatedStorage.java:1911)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.commitEntry(OAbstractPaginatedStorage.java:2364)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.commit(OAbstractPaginatedStorage.java:1111)
at com.orientechnologies.orient.core.tx.OTransactionOptimistic.doCommit(OTransactionOptimistic.java:609)
at com.orientechnologies.orient.core.tx.OTransactionOptimistic.commit(OTransactionOptimistic.java:156)
at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:2582)
at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:2551)
at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.commit(ONetworkProtocolBinary.java:1221)
at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.executeRequest(ONetworkProtocolBinary.java:400)
at com.orientechnologies.orient.server.network.protocol.binary.OBinaryNetworkProtocolAbstract.execute(OBinaryNetworkProtocolAbstract.java:223)
at com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:77)
更新2-测试用例 我使用这个测试用例再现了错误。如果我做错了什么导致了这个问题,我会很高兴的…:-

在静态块中使用OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue-1更新3更新的测试用例

package se.infogile.persistence.orientdb;

import com.orientechnologies.orient.client.remote.OServerAdmin;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.exception.OConcurrentModificationException;
import com.orientechnologies.orient.core.exception.OConfigurationException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.tx.OTransaction;
import com.orientechnologies.orient.enterprise.channel.binary.OResponseProcessingException;
import com.orientechnologies.orient.server.OServer;
import com.orientechnologies.orient.server.OServerMain;
import com.orientechnologies.orient.server.config.OServerConfiguration;
import com.orientechnologies.orient.server.config.OServerConfigurationLoaderXml;
import com.orientechnologies.orient.server.config.OServerNetworkListenerConfiguration;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;


/**
 * Created by heintz on 14/10/15.
 */
public class OrientDBEdgeProblemTest {
    static {
        OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue(-1);
    }
    private static OPartitionedDatabasePoolFactory dbPoolFactory = new OPartitionedDatabasePoolFactory(100);
    private static Logger logger = LoggerFactory.getLogger(OrientDBEdgeProblemTest.class);
    private OServer server = null;
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
    private static final String dbName = "edgeproblemtest";

    @Test
    public void testVersionIncrementError() throws Throwable {
        OrientGraph graph = getGraph(dbName);
        graph.getRawGraph().setDefaultTransactionMode();
        graph.createVertexType("Product");
        graph.createVertexType("Color");
        graph.createEdgeType("HasColor");
        graph.getRawGraph().begin(OTransaction.TXTYPE.OPTIMISTIC);

//        graph.begin();
        Vertex v1 = graph.addVertex("Color", "name", "Blue");
        graph.commit();
        graph.shutdown();

        char[] alphabet = new char[] {'A','B','C','D','E','F','G'};

        List<Future> futures = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            int pos = i;
            futures.add(executorService.submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    OrientGraph g = getGraph(dbName);
                    try {
                        g.begin();
                        Vertex v2 = g.addVertex("Product", "name", "Product "+alphabet[pos]);
                        g.addEdge(null, v2, v1, "HasColor");
                        Thread.sleep(200);
                        g.commit();
                    } catch (OConcurrentModificationException ocme) {
                        logger.error("Exception while saving: ", ocme);
                        Assert.fail("OConcurrentModificationException");
                    } finally {
                        g.shutdown();
                    }
                    return null;
                }
            }));
        }
        for (Future f : futures) {
            f.get();
        }

        executorService.shutdown();
        executorService.awaitTermination(5, TimeUnit.SECONDS);
    }

    @AfterSuite
    public void tearDown() throws Exception {
        logger.info("Shutting down OrientDB");
        if (server != null) {
            server.shutdown();
        }
    }


    private OrientGraph getGraph(String dbName) {
        String _db = "remote:localhost:3424";
        String url = _db + "/" + dbName;
        ODatabaseDocumentTx db = null;
        try {
            OPartitionedDatabasePool pool = dbPoolFactory.get(url,
                    "root",
                    "admin");
            db = pool.acquire();
        } catch (OResponseProcessingException | OConfigurationException | OStorageException oce) {
            try {
                logger.info("creating new database named " + dbName);
                System.err.println("Before DB creation");
                OServerAdmin serverAdmin = new OServerAdmin(_db).connect(
                        "root",
                        "admin"
                );
                serverAdmin.createDatabase(dbName, "document", "plocal");
                serverAdmin.close();
                System.err.println("After DB creation");
            } catch (IOException ex) {
                logger.error("Unable to create database " + dbName, ex);
            }

            OPartitionedDatabasePool pool = dbPoolFactory.get(url,
                    "root",
                    "admin");
            db = pool.acquire();
        }

        return new OrientGraph(db);
    }

    @BeforeSuite
    public void setUpDatabase() throws Exception {
        File f = new File(".");
        InputStream is = GraphPersistenceServiceTest.class.getResourceAsStream("/orientdb.config");
        Assert.assertNotNull(is);
        logger.info("Starting OrientDB");
        server = OServerMain.create();

        OServerConfigurationLoaderXml loaderXml = new OServerConfigurationLoaderXml(OServerConfiguration.class, GraphPersistenceServiceTest.class.getResourceAsStream("/orientdb.config"));
        OServerConfiguration oServerConfiguration = new OServerConfiguration(loaderXml);
        System.setProperty("ORIENTDB_ROOT_PASSWORD", "admin");
        System.setProperty("RUNMODE", "UNITTEST");

        OServerNetworkListenerConfiguration networkConfig = oServerConfiguration.network.listeners.iterator().next();
        networkConfig.portRange = "3424-3430";

        server.setServerRootDirectory("./target/orientdb");

        server.startup(oServerConfiguration);

        File serverDir = new File("./target/orientdb");
        if (serverDir.exists()) {
            FileUtils.deleteDirectory(serverDir);
        }
        serverDir.mkdirs();
        File dbDir = new File(serverDir, "databases");
        dbDir.mkdirs();

        server.activate();
        OGlobalConfiguration.dumpConfiguration(System.out);
        Thread.sleep(2000);
    }
}

这是因为在向顶点添加边时,顶点本身会被修改以存储此信息,但当有关边的信息存储在单独的对象中时,您可以在模式下工作。仅使用财产
-DridBag.embeddedToSbtreeBonsaiThreshold=true,您将消除此异常。

您是否有关于此异常的日志?您是否有一些代码要共享或要复制它的测试用例?我将在未来几天内尝试创建一个测试用例。问题将随测试用例更新。希望存在SBS问题,但除此之外,它可以在许多其他方面帮助改进一个优秀的数据库。但根据源代码,OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD是一个整数,我已根据文档和源代码以编程方式将其设置为-1,请参阅我的问题。为了验证,我将其设置为true并引发NumberFormatException。您应该在静态初始值设定项或命令行中设置此属性。把它放在方法上太晚了。你能试试吗?&@Lvca,现在我偶尔会得到一个NullPointerException,但大部分都是同一个OConcurrentModificationException。我提供的测试用例从来没有成功过,我仍然觉得它是一个有效的案例,多个线程有一个打开的事务,并向同一个顶点添加一条边。。。原因:com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeRidBag.serializeOSBTreeRidBag.java:816在com.orientechnologies.orient.core.db.record.ridbag.ORidBag.toStreamORidBag.java:277我正在用我的静态块更新原始帖子中的测试用例。