/* * 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 de.mxro.thrd.babudb05.lsmdb; import java.io.File; import java.io.IOException; import java.nio.channels.ClosedByInterruptException; import de.mxro.thrd.babudb05.BabuDBRequestResultImpl; import de.mxro.thrd.babudb05.api.database.DatabaseInsertGroup; import de.mxro.thrd.babudb05.api.database.DatabaseRequestResult; import de.mxro.thrd.babudb05.api.database.ResultSet; import de.mxro.thrd.babudb05.api.database.UserDefinedLookup; import de.mxro.thrd.babudb05.api.dev.BabuDBInternal; import de.mxro.thrd.babudb05.api.dev.DatabaseInternal; import de.mxro.thrd.babudb05.api.exception.BabuDBException; import de.mxro.thrd.babudb05.api.exception.BabuDBException.ErrorCode; import de.mxro.thrd.babudb05.api.index.ByteRangeComparator; import de.mxro.thrd.babudb05.log.DiskLogger.SyncMode; import de.mxro.thrd.babudb05.snapshots.SnapshotConfig; import de.mxro.thrd.xstreemfs.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. */ protected 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 */ protected 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); } }