All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mapdb.Engine Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 2.0-beta12
Show newest version
/*
 *  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.io.Closeable;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 

* Centerpiece for record management, {@code Engine} is simple key value store. * Engine is low-level interface and is not meant to be used directly * by user. For most operations user should use {@link org.mapdb.DB} class. *

* * In this store key is primitive {@code long} number, typically pointer to index table. * Value is class instance. To turn value into/from binary form serializer is * required as extra argument for most operations. *

* * Unlike other DBs MapDB does not expect user to (de)serialize data before * they are passed as arguments. Instead MapDB controls (de)serialization itself. * This gives DB a lot of flexibility: for example instances may be held in * cache to minimise number of deserializations, or modified instance can * be placed into queue and asynchronously written on background thread. *

* * There is {@link Store} subinterface for raw persistence *

* * In default configuration MapDB runs with this {@code Engine} stack: *

* *
    *
  1. DISK - raw file or memory *
  2. {@link org.mapdb.StoreWAL} - permanent record store with transactions *
  3. USER - {@link DB} and collections *
* *

* TODO Engine Wrappers are sort of obsole, update this whole section *

* * Engine uses {@code recid} to identify records. There is zero error handling in case recid is invalid * (random number or already deleted record). Passing illegal recid may result into anything * (return null, throw EOF or even corrupt store). Engine is considered low-level component * and it is responsibility of upper layers (collections) to ensure recid is consistent. * Lack of error handling is trade of for speed (similar way as manual memory management in C++) *

* * Engine must support {@code null} record values. You may insert, update and fetch null records. * Nulls play important role in recid preallocation and asynchronous writes. *

* Recid can be reused after it was deleted. If your application relies on unique being unique, * you should update record with null value, instead of delete. * Null record consumes only 8 bytes in store and is preserved during defragmentation. *

* @author Jan Kotek */ public interface Engine extends Closeable { /** *

* Content of this map is manipulated by {@link org.mapdb.DB} class. *

* There are 8 reserved record ids. They store information relevant to * {@link org.mapdb.DB} and higher level functions. Those are preallocated when store is created. */ long RECID_NAME_CATALOG = 1; /** *

* Points to class catalog. A list of classes used in {@link org.mapdb.SerializerPojo} * to serialize java objects. *

* There are 8 reserved record ids. They store information relevant to * {@link org.mapdb.DB} and higher level functions. Those are preallocated when store is created. */ long RECID_CLASS_CATALOG = 2; /** *

* Recid used for 'record check'. This record is loaded when store is open, * to ensure configuration such as encryption and compression is correctly set and \ * data are read-able. *

* There are 8 reserved record ids. They store information relevant to * {@link org.mapdb.DB} and higher level functions. Those are preallocated when store is created. *

*/ long RECID_RECORD_CHECK = 3; /** *

* There are 8 reserved record ids. They store information relevant to * {@link org.mapdb.DB} and higher level functions. Those are preallocated when store is created. *

* This value is last reserved record id. User ids (recids returned by {@link Engine#put(Object, Serializer)}) * starts from {@code RECID_LAST_RESERVED+1} *

*/ long RECID_LAST_RESERVED = 7; /** *

* There are 8 reserved record ids. They store information relevant to * {@link org.mapdb.DB} and higher level functions. Those are preallocated when store is created. *

* This constant is first recid available to user. It is first value returned by {@link #put(Object, Serializer)} if store is empty. *

*/ long RECID_FIRST = RECID_LAST_RESERVED+1; /** * Preallocates recid for not yet created record. It does not insert any data into it. * @return new recid */ //TODO in some cases recid is persisted and used between compaction. perhaps use put(null). Much latter: in what cases? I do not recall any. //TODO clarify difference between put/update(null) and delete/preallocate long preallocate(); /** * Insert new record. * * @param value records to be added * @param serializer used to convert record into/from binary form * @return recid (record identifier) under which record is stored. * @throws java.lang.NullPointerException if serializer is null */ long put(A value, Serializer serializer); /** *

* Get existing record. *

* * Recid must be a number returned by 'put' method. * Behaviour for invalid recid (random number or already deleted record) * is not defined, typically it returns null or throws 'EndOfFileException' *

* * @param recid (record identifier) under which record was persisted * @param serializer used to deserialize record from binary form * @return record matching given recid, or null if record is not found under given recid. * @throws java.lang.NullPointerException if serializer is null */
A get(long recid, Serializer serializer); /** *

* Update existing record with new value. *

* Recid must be a number returned by 'put' method. * Behaviour for invalid recid (random number or already deleted record) * is not defined, typically it throws 'EndOfFileException', * but it may also corrupt store. *

* * @param recid (record identifier) under which record was persisted. * @param value new record value to be stored * @param serializer used to serialize record into binary form * @throws java.lang.NullPointerException if serializer is null */
void update(long recid, A value, Serializer serializer); /** *

* Updates existing record in atomic (Compare And Swap) manner. * Value is modified only if old value matches expected value. There are three ways to match values, MapDB may use any of them: *

*
    *
  1. Equality check oldValue==expectedOldValue when old value is found in instance cache
  2. *
  3. Deserializing oldValue using serializer and checking oldValue.equals(expectedOldValue)
  4. *
  5. Serializing expectedOldValue using serializer and comparing binary array with already serialized oldValue *
*

* Recid must be a number returned by 'put' method. * Behaviour for invalid recid (random number or already deleted record) * is not defined, typically it throws 'EndOfFileException', * but it may also corrupt store. *

* * @param recid (record identifier) under which record was persisted. * @param expectedOldValue old value to be compared with existing record * @param newValue to be written if values are matching * @param serializer used to serialize record into binary form * @return true if values matched and newValue was written * @throws java.lang.NullPointerException if serializer is null */ boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer); /** *

* Remove existing record from store/cache *

* * Recid must be a number returned by 'put' method. * Behaviour for invalid recid (random number or already deleted record) * is not defined, typically it throws 'EndOfFileException', * but it may also corrupt store. *

* * @param recid (record identifier) under which was record persisted * @param serializer which may be used in some circumstances to deserialize and store old object * @throws java.lang.NullPointerException if serializer is null */
void delete(long recid, Serializer serializer); /** *

* Close store/cache. This method must be called before JVM exits to flush all caches and prevent store corruption. * Also it releases resources used by MapDB (disk, memory..). *

* * Engine can no longer be used after this method was called. If Engine is used after closing, it may * throw any exception including NullPointerException *

* * There is an configuration option {@link DBMaker.Maker#closeOnJvmShutdown()} which uses shutdown hook to automatically * close Engine when JVM shutdowns. *

*/ void close(); /** * Checks whether Engine was closed. * * @return true if engine was closed */ public boolean isClosed(); /** * Makes all changes made since the previous commit/rollback permanent. * In transactional mode (on by default) it means creating journal file and replaying it to storage. * In other modes it may flush disk caches or do nothing at all (check your config options) */ void commit(); /** * Undoes all changes made in the current transaction. * If transactions are disabled it throws {@link UnsupportedOperationException}. * * @throws UnsupportedOperationException if transactions are disabled */ void rollback() throws UnsupportedOperationException; /** * Check if you can write into this Engine. It may be readonly in some cases (snapshot, read-only files). * * @return true if engine is read-only */ boolean isReadOnly(); /** @return true if engine supports rollback*/ boolean canRollback(); /** @return true if engine can create read-only snapshots*/ boolean canSnapshot(); /** * Returns read-only snapshot of data in Engine. * * @throws UnsupportedOperationException if snapshots are not supported/enabled */ Engine snapshot() throws UnsupportedOperationException; /** if this is wrapper return underlying engine, or null */ Engine getWrappedEngine(); /** clears any underlying cache */ void clearCache(); void compact(); abstract class ReadOnly implements Engine{ @Override public long preallocate() { throw new UnsupportedOperationException("Read-only"); } @Override public
boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer) { throw new UnsupportedOperationException("Read-only"); } @Override public long put(A value, Serializer serializer) { throw new UnsupportedOperationException("Read-only"); } @Override public void commit() { throw new UnsupportedOperationException("Read-only"); } @Override public void rollback() { throw new UnsupportedOperationException("Read-only"); } @Override public boolean isReadOnly() { return true; } @Override public void update(long recid, A value, Serializer serializer) { throw new UnsupportedOperationException("Read-only"); } @Override public void delete(long recid, Serializer serializer){ throw new UnsupportedOperationException("Read-only"); } @Override public void compact() { throw new UnsupportedOperationException("Read-only"); } } /** * Wraps an Engine and throws * UnsupportedOperationException("Read-only") * on any modification attempt. */ final class ReadOnlyWrapper extends ReadOnly{ protected final Engine engine; public ReadOnlyWrapper(Engine engine){ this.engine = engine; } @Override public A get(long recid, Serializer serializer) { return engine.get(recid, serializer); } @Override public void close() { engine.close(); } @Override public boolean isClosed() { return engine.isClosed(); } @Override public boolean canRollback() { return engine.canRollback(); } @Override public Engine getWrappedEngine() { return engine; } @Override public void clearCache() { engine.clearCache(); } @Override public boolean canSnapshot() { return engine.canSnapshot(); } @Override public Engine snapshot() throws UnsupportedOperationException { return engine.snapshot(); } } /** * Closes Engine on JVM shutdown using shutdown hook: {@link Runtime#addShutdownHook(Thread)} * If engine was closed by user before JVM shutdown, hook is removed to save memory. */ class CloseOnJVMShutdown implements Engine{ final protected AtomicBoolean shutdownHappened = new AtomicBoolean(false); final Runnable hookRunnable = new Runnable() { @Override public void run() { shutdownHappened.set(true); CloseOnJVMShutdown.this.hook = null; if(CloseOnJVMShutdown.this.isClosed()) return; CloseOnJVMShutdown.this.close(); } }; protected final Engine engine; protected Thread hook; public CloseOnJVMShutdown(Engine engine) { this.engine = engine; hook = new Thread(hookRunnable,"MapDB shutdown hook"); Runtime.getRuntime().addShutdownHook(hook); } @Override public long preallocate() { return engine.preallocate(); } @Override public long put(A value, Serializer serializer) { return engine.put(value,serializer); } @Override public A get(long recid, Serializer serializer) { return engine.get(recid,serializer); } @Override public void update(long recid, A value, Serializer serializer) { engine.update(recid,value,serializer); } @Override public boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer) { return engine.compareAndSwap(recid,expectedOldValue,newValue,serializer); } @Override public void delete(long recid, Serializer serializer) { engine.delete(recid,serializer); } @Override public void close() { engine.close(); if(!shutdownHappened.get() && hook!=null){ Runtime.getRuntime().removeShutdownHook(hook); } hook = null; } @Override public boolean isClosed() { return engine.isClosed(); } @Override public void commit() { engine.commit(); } @Override public void rollback() throws UnsupportedOperationException { engine.rollback(); } @Override public boolean isReadOnly() { return engine.isReadOnly(); } @Override public boolean canRollback() { return engine.canRollback(); } @Override public boolean canSnapshot() { return engine.canSnapshot(); } @Override public Engine snapshot() throws UnsupportedOperationException { return engine.snapshot(); } @Override public Engine getWrappedEngine() { return engine; } @Override public void clearCache() { engine.clearCache(); } @Override public void compact() { engine.compact(); } } /** throws {@code IllegalAccessError("already closed")} on all access */ Engine CLOSED_ENGINE = new Engine(){ @Override public long preallocate() { throw new IllegalAccessError("already closed"); } @Override public long put(A value, Serializer serializer) { throw new IllegalAccessError("already closed"); } @Override public A get(long recid, Serializer serializer) { throw new IllegalAccessError("already closed"); } @Override public void update(long recid, A value, Serializer serializer) { throw new IllegalAccessError("already closed"); } @Override public boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer) { throw new IllegalAccessError("already closed"); } @Override public void delete(long recid, Serializer serializer) { throw new IllegalAccessError("already closed"); } @Override public void close() { throw new IllegalAccessError("already closed"); } @Override public boolean isClosed() { return true; } @Override public void commit() { throw new IllegalAccessError("already closed"); } @Override public void rollback() throws UnsupportedOperationException { throw new IllegalAccessError("already closed"); } @Override public boolean isReadOnly() { throw new IllegalAccessError("already closed"); } @Override public boolean canRollback() { throw new IllegalAccessError("already closed"); } @Override public boolean canSnapshot() { throw new IllegalAccessError("already closed"); } @Override public Engine snapshot() throws UnsupportedOperationException { throw new IllegalAccessError("already closed"); } @Override public Engine getWrappedEngine() { throw new IllegalAccessError("already closed"); } @Override public void clearCache() { throw new IllegalAccessError("already closed"); } @Override public void compact() { throw new IllegalAccessError("already closed"); } }; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy