git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@482 0109f412-320b-0410-ab79-c3e0c5ffbbe6
450 lines
12 KiB
C++
450 lines
12 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Contains routines for aborting a transaction.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License as published by the Free Software Foundation.
|
|
//
|
|
// 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.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; 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"
|
|
|
|
/****************************************************************************
|
|
Desc: This routine aborts an active transaction for a particular
|
|
database. If the database is open via a server, a message is
|
|
sent to the server to abort the transaction. Otherwise, the
|
|
transaction is rolled back locally.
|
|
****************************************************************************/
|
|
RCODE F_Db::abortTrans(
|
|
FLMBOOL bOkToLogAbort)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
eDbTransType eSaveTransType;
|
|
SFLM_DB_HDR * pLastCommittedDbHdr;
|
|
SFLM_DB_HDR * pUncommittedDbHdr;
|
|
FLMBOOL bDumpedCache = FALSE;
|
|
FLMBOOL bKeepAbortedTrans;
|
|
FLMUINT64 ui64TransId;
|
|
F_Rfl * pRfl = m_pDatabase->m_pRfl;
|
|
RCODE tmpRc;
|
|
|
|
// Should never be calling on a temporary database.
|
|
|
|
flmAssert( !m_pDatabase->m_bTempDb);
|
|
|
|
// Get transaction type
|
|
|
|
if (m_eTransType == SFLM_NO_TRANS)
|
|
{
|
|
goto Exit; // Will return SUCCESS.
|
|
}
|
|
|
|
// No recovery required if it is a read transaction.
|
|
|
|
if (m_eTransType == SFLM_READ_TRANS)
|
|
{
|
|
if (m_bKrefSetup)
|
|
{
|
|
// krefCntrlFree could be called w/o checking bKrefSetup because
|
|
// it checks the flag, but it is more optimal to check the
|
|
// flag before making the call because most of the time it will
|
|
// be false.
|
|
|
|
krefCntrlFree();
|
|
}
|
|
|
|
goto Unlink_From_Trans;
|
|
}
|
|
|
|
#ifdef FLM_DBG_LOG
|
|
flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID,
|
|
0, 0, NE_SFLM_OK, "TAbrt");
|
|
#endif
|
|
|
|
// Disable DB header writes
|
|
|
|
pRfl->clearDbHdrs();
|
|
|
|
// If the transaction had no update operations, restore it
|
|
// to its pre-transaction state - make it appear that no
|
|
// transaction ever happened.
|
|
|
|
pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr;
|
|
pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr;
|
|
ui64TransId = m_ui64CurrTransID;
|
|
|
|
// Free up all keys associated with this database. This is done even
|
|
// if we didn't have any update operations because the KREF may
|
|
// have been initialized by key generation operations performed
|
|
// by cursors, etc.
|
|
|
|
krefCntrlFree();
|
|
|
|
if (m_bHadUpdOper)
|
|
{
|
|
|
|
// Dump any start and stop indexing stubs that should be aborted.
|
|
|
|
indexingAfterAbort();
|
|
|
|
// Log the abort record to the rfl file, or throw away the logged
|
|
// records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL
|
|
// flag. If the RFL volume is bad, we will not attempt to keep this
|
|
// transaction in the RFL.
|
|
|
|
if (!pRfl->seeIfRflVolumeOk())
|
|
{
|
|
bKeepAbortedTrans = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bKeepAbortedTrans =
|
|
(pUncommittedDbHdr->ui8RflKeepAbortedTrans)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bKeepAbortedTrans = FALSE;
|
|
}
|
|
|
|
// Log an abort transaction record to the roll-forward log or
|
|
// throw away the entire transaction, depending on the
|
|
// bKeepAbortedTrans flag.
|
|
|
|
// If the transaction is being "dumped" because of a failed commit,
|
|
// don't log anything to the RFL.
|
|
|
|
if (bOkToLogAbort)
|
|
{
|
|
#ifdef FLM_DEBUG
|
|
if( pRfl->isLoggingEnabled())
|
|
{
|
|
flmAssert( m_ui64CurrTransID == pRfl->getCurrTransID());
|
|
}
|
|
#endif
|
|
|
|
if (RC_BAD( rc = pRfl->logEndTransaction(
|
|
this, RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans)))
|
|
{
|
|
goto Exit1;
|
|
}
|
|
}
|
|
#ifdef FLM_DEBUG
|
|
else
|
|
{
|
|
// If bOkToLogAbort is FALSE, this always means that either a
|
|
// commit failed while trying to log an end transaction packet or a
|
|
// commit packet was logged and the transaction commit subsequently
|
|
// failed for some other reason. In either case, the RFL should be
|
|
// in a good state, with its current transaction ID reset to 0. If
|
|
// not, either bOkToLogAbort is being used incorrectly by the caller
|
|
// or there is a bug in the RFL logic.
|
|
|
|
flmAssert( pRfl->getCurrTransID() == 0);
|
|
}
|
|
#endif
|
|
|
|
// If there were no operations in the transaction, restore
|
|
// everything as if the transaction never happened.
|
|
|
|
// Even empty transactions can have modified rows to clean up
|
|
// so we need to call this no matter what.
|
|
|
|
m_pDatabase->freeModifiedRows( this, m_ui64CurrTransID - 1);
|
|
|
|
if (!m_bHadUpdOper)
|
|
{
|
|
|
|
// Pretend we dumped cache - shouldn't be any to worry about at
|
|
// this point.
|
|
|
|
bDumpedCache = TRUE;
|
|
goto Exit1;
|
|
}
|
|
|
|
// Dump ALL modified cache blocks associated with the DB.
|
|
// NOTE: This needs to be done BEFORE the call to flmGetDbHdrInfo
|
|
// below, because that call will change pDb->m_ui64CurrTransID,
|
|
// and that value is used by freeModifiedRows.
|
|
|
|
m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID);
|
|
bDumpedCache = TRUE;
|
|
|
|
// Reset the Db header from the last committed DB header in pFile.
|
|
|
|
getDbHdrInfo( pLastCommittedDbHdr);
|
|
if (RC_BAD( rc = physRollback( (FLMUINT)pUncommittedDbHdr->ui32RblEOF,
|
|
m_pDatabase->m_uiFirstLogBlkAddress, FALSE, 0)))
|
|
{
|
|
goto Exit1;
|
|
}
|
|
|
|
m_pDatabase->lockMutex();
|
|
|
|
// Put the new transaction ID into the log header even though
|
|
// we are not committing. We want to keep the transaction IDs
|
|
// incrementing even though we aborted.
|
|
|
|
pLastCommittedDbHdr->ui64CurrTransID = ui64TransId;
|
|
|
|
// Preserve where we are at in the roll-forward log. Even though
|
|
// the transaction aborted, we may have kept it in the RFL instead of
|
|
// throw it away.
|
|
|
|
pLastCommittedDbHdr->ui32RflCurrFileNum =
|
|
pUncommittedDbHdr->ui32RflCurrFileNum;
|
|
pLastCommittedDbHdr->ui32RflLastTransOffset =
|
|
pUncommittedDbHdr->ui32RflLastTransOffset;
|
|
f_memcpy( pLastCommittedDbHdr->ucLastTransRflSerialNum,
|
|
pUncommittedDbHdr->ucLastTransRflSerialNum,
|
|
SFLM_SERIAL_NUM_SIZE);
|
|
f_memcpy( pLastCommittedDbHdr->ucNextRflSerialNum,
|
|
pUncommittedDbHdr->ucNextRflSerialNum,
|
|
SFLM_SERIAL_NUM_SIZE);
|
|
|
|
// The following items tell us where we are at in the roll-back log.
|
|
// During a transaction we may log blocks for the checkpoint or for
|
|
// read transactions. So, even though we are aborting this transaction,
|
|
// there may be other things in the roll-back log that we don't want
|
|
// to lose. These items should not be reset until we do a checkpoint,
|
|
// which is when we know it is safe to throw away the entire roll-back log.
|
|
|
|
pLastCommittedDbHdr->ui32RblEOF =
|
|
pUncommittedDbHdr->ui32RblEOF;
|
|
pLastCommittedDbHdr->ui32RblFirstCPBlkAddr =
|
|
pUncommittedDbHdr->ui32RblFirstCPBlkAddr;
|
|
|
|
m_pDatabase->unlockMutex();
|
|
|
|
pRfl->commitDbHdrs( pLastCommittedDbHdr,
|
|
&m_pDatabase->m_checkpointDbHdr);
|
|
|
|
Exit1:
|
|
|
|
// Dump cache, if not done above.
|
|
|
|
if (!bDumpedCache)
|
|
{
|
|
m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID);
|
|
m_pDatabase->freeModifiedRows( this, m_ui64CurrTransID - 1);
|
|
bDumpedCache = TRUE;
|
|
}
|
|
|
|
// Throw away IXD_FIXUPs
|
|
|
|
if (m_pIxdFixups)
|
|
{
|
|
IXD_FIXUP * pIxdFixup;
|
|
IXD_FIXUP * pDeleteIxdFixup;
|
|
|
|
pIxdFixup = m_pIxdFixups;
|
|
while (pIxdFixup)
|
|
{
|
|
pDeleteIxdFixup = pIxdFixup;
|
|
pIxdFixup = pIxdFixup->pNext;
|
|
f_free( &pDeleteIxdFixup);
|
|
}
|
|
m_pIxdFixups = NULL;
|
|
}
|
|
|
|
if (m_eTransType == SFLM_UPDATE_TRANS &&
|
|
gv_SFlmSysData.EventHdrs[ SFLM_EVENT_UPDATES].pEventCBList)
|
|
{
|
|
flmTransEventCallback( SFLM_EVENT_ABORT_TRANS, this, rc,
|
|
ui64TransId);
|
|
}
|
|
|
|
Unlink_From_Trans:
|
|
|
|
eSaveTransType = m_eTransType;
|
|
|
|
if (m_uiFlags & FDB_HAS_WRITE_LOCK)
|
|
{
|
|
if (RC_BAD( tmpRc = pRfl->completeTransWrites( this, FALSE, FALSE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (eSaveTransType == SFLM_UPDATE_TRANS)
|
|
{
|
|
|
|
// Before unlocking, restore collection information.
|
|
|
|
if (m_uiFlags & FDB_UPDATED_DICTIONARY)
|
|
{
|
|
m_pDatabase->lockMutex();
|
|
flmAssert( m_pDict);
|
|
unlinkFromDict();
|
|
if (m_pDatabase->m_pDictList)
|
|
{
|
|
|
|
// Link the F_Db to the right F_Dict object so it will
|
|
// fixup the correct F_COLLECTION structures.
|
|
|
|
linkToDict( m_pDatabase->m_pDictList);
|
|
}
|
|
m_pDatabase->unlockMutex();
|
|
}
|
|
|
|
if (m_pDict)
|
|
{
|
|
F_TABLE * pTable;
|
|
FLMUINT uiLfNum;
|
|
#ifdef FLM_DEBUG
|
|
F_INDEX * pIndex;
|
|
FLMUINT uiRootBlk;
|
|
#endif
|
|
|
|
// Only need to do tables. Nothing from the LFH of
|
|
// an index is stored in memory except for the root block
|
|
// address, and whenever that is changed, we get a new
|
|
// dictionary. Since the new dictionary will be discarded
|
|
// in that case, there is nothing to restore for an index.
|
|
|
|
for (uiLfNum = 1, pTable = m_pDict->m_pTableTbl;
|
|
uiLfNum <= m_pDict->m_uiHighestTableNum;
|
|
uiLfNum++, pTable++)
|
|
{
|
|
if (pTable->uiTableNum)
|
|
{
|
|
#ifdef FLM_DEBUG
|
|
uiRootBlk = pTable->lfInfo.uiRootBlk;
|
|
#endif
|
|
if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, &pTable->lfInfo)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
}
|
|
#ifdef FLM_DEBUG
|
|
else
|
|
{
|
|
// Make sure root block did not change - should not
|
|
// have because root block changes are done by creating
|
|
// a new dictionary, and we have already discarded
|
|
// any new dictionary. Hence, root block address should
|
|
// be the same in memory as it is no disk.
|
|
|
|
flmAssert( uiRootBlk == pTable->lfInfo.uiRootBlk);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Do indexes in debug mode to make sure uiRootBlk is correct
|
|
|
|
#ifdef FLM_DEBUG
|
|
for (uiLfNum = 1, pIndex = m_pDict->m_pIndexTbl;
|
|
uiLfNum <= m_pDict->m_uiHighestIndexNum;
|
|
uiLfNum++, pIndex++)
|
|
{
|
|
if (pIndex->uiIndexNum)
|
|
{
|
|
uiRootBlk = pIndex->lfInfo.uiRootBlk;
|
|
if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, &pIndex->lfInfo)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Make sure root block did not change - should not
|
|
// have because root block changes are done by creating
|
|
// a new dictionary, and we have already discarded
|
|
// any new dictionary. Hence, root block address should
|
|
// be the same in memory as it is no disk.
|
|
|
|
flmAssert( uiRootBlk == pIndex->lfInfo.uiRootBlk);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Unlink the database from the transaction list.
|
|
|
|
unlinkFromTransList( FALSE);
|
|
|
|
if (m_pDbStats)
|
|
{
|
|
FLMUINT64 ui64ElapMilli = 0;
|
|
|
|
flmAddElapTime( &m_TransStartTime, &ui64ElapMilli);
|
|
m_pDbStats->bHaveStats = TRUE;
|
|
if (eSaveTransType == SFLM_READ_TRANS)
|
|
{
|
|
m_pDbStats->ReadTransStats.AbortedTrans.ui64Count++;
|
|
m_pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli +=
|
|
ui64ElapMilli;
|
|
}
|
|
else
|
|
{
|
|
m_pDbStats->UpdateTransStats.AbortedTrans.ui64Count++;
|
|
m_pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli +=
|
|
ui64ElapMilli;
|
|
}
|
|
}
|
|
|
|
if (m_pStats)
|
|
{
|
|
(void)flmStatUpdate( &m_Stats);
|
|
}
|
|
|
|
Exit:
|
|
|
|
m_AbortRc = NE_SFLM_OK;
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Area : TRANSACTION
|
|
Desc : Aborts an active transaction.
|
|
*END************************************************************************/
|
|
RCODE F_Db::transAbort( void)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
|
|
if (m_eTransType == SFLM_NO_TRANS)
|
|
{
|
|
rc = RC_SET( NE_SFLM_NO_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
rc = abortTrans();
|
|
|
|
Exit:
|
|
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = checkState( __FILE__, __LINE__);
|
|
}
|
|
|
|
return( rc);
|
|
}
|