/*
* 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.
*
*/
package org.chaidb.db.transaction.recover;
import org.apache.log4j.Logger;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.LogRecord;
import org.chaidb.db.log.Lsn;
import org.chaidb.db.log.logrecord.TxnFuzzyCkpLogRecord;
import org.chaidb.db.transaction.TransactionManager;
/**
* This class implements normal transaction recovery.
* It extends from abstract class TransactionRecoverImpl.
*
* @version 1.0
*/
public class NormalTransactionRecoverImpl extends TransactionRecoverImpl {
private static final Logger logger = Logger.getLogger(NormalTransactionRecoverImpl.class);
/**
* Constructor.
*
* @param txnManager The transaction manager is responsible for managing
* the normal transaction recovery implement.
*/
public NormalTransactionRecoverImpl(TransactionManager txnManager) {
super(txnManager);
}
/**
* 1.do undo to smallest_lsn saved in fuzzy checkpoint in V3.1
* 2.do undo to checkpoint in V3.0
* V3.1 should be compatible to V3.0
*/
protected boolean undo() throws ChaiDBException {
this.status = UNDO_STAGE;
Lsn cursorLsn = null;
LogRecord cursorLogRecord = null;
int countCkp = 0; //checkpoint count number
//Gets the lastest lsn in lsn.idb file.
cursorLsn = txnManager.getLogManager().getLastLsnInFlush();
boolean isEnd = false;
Lsn smallestLsn = null;
while (!isEnd) {
try {
cursorLogRecord = txnManager.getLogManager().get(cursorLsn);
} catch (ChaiDBException e) {
logger.error(e);
throw e;
}
/* according to different log type to do different handling */
switch (cursorLogRecord.getType()) {
case LogRecord.LOG_TXN_CHILD:
break;
case LogRecord.LOG_TXN_CHECKPOINT://V3.0 standard checkpoint consistent state
countCkp++;
if (countCkp == 1) {
this.txnManager.setLastCkp(cursorLsn);
} else if (countCkp == 2) {
isEnd = true;
}
break;
case LogRecord.LOG_TXN_FUZZY_CHECKPOINT://V3.1 fuzzy checkpoint
countCkp++;
if (countCkp == 1) {
this.txnManager.setLastCkp(cursorLsn);
} else if (countCkp == 2) {
TxnFuzzyCkpLogRecord fuzzyCkpLog = (TxnFuzzyCkpLogRecord) cursorLogRecord;
/* V3.1, if no active txn while doing fuzzy checkpoint,
smallestLsn is equals to current checkpoint log lsn,if
has active txns,it's euqals to smallest first_lsn of all
active txns. */
smallestLsn = fuzzyCkpLog.getSmallestLsn();
}
break;
default:
//Adding TxnRegopLogRecord to redostack,and do its redo function for release txn resource of btree.
if (cursorLogRecord.getType() == LogRecord.LOG_TXN_REGOP) {
Integer txnid = new Integer(cursorLogRecord.getTxnId());
redoTable.put(txnid, txnid);
}
boolean doUNDO = false;
if (!redoTable.containsKey(new Integer(cursorLogRecord.getTxnId()))) {
doUNDO = true;
}
if (cursorLogRecord.getType() == LogRecord.LOG_DELETE_FILES) {
if (!redoTable.containsKey(new Integer(cursorLogRecord.getTxnId()))) {
Integer txnid = new Integer(cursorLogRecord.getTxnId());
redoTable.put(txnid, txnid);
}
doUNDO = false;
}
if (doUNDO) {
try {
cursorLogRecord.recover(LogRecord.UNDO);
} catch (ChaiDBException ie11) {
logger.error(ie11);
continue;
}
} else { //push into redo stack
redoStack.push(cursorLogRecord);
if (redoStack.size() == STACK_MAXSIZE) {
logger.info("Stack change. " + redoStack.size());
saveStack(cursorLsn);
logger.info(", New=" + redoStack.size() + " Stack" + (++stacks));
}
}
}//switch
if (cursorLsn.getOffset() == LogManager.FIRSTREC_PREVOFFSET || //current lsn is the first lsn of all log files
(smallestLsn != null && cursorLsn.compare(smallestLsn) == 0)//current lsn is equal to smallest lsn
) {
isEnd = true;
}
cursorLsn = new Lsn(cursorLsn.getFileId(), cursorLogRecord.getHeader().getPrevOffset());
}//while
if (countCkp == 0) {
this.txnManager.setLastCkp(new Lsn(LogManager.FIRSTREC_LSN));
}
return true;
}
}