Package org.chaidb.db.transaction

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

/*
* 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.Db;
import org.chaidb.db.DbEnvironment;
import org.chaidb.db.DBState;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.Config;
import org.chaidb.db.helper.DaemonThreadManager;
import org.chaidb.db.helper.Queue;
import org.chaidb.db.lock.LockManager;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.Lsn;
import org.chaidb.db.log.MemLogManager;
import org.chaidb.db.transaction.recover.CatastrophicTxnRecoverImpl;
import org.chaidb.db.transaction.recover.NormalTransactionRecoverImpl;
import org.chaidb.db.transaction.recover.TransactionRecover;

import java.util.Enumeration;
import java.util.Hashtable;

public class TransactionManagerImpl implements TransactionManager {

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

    /*
     * List of active transactions.
     */
    private Hashtable txnChain;

    /*
     * Free transaction pool.
     */
    private Queue freePool;

    /* Singleton instance.  */
    private static TransactionManagerImpl txnManagerImpl = null;


    /**
     * The maximum number of transactions possible.
     * Conflict with maxnActiveTxns ?!
     */
    private int maxTxns;

    /*
     * The flags of the transaction manager.
     */
    private static int flags;

    private Checkpoint ckpObj;
    /*
     * The time of the last completed checkpoint finished.
     */
    private long timeCkp;


    /*
     * The lock manager related to the transaction manager.
     */
    protected LockManager lockManager;

    /* The log manager related to the transaction manager. */
    protected LogManager logManager;

    /* The memory log manager related to the transaction manager. */
    protected MemLogManager memLogManager;


    /* The maximum number of active transactions at any one time. */
    private int maxnActiveTxns;

    /*
     * The last transaction ID allocated.
     */
    private int lastTxnId;

    /*
     * The Lsn of the last checkpoint.*/
    private Lsn lastCkp;


    /* The number of active TXNs */
    volatile int nActiveTxns;


    /* The number of begun TXNs */
    private int nBegunTxns;

    /* The number of committed TXNs */
    private int nCommittedTxns;

    /* number of restored TXNs */
    //private int nRestoredTxns;

    /* The number of aborted TXNs */
    private int nAbortedTxns;

    private TransactionRecover txnRecover;

    //static Object checkpointOjb = new Object();

    /* file separator */
    private static final int CKP_DEFAULT_KBYTES = 1000;

    private static final int CKP_DEFAULT_MINS = 5;

    private static final int CKP_KBYTES = Config.getConfig("checkpoint.kbytes", CKP_DEFAULT_KBYTES);

    private static final int CKP_MINS = Config.getConfig("checkpoint.mins", CKP_DEFAULT_MINS);


    /**
     * Private Constructor.
     */
    private TransactionManagerImpl() {
        init(DEF_MAX_TXNS);

    }

    /**
     * Singleton method.
     *
     * @return The unique TransactionManagerImpl instance.
     */
    public static TransactionManager getInstance() {
        if (txnManagerImpl == null) {
            txnManagerImpl = new TransactionManagerImpl(); //only callable from within
            return txnManagerImpl;
        } else return txnManagerImpl;
        //throw new ChaiDBException("no further instances");
    }


    /**
     * Gets the free transaction pool.
     *
     * @return The free transaction pool.
     */
    public Queue getFreePool() {
        return freePool;
    }

    /**
     * Gets the maximum number of transactions.
     *
     * @return The maximum number of transactions.
     */
    public int getMaxTxns() {
        return maxTxns;
    }

    /**
     * Sets the maximum number of transactions.
     *
     * @param maxTxns The maximum number of transactions.
     */
    public void setMaxTxn(int maxTxns) {
        this.maxTxns = maxTxns;
    }

    /**
     * Begin a transaction, return a transaction object.
     *
     * @param parent The parent transaction. If the parent argument is non-null,
     *               the new transaction will be a nested transaction, with the transaction
     *               indicated by parent as its parent. Transactions may be nested to any level.
     * @param flags  The flags parameter must be set to 0 or one of the following
     *               values:
     *               TXN_NOSYNC
     *               Do not synchronously flush the log when this transaction commits or
     *               prepares. This means the transaction will exhibit the ACI (atomicity,
     *               consistency and isolation) properties, but not D (durability), i.e.,
     *               database integrity will be maintained but it is possible that this
     *               transaction may be undone during recovery instead of being redone.
     *               TXN_NOWAIT
     *               If a lock is unavailable for any operation performed in the context
     *               of this transaction, return immediately instead of blocking on the
     *               lock. The exception throw in the case will be LockNotGrantedException.
     *               TXN_SYNC
     *               Synchronously flush the log when this transaction commits or prepares.
     *               This means the transaction will exhibit all of the ACID (atomicity,
     *               consistency and isolation and durability) properties.
     * @return A transaction object
     */
    public synchronized Transaction beginTxn(Transaction parent, int flags) throws ChaiDBException {
        TransactionImpl txn = null;
        //int ilockerId;
        //Lock lock = null;
        //Judge whether system are doing checkpoint.
        //ilockerId = lockManager.id();
        //lock = lockManager.get(ilockerId, lockManager.LOCK_WAITING, lockManager.OBJECT_OBJECT, checkpointOjb, lockManager.LOCK_READ);
        //lockManager.put(lock);

        if (DbEnvironment.READ_ONLY) throw new ChaiDBException(ErrorCode.DATABASE_IS_READONLY);

        //I don't know whether the logic is right ? Check it when release.
        if (flagCheck(flags, DB_TXN_NOWAIT | DB_TXN_NOSYNC | DB_TXN_SYNC))
            throw new ChaiDBException(ErrorCode.ILLEGAL_FLAG_BEGIN_TXN);
        if (flagCombinationCheck(flags, DB_TXN_NOSYNC, DB_TXN_SYNC) == false)
            throw new ChaiDBException(ErrorCode.ILLEGAL_COMBINATION_FLAG_BEGIN_TXN);
        //if ( txnChain.size() >= maxTxns)
        //    throw new ChaiDBException(ErrorCode.MAXIMUM_ACTIVE_TXNS, "The number of transaction is reach maximum.");
        if (freePool.isEmpty()) {   //Free transaction pool is empty.
            txn = new TransactionImpl(this)//Allocate memory from heap.
        } else {
            txn = (TransactionImpl) freePool.remove(); //Removes an element from the pool.
        }
        /* Sets the flags of transaction. */
        if ((flags & Transaction.TXN_DIRTY_READ) != 0) txn.setFlags(Transaction.TXN_DIRTY_READ);
        if ((flags & Transaction.TXN_NOSYNC) != 0) txn.setFlags(Transaction.TXN_NOSYNC);
        if ((flags & Transaction.TXN_SYNC) != 0) txn.setFlags(Transaction.TXN_SYNC);
        if ((flags & Transaction.TXN_NOWAIT) != 0) txn.setFlags(Transaction.TXN_NOWAIT);

        /*
         * We do not have to write begin records (and if we do not, then we
         * need never write records for read-only transactions).  However,
         * we do need to find the current LSN so that we can store it in the
         * transaction structure, so we can know where to take checkpoints.
* Note: Here Zhu Liang will put the value into the beginLsn field of the begun transaction instance.
         */

        /* Environment is being recovered by recover routine. Note: <this.flags> */
        if ((TransactionManagerImpl.flags & TXN_IN_RECOVERY) != 0) {
            throw new ChaiDBException(ErrorCode.IN_RECOVER_PROCESS);
        }

        /* Make sure that we aren't still recovering prepared transactions. Using only during XA recovery.*/
        //if (nRestoredTxns != 0){
        //  throw new ChaiDBException("txn_begin: recovery of prepared but not yet committed transactions is incomplete.");
        //}

        if (this.lastTxnId == TXN_INVALID_ID)
            //throw new ChaiDBException("Transaction ID wrapped.  Exit the database environment.\n and restart the application as if application failure had occurred");
            this.lastTxnId = TXN_MINIMUM;

        //Pending I don't know if cast to TransactionImpl ?!
        txn.setParent((TransactionImpl) parent);
        txn.setTxnId(lastTxnId++);
        saveEnvironment();
        txn.setStatus(Transaction.TXN_RUNNING);

        /* This statistic information set back 'zero' when system restart.*/
        nBegunTxns++;

        ++nActiveTxns;
        if (nActiveTxns > maxnActiveTxns) maxnActiveTxns = nActiveTxns;
        /* Initialize last LSN to 'zero'. */
        txn.getLastLsn().setFileId(LogManager.INVALID_LSN_FILE_ID);
        txn.getLastLsn().setOffset(LogManager.INVALID_LSN_OFFSET);

        /*
         * If this is a transaction family, we must link the child to the
         * maximal grandparent in the lock table for deadlock detection.
         * pending!?
         */
        //if (txn.getParent() != null)
        //    if (lockManager.lock_addfamilyLocker(txn.getParent().getTxnId(),txn.getTxnId()) == false)
        //Pending?! I don't know if infect the system when return false, and I don't know it is necessary to throw exception.

        if (txn != null && txn.getParent() != null) txn.getParent().getChilds().put(new Integer(txn.getTxnId()), txn);

        /* Place transaction on active transaction list. */
        txnChain.put(new Integer(txn.getTxnId()), txn);

        return txn;


    }

    /**
     * General flags checking routine.
     */
    private boolean flagCheck(int flags, int ok_flags) {
        if ((flags & (~ok_flags)) != 0) return false;
        else return true;
    }

    /**
     * General combination flags checking routine.
     */
    private boolean flagCombinationCheck(int flags, int flag1, int flag2) {
        if ((flags & flag1) != 0 && (flags & flag2) != 0) return false;
        else return true; //not conflict
    }

    /**
     * Active TXN list.
     *
     * @return The active transaction hashtable.
     */
    public Hashtable getTxnChain() {
        return this.txnChain;
    }


    /**
     * Gets the number of aborted TXNs.
     *
     * @return The number of aborted TXNs.
     */
    public int getAbortedTxns() {
        return nAbortedTxns;
    }

    /**
     * Set the number of aborted TXNs.
     *
     * @param abortedTxns The new number of aborted TXNs.
     */
    public void setAbortedTxns(int abortedTxns) {
        nAbortedTxns = abortedTxns;
    }


    /**
     * Gets the number of committed TXNs.
     *
     * @return The number of committed TXNs.
     */
    public int getCommittedTxns() {
        return nCommittedTxns;
    }

    /**
     * Set the number of committed TXNs.
     *
     * @param committedTxns The new number of committed TXNs.
     */
    public void setCommittedTxns(int committedTxns) {
        nCommittedTxns = committedTxns;
    }


    /**
     * Gets the number of begun TXNs.
     *
     * @return The number of begun TXNs.
     */
    public int getBegunTxns() {
        return nBegunTxns;
    }

    /**
     * Gets the number of active TXNs.
     *
     * @return The number of active TXNs.
     */
    public int getActiveTxns() {
        return nActiveTxns;

    }


    /*
     * Gets the lock manager related to the transaction manager.
     */
    public LockManager getLockManager() {
        return this.lockManager;
    }


    /**
     * Checkpoint the transaction subsystem - flushes the underlying memory pool,
     * writes a checkpoint record to the log and then flushes the log.
     * <p>If either kbyte or min is non-zero, the checkpoint is only done if there
     * has been activity since the last checkpoint and either more than min
     * minutes have passed since the last checkpoint, or if more than kbyte
     * kilobytes of log data have been written since the last checkpoint.
     *
     * @param kbytes
     * @param mins
     * @param flags  The flags parameter must be set to 0 or one of the following
     *               values:
     *               DB_FORCE
     *               Force a checkpoint record even if there has been no activity since
     *               the last checkpoint.
     */
    public void checkpoint(int kbytes, int mins, int flags) throws ChaiDBException {
        CheckpointThread ckpThread = (CheckpointThread) DaemonThreadManager.getInstance().getThread(CheckpointThread.CHECKPOINT_THREADNAME);
        if (ckpThread == null) {
            logger.info("Begin to start checkpoint thread.");
            ckpThread = new CheckpointThread(this, kbytes, mins, flags);
            ckpThread.start();
        } else {
            if (ckpThread.isAlive()) {
                ckpThread.setKbytes(kbytes);
                ckpThread.setMins(mins);
                ckpThread.setFlags(flags);
            }
        }
    }

    /**
     * Just for test!?
     */
    public boolean isCheckpointAlive() {
        CheckpointThread ckpThread = (CheckpointThread) DaemonThreadManager.getInstance().getThread(CheckpointThread.CHECKPOINT_THREADNAME);
        if (ckpThread != null) {
            if (ckpThread.isAlive()) return true;
            else return false;
        } else return false;
    }


    /**
     * Initialize routine.
     */
    void init(int maxTxns) {

        this.maxTxns = maxTxns;

        //this.lastTxnId = TXN_MINIMUM;
        loadEnvironment();

        txnChain = new Hashtable(maxTxns);
        freePool = new Queue();

        /* Initialize lastCkp Lsn to 'zero'. */
        lastCkp = new Lsn(LogManager.FIRSTREC_LSN);


        timeCkp = 0;
        nAbortedTxns = 0;
        nCommittedTxns = 0;
        nBegunTxns = 0;
        nActiveTxns = 0;

        maxnActiveTxns = 0;

        lockManager = Db.getLockManager();
        logManager = Db.getLogManager();
        memLogManager = Db.getMemLogManager();
        startupCkp();

    }

    public void startupCkp() {
        if (Config.TXN_SUPPORT) {
            if (CKP_KBYTES != 0 || CKP_MINS != 0) {
                try {
                    this.checkpoint(CKP_KBYTES, CKP_MINS, DB_PERIOD);
                } catch (ChaiDBException e) {
                    logger.error(e);
                }
            }
        }
    }

    public Transaction getActiveTxn(int txnId) {
        return (Transaction) txnChain.get(new Integer(txnId));
    }

    public TransactionImpl getTxn(int txnId) {
        return (TransactionImpl) txnChain.get(new Integer(txnId));
    }


    public LogManager getLogManager() {
        return this.logManager;
    }

    public MemLogManager getMemLogManager() {
        return this.memLogManager;
    }

    public synchronized int getFlags() {
        return flags;
    }

    public synchronized void setFlags(int argFlags) {
        flags |= argFlags;
    }

    public synchronized void clearFlags(int flags) {
        TransactionManagerImpl.flags &= ~(flags);
    }

    public boolean isOnRecovery() {
        if ((flags & TXN_IN_RECOVERY) != 0) {
            return true;
        } else return false;
    }


    /**
     * Set transaction abort recover function
     *
     * @param type the type parameter can be one of the following values
     *             TransactionRecover.CATASTROPHIC_RECOVER or TransactionRecover.NORMAL_RECOVER
     * @return The reference of TransactionRecover instance implements TransactionRecover interface.
     */
    public TransactionRecover setRecover(int type) {
        if (txnRecover == null) {
            if (type == TransactionRecover.NORMAL_RECOVER) {
                txnRecover = new NormalTransactionRecoverImpl(this);
            } else if (type == TransactionRecover.CATASTROPHIC_RECOVER) {
                txnRecover = new CatastrophicTxnRecoverImpl(this);
            }
        }
        return txnRecover;
    }

    /**
     * Gets the Lsn of the last checkpoint.
     *
     * @return The Lsn of the last checkpoint.
     */
    public Lsn getLastCkp() {
        return new Lsn(this.lastCkp);
    }

    /**
     * Sets the Lsn of the last checkpoint.
     *
     * @param lastCkp The Lsn of the last checkpoint.
     */
    public void setLastCkp(Lsn lastCkp) {
        this.lastCkp.setFileId(lastCkp.getFileId());
        this.lastCkp.setOffset(lastCkp.getOffset());
        try {
            DBState.getInstance().setLatestCheckPoint(lastCkp);
        } catch (Exception e) {
            logger.error(e);
        }
    }

    /**
     * Gets the last transaction ID allocated.
     *
     * @return The last txn id allocated.
     */
    public int getLastTxnId() {
        return this.lastTxnId;
    }

    /**
     * Allocates the last transaction ID and saves it to persistent storage.
     *
     * @param lastTxnId The last txn id.
     */
    public void setLastTxnId(int lastTxnId) {
        this.lastTxnId = lastTxnId;
        try {
            saveEnvironment();
        } catch (ChaiDBException e) {
            logger.error(e);
        }
    }

    /**
     * Sets the time of the last completed checkpoint finished.
     *
     * @param timeCkp The time of the last completed checkpoint finished.
     */
    public void setTimeCkp(long timeCkp) {
        this.timeCkp = timeCkp;
    }

    /**
     * Gets the time of the last completed checkpoint finished.
     *
     * @return The time of the last completed checkpoint finished.
     */
    public long getTimeCkp() {
        return this.timeCkp;
    }

    /*
     * Load environment.
     */
    private synchronized void loadEnvironment() {
//        byte[] stateValue = new byte[4];
        try {
            lastTxnId = DBState.getInstance().getLatestTxnId();
        } catch (Exception e) {
            lastTxnId = TXN_MINIMUM;
        }

    }

    /**
     * Close the checkpoint thread.
     */
    public void closeCheckpoint() {
        CheckpointThread ckpThread = (CheckpointThread) DaemonThreadManager.getInstance().getThread(CheckpointThread.CHECKPOINT_THREADNAME);
        if (ckpThread != null) {
            ckpThread.setFinish();
            while (isCheckpointAlive()) {

            }
            ckpThread = null;
            logger.info("Checkpoint thread is closed.");
        }

    }


    public void doCheckpoint() throws ChaiDBException {
        doCheckpoint(false);
    }

    public void doCheckpoint(boolean sync) throws ChaiDBException {
        if (Config.TXN_SUPPORT) {
            closeCheckpoint();
            if (ckpObj == null) {
                ckpObj = new Checkpoint(this, TransactionManager.DB_FORCE);
            }
            try {
                logger.info("Invoking a checkpoint manually.");
                ckpObj.doCheckpoint(sync);
            } catch (ChaiDBException e) {
                logger.error(e);
                throw e;
            } finally {
                DaemonThreadManager.getInstance().shutdown(CheckpointThread.CHECKPOINT_THREADNAME);
            }
        }
    }

    /*
     * Save environment.
     */
    private synchronized void saveEnvironment() throws ChaiDBException {
        try {
            DBState.getInstance().setLatestTxnId(lastTxnId);
        } catch (Exception e) {
            logger.error(e);
            throw new ChaiDBException(ErrorCode.TXN_ENV_SAVE_FAILED, e.toString());
        }

    }

    /**
     * dump buffer information to String as XML format
     *
     * @return String
     */
    public String dump() {
        StringBuffer buf = new StringBuffer("<TransactionManagerDump>");
        buf.append("<ActiveTxns>" + this.nActiveTxns + "</ActiveTxns>");
        buf.append("<LastCheckpoint>" + "<FileId>" + this.lastCkp.getFileId() + "</FileId>" + "<Offset>" + this.lastCkp.getOffset() + "</Offset></LastCheckpoint>");
        buf.append("<LastCheckpointTime>" + this.timeCkp + "</LastCheckpointTime>");
        buf.append("<LastTxnId>" + Integer.toHexString(this.lastTxnId) + "(" + lastTxnId + ")" + "</LastTxnId>");
        Enumeration txns = txnChain.elements();
        if (txns == null) {
            Hashtable tmp = new Hashtable();
            txns = tmp.keys();
        }
        while (txns.hasMoreElements()) {
            TransactionImpl txnImpl = (TransactionImpl) txns.nextElement();
            if (txnImpl != null) buf.append(txnImpl.dump());
        }
        buf.append("</TransactionManagerDump>");
        return buf.toString();
    }

    /**
     * get smallest Lsn of all active txn begin_lsn
     */
    public Lsn getSmallestLsnOfAllActiveTxn() {
        if ((txnChain == null) || (txnChain.isEmpty())) return null;
        Lsn tempLsn = null;
        Enumeration txnEnum = txnChain.elements();
        Lsn txnBeginLsn = null;
        TransactionImpl tempTxn;
        while (txnEnum.hasMoreElements()) {
            tempTxn = (TransactionImpl) txnEnum.nextElement();
            txnBeginLsn = tempTxn.getBeginLsn();

            /* modified by marriane 2002-6-18 ignore transactions which just
                began and no log records during these txns,so its beginLsn is
                equals to (-1,-1). */
            if (txnBeginLsn.compare(new Lsn(LogManager.INVALID_LSN_FILE_ID, LogManager.INVALID_LSN_OFFSET)) == 0) {
                continue;
            }

            /*modified by marriane 2002-6-20,new Lsn, for not using the beginLsn
                reference,because it'll be set to (-1,-1) while txn end. Or it
                will cause recover couldn't find log file because the log file
                has been moved by archive,but the second-to-last checkpoint's
                smallest lsn is (-1,-1),and it leads to recover doing to
                (-1,-1)*/
            if (tempLsn == null) {
                tempLsn = new Lsn(txnBeginLsn);
            } else if (tempLsn.compare(txnBeginLsn) > 0) {
                /* tempLsn is larger than txnBeginLsn */
                tempLsn = new Lsn(txnBeginLsn);//new Lsn
            }
        }
        return tempLsn;
    }

    public void decActiveTxns() {
        if (nActiveTxns > 0) nActiveTxns--;
    }

}
TOP

Related Classes of org.chaidb.db.transaction.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.