添加边时出现OConcurrentModificationException

时间:2015-10-13 20:50:22

标签: graph concurrency orientdb

我们正在开发一个基于OrientDB图表的系统(OrientDB 2.1.3)。在应用程序中,我们有一个瘦的pojo->图形持久层,可以正常工作,但是当多个线程更新数据库时,我得到OConcurrentModificationException。

以下是一个示例场景:

  1. 创建一个边缘为颜色的产品顶点"蓝色"
  2. 同时(在创建产品1的事务处于打开状态时)创建另一个产品顶点,并为Color" Blue"添加边缘。
  3. 自从Color" Blue"的版本以来,
  4. OConcurrentModificationException被抛出顶点已更新。请注意,我并未尝试保存或修改Color" Blue"顶点本身。
  5. 据我所知,http://orientdb.com/docs/2.1/Concurrency.html#concurrency-on-adding-edges设置-DridBag.embeddedToSbtreeBonsaiThreshold=-1的文档可以帮助我避免我的问题,尽管它仍然无效。

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

    更新

    例外的Stacktrace:

    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 - 测试用例 我使用此测试用例重现了错误。如果我做错了导致问题的话,我会很高兴...: - )

    更新3 在静态块中使用OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue(-1)更新了测试用例。

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

1 个答案:

答案 0 :(得分:0)

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