Package org.xtreemfs.babudb

Source Code of org.xtreemfs.babudb.TransactionManagerImpl

/*
* Copyright (c) 2010 - 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;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.xtreemfs.babudb.api.dev.transaction.InMemoryProcessing;
import org.xtreemfs.babudb.api.dev.transaction.OperationInternal;
import org.xtreemfs.babudb.api.dev.transaction.TransactionInternal;
import org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal;
import org.xtreemfs.babudb.api.exception.BabuDBException;
import org.xtreemfs.babudb.api.exception.BabuDBException.ErrorCode;
import org.xtreemfs.babudb.api.transaction.TransactionListener;
import org.xtreemfs.babudb.api.transaction.Operation;
import org.xtreemfs.babudb.log.DiskLogger;
import org.xtreemfs.babudb.log.LogEntry;
import org.xtreemfs.babudb.log.SyncListener;
import org.xtreemfs.babudb.lsmdb.LSN;
import org.xtreemfs.foundation.buffer.BufferPool;
import org.xtreemfs.foundation.buffer.ReusableBuffer;
import org.xtreemfs.foundation.logging.Logging;

/**
* Default implementation of the {@link TransactionManagerInternal} interface using
* the {@link DiskLogger} to let operations become persistent.
*
* @author flangner
* @since 11/03/2010
*/
class TransactionManagerImpl extends TransactionManagerInternal {
   
    private final AtomicReference<DiskLogger> diskLogger = new AtomicReference<DiskLogger>(null);
   
    /**
     * list of transaction listeners
     */
    private final List<TransactionListener>   listeners  = new LinkedList<TransactionListener>();
   
    private volatile LSN                      latestOnDisk;
   
    private final boolean                     isAsync;
   
    public TransactionManagerImpl (boolean isAsync) {
        this.isAsync = isAsync;
    }
   
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#init(
     *          org.xtreemfs.babudb.lsmdb.LSN)
     */
    public void init(LSN initial) {
        latestOnDisk = initial;
    }
   
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#setLogger(
     *          org.xtreemfs.babudb.log.DiskLogger)
     */
    public void setLogger(DiskLogger logger) {
       
        synchronized (diskLogger) {
           
            DiskLogger old = diskLogger.getAndSet(logger);
            if (logger == null) {
                latestOnDisk = old.getLatestLSN();
            } else {
                diskLogger.notify();
            }
        }
    }
     
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#makePersistent(
     *          org.xtreemfs.babudb.api.dev.transaction.TransactionInternal,
     *          org.xtreemfs.foundation.buffer.ReusableBuffer,
     *          org.xtreemfs.babudb.BabuDBRequestResultImpl)
     */
    @Override
    public void makePersistent(final TransactionInternal txn, ReusableBuffer payload,
            BabuDBRequestResultImpl<Object> future) throws BabuDBException {

        Logging.logMessage(Logging.LEVEL_DEBUG, this, "Trying to perform transaction %s ...",
                txn.toString());
       
        try {
                   
            Object[] result = inMemory(txn, payload);
            LogEntry entry = generateLogEntry(txn, payload, future, result);
                       
            onDisk(txn, entry);
                       
            // notify listeners (async)
            if (isAsync) {
                future.finished(result, null);
               
                for (TransactionListener l : listeners) {
                    l.transactionPerformed(txn);
                }
            }
           
        } finally {
            txn.unlockWorkers();
        }
    }
   
    /**
     * Method to create a logEntry for the given transaction.
     *
     * @param txn
     * @param payload
     * @param listener
     *
     * @return a prepared logEntry.
     */
   private final LogEntry generateLogEntry(final TransactionInternal txn,
           ReusableBuffer payload, final BabuDBRequestResultImpl<Object> listener,
           final Object[] results) {
       
        LogEntry result = new LogEntry(payload, new SyncListener() {
           
            @Override
            public void synced(LSN lsn) {
       
                try {
                   
                    if (!isAsync) {
                        BabuDBException irregs = txn.getIrregularities();
                       
                        if (irregs == null) {
                            listener.finished(results, lsn);
                        } else {
                            throw new BabuDBException(irregs.getErrorCode(), "Transaction "
                              + "failed at the execution of the " + (txn.size() + 1)
                                + "th operation, because: " + irregs.getMessage(), irregs);
                        }
                    }
                } catch (BabuDBException error) {
                   
                    if (!isAsync) {
                        listener.failed(error);
                    } else {
                        Logging.logError(Logging.LEVEL_WARN, this, error);
                    }
                } finally {
                   
                    Logging.logMessage(Logging.LEVEL_DEBUG, this, "... transaction %s finished.",
                            txn.toString());
                   
                    // notify listeners (sync)
                    if (!isAsync) {
                        for (TransactionListener l : listeners) {
                            l.transactionPerformed(txn);
                        }
                    }
                }
            }
           
            @Override
            public void failed(Exception ex) {
                if (!isAsync) {
                    listener.failed((ex != null && ex instanceof BabuDBException) ?
                            (BabuDBException) ex : new BabuDBException(
                                    ErrorCode.INTERNAL_ERROR, ex.getMessage()));
                }
            }
        }, LogEntry.PAYLOAD_TYPE_TRANSACTION);
       
        return result;
    }
   
    /**
     * Initialize the on-disk processing. The transaction's log entry is send to the diskLogger.
     *
     * @param txn
     * @param entry
     */
    private final void onDisk(TransactionInternal txn, LogEntry entry) throws BabuDBException {
       
        // append the entry to the DiskLogger if available, wait otherwise
        try {
           
            synchronized (diskLogger) {
               
                while (diskLogger.get() == null) {
                    diskLogger.wait();
                }
               
                diskLogger.get().append(entry);
            }
        } catch (InterruptedException ie) {
           
            if (entry != null) entry.free();
            throw new BabuDBException(ErrorCode.INTERRUPTED, "Operation " +
                        "could not have been stored persistent to disk and " +
                        "will therefore be discarded.", ie.getCause());
        }
    }
   
    /**
     * Internal method to process the in-memory changes on BabuDB for a given transaction.
     *
     * @param txn
     * @throws BabuDBException
     */
    private final Object[] inMemory(TransactionInternal txn, ReusableBuffer payload)
            throws BabuDBException {
       
        List<Object> operationResults = new ArrayList<Object>();
               
        // in memory processing
        for (int i = 0; i < txn.size(); i++) {
            try {
                OperationInternal operation = txn.get(i);
                txn.lockResponsibleWorker(operation.getDatabaseName());
                operationResults.add(
                        inMemoryProcessing.get(operation.getType()).process(operation));
               
            } catch (BabuDBException be) {
               
                // have there already been some successful executions?
                if (i > 0) {
               
                    // trim the transaction
                    txn.cutOfAt(i, be);
                   
                    try {
                        payload.shrink(txn.getSize());
                        break;
                    } catch (IOException ioe) {
                       
                        BufferPool.free(payload);
                        throw new BabuDBException(ErrorCode.IO_ERROR, ioe.getMessage(), ioe);
                    }
                } else {
                   
                    // no operation could have been executed so far
                    BufferPool.free(payload);
                    throw be;
                }
            }
        }
       
        return operationResults.toArray();
    }
   
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#
     *          replayTransaction(org.xtreemfs.babudb.api.dev.transaction.TransactionInternal)
     */
    @Override
    public void replayTransaction(TransactionInternal txn) throws BabuDBException {

        for (OperationInternal operation : txn) {
           
            byte type = operation.getType();
           
            // exclude database create/copy/delete calls from replay
            if (type != Operation.TYPE_COPY_DB &&
                type != Operation.TYPE_CREATE_DB &&
                type != Operation.TYPE_DELETE_DB) {
               
                // get processing logic
                InMemoryProcessing processing = inMemoryProcessing.get(type);
               
                // replay in-memory changes
                try {
                    processing.process(operation);  
                } catch (BabuDBException be) {
                   
                    // there might be false positives if a snapshot to delete has already been
                    // deleted or a snapshot to create has already been created.
                    // also there could be inserts for databases that have been deleted already.
                    if (!(type == Operation.TYPE_CREATE_SNAP && (be.getErrorCode() == ErrorCode.SNAP_EXISTS || be
                            .getErrorCode() == ErrorCode.NO_SUCH_DB))
                     && !(type == Operation.TYPE_DELETE_SNAP &&
                            be.getErrorCode() == ErrorCode.NO_SUCH_SNAPSHOT)
                     && !(type == Operation.TYPE_GROUP_INSERT &&
                            be.getErrorCode().equals(ErrorCode.NO_SUCH_DB))){
                       
                        throw be;
                    }
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#lockService()
     */
    @Override
    public void lockService() throws InterruptedException {
        DiskLogger logger = diskLogger.get();
        if(logger != null) {
            logger.lock();
        }
    }

    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#unlockService()
     */
    @Override
    public void unlockService() {
       
        DiskLogger logger = diskLogger.get();
        if (logger != null && logger.hasLock()) {
            logger.unlock();
        }
    }

    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.transaction.TransactionManagerInternal#getLatestOnDiskLSN()
     */
    @Override
    public LSN getLatestOnDiskLSN() {
        DiskLogger logger = diskLogger.get();
        if (logger != null) {
            return logger.getLatestLSN();
        } else {
            return latestOnDisk;
        }
    }
   
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.TransactionManagerInternal#addTransactionListener(
     *          org.xtreemfs.babudb.api.transaction.TransactionListener)
     */
    @Override
    public void addTransactionListener(TransactionListener listener) {
        if (listener == null) throw new NullPointerException();
       
        listeners.add(listener);
    }
   
    /* (non-Javadoc)
     * @see org.xtreemfs.babudb.api.dev.TransactionManagerInternal#removeTransactionListener(
     *          org.xtreemfs.babudb.api.transaction.TransactionListener)
     */
    @Override
    public void removeTransactionListener(TransactionListener listener) {
        listeners.remove(listener);
    }
}
TOP

Related Classes of org.xtreemfs.babudb.TransactionManagerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.