- Notifications
You must be signed in to change notification settings - Fork124
Description
We have a use case where LMDB envs are created as transient stores for search result data so are created, filled, queried then closed and deleted when no longer needed. There are multiple threads involved. We had a bug in our app that resulted in the env being closed while a thread was still trying to query the lmdb db. This resulted in a SIGSEGV and our whole app going down.
I appreciate that querying the db after the env is closed is wrong, but it would be preferable to get an exception rather than have the whole app crash.
I added the following test toEnvTest
to demonstrate this. I should imagine the same will happen with other cursor ops and possibly gets.
@TestpublicvoidcannotReadOnceClosed()throwsIOException,InterruptedException,ExecutionException {finalFilepath =tmp.newFile();finalEnv<ByteBuffer>env =create() .setMaxReaders(5) .open(path,MDB_NOSUBDIR);finalDbi<ByteBuffer>dbi =env.openDbi(DB_1,MDB_CREATE);dbi.put(bb(1),bb(10));dbi.put(bb(2),bb(20));try (Txn<ByteBuffer>roTxn =env.txnRead()) {assertThat(dbi.get(roTxn,bb(1)).getInt(),is(10));assertThat(dbi.get(roTxn,bb(2)).getInt(),is(20)); }System.out.println("Done puts");finalCountDownLatchfirstGetFinishedLatch =newCountDownLatch(1);finalCountDownLatchenvClosedLatch =newCountDownLatch(1);finalCompletableFuture<Void>future =CompletableFuture.runAsync(() -> {System.out.println("Running async task");try (Txn<ByteBuffer>roTxn =env.txnRead()) {try (CursorIterable<ByteBuffer>iterable =dbi.iterate(roTxn,KeyRange.all())) {finalIterator<CursorIterable.KeyVal<ByteBuffer>>iterator =iterable.iterator();try {System.out.println("Getting first entry");CursorIterable.KeyVal<ByteBuffer>keyVal =iterator.next();assertThat(keyVal.val().getInt(),is(10));firstGetFinishedLatch.countDown();try {System.out.println("Waiting for env to close");envClosedLatch.await(); }catch (InterruptedExceptione) {Thread.currentThread().interrupt();thrownewRuntimeException(e); }assertThat(env.isClosed(),is(true));System.out.println("Getting second entry, but env is now closed");keyVal =iterator.next();assertThat(keyVal.val().getInt(),is(20)); }catch (RuntimeExceptione) {e.printStackTrace(); } } } });System.out.println("Waiting for cursor to get first entry");firstGetFinishedLatch.await();System.out.println("Closing env");env.close();envClosedLatch.countDown();System.out.println("Waiting for completion");future.get(); }
This produces the output
Done putsWaiting for cursor to get first entryRunning async taskGetting first entryWaiting for env to closeClosing envWaiting for completionGetting second entry, but env is now closed## A fatal error has been detected by the Java Runtime Environment:## SIGSEGV (0xb) at pc=0x00007fb5d37dab28, pid=2052, tid=2086## JRE version: OpenJDK Runtime Environment (15.0.2+7) (build 15.0.2+7-27)# Java VM: OpenJDK 64-Bit Server VM (15.0.2+7-27, mixed mode, sharing, tiered, compressed oops, g1 gc, linux-amd64)# Problematic frame:# C [lmdbjava-native-library-17311548015087855682.so+0x6b28] mdb_cursor_next+0x138## No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again## An error report file with more information is saved as:# /home/dev/git_work/lmdbjava/hs_err_pid2052.log## If you would like to submit a bug report, please visit:# https://bugreport.java.com/bugreport/crash.jsp# The crash happened outside the Java Virtual Machine in native code.# See problematic frame for where to report the bug.#Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
I have raised PR#186 with some added guards for the env being closed.