BadReaderLockException on concurrent read transactions
See original GitHub issueOpening two concurrent read txns gives an error. Here’s a unit test to reproduce (for TxnTest.java):
@Test
public void readOnlyConcurrentTxnAllowedInReadOnlyEnv() {
env.openDbi(DB_1, MDB_CREATE);
final Env<ByteBuffer> roEnv = create().open(path, MDB_NOSUBDIR,
MDB_RDONLY_ENV);
Txn<ByteBuffer> txn1 = roEnv.txnRead();
Txn<ByteBuffer> txn2 = roEnv.txnRead();
assertThat(txn1, is(notNullValue()));
assertThat(txn2, is(notNullValue()));
assertThat(txn1, is(not(sameInstance(txn2))));
assertThat(txn1.getId(), is(not(txn2.getId())));
txn1.close();
txn2.close();
}
Stacktrace:
org.lmdbjava.Txn$BadReaderLockException: Invalid reuse of reader locktable slot (-30783)
at org.lmdbjava.ResultCodeMapper.<clinit>(ResultCodeMapper.java:54)
at org.lmdbjava.Env$Builder.open(Env.java:369)
at org.lmdbjava.TxnTest.before(TxnTest.java:81)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Process finished with exit code 255
Issue Analytics
- State:
- Created 7 years ago
- Comments:18 (14 by maintainers)
Top Results From Across the Web
ERROR: limit of concurrent read/write transactions ... - IBM
Answer. If you attempt a SQL statement, you may receive the following error message: ERROR: limit of concurrent read/write transactions ...
Read more >Transaction locking in Cloud Spanner | Google Cloud Blog
It is designed for highly concurrent applications that read and update data, for example, to process payments or for online game play.
Read more >Documentation: 15: 13.2. Transaction Isolation - PostgreSQL
The phenomena which are prohibited at various levels are: dirty read. A transaction reads data written by a concurrent uncommitted transaction. nonrepeatable ...
Read more >Handling Concurrency Conflicts - EF Core - Microsoft Learn
Managing conflicts when the same data is updated concurrently with ... consistency is the repeatable reads transaction isolation level.
Read more >9 Data Concurrency and Consistency - Oracle Help Center
The point in time to which a single SQL statement is consistent depends on the transaction isolation level and the nature of the...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I have just reverted of most of the Txn thread guard work.
Upon further reflection and working on my own LmdbJava-reliant applications in recent days, I can see that
ThreadLocal
-enforced single transactions per thread will impose unreasonable restrictions in certain cases. There’s a lot of flexibility provided byMDB_NOTLS
if users know what they’re doing (cf @phraktle’s wrapper code), and as such it seems unwise to block advanced usage via aThreadLocal
in lower-level types such asEnv
.My primary concerns are code complexity (thus maintenance effort) and blocking paths for advanced users who determine they require a different model for
Txn
management (eg their own reuse approaches, or multipleTxn
s per thread, or just the default native library managed handle reuse etc).I am not opposed to adding a strategy interface to LmdbJava to simplify alternate transaction handle allocators. For example, it would receive callbacks on
Env
creation,Txn
acquisition andTxn
close. If we had this users would be able to use standardEnv
methods to acquire and dispose of transactions without having to know about handle recycling internals. The applicable strategy could just be set in theEnv.Builder
. I would not mind if aThreadLocalTxnAllocator
was shipped with LmdbJava, beside the defaultNativeLibTxnAllocator
(just quick example names). Does this seem a reasonable way forward?