{"action":"created","issue":{"url":"https://api.github.com/repos/orientechnologies/orientdb/issues/5152","labels_url":"https://api.github.com/repos/orientechnologies/orientdb/issues/5152/labels{/name}","comments_url":"https://api.github.com/repos/orientechnologies/orientdb/issues/5152/comments","events_url":"https://api.github.com/repos/orientechnologies/orientdb/issues/5152/events","html_url":"https://github.com/orientechnologies/orientdb/issues/5152","id":112094275,"number":5152,"title":"OConcurrentModificationException while adding edges","user":{"login":"aheintz","id":4854621,"avatar_url":"https://avatars.githubusercontent.com/u/4854621?v=3","gravatar_id":"","url":"https://api.github.com/users/aheintz","html_url":"https://github.com/aheintz","followers_url":"https://api.github.com/users/aheintz/followers","following_url":"https://api.github.com/users/aheintz/following{/other_user}","gists_url":"https://api.github.com/users/aheintz/gists{/gist_id}","starred_url":"https://api.github.com/users/aheintz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aheintz/subscriptions","organizations_url":"https://api.github.com/users/aheintz/orgs","repos_url":"https://api.github.com/users/aheintz/repos","events_url":"https://api.github.com/users/aheintz/events{/privacy}","received_events_url":"https://api.github.com/users/aheintz/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/orientechnologies/orientdb/labels/bug","name":"bug","color":"ededed"},{"url":"https://api.github.com/repos/orientechnologies/orientdb/labels/in%20progress","name":"in progress","color":"CCCCCC"}],"state":"closed","locked":false,"assignee":{"login":"laa","id":105066,"avatar_url":"https://avatars.githubusercontent.com/u/105066?v=3","gravatar_id":"","url":"https://api.github.com/users/laa","html_url":"https://github.com/laa","followers_url":"https://api.github.com/users/laa/followers","following_url":"https://api.github.com/users/laa/following{/other_user}","gists_url":"https://api.github.com/users/laa/gists{/gist_id}","starred_url":"https://api.github.com/users/laa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/laa/subscriptions","organizations_url":"https://api.github.com/users/laa/orgs","repos_url":"https://api.github.com/users/laa/repos","events_url":"https://api.github.com/users/laa/events{/privacy}","received_events_url":"https://api.github.com/users/laa/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/orientechnologies/orientdb/milestones/17","html_url":"https://github.com/orientechnologies/orientdb/milestones/2.2","labels_url":"https://api.github.com/repos/orientechnologies/orientdb/milestones/17/labels","id":613160,"number":17,"title":"2.2","description":"","creator":{"login":"lvca","id":312606,"avatar_url":"https://avatars.githubusercontent.com/u/312606?v=3","gravatar_id":"","url":"https://api.github.com/users/lvca","html_url":"https://github.com/lvca","followers_url":"https://api.github.com/users/lvca/followers","following_url":"https://api.github.com/users/lvca/following{/other_user}","gists_url":"https://api.github.com/users/lvca/gists{/gist_id}","starred_url":"https://api.github.com/users/lvca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lvca/subscriptions","organizations_url":"https://api.github.com/users/lvca/orgs","repos_url":"https://api.github.com/users/lvca/repos","events_url":"https://api.github.com/users/lvca/events{/privacy}","received_events_url":"https://api.github.com/users/lvca/received_events","type":"User","site_admin":false},"open_issues":260,"closed_issues":114,"state":"open","created_at":"2014-03-28T13:17:36Z","updated_at":"2015-11-17T17:36:54Z","due_on":"2015-11-19T06:00:00Z","closed_at":null},"comments":5,"created_at":"2015-10-19T08:09:32Z","updated_at":"2015-11-17T17:36:54Z","closed_at":"2015-11-17T17:36:54Z","body":"We're developing a system that we're basing on OrientDB graphs (OrientDB 2.1.3). In the application, we have a thin pojo->graph persistence layer that should do the work properly, but I get OConcurrentModificationException when having multiple threads updating the database.\r\n\r\nHere's an example scenario:\r\n\r\n 1. Create a Product vertex with edge to Color \"Blue\"  \r\n 2. Simultaneously (while the transaction for creating Product 1 is open) create another Product vertex is created and also adds an edge to Color \"Blue\". \r\n 3. `OConcurrentModificationException` is thrown since the version of Color \"Blue\" vertex is updated. Note that I'm not trying to save or modify the Color \"Blue\" vertex itself.\r\n\r\nAs I understood the docs at http://orientdb.com/docs/2.1/Concurrency.html#concurrency-on-adding-edges setting `-DridBag.embeddedToSbtreeBonsaiThreshold=-1` should help me avoid my problem, although it still doesn't work.\r\n\r\nI think this is a perfectly valid case for a database like OrientDB, and according to the documentation, it should work.\r\n\r\nStacktrace of the exception:\r\n\r\n    Error on releasing database 'infogileorientdatabasetest' in pool\r\n    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)\r\n\tat com.orientechnologies.orient.core.conflict.OVersionRecordConflictStrategy.checkVersions(OVersionRecordConflictStrategy.java:55)\r\n\tat com.orientechnologies.orient.core.conflict.OVersionRecordConflictStrategy.onUpdate(OVersionRecordConflictStrategy.java:42)\r\n\tat com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.checkAndIncrementVersion(OAbstractPaginatedStorage.java:2279)\r\n\tat com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.doUpdateRecord(OAbstractPaginatedStorage.java:1911)\r\n\tat com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.commitEntry(OAbstractPaginatedStorage.java:2364)\r\n\tat com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.commit(OAbstractPaginatedStorage.java:1111)\r\n\tat com.orientechnologies.orient.core.tx.OTransactionOptimistic.doCommit(OTransactionOptimistic.java:609)\r\n\tat com.orientechnologies.orient.core.tx.OTransactionOptimistic.commit(OTransactionOptimistic.java:156)\r\n\tat com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:2582)\r\n\tat com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.commit(ODatabaseDocumentTx.java:2551)\r\n\tat com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.commit(ONetworkProtocolBinary.java:1221)\r\n\tat com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.executeRequest(ONetworkProtocolBinary.java:400)\r\n\tat com.orientechnologies.orient.server.network.protocol.binary.OBinaryNetworkProtocolAbstract.execute(OBinaryNetworkProtocolAbstract.java:223)\r\n\tat com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:77)\r\n\r\n\r\n**Test case**\r\nI have reproduced the error using this test case (I would be delighted if there's something else I've done wrong to cause the problem... :-)\r\n\r\n`OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue(-1)` in a static block.\r\n\r\n    package se.infogile.persistence.orientdb;\r\n\r\n    import com.orientechnologies.orient.client.remote.OServerAdmin;\r\n    import com.orientechnologies.orient.core.config.OGlobalConfiguration;\r\n    import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;\r\n    import com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory;\r\n    import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;\r\n    import com.orientechnologies.orient.core.exception.OConcurrentModificationException;\r\n    import com.orientechnologies.orient.core.exception.OConfigurationException;\r\n    import com.orientechnologies.orient.core.exception.OStorageException;\r\n    import com.orientechnologies.orient.core.tx.OTransaction;\r\n    import com.orientechnologies.orient.enterprise.channel.binary.OResponseProcessingException;\r\n    import com.orientechnologies.orient.server.OServer;\r\n    import com.orientechnologies.orient.server.OServerMain;\r\n    import com.orientechnologies.orient.server.config.OServerConfiguration;\r\n    import com.orientechnologies.orient.server.config.OServerConfigurationLoaderXml;\r\n    import com.orientechnologies.orient.server.config.OServerNetworkListenerConfiguration;\r\n    import com.tinkerpop.blueprints.Vertex;\r\n    import com.tinkerpop.blueprints.impls.orient.OrientGraph;\r\n    import org.apache.commons.io.FileUtils;\r\n    import org.slf4j.Logger;\r\n    import org.slf4j.LoggerFactory;\r\n    import org.testng.Assert;\r\n    import org.testng.annotations.AfterSuite;\r\n    import org.testng.annotations.BeforeSuite;\r\n    import org.testng.annotations.Test;\r\n\r\n    import java.io.File;\r\n    import java.io.IOException;\r\n    import java.io.InputStream;\r\n    import java.util.ArrayList;\r\n    import java.util.List;\r\n    import java.util.concurrent.*;\r\n\r\n\r\n    /**\r\n     * Created by heintz on 14/10/15.\r\n     */\r\n    public class OrientDBEdgeProblemTest {\r\n        static {\r\n            OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue(-1);\r\n        }\r\n        private static OPartitionedDatabasePoolFactory dbPoolFactory = new OPartitionedDatabasePoolFactory(100);\r\n        private static Logger logger = LoggerFactory.getLogger(OrientDBEdgeProblemTest.class);\r\n        private OServer server = null;\r\n        private static ExecutorService executorService = Executors.newFixedThreadPool(10);\r\n        private static final String dbName = \"edgeproblemtest\";\r\n\r\n        @Test\r\n        public void testVersionIncrementError() throws Throwable {\r\n            OrientGraph graph = getGraph(dbName);\r\n            graph.getRawGraph().setDefaultTransactionMode();\r\n            graph.createVertexType(\"Product\");\r\n            graph.createVertexType(\"Color\");\r\n            graph.createEdgeType(\"HasColor\");\r\n            graph.getRawGraph().begin(OTransaction.TXTYPE.OPTIMISTIC);\r\n\r\n    //        graph.begin();\r\n            Vertex v1 = graph.addVertex(\"Color\", \"name\", \"Blue\");\r\n            graph.commit();\r\n            graph.shutdown();\r\n\r\n            char[] alphabet = new char[] {'A','B','C','D','E','F','G'};\r\n\r\n            List<Future> futures = new ArrayList<>();\r\n            for (int i = 0; i < 2; i++) {\r\n                int pos = i;\r\n                futures.add(executorService.submit(new Callable<Object>() {\r\n                    @Override\r\n                    public Object call() throws Exception {\r\n                        OrientGraph g = getGraph(dbName);\r\n                        try {\r\n                            g.begin();\r\n                            Vertex v2 = g.addVertex(\"Product\", \"name\", \"Product \"+alphabet[pos]);\r\n                            g.addEdge(null, v2, v1, \"HasColor\");\r\n                            Thread.sleep(200);\r\n                            g.commit();\r\n                        } catch (OConcurrentModificationException ocme) {\r\n                            logger.error(\"Exception while saving: \", ocme);\r\n                            Assert.fail(\"OConcurrentModificationException\");\r\n                        } finally {\r\n                            g.shutdown();\r\n                        }\r\n                        return null;\r\n                    }\r\n                }));\r\n            }\r\n            for (Future f : futures) {\r\n                f.get();\r\n            }\r\n\r\n            executorService.shutdown();\r\n            executorService.awaitTermination(5, TimeUnit.SECONDS);\r\n        }\r\n\r\n        @AfterSuite\r\n        public void tearDown() throws Exception {\r\n            logger.info(\"Shutting down OrientDB\");\r\n            if (server != null) {\r\n                server.shutdown();\r\n            }\r\n        }\r\n\r\n\r\n        private OrientGraph getGraph(String dbName) {\r\n            String _db = \"remote:localhost:3424\";\r\n            String url = _db + \"/\" + dbName;\r\n            ODatabaseDocumentTx db = null;\r\n            try {\r\n                OPartitionedDatabasePool pool = dbPoolFactory.get(url,\r\n                        \"root\",\r\n                        \"admin\");\r\n                db = pool.acquire();\r\n            } catch (OResponseProcessingException | OConfigurationException | OStorageException oce) {\r\n                try {\r\n                    logger.info(\"creating new database named \" + dbName);\r\n                    System.err.println(\"Before DB creation\");\r\n                    OServerAdmin serverAdmin = new OServerAdmin(_db).connect(\r\n                            \"root\",\r\n                            \"admin\"\r\n                    );\r\n                    serverAdmin.createDatabase(dbName, \"document\", \"plocal\");\r\n                    serverAdmin.close();\r\n                    System.err.println(\"After DB creation\");\r\n                } catch (IOException ex) {\r\n                    logger.error(\"Unable to create database \" + dbName, ex);\r\n                }\r\n\r\n                OPartitionedDatabasePool pool = dbPoolFactory.get(url,\r\n                        \"root\",\r\n                        \"admin\");\r\n                db = pool.acquire();\r\n            }\r\n\r\n            return new OrientGraph(db);\r\n        }\r\n\r\n        @BeforeSuite\r\n        public void setUpDatabase() throws Exception {\r\n            File f = new File(\".\");\r\n            InputStream is = GraphPersistenceServiceTest.class.getResourceAsStream(\"/orientdb.config\");\r\n            Assert.assertNotNull(is);\r\n            logger.info(\"Starting OrientDB\");\r\n            server = OServerMain.create();\r\n\r\n            OServerConfigurationLoaderXml loaderXml = new OServerConfigurationLoaderXml(OServerConfiguration.class, GraphPersistenceServiceTest.class.getResourceAsStream(\"/orientdb.config\"));\r\n            OServerConfiguration oServerConfiguration = new OServerConfiguration(loaderXml);\r\n            System.setProperty(\"ORIENTDB_ROOT_PASSWORD\", \"admin\");\r\n            System.setProperty(\"RUNMODE\", \"UNITTEST\");\r\n\r\n            OServerNetworkListenerConfiguration networkConfig = oServerConfiguration.network.listeners.iterator().next();\r\n            networkConfig.portRange = \"3424-3430\";\r\n\r\n            server.setServerRootDirectory(\"./target/orientdb\");\r\n\r\n            server.startup(oServerConfiguration);\r\n\r\n            File serverDir = new File(\"./target/orientdb\");\r\n            if (serverDir.exists()) {\r\n                FileUtils.deleteDirectory(serverDir);\r\n            }\r\n            serverDir.mkdirs();\r\n            File dbDir = new File(serverDir, \"databases\");\r\n            dbDir.mkdirs();\r\n\r\n            server.activate();\r\n            OGlobalConfiguration.dumpConfiguration(System.out);\r\n            Thread.sleep(2000);\r\n        }\r\n    }\r\n\r\nRunning this test case, I also occasionally get a NullPointerException, but mostly the same OConcurrentModificationException as above. The test case I have provided never succeeds, I still feel it's a valid case where multiple threads have an open transaction and adding an edge to the same vertex... \r\n\r\n    Caused by: java.lang.NullPointerException at com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeRidBag.serializ‌​e(OSBTreeRidBag.java:816) at com.orientechnologies.orient.core.db.record.ridbag.ORidBag.toStream(ORidBag.java‌​:277)"},"comment":{"url":"https://api.github.com/repos/orientechnologies/orientdb/issues/comments/157447353","html_url":"https://github.com/orientechnologies/orientdb/issues/5152#issuecomment-157447353","issue_url":"https://api.github.com/repos/orientechnologies/orientdb/issues/5152","id":157447353,"user":{"login":"laa","id":105066,"avatar_url":"https://avatars.githubusercontent.com/u/105066?v=3","gravatar_id":"","url":"https://api.github.com/users/laa","html_url":"https://github.com/laa","followers_url":"https://api.github.com/users/laa/followers","following_url":"https://api.github.com/users/laa/following{/other_user}","gists_url":"https://api.github.com/users/laa/gists{/gist_id}","starred_url":"https://api.github.com/users/laa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/laa/subscriptions","organizations_url":"https://api.github.com/users/laa/orgs","repos_url":"https://api.github.com/users/laa/repos","events_url":"https://api.github.com/users/laa/events{/privacy}","received_events_url":"https://api.github.com/users/laa/received_events","type":"User","site_admin":false},"created_at":"2015-11-17T17:36:54Z","updated_at":"2015-11-17T17:36:54Z","body":"Hi fixed.\r\nAlso I have added correct test. https://gist.github.com/laa/fa5b728bdcf6dbfe6234 In nutshell you have to have at least one edge to avoid CME for the rest of edges. Also you should not share vertexes between threads."}}