org.mapdb.AsyncWriteEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapdb Show documentation
Show all versions of mapdb Show documentation
MapDB provides concurrent Maps, Sets and Queues backed by disk storage or off-heap memory. It is a fast, scalable and easy to use embedded Java database.
/*
* Copyright (c) 2012 Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapdb;
import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* {@link Engine} wrapper which provides asynchronous serialization and asynchronous write.
* This class takes an object instance, passes it to background writer thread (using Write Cache)
* where it is serialized and written to disk. Async write does not affect commit durability,
* write cache is flushed into disk on each commit. Modified records are held in small instance cache,
* until they are written into disk.
*
* This feature is disabled by default and can be enabled by calling {@link DBMaker#asyncWriteEnable()}.
* Write Cache is flushed in regular intervals or when it becomes full. Flush interval is 100 ms by default and
* can be controlled by {@link DBMaker#asyncWriteFlushDelay(int)}. Increasing this interval may improve performance
* in scenarios where frequently modified items should be cached, typically {@link BTreeMap} import where keys
* are presorted.
*
* Asynchronous write does not affect commit durability. Write Cache is flushed during each commit, rollback and close call.
* You may also flush Write Cache manually by using {@link org.mapdb.AsyncWriteEngine#clearCache()} method.
* There is global lock which prevents record being updated while commit is in progress.
*
* This wrapper starts one threads named `MapDB writer #N` (where N is static counter).
* Async Writer takes modified records from Write Cache and writes them into store.
* It also preallocates new recids, as finding empty `recids` takes time so small stash is pre-allocated.
* It runs as `daemon`, so it does not prevent JVM to exit.
*
* Asynchronous Writes have several advantages (especially for single threaded user). But there are two things
* user should be aware of:
*
* * Because data are serialized on back-ground thread, they need to be thread safe or better immutable.
* When you insert record into MapDB and modify it latter, this modification may happen before item
* was serialized and you may not be sure what version was persisted
*
* * Asynchronous writes have some overhead and introduce single bottle-neck. This usually not issue for
* single or two threads, but in multi-threaded environment it may decrease performance.
* So in truly concurrent environments with many updates (network servers, parallel computing )
* you should disable Asynchronous Writes.
*
*
* @see Engine
* @see EngineWrapper
*
* @author Jan Kotek
*
*
*
*/
public class AsyncWriteEngine extends EngineWrapper implements Engine {
/** ensures thread name is followed by number */
protected static final AtomicLong threadCounter = new AtomicLong();
/** used to signal that object was deleted*/
protected static final Object TOMBSTONE = new Object();
protected final int maxSize;
protected final AtomicInteger size = new AtomicInteger();
protected final long[] newRecids = new long[CC.ASYNC_RECID_PREALLOC_QUEUE_SIZE];
protected int newRecidsPos = 0;
protected final ReentrantLock newRecidsLock = new ReentrantLock(CC.FAIR_LOCKS);
/** Associates `recid` from Write Queue with record data and serializer. */
protected final LongConcurrentHashMap> writeCache
= new LongConcurrentHashMap>();
/** Each insert to Write Queue must hold read lock.
* Commit, rollback and close operations must hold write lock
*/
protected final ReentrantReadWriteLock commitLock = new ReentrantReadWriteLock(CC.FAIR_LOCKS);
/** number of active threads running, used to await thread termination on close */
protected final CountDownLatch activeThreadsCount = new CountDownLatch(1);
/** If background thread fails with exception, it is stored here, and rethrown to all callers.*/
protected volatile Throwable threadFailedException = null;
/** indicates that `close()` was called and background threads are being terminated*/
protected volatile boolean closeInProgress = false;
/** flush Write Queue every N milliseconds */
protected final int asyncFlushDelay;
protected final AtomicReference action = new AtomicReference(null);
/**
* Construct new class and starts background threads.
* User may provide executor in which background tasks will be executed,
* otherwise MapDB starts two daemon threads.
*
* @param engine which stores data.
* @param _asyncFlushDelay flush Write Queue every N milliseconds
* @param executor optional executor to run tasks. If null daemon threads will be created
*/
public AsyncWriteEngine(Engine engine, int _asyncFlushDelay, int queueSize, Executor executor) {
super(engine);
this.asyncFlushDelay = _asyncFlushDelay;
this.maxSize = queueSize;
startThreads(executor);
}
public AsyncWriteEngine(Engine engine) {
this(engine, CC.ASYNC_WRITE_FLUSH_DELAY, CC.ASYNC_WRITE_QUEUE_SIZE, null);
}
protected static final class WriterRunnable implements Runnable{
protected final WeakReference engineRef;
protected final long asyncFlushDelay;
protected final AtomicInteger size;
protected final int maxParkSize;
private final ReentrantReadWriteLock commitLock;
public WriterRunnable(AsyncWriteEngine engine) {
this.engineRef = new WeakReference(engine);
this.asyncFlushDelay = engine.asyncFlushDelay;
this.commitLock = engine.commitLock;
this.size = engine.size;
this.maxParkSize = engine.maxSize/4;
}
@Override public void run() {
try{
//run in loop
for(;;){
//if conditions are right, slow down writes a bit
if(asyncFlushDelay!=0 && !commitLock.isWriteLocked() && size.get()> iter = writeCache.longMapIterator();
while(iter.moveToNext()){
//usual write
final long recid = iter.key();
Fun.Tuple2
© 2015 - 2025 Weber Informatics LLC | Privacy Policy