Files
mars-flaim/flaim/src/translog.cpp
ahodgkinson f54e6ce080 Changed license to LGPL.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1009 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 09:38:48 +00:00

308 lines
7.8 KiB
C++

//-------------------------------------------------------------------------
// Desc: Rollback logging.
// Tabs: 3
//
// Copyright (c) 1991-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library 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
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com.
//
// $Id$
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC void FLMAPI lgWriteComplete(
IF_IOBuffer * pIOBuffer,
void * pvData);
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_DBG_LOG
void scaLogWrite(
FLMUINT uiFFileId,
FLMUINT uiWriteAddress,
FLMBYTE * pucBlkBuf,
FLMUINT uiBufferLen,
FLMUINT uiBlockSize,
char * pszEvent)
{
FLMUINT uiOffset = 0;
FLMUINT uiBlkAddress;
while (uiOffset < uiBufferLen)
{
uiBlkAddress = (FLMUINT)(GET_BH_ADDR( pucBlkBuf));
// A uiWriteAddress of zero means we are writing exactly at the
// block address - i.e., it is the data block, not the log block.
flmDbgLogWrite( uiFFileId, uiBlkAddress,
(FLMUINT)((uiWriteAddress)
? uiWriteAddress + uiOffset
: uiBlkAddress),
(FLMUINT)(FB2UD( &pucBlkBuf [BH_TRANS_ID])), pszEvent);
uiOffset += uiBlockSize;
pucBlkBuf += uiBlockSize;
}
}
#endif
/****************************************************************************
Desc: This is the callback routine that is called when a disk write is
completed.
****************************************************************************/
FSTATIC void FLMAPI lgWriteComplete(
IF_IOBuffer * pIOBuffer,
void * pvData)
{
#ifdef FLM_DBG_LOG
FFILE * pFile = (FFILE *)pIOBuffer->getCallbackData( 0);
FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize;
FLMUINT uiLength = pIOBuffer->getBufferSize();
char * pszEvent;
#endif
DB_STATS * pDbStats = (DB_STATS *)pvData;
#ifdef FLM_DBG_LOG
pszEvent = (char *)(RC_OK( pIOBuffer->getCompletionCode())
? (char *)"LGWRT"
: (char *)"LGWRT-FAIL");
scaLogWrite( pFile->uiFFileId, 0, pIOBuffer->getBuffer(), uiLength,
uiBlockSize, pszEvent);
#endif
if (pDbStats)
{
// Must lock mutex, because this may be called from async write
// completion at any time.
f_mutexLock( gv_FlmSysData.hShareMutex);
pDbStats->LogBlockWrites.ui64ElapMilli += pIOBuffer->getElapsedTime();
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
}
/****************************************************************************
Desc: This routine flushes a log buffer to the log file.
****************************************************************************/
RCODE lgFlushLogBuffer(
DB_STATS * pDbStats,
F_SuperFileHdl * pSFileHdl,
FFILE * pFile)
{
RCODE rc = FERR_OK;
if( pDbStats)
{
pDbStats->bHaveStats = TRUE;
pDbStats->LogBlockWrites.ui64Count++;
pDbStats->LogBlockWrites.ui64TotalBytes += pFile->uiCurrLogWriteOffset;
}
pFile->pCurrLogBuffer->setCompletionCallback( lgWriteComplete, pDbStats);
pFile->pCurrLogBuffer->addCallbackData( (void *)pFile);
pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize);
pSFileHdl->setExtendSize( pFile->uiFileExtendSize);
if( RC_BAD( rc = pSFileHdl->writeBlock( pFile->uiCurrLogBlkAddr,
pFile->uiCurrLogWriteOffset, pFile->pCurrLogBuffer)))
{
if (pDbStats)
{
pDbStats->uiWriteErrors++;
}
goto Exit;
}
Exit:
pFile->uiCurrLogWriteOffset = 0;
pFile->pCurrLogBuffer->Release();
pFile->pCurrLogBuffer = NULL;
return( rc);
}
/****************************************************************************
Desc: This routine writes a block to the log file.
****************************************************************************/
RCODE lgOutputBlock(
DB_STATS * pDbStats,
F_SuperFileHdl * pSFileHdl,
FFILE * pFile,
SCACHE * pLogBlock,
FLMBYTE * pucBlk,
FLMUINT * puiLogEofRV)
{
RCODE rc = FERR_OK;
FLMUINT uiFilePos = *puiLogEofRV;
FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize;
FLMBYTE * pucLogBlk;
FLMUINT uiBlkAddress;
FLMUINT uiLogBufferSize;
// Time for a new block file?
if (FSGetFileOffset( uiFilePos) >= pFile->uiMaxFileSize)
{
FLMUINT uiFileNumber;
// Write out the current buffer, if it has anything in it.
if (pFile->uiCurrLogWriteOffset)
{
if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, pFile)))
{
goto Exit;
}
}
uiFileNumber = FSGetFileNumber( uiFilePos);
if (!uiFileNumber)
{
uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER(
pFile->FileHdr.uiVersionNum);
}
else
{
uiFileNumber++;
}
if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER(
pFile->FileHdr.uiVersionNum))
{
rc = RC_SET( FERR_DB_FULL);
goto Exit;
}
if (RC_BAD( rc = pSFileHdl->createFile( uiFileNumber )))
{
goto Exit;
}
uiFilePos = FSBlkAddress( uiFileNumber, 0 );
}
// Copy the log block to the log buffer.
if (!pFile->uiCurrLogWriteOffset)
{
pFile->uiCurrLogBlkAddr = uiFilePos;
// Get a buffer for logging.
//
// NOTE: Buffers are not kept by the FFILE's buffer manager,
// so once we are done with this buffer, it will be freed
uiLogBufferSize = MAX_LOG_BUFFER_SIZE;
for( ;;)
{
if (RC_BAD( rc = pFile->pBufferMgr->getBuffer(
uiLogBufferSize, &pFile->pCurrLogBuffer)))
{
// If we failed to get a buffer of the requested size,
// reduce the buffer size by half and try again
if( rc == FERR_MEM)
{
uiLogBufferSize /= 2;
if( uiLogBufferSize < uiBlkSize)
{
goto Exit;
}
rc = FERR_OK;
continue;
}
goto Exit;
}
break;
}
}
// Copy data from log block to the log buffer
pucLogBlk = pFile->pCurrLogBuffer->getBufferPtr() +
pFile->uiCurrLogWriteOffset;
f_memcpy( pucLogBlk, pLogBlock->pucBlk, uiBlkSize);
// If we are logging this block for the current update
// transaction, set the BEFORE IMAGE (BI) flag in the block header
// so we will know that this block is a before image block that
// needs to be restored when aborting the current update
// transaction
if (pLogBlock->ui16Flags & CA_WRITE_TO_LOG)
{
BH_SET_BI( pucLogBlk);
}
// If this is an index block, and it is encrypted, we need to encrypt
// it before we calculate the checksum
if (BH_GET_TYPE( pucLogBlk) != BHT_FREE && pucLogBlk[ BH_ENCRYPTED])
{
FLMUINT uiBufLen = getEncryptSize( pucLogBlk);
flmAssert( uiBufLen <= uiBlkSize);
if (RC_BAD( rc = ScaEncryptBlock( pLogBlock->pFile,
pucLogBlk,
uiBufLen,
uiBlkSize)))
{
goto Exit;
}
}
// Calculate the block checksum
uiBlkAddress = GET_BH_ADDR( pucLogBlk);
BlkCheckSum( pucLogBlk, CHECKSUM_SET, uiBlkAddress, uiBlkSize);
// Set up for next log block write
pFile->uiCurrLogWriteOffset += uiBlkSize;
// If this log buffer is full, write it out
if( pFile->uiCurrLogWriteOffset ==
pFile->pCurrLogBuffer->getBufferSize())
{
if( RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, pFile)))
{
goto Exit;
}
}
// Save the previous block address into the modified block's
// block header area. Also save the transaction id
UD2FBA( (FLMUINT32)uiFilePos, &pucBlk [BH_PREV_BLK_ADDR]);
f_memcpy( &pucBlk [BH_PREV_TRANS_ID], &pLogBlock->pucBlk [BH_TRANS_ID], 4);
*puiLogEofRV = uiFilePos + uiBlkSize;
Exit:
return( rc);
}