/*
* 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.Db;
import org.chaidb.db.DBState;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.helper.Config;
import org.chaidb.db.log.*;
import org.chaidb.db.log.logrecord.TxnCkpLogRecord;
import org.chaidb.db.transaction.TransactionManager;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.Date;
/**
* This class implements catastrophic transaction recovery.
* It extends from abstract class TransactionRecoverImpl.
*
* @version 1.0
*/
public class CatastrophicTxnRecoverImpl extends TransactionRecoverImpl {
private static final Logger logger = Logger.getLogger(CatastrophicTxnRecoverImpl.class);
protected final static String ARCHIVE_DIR = "archive.dir";
protected final static String BACKUP_DIR = "backup.dir";
private Lsn smallestLsn = null;
/**
* The length of logic lsn.
*/
private int logic_lsn_length = -1;
/**
* The average length of logic lsn(1/5).
*/
private int logic_lsn_average = -1;
/**
* The current lsn at which operate undo.
*/
private Lsn cursorLsn = null;
/**
* The last lsn from which start to undo.
*/
private Lsn lastLsn = null;
private Timestamp timestamp = null;
private Lsn backupLastLsn = null;
private Lsn pitCheckPoint = null;
public Lsn getPitCheckPoint() {
return pitCheckPoint;
}
public void setBackupLastLsn(Lsn backupLastLsn) {
this.backupLastLsn = backupLastLsn;
}
public void setTimestamp(Timestamp timestamp) {
this.timestamp = timestamp;
}
/**
* Constructor.
*
* @param txnManager The transaction manager
*/
public CatastrophicTxnRecoverImpl(TransactionManager txnManager) {
super(txnManager);
}
/**
* Calculate the logic length between preLsn and nextLsn.
*
* @param prevLsn The previous lsn.
* @param nextLsn The next lsn.
* @return The logic length.
*/
private int calLogicLength(Lsn prevLsn, Lsn nextLsn) {
if (nextLsn.getFileId() == prevLsn.getFileId()) {
return LogManagerImpl.LOG_FILE_MAX_SIZE * (nextLsn.getFileId() - prevLsn.getFileId()) + (nextLsn.getOffset() - prevLsn.getOffset());
} else {
return LogManagerImpl.LOG_FILE_MAX_SIZE * (nextLsn.getFileId() - prevLsn.getFileId()) + (LogManagerImpl.LOG_FILE_MAX_SIZE - prevLsn.getOffset());
}
}
/*
* Undo operation.
* @exception ChaiDBException Exception while doing undo operation.
*/
protected boolean undo() throws ChaiDBException {
this.status = UNDO_STAGE;
LogRecord cursorLogRecord = null;
////////////////////////////// UNDO OPERATION /////////////////////////////////
//Gets the lastest lsn in lsn.idb file.
/* Begin to get last lsn from LogManager*/
cursorLsn = txnManager.getLogManager().getLastLsnInFlush();
lastLsn = new Lsn(cursorLsn.getFileId(), cursorLsn.getOffset()); //Keep last lsn.
/* End to get last lsn from LogManager*/
//it can be changed to do incompleted recovery.
logic_lsn_length = calLogicLength(smallestLsn, cursorLsn); //calculate the logic length need do recovery.
logic_lsn_average = logic_lsn_length / 5;
int currentProcess = 0;
int percent = 0;
boolean scan = false;
if (timestamp != null && backupLastLsn != null) {
scan = true;
}
Timestamp rtime = new Timestamp(0);
boolean setCp = false;
// for (int i=0; cursorLsn.getOffset() != LogManager.FIRSTREC_PREVOFFSET ;i++)
while ((cursorLsn.compare(smallestLsn) >= 0) && (cursorLsn.getOffset() != LogManager.FIRSTREC_PREVOFFSET)) {
if (scan && cursorLsn.compare(backupLastLsn) < 0) {
System.err.println("\nCan't find checkpoint from the given timestamp. Please try to \n" + "perform PIT recovery again and input a later timestamp or restore \n" + "to certain state of the backup.");
return false;
}
percent = calLogicLength(cursorLsn, lastLsn) / logic_lsn_average;
if (percent != currentProcess) {
currentProcess++;
if (currentProcess <= 5) {
System.out.print("\r" + currentProcess + "0% completed.");
}
}
try {
cursorLogRecord = txnManager.getLogManager().get(cursorLsn, smallestLsn);
} catch (ChaiDBException e) {
logger.error(e);
throw e;
}
switch (cursorLogRecord.getType()) {
case LogRecord.LOG_TXN_CHILD:
break;
case LogRecord.LOG_TXN_CHECKPOINT:
// break;
case LogRecord.LOG_TXN_FUZZY_CHECKPOINT://V3.1 fuzzy checkpoint
if (scan) {
// if (cursorLsn.compare(backupLastLsn) < 0) {
// System.err.println(
// "\nCan't find checkpoint from the given timestamp. Please try to \n" +
// "restore again and input another timestamp or skip PIT recovery.");
// return false;
// }
rtime.setTime(((TxnCkpLogRecord) cursorLogRecord).getTimeStamp());
//there are 3 conditions which we locate the checkpoint
//1. checkpoint is before the timestamp
//2. checkpoint is after backupLastLsn
//3. checkpoint is after the timestamp but diff is less than half
// of checkpoint inteval.
if (rtime.after(timestamp) && cursorLsn.compare(backupLastLsn) > 0) {
long dt = (rtime.getTime() - timestamp.getTime()) / 1000;
if (dt < 0) {
dt = -dt;
}
int chkmins = Config.getConfig("checkpoint.mins", 5);
if (dt > (chkmins * 60 / 2)) {
break;
}
}
pitCheckPoint = cursorLsn;
recoverToCheckpoint(cursorLogRecord);
scan = false;
}
if (!setCp) {
txnManager.setLastCkp(cursorLsn);
setCp = true;
}
break;
default:
if (scan == false) {
//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 ie111) {
logger.error(ie111.getMessage());
continue;
}
} else { //push into redo stack
redoStack.push(cursorLogRecord);
redo_length++;
if (redoStack.size() == STACK_MAXSIZE) {
logger.info("Stack change. " + redoStack.size());
saveStack(cursorLsn);
logger.info(", New=" + redoStack.size() + " Stack" + (++stacks));
}
}
}
}
cursorLsn = new Lsn(cursorLsn.getFileId(), cursorLogRecord.getHeader().getPrevOffset());
}///End for
if (currentProcess < 5) {
for (int i = currentProcess + 1; i <= 5; i++) {
System.out.print("\r" + i + "0% completed.");
}
}
redo_average = redo_length / 5;
/* commented by marriane 2002-1-9 couldn't close btree because it need
to do redo and release resource later.*/
/* try{
releaseBtrees();
}catch(ChaiDBException e){
throw e;
}
*/
return true;
}
private void recoverToCheckpoint(LogRecord cursorLogRecord) throws ChaiDBException {
DateFormat df = DateFormat.getDateTimeInstance();
Date date = new Date(((TxnCkpLogRecord) cursorLogRecord).getTimeStamp());
System.err.println("Found a checkpoint at: " + df.format(date));
int offset = cursorLsn.getOffset() + cursorLogRecord.getHeader().getLength();
int fileId = cursorLsn.getFileId();
if (offset >= LogManagerImpl.LOG_FILE_MAX_SIZE) {
fileId++;
offset = 0;
}
LogManager logManager = Db.getLogManager();
try {
// modify chaidb.state
logManager.closeLogFile();
// truncate log file
DefaultLogFile.getInstance().appendLastLsnToEnd(fileId, offset, cursorLsn);
} catch (ChaiDBException e) {
logger.error(e);
}
logManager.setCheckpointValueFromDisk();
logManager.setCurLsn(new Lsn(fileId, offset), cursorLsn.getOffset());
logManager.setLastLsnInFlush(cursorLsn);
logManager.setLastCheckpointLsn(cursorLsn);
txnManager.setLastCkp(cursorLsn);
try {
DBState.getInstance().setLatestLogFileId((short) fileId);
} catch (Exception e) {
logger.error(e);
}
}
/**
* set the ending point of undo, i.e. the starting point of redo.
* added by haman lee in 2002-4-4
*/
public void setSmallestLsn(Lsn lsn) {
smallestLsn = lsn;
}
}