{"action":"created","issue":{"url":"https://api.github.com/repos/neo4j/neo4j/issues/6036","labels_url":"https://api.github.com/repos/neo4j/neo4j/issues/6036/labels{/name}","comments_url":"https://api.github.com/repos/neo4j/neo4j/issues/6036/comments","events_url":"https://api.github.com/repos/neo4j/neo4j/issues/6036/events","html_url":"https://github.com/neo4j/neo4j/issues/6036","id":120632276,"number":6036,"title":"Unnecessary deadlocks occur when using tx.acquireReadLock(..)","user":{"login":"bachmanm","id":629665,"avatar_url":"https://avatars.githubusercontent.com/u/629665?v=3","gravatar_id":"","url":"https://api.github.com/users/bachmanm","html_url":"https://github.com/bachmanm","followers_url":"https://api.github.com/users/bachmanm/followers","following_url":"https://api.github.com/users/bachmanm/following{/other_user}","gists_url":"https://api.github.com/users/bachmanm/gists{/gist_id}","starred_url":"https://api.github.com/users/bachmanm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bachmanm/subscriptions","organizations_url":"https://api.github.com/users/bachmanm/orgs","repos_url":"https://api.github.com/users/bachmanm/repos","events_url":"https://api.github.com/users/bachmanm/events{/privacy}","received_events_url":"https://api.github.com/users/bachmanm/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/neo4j/neo4j/labels/bug","name":"bug","color":"fc2929"},{"url":"https://api.github.com/repos/neo4j/neo4j/labels/kernel","name":"kernel","color":"0052cc"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-12-06T12:46:54Z","updated_at":"2015-12-07T08:43:43Z","closed_at":null,"body":"Neo4j 2.3.1. \r\n\r\nSimplified scenario of a real-world use case. I would like to make sure that threads that update something in the graph peacefully co-exist with reader threads. I was under the impression that this could be achieved using locks. Consider this test, where a single node exists in the database. Some threads are updating a single property, some threads are reading it. \r\n\r\n```java\r\npublic class BugTest {\r\n\r\n    private GraphDatabaseService database;\r\n\r\n    @Before\r\n    public void setUp() {\r\n        database = new TestGraphDatabaseFactory().newImpermanentDatabase();\r\n        try (Transaction tx = database.beginTx()) {\r\n            database.createNode(DynamicLabel.label(\"Test\"));\r\n            tx.success();\r\n        }\r\n    }\r\n\r\n    @After\r\n    public void tearDown() {\r\n        database.shutdown();\r\n    }\r\n\r\n    @Test\r\n    public void verifyConcurrentInsertsAndFetchesFromTheLinkedList() throws InterruptedException {\r\n        ExecutorService createExecutor = Executors.newFixedThreadPool(50);\r\n        int noRequests = 1000;\r\n        final AtomicBoolean success = new AtomicBoolean(true);\r\n\r\n        for (int i = 0; i < noRequests; i++) {\r\n            createExecutor.execute(new Runnable() {\r\n                @Override\r\n                public void run() {\r\n                    try {\r\n                        increment();\r\n                    } catch (Exception e) {\r\n                        success.set(false);\r\n                        e.printStackTrace();\r\n                    }\r\n                }\r\n            });\r\n            createExecutor.execute(new Runnable() {\r\n                @Override\r\n                public void run() {\r\n                    try {\r\n                        System.out.println(\"counter = \" + get());\r\n                    } catch (Exception e) {\r\n                        success.set(false);\r\n                        e.printStackTrace();\r\n                    }\r\n                }\r\n            });\r\n        }\r\n\r\n        long start = System.currentTimeMillis();\r\n\r\n        createExecutor.shutdown();\r\n        createExecutor.awaitTermination(120, TimeUnit.SECONDS);\r\n\r\n        System.out.println(\"Took \" + (System.currentTimeMillis() - start) + \" ms\");\r\n\r\n        assertTrue(\"At least one operation failed\", success.get());\r\n\r\n        try (Transaction tx = database.beginTx()) {\r\n            assertEquals(noRequests, database.getNodeById(0).getProperty(\"counter\"));\r\n            tx.success();\r\n        }\r\n    }\r\n\r\n    private void increment() {\r\n        try (Transaction tx = database.beginTx()) {\r\n            Node node = database.getNodeById(0);\r\n\r\n            tx.acquireWriteLock(node);\r\n\r\n            node.setProperty(\"counter\", (int) node.getProperty(\"counter\", 0) + 1);\r\n\r\n            tx.success();\r\n        }\r\n    }\r\n\r\n    private int get() {\r\n        int result = -1;\r\n\r\n        try (Transaction tx = database.beginTx()) {\r\n            Node node = database.getNodeById(0);\r\n            tx.acquireReadLock(node);\r\n            result = (int) node.getProperty(\"counter\", 0);\r\n            tx.success();\r\n        }\r\n\r\n        return result;\r\n    }\r\n}\r\n```\r\n\r\nThis results in `DeadlockDetectedException` where the first one always look like this:\r\n\r\n```\r\norg.neo4j.kernel.DeadlockDetectedException: ForsetiClient[48] can't acquire UpdateLock{objectId=1486737951, refCount=1, holder=ForsetiClient[45]} on NODE(0), because holders of that lock are waiting for ForsetiClient[48].\r\n Wait list:SharedLock[\r\nClient[45] waits for []]\r\n\tat org.neo4j.kernel.impl.enterprise.lock.forseti.ForsetiClient.markAsWaitingFor(ForsetiClient.java:782)\r\n\tat org.neo4j.kernel.impl.enterprise.lock.forseti.ForsetiClient.acquireExclusive(ForsetiClient.java:276)\r\n\tat org.neo4j.kernel.impl.api.LockingStatementOperations.acquireExclusive(LockingStatementOperations.java:504)\r\n```\r\n\r\nFrom the message, it actually seems it should not be a deadlock, because one of the clients waits for nothing (45 in this case).\r\n\r\nNote that removing `tx.acquireReadLock(node)` makes the tests pass, but there is a point to having it for the real-world use case, which reads/writes subgraphs (trees) rooted at that node. The idea is to have that root serve as *the* lock for the entire tree. \r\n\r\nCheers!"},"comment":{"url":"https://api.github.com/repos/neo4j/neo4j/issues/comments/162449355","html_url":"https://github.com/neo4j/neo4j/issues/6036#issuecomment-162449355","issue_url":"https://api.github.com/repos/neo4j/neo4j/issues/6036","id":162449355,"user":{"login":"bachmanm","id":629665,"avatar_url":"https://avatars.githubusercontent.com/u/629665?v=3","gravatar_id":"","url":"https://api.github.com/users/bachmanm","html_url":"https://github.com/bachmanm","followers_url":"https://api.github.com/users/bachmanm/followers","following_url":"https://api.github.com/users/bachmanm/following{/other_user}","gists_url":"https://api.github.com/users/bachmanm/gists{/gist_id}","starred_url":"https://api.github.com/users/bachmanm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bachmanm/subscriptions","organizations_url":"https://api.github.com/users/bachmanm/orgs","repos_url":"https://api.github.com/users/bachmanm/repos","events_url":"https://api.github.com/users/bachmanm/events{/privacy}","received_events_url":"https://api.github.com/users/bachmanm/received_events","type":"User","site_admin":false},"created_at":"2015-12-07T08:43:42Z","updated_at":"2015-12-07T08:43:42Z","body":"Actually, under heavier concurrency, the test fails even if everybody just tries to take a write lock. "}}