/* * Copyright (c) 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.api.dev.transaction; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import de.mxro.thrd.babudb05.api.database.DatabaseRequestResult; import de.mxro.thrd.babudb05.api.dev.transaction.OperationInternal; import de.mxro.thrd.babudb05.api.exception.BabuDBException; import de.mxro.thrd.babudb05.api.index.ByteRangeComparator; import de.mxro.thrd.babudb05.api.transaction.Operation; import de.mxro.thrd.babudb05.api.transaction.Transaction; import de.mxro.thrd.babudb05.lsmdb.BabuDBTransaction; import de.mxro.thrd.babudb05.lsmdb.InsertRecordGroup; import de.mxro.thrd.babudb05.lsmdb.LSMDatabase; import de.mxro.thrd.babudb05.snapshots.SnapshotConfig; import de.mxro.thrd.xstreemfs.foundation.buffer.ReusableBuffer; import de.mxro.thrd.xstreemfs.foundation.logging.Logging; /** * Internal interface for BabuDB's lightwight transactions. * * @author flangner * @since 05/06/2011 */ public abstract class TransactionInternal extends LinkedList<OperationInternal> implements Transaction, Iterable<OperationInternal> { private static final long serialVersionUID = 1383031301195486005L; private Map<String, DatabaseRequestResult<AtomicBoolean>> databaseLockFutureMap = null; /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#createSnapshot(java.lang.String, * org.xtreemfs.babudb.snapshots.SnapshotConfig) */ @Override public abstract TransactionInternal createSnapshot(String databaseName, SnapshotConfig config); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#deleteSnapshot(java.lang.String, * java.lang.String) */ @Override public abstract TransactionInternal deleteSnapshot(String databaseName, String snapshotName); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#createDatabase(java.lang.String, int) */ @Override public abstract TransactionInternal createDatabase(String databaseName, int numIndices); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#createDatabase(java.lang.String, int, * org.xtreemfs.babudb.api.index.ByteRangeComparator[]) */ @Override public abstract TransactionInternal createDatabase(String databaseName, int numIndices, ByteRangeComparator[] comparators); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#copyDatabase(java.lang.String, * java.lang.String) */ @Override public abstract TransactionInternal copyDatabase(String sourceName, String destinationName); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#deleteDatabase(java.lang.String) */ @Override public abstract TransactionInternal deleteDatabase(String databaseName); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#insertRecord(java.lang.String, int, * byte[], byte[]) */ @Override public abstract TransactionInternal insertRecord(String databaseName, int indexId, byte[] key, byte[] value); /* (non-Javadoc) * @see org.xtreemfs.babudb.api.transaction.Transaction#deleteRecord(java.lang.String, int, * byte[]) */ @Override public abstract TransactionInternal deleteRecord(String databaseName, int indexId, byte[] key); /* (non-Javadoc) * @see java.util.AbstractCollection#toString() */ @Override public abstract String toString(); /** * There are 3 kinds of database manipulating operations covered by transactions: * <ol> * <li>database create/copy/delete * <li>snapshots * <li>database key-value inserts/deletes * </ol> * These form different level of isolation that have to be respected by the replication for * example. This method aggregates all occurrences of operation to make it possible to * comprehend the level of restriction for this transaction. * * @return an aggregation of all types of operation occurring in this transaction. */ public final byte aggregateOperationTypes() { // initial value, if transaction contains no operation byte result = 0; for (Operation op : this) { result |= 1 << op.getType(); } return result; } /** * @return a set of names of databases affected by this transaction. */ public final Set<String> databasesAffected() { Set<String> result = new HashSet<String>(); for (Operation op : this) { result.add(op.getDatabaseName()); } return result; } /** * Mapping of databases to lock futures for the worker threads being responsible for them. * * @param databaseLockFutureMap - a map of lock futures for database workers affected by this * transaction. */ public final synchronized void updateWorkerLocks( Map<String, DatabaseRequestResult<AtomicBoolean>> databaseLockFutureMap) { this.databaseLockFutureMap = databaseLockFutureMap; } /** * Method to lock the worker responsible for database with databaseName. * * @param databaseName * @throws BabuDBException if the lock could not have been acquired. */ public final synchronized void lockResponsibleWorker(String databaseName) throws BabuDBException { if (databaseLockFutureMap != null) { DatabaseRequestResult<AtomicBoolean> lockFuture = databaseLockFutureMap.get(databaseName); if (lockFuture != null) { lockFuture.get(); } } } /** * Method to unlock the worker threads that have been locked during this transaction. */ public final synchronized void unlockWorkers() { if (databaseLockFutureMap != null) { Set<DatabaseRequestResult<AtomicBoolean>> lockFutures = new HashSet<DatabaseRequestResult<AtomicBoolean>>(databaseLockFutureMap.values()); for (DatabaseRequestResult<AtomicBoolean> lockFuture : lockFutures) { try { final AtomicBoolean workerLock = lockFuture.get(); synchronized (workerLock) { workerLock.set(false); workerLock.notify(); } } catch (BabuDBException be) { Logging.logMessage(Logging.LEVEL_DEBUG, this, "The worker lock could not have" + " been acquired for unlock."); } } } } /** * Counterpart to aggregateOperationTypes. Proves existence of given type. * * @param aggregate * @param type * @return true if the transaction with the aggregate of operation types contains at least one * operation of <code>type</code>, false otherwise. */ public final static boolean containsOperationType(byte aggregate, byte type) { return ((aggregate >>> type) & 1) == 1; } /** * Add a new group insert operation to this transaction. * * @param databaseName - the name of the database. * @param irg - insert group. * * @return the resulting Transaction. */ public abstract TransactionInternal insertRecordGroup(String databaseName, InsertRecordGroup irg); /** * Add a new group insert operation to this transaction. * * @param databaseName - the name of the database. * @param db - where the group has to be inserted. * @param irg - insert group. * * @return the resulting Transaction. */ public abstract TransactionInternal insertRecordGroup(String databaseName, InsertRecordGroup irg, LSMDatabase db); /** * Adds a custom operation to the transaction. * * @param op */ public abstract TransactionInternal addOperation(OperationInternal op); /** * @return the size of this transaction in bytes if serialized. * @throws IOException */ public abstract int getSize() throws IOException; /** * Serializes the transaction to a buffer. * <p> * The following format is used: * <ol> * <li> length of the list of all database names (4 bytes) * <li> all database names (4 bytes for the length + #chars) * <li> length of the list of operations (4 bytes) * <li> all operations (variable size) * </ol> * * Resulting buffer needs to be flip()-ed before usage. * </p> * * @param buffer the buffer * @throws IOException * * @return the buffer. */ public abstract ReusableBuffer serialize(ReusableBuffer buffer) throws IOException; /** * Drops operations from position on. reason for dropping the operations. * * @param position */ public abstract void cutOfAt(int position, BabuDBException reason); /** * @return an exception describing irregularities that occurred during execution, or null, if * this transaction was executed correctly. */ public abstract BabuDBException getIrregularities(); /** * Deserializes the transaction. * * @param buffer * @return the deserialized transaction. * @throws IOException */ public final static TransactionInternal deserialize(ReusableBuffer buffer) throws IOException { BabuDBTransaction txn = new BabuDBTransaction(); // check whether the transaction used to be empty if (buffer.remaining() > 0 || buffer.limit() > 0) { assert(buffer.remaining() > 0); // deserialize the list of database names int length = buffer.getInt(); String[] dbNames = new String[length]; for (int i = 0; i < length; i++) { byte[] bytes = new byte[buffer.getInt()]; buffer.get(bytes); dbNames[i] = new String(bytes); } // deserialize the list of operations length = buffer.getInt(); for (int i = 0; i < length; i++) { txn.addOperation(OperationInternal.deserialize(dbNames, buffer)); } } return txn; } }