private void doMultiThreadTest(final LmdbEnv lmdbEnv,
                                   final AbstractLmdbDb<String, String> database,
                                   final boolean isReaderBlockedByWriter,
                                   final int expectedNumReadersHighWaterMark,
                                   final boolean doWrites) {


        final HighWaterMarkTracker readersHighWaterMarkTracker = new HighWaterMarkTracker();
        final HighWaterMarkTracker writersHighWaterMarkTracker = new HighWaterMarkTracker();

        Assertions.assertThat(lmdbEnv.info().numReaders)
                .isEqualTo(0);


        database.logDatabaseContents(LOGGER::info);

        Assertions.assertThat(lmdbEnv.info().numReaders)
                .isEqualTo(1);

        final int threadCount = 50;
        LOGGER.info("threadCount: {}", threadCount);

        final CountDownLatch threadsStartedLatch = new CountDownLatch(threadCount);
        final CountDownLatch releaseThreadsLatch = new CountDownLatch(1);
        final Queue<String> threads = new ConcurrentLinkedQueue<>();
        final List<Exception> exceptions = new ArrayList<>();

        final int readerThreads = doWrites
                ? threadCount / 2
                : threadCount;
        final int writerThreads = readerThreads;

        // load the data
        // pad the keys to a fixed length so they sort in number order
        lmdbEnv.doWithWriteTxn(writeTxn -> {
            IntStream.rangeClosed(1, writerThreads).forEach(i -> {
                database.put(writeTxn, buildKey(i), buildValue(i), false);
            });
        });

        final List<CompletableFuture<Void>> futures = new ArrayList<>();
        IntStream.rangeClosed(1, readerThreads)
                .forEach(i -> {
                    futures.add(createReaderThread(
                            lmdbEnv,
                            database,
                            readersHighWaterMarkTracker,
                            threadsStartedLatch,
                            releaseThreadsLatch,
                            threads,
                            exceptions,
                            i));

                    if (doWrites) {
                        futures.add(createWriterThread(
                                lmdbEnv,
                                database,
                                writersHighWaterMarkTracker,
                                threadsStartedLatch,
                                releaseThreadsLatch,
                                threads,
                                exceptions,
                                i));
                    }
                });

        // wait for all threads to be ready to work
        try {
            threadsStartedLatch.await();

            // Release all threads at once to hit LMDB
            releaseThreadsLatch.countDown();

//            do {
//                LOGGER.info("numReaders: {}, threadsFinishedLatch: {}, threads: {}",
//                        lmdbEnv.info().numReaders,
//                        threadsFinishedLatch.getCount(),
//                        String.join(", ", threads));
//
//            } while (!threadsFinishedLatch.await(50, TimeUnit.MILLISECONDS));
//
//            // Wait for all threads to finish
//            threadsFinishedLatch.await();

            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            LOGGER.info("Exception count: {}", exceptions.size());

            // We don't want any max readers exceptions
            Assertions.assertThat(exceptions)
                    .isEmpty();

            // Can't really test for a min value on the readers to ensure they
            // are working concurrently as this will be run by github actions
            // which only has one core. Useful for manually checking locally though

            LOGGER.info("numReaders: {}", lmdbEnv.info().numReaders);
            // numreaders is a high water mark of max concurrent readers
            Assertions.assertThat(lmdbEnv.info().numReaders)
                    .isLessThanOrEqualTo(expectedNumReadersHighWaterMark);

            LOGGER.info("readersHighWaterMarkTracker: {}", readersHighWaterMarkTracker);
            Assertions.assertThat(readersHighWaterMarkTracker.getHighWaterMark())
                    .isLessThanOrEqualTo(expectedNumReadersHighWaterMark);

            if (doWrites) {
                LOGGER.info("writersHighWaterMarkTracker: {}", writersHighWaterMarkTracker);
                Assertions.assertThat(writersHighWaterMarkTracker.getHighWaterMark())
                        .isEqualTo(1);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
Have multiple threads open an lmdb env and write/read from the db.
More of a manual test to check that many envs don't cause problems.
Have tried it with 1000.
stroom-lmdb/src/test/java/stroom/lmdb/TestLmdbEnv.java