package com.neocoretechs.bigsack.io;
import java.io.IOException;
import com.neocoretechs.arieslogger.core.LogInstance;
import com.neocoretechs.arieslogger.core.impl.FileLogger;
import com.neocoretechs.arieslogger.core.impl.LogToFile;
import com.neocoretechs.bigsack.DBPhysicalConstants;
import com.neocoretechs.bigsack.io.pooled.BlockAccessIndex;
import com.neocoretechs.bigsack.io.pooled.GlobalDBIO;
import com.neocoretechs.bigsack.io.pooled.ObjectDBIO;
/*
* Copyright (c) 1997,2002,2003,2014 NeoCoreTechs
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* Neither the name of NeoCoreTechs nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* This class is the bridge to the recovery log subsystem which is based on the ARIES protocol.
* LogToFile is the main ARIES subsystem class used, with FileLogger being the higher level construct.
* We keep arrays of FileLogger, LogToFile, etc by tablespace and multiplex the calls to the proper tablespace
* This module is where the recovery logs are initialized because the logs operate at the block (database page) level.
* When this module is instantiated the RecoveryLogManager is assigned and a roll forward recovery
* is started. If there are any records in the log file they will scanned for low water marks and
* checkpoints etc and the determination is made based on the type of log record encountered.
* Our log granularity is the page level. We store DB blocks and their original mirrors to use in
* recovery. At the end of recovery we restore the logs to their initial state, as we do on a commit.
* There is a simple paradigm at work here, we carry a single block access index in this class and use it
* to cursor through the blocks as we access them.
* @author Groff
*/
public final class RecoveryLogManager {
private static boolean DEBUG = false;
private ObjectDBIO blockIO;
private FileLogger fl = null;
private LogToFile ltf = null;
private LogInstance firstTrans = null;
private BlockAccessIndex tblk = null;
private int tablespace;
public ObjectDBIO getBlockIO() {
return blockIO;
}
/**
* Call with IO manager, will create the LogToFile
* @param tglobalio
* @throws IOException
*/
public RecoveryLogManager(ObjectDBIO tglobalio, int tablespace) throws IOException {
blockIO = tglobalio;
this.tablespace = tablespace;
tblk = new BlockAccessIndex(true); // for writeLog reserved
ltf = new LogToFile(blockIO, tablespace);
fl = (FileLogger) ltf.getLogger();
ltf.boot();
}
/**
* Close the random access files and buffers for recovery log. Call stop in LogToFile
* Closes logAccessfile and calls deleteObsoleteLogFiles IF NO CORRUPTION
* @throws IOException
*/
public synchronized void stop( ) throws IOException {
if( DEBUG )
System.out.println("RecoveryLogManager.stop invoked. tablespace"+tablespace);
ltf.stop();
if( DEBUG )
System.out.println("RecoveryLogManager.stop terminated.");
}
public synchronized void stop(int tblsp) throws IOException {
if( DEBUG )
System.out.println("RecoveryLog.stop invoked for tablespace "+tblsp);
ltf.stop();
}
public LogToFile getLogToFile() {
return ltf;
}
/**
* Write log entry - uses current db. Set inlog true
* This is initiated before buffer pool block flush (writeblk). Get the original block
* from deep store and log it as undoable
* @param blk The block instance, payload of block about to be written to log
* @exception IOException if cannot open or write
*/
public synchronized void writeLog(BlockAccessIndex blk) throws IOException {
if( DEBUG ) {
System.out.println("RecoveryLogManager.writeLog "+blk.toString());
}
tblk.setBlockNumber(blk.getBlockNum());
assert( tablespace == GlobalDBIO.getTablespace(blk.getBlockNum()));
// Write directly to deep store at this point.
//synchronized(blockIO.getIOManager().getDirectIO(tablespace)) {
// blockIO.getIOManager().getDirectIO(tablespace).Fseek(GlobalDBIO.getBlock(blk.getBlockNum()));
// tblk.getBlk().read(blockIO.getIOManager().getDirectIO(tablespace));
//}
blockIO.getIOManager().readDirect(tablespace, GlobalDBIO.getBlock(blk.getBlockNum()), tblk.getBlk());
UndoableBlock undoBlk = new UndoableBlock(tblk, blk);
if( firstTrans == null )
firstTrans = fl.logAndDo(blockIO, undoBlk);
else
fl.logAndDo(blockIO, undoBlk);
blk.getBlk().setInlog(true);
blk.getBlk().setIncore(false);
tblk.resetBlock();
if( DEBUG ) {
System.out.println("RecoveryLogManager.writeLog EXIT with "+blk.toString());
}
}
/**
* Remove archived files and reset log file 1 to its primordial state
*
* @throws IOException
*/
public synchronized void commit() throws IOException {
if(DEBUG) System.out.println("RecoveryLogManager.commit called for db "+ltf.getDBName()+" tablespace "+tablespace+" log seq. int.");
firstTrans = null;
ltf.stop();
ltf.deleteObsoleteLogfilesOnCommit();
ltf.initializeLogFileSequence();
}
/**
* Version of method called when starting and we see an undolog ready to restore
* @throws IOException
*/
public synchronized void rollBack() throws IOException {
rollBackCache(); // synch main file buffs
blockIO.forceBufferClear(); // flush buffer pools
if( firstTrans != null) {
fl.undo(blockIO, firstTrans, null);
if(DEBUG) System.out.println("RecoveryLogManager.rollback Undo initial transaction recorded for rollback in tablespace "+tablespace+" in "+ltf.getDBName());
firstTrans = null;
ltf.deleteObsoleteLogfilesOnCommit();
ltf.initializeLogFileSequence();
}
}
/**
* This operation reads restored blocks to cache if they exist
* therein
* @exception IOException If we can't replace blocks
*/
public synchronized void rollBackCache() throws IOException {
if(DEBUG )
System.out.println("RecoveryLogManager.rollbackCache invoked");
blockIO.getIOManager().Fforce(); // make sure we synch our main file buffers
}
/**
* Take a checkpoint. Force buffer flush, then write checkpoint. A checkpoint demarcates
* a recovery position in logs from which a recovery will roll forward. An undo from a checkpoint
* will restore the raw store to its state at that checkpoint. Use with caution and only with wise counsel.
* TODO Make it a request? Or is a checkpoint imperative?
* @throws IllegalAccessException
* @throws IOException
*/
public synchronized void checkpoint() throws IllegalAccessException, IOException {
blockIO.forceBufferWrite();
ltf.checkpoint(true);
if( DEBUG ) System.out.println("RecoveryLogManager.checkpoint. Checkpoint taken for db "+blockIO.getDBName());
}
}