Package org.chaidb.db.transaction

Source Code of org.chaidb.db.transaction.TransactionImpl

/*
* Copyright (C) 2006  http://www.chaidb.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
*/

/* Generated by Together */

package org.chaidb.db.transaction;

import org.apache.log4j.Logger;
import org.chaidb.db.KernelContext;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.FileUtil;
import org.chaidb.db.helper.MailUtil;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.LogRecord;
import org.chaidb.db.log.Lsn;
import org.chaidb.db.log.logrecord.FilesDeleteLogRecord;
import org.chaidb.db.log.logrecord.TxnChildLogRecord;
import org.chaidb.db.log.logrecord.TxnRegopLogRecord;

import java.io.File;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

public class TransactionImpl implements Transaction {

    private static final Logger logger = Logger.getLogger(TransactionImpl.class);

    private static PageBufferManager bpm = PageBufferManager.getInstance();
    /* Unique transaction id. */
    private int txnId;

    /*
     * @supplierCardinality 1
     * @clientCardinality *
     * @supplierRole manager
     */
    private TransactionManager lnkTransactionManager;

    /* Pointer to transaction's parent. */
    private TransactionImpl parent;

    /* Child transactions. */
    private Hashtable childs;

    /* Flags of the transaction */
    private int flags;

    /* Status of the transaction */
    private int status;

    /* Last lsn written for this txn. */
    private Lsn lastLsn;

    /*
     * @associates <{org.chaidb.db.log.Lsn}>
     */
    private Lsn beginLsn;

    /* The kernel context associating with this transaction. */
    private KernelContext kCtx;


    /* container for saving delete collection/index absolute path
       It's used for killing inconsistent issue in DDL operation.
    */
    private Vector deleteFiles = new Vector();

    /**
     * A abortHook to be performed when abort
     */
    private AbortHook abortHook;


    /**
     * Gets the last lsn of this transaction.
     *
     * @return The last lsn of this transaction.
     */
    public Lsn getLastLsn() {
        return lastLsn;
    }

    /**
     * Sets the last lsn of this transaction.
     *
     * @param lastLsn The last lsn of this transaction should be set.
     */
    public void setLastLsn(Lsn lastLsn) {
        this.lastLsn.setFileId(lastLsn.getFileId());
        this.lastLsn.setOffset(lastLsn.getOffset());
    }


    /**
     * Sets the begin lsn of the transaction.
     *
     * @param beginLsn The begin lsn of this transaction should be set.
     */
    public void setBeginLsn(Lsn beginLsn) {
        this.beginLsn.setFileId(beginLsn.getFileId());
        this.beginLsn.setOffset(beginLsn.getOffset());
    }

    /**
     * Gets the begin lsn of this transaction.
     *
     * @return The begin lsn of this transaction.
     */
    public Lsn getBeginLsn() {
        return this.beginLsn;
    }

    /**
     * The constructor.
     *
     * @param txnManager The transaction manager is responsible for managing the transactions.
     */
    public TransactionImpl(TransactionManager txnManager) {
        lnkTransactionManager = txnManager;
        childs = new Hashtable();
        //initialize beginLsn ans lastLsn.
        beginLsn = new Lsn(LogManager.INVALID_LSN_FILE_ID, LogManager.INVALID_LSN_OFFSET);
        lastLsn = new Lsn(LogManager.INVALID_LSN_FILE_ID, LogManager.INVALID_LSN_OFFSET);

        this.flags = 0;
        this.status = 0;

    }


    /**
     * Gets the transaction's parent.
     *
     * @return The transaction's parent.
     */
    public TransactionImpl getParent() {
        return parent;
    }

    /**
     * Sets the transaction's parent.
     *
     * @param parent transaction's parent.
     */
    public void setParent(TransactionImpl parent) {
        this.parent = parent;
    }

    /**
     * Gets the transaction manager
     */
    public TransactionManager getTxnManager() {
        return this.lnkTransactionManager;
    }

    /**
     * Gets the unique transaction id hold by this transaction.
     *
     * @return The unique transaction id.
     */
    public int getTxnId() {
        return this.txnId;
    }

    /**
     * Sets the unique transaction id hold by this transaction.
     *
     * @param txnId The unique transaction id.
     */
    public void setTxnId(int txnId) {
        this.txnId = txnId;

    }


    /**
     * Gets the child transactions belong to the transaction.
     *
     * @return The child transactions belong to the transaction.
     */
    public Hashtable getChilds() {
        return childs;
    }

    /**
     * Adds child transaction to parent transaction, viz. itself.
     *
     * @param child The child transaction.
     */
    public void addChild(Transaction child) {
        this.childs.put(new Integer(child.getTxnId()), child);
    }

    /**
     * Gets the flags of the transaction.
     *
     * @return The flags of the transaction.
     *         The flags parameter can be one of the below values.
     *         Transaction.TXN_SYNC or Transaction.TXN_NOSYNC or Transaction.TXN_NOWAIT
     */
    public int getFlags() {
        return flags;
    }

    /**
     * Set the flags of the transaction.
     *
     * @param flags The flags can be one of the below values
     *              Transaction.TXN_SYNC or Transaction.TXN_NOSYNC or Transaction.TXN_NOWAIT
     */
    public void setFlags(int flags) {
        this.flags |= flags;
    }

    /**
     * Gets the status of the transaction.
     *
     * @return The status of the transaction.
     *         The status of the transaction can be one of the below values
     *         Transaction.TXN_RUNNING or Transaction.TXN_ABORTING or Transaction.TXN_COMMITING
     */
    public int getStatus() {
        return status;
    }


    /**
     * Sets the status of the transaction.
     *
     * @param status The status of the transaction, it can be the following value:
     *               Transaction.TXN_RUNNING or Transaction.TXN_ABORTING or Transaction.TXN_COMMITING
     */
    public void setStatus(int status) {
        this.status = status;
    }

    public void addAbortHook(AbortHook hook) {
        if (this.abortHook != null) {
            hook.setNextHook(this.abortHook);
        }
        this.abortHook = hook;
    }

    public AbortHook getAbortHook() {
        return abortHook;
    }

    /**
     * Abort this transaction.
     */
    public void abort() throws ChaiDBException {
        Enumeration enumChilds;
        TransactionImpl child;
        switch (isTxnValid(TXN_ABORTING)) {
            case 0:
                break;
            case 1:
                throw new ChaiDBException(ErrorCode.IN_RECOVER_PROCESS);
            case 2:
                throw new ChaiDBException(ErrorCode.RECOMMITED_FAILURE);
            case 3:
                throw new ChaiDBException(ErrorCode.REABORTTED_FAILURE);
            case 4:
                break;
            case 5:
                break;
        }

        if (abortHook != null) {
            abortHook.beforeAbort();
        }

        this.status = TXN_ABORTING;

        /* Abort any unresolved children. */
        enumChilds = this.childs.elements();
        while (enumChilds.hasMoreElements()) {
            child = (TransactionImpl) enumChilds.nextElement();
            try {
                child.abort();
            } catch (ChaiDBException e) {
                logger.error(e);
                throw e;
            }
        }
        try {
            undo();
            lnkTransactionManager.getMemLogManager().rollback(txnId);
        } catch (ChaiDBException e) {
            logger.fatal(e);
            String body = "Dear user,Transaction undo failed. The server will shut down. Please reboot the server.";
            MailUtil.notify(body);
            System.exit(-1);
        } catch (Exception e1) {
            logger.fatal(e1);
            String body = "Dear user,Transaction undo failed. The server will shut down. Please reboot the server.";
            MailUtil.notify(body);
            System.exit(-1);
        } finally {
            this.status = Transaction.TXN_RUNNING;
            if (abortHook != null) {
                abortHook.afterAbort();
            }
            //Move it into finally to guarantee to release resource and put locks
            this.endTxn(false);
        }


    }


    /**
     * Commit this transaction.
     */
    public void commit() throws ChaiDBException {
        boolean isCommit = true;
        Transaction child, child1;
        switch (isTxnValid(TXN_COMMITING)) {
            case 0:
                break;
            case 1:
                throw new ChaiDBException(ErrorCode.IN_RECOVER_PROCESS);
            case 2:
                throw new ChaiDBException(ErrorCode.RECOMMITED_FAILURE);
            case 3:
                throw new ChaiDBException(ErrorCode.REABORTTED_FAILURE);
            case 4:
                break;
            case 5:
                break;
        }
        //Set status to committed
        status = TXN_COMMITING;

        /*
         * Commit any unresolved children.  If there's an error, abort any
         * unresolved children and the parent.
         */
        Enumeration childTxns = this.getChilds().elements();
        while (childTxns.hasMoreElements()) {
            child = (Transaction) childTxns.nextElement();
            try {
                child.commit();
            } catch (ChaiDBException txnAbortedEx) {  //If one child txn isn't committed successfully, all childs txns will be aborted.
                logger.error(txnAbortedEx);
                Enumeration childTxns1 = this.getChilds().elements();
                while (childTxns1.hasMoreElements()) {
                    child1 = (Transaction) childTxns1.nextElement();
                    try {
                        child1.abort();
                    } catch (ChaiDBException e) {
                        logger.error(e);
                        // preserving all the previous abort reports -ranjeet
                        ChaiDBException highLevel = new ChaiDBException(ErrorCode.ABORT_FAILURE, txnAbortedEx);
                        throw highLevel;
                    }
                }//end while
                try {
                    this.abort(); // Abort itself parent.
                } catch (ChaiDBException e) {
                    logger.error(e);
                    // preserving all the previous abort reports -ranjeet
                    ChaiDBException highLevel = new ChaiDBException(ErrorCode.ABORT_FAILURE, txnAbortedEx);
                    throw highLevel;
                }
                throw new ChaiDBException(ErrorCode.COMMIT_FAILURE, txnAbortedEx);
            }//end try
        }//end while
        try {
            //At first we precommit the memory log
            lnkTransactionManager.getMemLogManager().preCommit(txnId);

            /*
             * If there are any log records, write a log record and sync the log,
             * else do no log writes.  If the commit is for a child transaction,
             * we do not need to commit the child synchronously since it may still
             * abort (if its parent aborts), and otherwise its parent or ultimate
             * ancestor will write synchronously.
             *
             * I'd rather return a logging error than a flag-wrong error, so if
             * the log routines fail, set "ret" without regard to previous value.
             */
            if (!(lastLsn.getFileId() == 0)) {
                bpm.flushChangedDocRoot(kCtx);
                if (this.parent == null) {
                    int flag = 0;
                    if ((this.flags & Transaction.TXN_NOSYNC) != 0) {
                        flag = Transaction.TXN_NOSYNC;
                    } else if ((this.flags & Transaction.TXN_SYNC) != 0) {
                        flag = Transaction.TXN_SYNC;
                    }

                    //Comment it out. Maybe it is caused long txn.
                    //The commit log record has inserted into log file.
                    //But before it will be removed from active txn list, there
                    //are some activity need to done, include memlogMgr.commit(),
                    //release resources, put locks etc. Once one of activity
                    //throw exception, the txn will never be removed from active txn list.
                    //I will move it into endTxn.

                    //(new TxnRegopLogRecord(TXN_COMMIT,(new Date()).getTime(),this.txnId)).log(flag);
                } else { /* Log the commit in the parent! */

                    (new TxnChildLogRecord(txnId, lastLsn, this.parent.getTxnId())).log(); //pending ?!
                    parent.setFlags(TXN_CHILDCOMMIT);
                }

            } //end if (!(lastLsn.getFileId() == 0))
            lnkTransactionManager.getMemLogManager().commit(txnId);
        } catch (ChaiDBException ie) {
            logger.error(ie);
            throw ie;
        } catch (Exception excp) {
            logger.error(excp);
            throw new ChaiDBException(ErrorCode.TXN_ERROR_BASE, excp);
        } finally {
            status = Transaction.TXN_RUNNING;
        }

        endTxn(isCommit);

    }


    /*
    * Judge whether the transaction is valid.
    * @param op The op parameter MUST be set one of the following values at present.
    * Transaction.TXN_OP_ABORT or Transaction.TXN_OP_COMMIT
    * @return 0 if the transaction is reasonable, otherwise panic.
    * The following return value is available:
    * 0 The transaction is valid, it is available to do other operation.
    * 1 The transaction manager is doing recovery process.
    * 2 The transaction is doing commit process,
    * 3 The transaction is doing abort process.
    *
    */
    private int isTxnValid(int op) {
        int rtn = 0;
        /* Check for recovery. */
        if ((lnkTransactionManager.getFlags() & TransactionManager.TXN_IN_RECOVERY) != 0) rtn = 1;

        /* Check transaction's status. */
        switch (status) {
            case TXN_COMMITING:
                rtn = 2;
                break;
            case TXN_ABORTING:
                rtn = 3;
                break;
            default:
                rtn = 4;
                break;
            case TXN_PREPARED:
                if (op == TXN_OP_PREPARE) {
                    rtn = 5;
                    break;
                }
                break;
            case TXN_RUNNING:
                break;
        }
        return rtn;
    }

    /*
     * Internal transaction end routine called by commit or abort method.
     */
    private void endTxn(boolean isCommit) throws ChaiDBException {

        /* begin: added by marriane 2001-12-20 release all btree resource of this txn */
        bpm.releaseResource(txnId);
        /* end: added by marriane 2001-12-20 release all btree resource of this txn */

        /* Release the locks owned by this transaction. */
        lnkTransactionManager.getLockManager().putAll(txnId);

        /* Updates the commit/abort state in transaction manager. */
        if (isCommit) lnkTransactionManager.setCommittedTxns(lnkTransactionManager.getCommittedTxns() + 1);
        else lnkTransactionManager.setAbortedTxns(lnkTransactionManager.getAbortedTxns() + 1);

        /* Updates the active state in transaction manager. */
        if (lnkTransactionManager.getActiveTxns() > 0) {
            lnkTransactionManager.decActiveTxns();
        }

        /* Remove it from parent. */
        if (parent != null) parent.getChilds().remove(new Integer(txnId));
        //To guarantee add one commit log record before removing txn from active txn list
        if (isCommit) {
            deleteAndLoggingDeletedFiles();
            (new TxnRegopLogRecord(TXN_COMMIT, (new Date()).getTime(), this.txnId)).log(TXN_SYNC);
        }

        /* Remove this transaction from transaction chain in the transaction manager. */
        lnkTransactionManager.getTxnChain().remove(new Integer(txnId));


        purge();

        /* Return the object to free pool. */
        lnkTransactionManager.getFreePool().insert(this);
    }

    private static final Lsn _INVALID_LSN = new Lsn(LogManager.INVALID_LSN_FILE_ID, LogManager.INVALID_LSN_OFFSET);

    /**
     * Undo the transaction.
     */
    private void undo() throws ChaiDBException {
        Lsn keyLsn;
        LogRecord logRecord;

        keyLsn = this.lastLsn;
        if (keyLsn.equals(_INVALID_LSN)) return;
        logRecord = lnkTransactionManager.getLogManager().get(keyLsn);

        /* Allocate a transaction list for children or aborted page creates. */
        for (keyLsn = this.lastLsn; !keyLsn.equals(_INVALID_LSN); keyLsn = logRecord.getPrevLsn()) {
            logRecord = lnkTransactionManager.getLogManager().get(keyLsn);
            try {
                logRecord.recover(LogRecord.UNDO);
            } catch (ChaiDBException ie) {
                logger.error(ie);
                throw new ChaiDBException(ErrorCode.UNDO_FAILURE, ie);
            }

        }//End for
    }

    /*
     * Purge the instance
     */
    void purge() {
        flags = 0;
        status = 0;
        txnId = TransactionManager.TXN_INVALID_ID;
        lastLsn.setFileId(LogManager.INVALID_LSN_FILE_ID);
        lastLsn.setOffset(LogManager.INVALID_LSN_OFFSET);
        beginLsn.setFileId(LogManager.INVALID_LSN_FILE_ID);
        beginLsn.setOffset(LogManager.INVALID_LSN_OFFSET);
        //lnkTransactionManager = null;
        parent = null;
        childs.clear();
        deleteFiles.clear();
        abortHook = null;
    }


    /**
     * Sets the kernel context associating with this transaction.
     *
     * @param kCtx The kernel context associating with this transaction.
     */
    public void setKernelContext(KernelContext kCtx) {
        this.kCtx = kCtx;
    }

    /**
     * Gets the kernel context associating with this transaction.
     *
     * @return The kernel context associating with this transaction.
     */
    public KernelContext getKernelContext() {
        return this.kCtx;
    }


    /**
     * Dumps the status information of the active transaction to String as XML format.
     *
     * @return The status information of the active transaction.
     */
    public String dump() {
        StringBuffer buf = new StringBuffer("<Transaction>");
        buf.append("<TxnId>" + Integer.toHexString(this.txnId) + "(" + txnId + ")" + "</TxnId>");
        String tmpStatus = null;
        switch (this.status) {
            case Transaction.TXN_RUNNING:
                tmpStatus = "TXN_RUNNING";
                break;
            case Transaction.TXN_ABORTING:
                tmpStatus = "TXN_ABORTING";
                break;
            case Transaction.TXN_COMMITING:
                tmpStatus = "TXN_COMMITING";
                break;
            default:
                tmpStatus = "UNKNOWN_STATUS";
        }
        buf.append("<TxnStatus>" + tmpStatus + "</TxnStatus>");
        buf.append("</Transaction>");
        return buf.toString();

    }

    /**
     * If a DDL operation is going to be done, it adds an event of deleting
     * file or directories in transaction impl,and all datalog logging and
     * folder or file deletion would be done during transaction committed.
     *
     * @param filePath
     */
    public void addFileDeleteOperationEvent(String filePath) {
        deleteFiles.add(filePath);
    }

    /**
     * files or folders deleted during DDL transaction committed.
     */
    private void deleteAndLoggingDeletedFiles() throws ChaiDBException {
        int deleteFilesNumber = deleteFiles.size();
        if (deleteFilesNumber <= 0) {
            return;
        }

        FilesDeleteLogRecord logRec = new FilesDeleteLogRecord(deleteFiles, txnId);
        logRec.log();

        for (int i = 0; i < deleteFilesNumber; i++) {
            String filePath = (String) deleteFiles.get(i);
            File deleteFile = new File(filePath);
            if (deleteFile.isFile()) {
                FileUtil.removeFileOrDirectory(deleteFile);
            } else {
                try {
                    bpm.dCloseAllTreesofCollection(deleteFile);
                } catch (Exception e) {
                    logger.error(e);
                } finally {
                    FileUtil.removeFileOrDirectory(deleteFile);
                }
            }
        }
    }

}
TOP

Related Classes of org.chaidb.db.transaction.TransactionImpl

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.