/*
* Copyright (c) 2009 - 2011, Jan Stender, Bjoern Kolbeck, Mikael Hoegqvist,
* Felix Hupfeld, Felix Langner, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
package org.xtreemfs.babudb.lsmdb;
import java.io.File;
import java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import org.xtreemfs.babudb.BabuDBRequestResultImpl;
import org.xtreemfs.babudb.api.database.DatabaseInsertGroup;
import org.xtreemfs.babudb.api.database.DatabaseRequestResult;
import org.xtreemfs.babudb.api.database.ResultSet;
import org.xtreemfs.babudb.api.database.UserDefinedLookup;
import org.xtreemfs.babudb.api.dev.BabuDBInternal;
import org.xtreemfs.babudb.api.dev.DatabaseInternal;
import org.xtreemfs.babudb.api.exception.BabuDBException;
import org.xtreemfs.babudb.api.exception.BabuDBException.ErrorCode;
import org.xtreemfs.babudb.api.index.ByteRangeComparator;
import org.xtreemfs.babudb.log.DiskLogger.SyncMode;
import org.xtreemfs.babudb.snapshots.SnapshotConfig;
import org.xtreemfs.foundation.logging.Logging;
public class DatabaseImpl implements DatabaseInternal {
private final BabuDBInternal dbs;
private LSMDatabase lsmDB;
/*
* constructors/destructors
*/
/**
* Creates a new Database.
*
* @param dbs
* @param lsmDB - the underlying LSM database.
*/
public DatabaseImpl(BabuDBInternal dbs, LSMDatabase lsmDB) {
this.dbs = dbs;
this.lsmDB = lsmDB;
}
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.lsmdb.DatabaseRO#shutdown()
*/
@Override
public void shutdown() throws BabuDBException {
try {
for (int index = 0; index < lsmDB.getIndexCount(); index++)
lsmDB.getIndex(index).destroy();
} catch (IOException exc) {
throw new BabuDBException(ErrorCode.IO_ERROR, exc.getMessage(), exc);
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.Database#createInsertGroup()
*/
@Override
public BabuDBInsertGroup createInsertGroup() {
return new BabuDBInsertGroup(lsmDB);
}
/*
* DB modification operations
*/
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.lsmdb.Database#singleInsert(int, byte[], byte[],
* java.lang.Object)
*/
@Override
public DatabaseRequestResult<Object> singleInsert(int indexId, byte[] key,
byte[] value, Object context) {
BabuDBInsertGroup irg = new BabuDBInsertGroup(lsmDB);
irg.addInsert(indexId, key, value);
return insert(irg, context);
}
/*
* (non-Javadoc)
*
* @seeorg.xtreemfs.babudb.lsmdb.Database#insert(org.xtreemfs.babudb.lsmdb.
* BabuDBInsertGroup, java.lang.Object)
*/
@Override
public DatabaseRequestResult<Object> insert(BabuDBInsertGroup irg, Object context) {
InsertRecordGroup ins = irg.getRecord();
int dbId = ins.getDatabaseId();
LSMDBWorker w = dbs.getWorker(dbId);
if (w != null) {
if (Logging.isDebug()) {
Logging.logMessage(Logging.LEVEL_DEBUG, this, "insert request"
+ " is sent to worker #" + dbId % dbs.getWorkerCount());
}
BabuDBRequestResultImpl<Object> result =
new BabuDBRequestResultImpl<Object>(context, dbs.getResponseManager());
try {
w.addRequest(new LSMDBRequest<Object>(lsmDB, result, ins));
} catch (InterruptedException ex) {
result.failed(new BabuDBException(ErrorCode.INTERRUPTED,
"operation was interrupted", ex));
}
return result;
} else {
return directInsert(irg, context);
}
}
/**
* Insert an group of inserts in the context of the invoking thread.
* Proper insertion is not guaranteed, since the result of the attempt to
* make the insert persistent is ignored, if {@link SyncMode} is ASYNC.
*
* @param irg - the group of inserts.
* @param context - the context object for this request.
*
* @return the request future.
*/
private DatabaseRequestResult<Object> directInsert(BabuDBInsertGroup irg, Object context) {
BabuDBRequestResultImpl<Object> result =
new BabuDBRequestResultImpl<Object>(context, dbs.getResponseManager());
try {
dbs.getTransactionManager().makePersistent(
dbs.getDatabaseManager().createTransaction().insertRecordGroup(
getName(), irg.getRecord(), getLSMDB()), result);
} catch (BabuDBException e) {
// if an exception occurred while writing the log, respond with an
// error message
result.failed(e);
}
return result;
}
/*
* DB lookup operations
*/
/* (non-Javadoc)
*
* @see org.xtreemfs.babudb.lsmdb.DatabaseRO#lookup(int, byte[],
* java.lang.Object)
*/
@Override
public DatabaseRequestResult<byte[]> lookup(int indexId, byte[] key,
Object context) {
BabuDBRequestResultImpl<byte[]> result =
new BabuDBRequestResultImpl<byte[]>(context, dbs.getResponseManager());
LSMDBWorker w = dbs.getWorker(lsmDB.getDatabaseId());
if (w != null) {
if (Logging.isDebug()) {
Logging.logMessage(Logging.LEVEL_DEBUG, this, "lookup request"
+ " is sent to worker #"
+ lsmDB.getDatabaseId() % dbs.getWorkerCount());
}
try {
w.addRequest(new LSMDBRequest<byte[]>(lsmDB, indexId, result,
key));
} catch (InterruptedException ex) {
result.failed(new BabuDBException(ErrorCode.INTERRUPTED,
"operation was interrupted", ex));
}
} else
directLookup(indexId, key, result);
return result;
}
/**
* Looks up a key in the database, without using a worker thread.
*
* @param indexId
* @param key
* @param listener
* the result listener.
*/
private void directLookup(int indexId, byte[] key,
BabuDBRequestResultImpl<byte[]> listener) {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0)) {
listener.failed(new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist"));
} else
listener.finished(lsmDB.getIndex(indexId).lookup(key));
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.DatabaseRO#prefixLookup(int, byte[], java.lang.Object)
*/
@Override
public DatabaseRequestResult<ResultSet<byte[], byte[]>> prefixLookup(
int indexId, byte[] key, Object context) {
return prefixLookup(indexId, key, context, true);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.DatabaseRO#reversePrefixLookup(int, byte[], java.lang.Object)
*/
@Override
public DatabaseRequestResult<ResultSet<byte[], byte[]>>
reversePrefixLookup(int indexId, byte[] key, Object context) {
return prefixLookup(indexId, key, context, false);
}
/**
* Performs a prefix lookup.
*
* @param indexId
* @param key
* @param context
* @param ascending
* @return the request result object.
*/
private DatabaseRequestResult<ResultSet<byte[], byte[]>> prefixLookup(
int indexId, byte[] key, Object context, boolean ascending) {
final BabuDBRequestResultImpl<ResultSet<byte[], byte[]>> result =
new BabuDBRequestResultImpl<ResultSet<byte[], byte[]>>(context,
dbs.getResponseManager());
// if there are worker threads, delegate the prefix lookup to the
// responsible worker thread
LSMDBWorker w = dbs.getWorker(lsmDB.getDatabaseId());
if (w != null) {
if (Logging.isDebug() && w != null) {
Logging.logMessage(Logging.LEVEL_DEBUG, this, "lookup request"
+ " is sent to worker #"
+ lsmDB.getDatabaseId() % dbs.getWorkerCount());
}
try {
w.addRequest(new LSMDBRequest<ResultSet<byte[], byte[]>>(
lsmDB, indexId, result, key, ascending));
} catch (InterruptedException ex) {
result.failed(new BabuDBException(ErrorCode.INTERRUPTED,
"operation was interrupted", ex));
}
}
// otherwise, perform a direct prefix lookup
else {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0))
result.failed(new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist"));
else
result.finished(lsmDB.getIndex(indexId).prefixLookup(key,
ascending));
}
return result;
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.DatabaseRO#rangeLookup(int, byte[], byte[],
* java.lang.Object)
*/
@Override
public DatabaseRequestResult<ResultSet<byte[], byte[]>> rangeLookup(
int indexId, byte[] from, byte[] to, Object context) {
return rangeLookup(indexId, from, to, context, true);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.DatabaseRO#reverseRangeLookup(int, byte[], byte[],
* java.lang.Object)
*/
@Override
public DatabaseRequestResult<ResultSet<byte[], byte[]>>
reverseRangeLookup(int indexId, byte[] from, byte[] to,
Object context) {
return rangeLookup(indexId, from, to, context, false);
}
/**
* Performs a range lookup.
*
* @param indexId
* @param from
* @param to
* @param context
* @param ascending
* @return the request result object.
*/
private DatabaseRequestResult<ResultSet<byte[], byte[]>> rangeLookup(
int indexId, byte[] from, byte[] to, Object context,
boolean ascending) {
final BabuDBRequestResultImpl<ResultSet<byte[], byte[]>> result =
new BabuDBRequestResultImpl<ResultSet<byte[], byte[]>>(context,
dbs.getResponseManager());
// if there are worker threads, delegate the range lookup to the
// responsible worker thread
LSMDBWorker w = dbs.getWorker(lsmDB.getDatabaseId());
if (w != null) {
if (Logging.isDebug() && w != null) {
Logging.logMessage(Logging.LEVEL_DEBUG, this, "lookup request"
+ " is sent to worker #"
+ lsmDB.getDatabaseId() % dbs.getWorkerCount());
}
try {
w.addRequest(new LSMDBRequest<ResultSet<byte[], byte[]>>(
lsmDB, indexId, result, from, to, ascending));
} catch (InterruptedException ex) {
result.failed(new BabuDBException(ErrorCode.INTERRUPTED,
"operation was interrupted", ex));
}
}
// otherwise, perform a direct range lookup
else {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0))
result.failed(new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist"));
else
result.finished(lsmDB.getIndex(indexId).rangeLookup(from, to,
ascending));
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* org.xtreemfs.babudb.lsmdb.DatabaseRO#userDefinedLookup(org.xtreemfs.babudb
* .UserDefinedLookup, java.lang.Object)
*/
@Override
public DatabaseRequestResult<Object> userDefinedLookup(
UserDefinedLookup udl, Object context) {
final BabuDBRequestResultImpl<Object> result =
new BabuDBRequestResultImpl<Object>(context, dbs.getResponseManager());
LSMDBWorker w = dbs.getWorker(lsmDB.getDatabaseId());
if (w != null) {
if (Logging.isNotice()) {
Logging.logMessage(Logging.LEVEL_NOTICE, this, "udl request is"
+ " sent to worker #"
+ lsmDB.getDatabaseId() % dbs.getWorkerCount());
}
try {
w.addRequest(new LSMDBRequest<Object>(lsmDB, result, udl));
} catch (InterruptedException ex) {
result.failed(new BabuDBException(ErrorCode.INTERRUPTED,
"operation was interrupted", ex));
}
} else
directUserDefinedLookup(udl, result);
return result;
}
/**
* Performs a user-defined lookup, without using a worker thread.
*
* @param udl
* @param listener
*/
private void directUserDefinedLookup(UserDefinedLookup udl,
BabuDBRequestResultImpl<Object> listener) {
final LSMLookupInterface lif = new LSMLookupInterface(lsmDB);
try {
Object result = udl.execute(lif);
listener.finished(result);
} catch (BabuDBException e) {
listener.failed(e);
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#directLookup(int, int, byte[])
*/
@Override
public byte[] directLookup(int indexId, int snapId, byte[] key)
throws BabuDBException {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0)) {
throw new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist");
}
return lsmDB.getIndex(indexId).lookup(key, snapId);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#directPrefixLookup(int, int, byte[],
* boolean)
*/
@Override
public ResultSet<byte[], byte[]> directPrefixLookup(int indexId,
int snapId, byte[] key, boolean ascending) throws BabuDBException {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0)) {
throw new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist");
}
return lsmDB.getIndex(indexId).prefixLookup(key, snapId, ascending);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#directRangeLookup(int, int, byte[], byte[],
* boolean)
*/
@Override
public ResultSet<byte[], byte[]> directRangeLookup(int indexId,
int snapId, byte[] from, byte[] to, boolean ascending)
throws BabuDBException {
if ((indexId >= lsmDB.getIndexCount()) || (indexId < 0)) {
throw new BabuDBException(ErrorCode.NO_SUCH_INDEX,
"index does not exist");
}
return lsmDB.getIndex(indexId).rangeLookup(from, to, snapId, ascending);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#proceedSnapshot(java.lang.String)
*/
@Override
public void proceedSnapshot(String destDB) throws BabuDBException, InterruptedException {
int[] ids;
try {
// critical block...
dbs.getTransactionManager().lockService();
ids = lsmDB.createSnapshot();
} finally {
dbs.getTransactionManager().unlockService();
}
File dbDir = new File(dbs.getConfig().getBaseDir() + destDB);
if (!dbDir.exists()) {
dbDir.mkdirs();
}
try {
LSN lsn = lsmDB.getOndiskLSN();
lsmDB.writeSnapshot(dbs.getConfig().getBaseDir() + destDB +
File.separatorChar, ids, lsn.getViewId(),
lsn.getSequenceNo());
} catch (IOException ex) {
throw new BabuDBException(ErrorCode.IO_ERROR,
"cannot write snapshot: " + ex, ex.getCause());
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#proceedCreateSnapshot()
*/
@Override
public int[] proceedCreateSnapshot() {
return lsmDB.createSnapshot();
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#proceedWriteSnapshot(int[],
* java.lang.String, org.xtreemfs.babudb.snapshots.SnapshotConfig)
*/
@Override
public void proceedWriteSnapshot(int[] snapIds, String directory, SnapshotConfig cfg)
throws BabuDBException {
try {
lsmDB.writeSnapshot(directory, snapIds, cfg);
} catch (IOException ex) {
throw new BabuDBException(ErrorCode.IO_ERROR, "cannot write snapshot: " + ex, ex);
}
}
/**
* Writes the snapshots to disk.
*
* @param viewId
* current viewId (i.e. of the last write)
* @param sequenceNo
* current sequenceNo (i.e. of the last write)
* @param snapIds
* the snapshot Ids (obtained via createSnapshot).
* @throws BabuDBException
* if a snapshot cannot be written
*/
public void writeSnapshot(int viewId, long sequenceNo, int[] snapIds) throws BabuDBException {
proceedWriteSnapshot(viewId, sequenceNo, snapIds);
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#proceedWriteSnapshot(int, long, int[])
*/
@Override
public void proceedWriteSnapshot(int viewId, long sequenceNo, int[] snapIds)
throws BabuDBException {
try {
lsmDB.writeSnapshot(viewId, sequenceNo, snapIds);
} catch (IOException ex) {
throw new BabuDBException(ErrorCode.IO_ERROR, "cannot write snapshot: " + ex, ex);
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#proceedCleanupSnapshot(int, long)
*/
@Override
public void proceedCleanupSnapshot(final int viewId, final long sequenceNo)
throws BabuDBException {
try {
lsmDB.cleanupSnapshot(viewId, sequenceNo);
} catch (ClosedByInterruptException ex) {
Logging.logError(Logging.LEVEL_DEBUG, this, ex);
} catch (IOException ex) {
throw new BabuDBException(ErrorCode.IO_ERROR, "cannot clean up: " + ex, ex);
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#dumpSnapshot(java.lang.String)
*/
@Override
public void dumpSnapshot(String baseDir) throws BabuDBException {
// destination directory of this
baseDir = baseDir.endsWith(File.separator) ? baseDir : baseDir + File.separator;
String destDir = baseDir + lsmDB.getDatabaseName();
try {
int ids[] = lsmDB.createSnapshot();
LSN lsn = lsmDB.getOndiskLSN();
lsmDB.writeSnapshot(destDir, ids, lsn.getViewId(), lsn.getSequenceNo());
} catch (IOException ex) {
throw new BabuDBException(ErrorCode.IO_ERROR, "cannot write snapshot: " + ex, ex);
}
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#setLSMDB(
* org.xtreemfs.babudb.lsmdb.LSMDatabase)
*/
@Override
public void setLSMDB(LSMDatabase lsmDatabase) {
this.lsmDB = lsmDatabase;
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.dev.DatabaseInternal#getLSMDB()
*/
@Override
public LSMDatabase getLSMDB() {
return lsmDB;
}
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.lsmdb.Database#getComparators()
*/
@Override
public ByteRangeComparator[] getComparators() {
return lsmDB.getComparators();
}
/*
* (non-Javadoc)
*
* @see org.xtreemfs.babudb.lsmdb.Database#getName()
*/
@Override
public String getName() {
return lsmDB.getDatabaseName();
}
/* (non-Javadoc)
* @see org.xtreemfs.babudb.api.database.Database#insert(org.xtreemfs.babudb.api.database.DatabaseInsertGroup, java.lang.Object)
*/
@Override
public DatabaseRequestResult<Object> insert(DatabaseInsertGroup irg, Object context) {
return insert((BabuDBInsertGroup) irg, context);
}
}