/**************************************************************************** Desc: Aborts an active transaction. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbTransAbort( HFDB hDb) { RCODE rc; FDB * pDb = (FDB *)hDb; FLMBOOL bIgnore; if (IsInCSMode( hDb)) { fdbInitCS( pDb); FCL_WIRE Wire( pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { rc = Wire.doTransOp( FCS_OP_TRANSACTION_ABORT, 0, 0, 0); } goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore))) { goto Exit; } // If there is an invisible transaction going, it should not be // abortable by an application. if ((pDb->uiTransType == FLM_NO_TRANS) || (pDb->uiFlags & FDB_INVISIBLE_TRANS)) { rc = RC_SET( FERR_NO_TRANS_ACTIVE); goto Exit; } rc = flmAbortDbTrans( pDb); Exit: if( RC_OK( rc)) { rc = flmCheckDatabaseState( pDb); } flmExit( FLM_DB_TRANS_ABORT, pDb, rc); return( rc); }
/**************************************************************************** Desc: Thread that will build an index in the background. Caller will create a pDb to use. This pDb must be freed at the conclusion of the routine. ****************************************************************************/ FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd( IF_Thread * pThread) { RCODE rc = FERR_OK; IXD * pIxd; F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); FLMBOOL bStartedTrans; FLMBOOL bDbInitialized; FLMUINT uiContainerNum; FLMUINT uiFirstDrn; FLMUINT uiIndexNum; FDB * pDb = NULL; FLMBOOL bForcedShutdown = FALSE; FLMBOOL bHitEnd; FINDEX_STATUS savedIxStatus; FlmRecord * pReusableRec = NULL; char szMsg[ 128]; FLMINT iErrorLine = 0; FLMBOOL bLimitedMode = FALSE; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); if( (pReusableRec = f_new FlmRecord) != NULL) { if( RC_BAD( pReusableRec->preallocSpace( 512, 1024 * 64))) { pReusableRec->Release(); pReusableRec = NULL; } } Loop_Again: rc = FERR_OK; uiIndexNum = pBackgroundIx->indexStatus.uiIndexNum; flmAssert( pThread->getThreadAppId() == uiIndexNum); bDbInitialized = FALSE; bStartedTrans = FALSE; pDb = NULL; // We could loop forever on flmOpenFile errors, check if we should exit. if( pThread->getShutdownFlag()) { bForcedShutdown = TRUE; goto Exit; } if( RC_BAD( rc = flmOpenFile( pBackgroundIx->pFile, NULL, NULL, NULL, 0, TRUE, NULL, NULL, pBackgroundIx->pFile->pszDbPassword, &pDb))) { // If the file is being closed, this is not an error. if( pBackgroundIx->pFile->uiFlags & DBF_BEING_CLOSED) { bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } flmAssert( pDb->pSFileHdl); bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bStartedTrans))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } flmAssert( !bStartedTrans); pDb->uiFlags |= FDB_BACKGROUND_INDEXING; for(;;) { // Set the thread's status pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); // See if we should shut down. if( pThread->getShutdownFlag()) { bForcedShutdown = TRUE; break; } // Obtain the file lock flmAssert( !(pDb->uiFlags & FDB_HAS_FILE_LOCK)); if( RC_BAD( rc = pDb->pFile->pFileLockObj->lock( pDb->hWaitSem, TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL))) { if( rc == FERR_IO_FILE_LOCK_ERR) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } // The lock needs to be marked as implicit so that flmCommitDbTrans // will unlock the file and allow the next update transaction to // begin before all writes are complete. pDb->uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); // If there are higher priority waiters in the lock queue, // or we are being told to shut down, we want to relinquish. if( pThread->getShutdownFlag() || pDb->pFile->pFileLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { if (RC_BAD( rc = pDb->pFile->pFileLockObj->unlock())) { iErrorLine = (FLMINT)__LINE__; goto Exit; } pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); continue; } // Start an update transaction if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT, FLM_DONT_POISON_CACHE))) { if( rc == FERR_IO_FILE_LOCK_ERR) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } bStartedTrans = TRUE; if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { // Index may have been deleted by another transaction, or // there may have been some other error. iErrorLine = (FLMINT)__LINE__; goto Exit; } // If we're running in limited mode, then we can't mess with encrypted // indexes. On the other hand, since the index is marked as offline, // but not suspended, this thread has to exist, or else it will cause // all kinds of problems elsewhere. So, in such a case, we will simply // sleep in an inifinite loop until the shutdown flag is set. // (We consider this acceptable becase running in limited mode is not // the norm, and Flaim probably won't be up for very long in this mode.) if( pDb->pFile->bInLimitedMode && pIxd->uiEncId) { bLimitedMode = TRUE; goto Exit; } // Look up the tracker info to determine where we need to being indexing if (RC_BAD( rc = flmGetIxTrackerInfo( pDb, pBackgroundIx->indexStatus.uiIndexNum, &uiContainerNum, &uiFirstDrn, NULL, &pBackgroundIx->indexStatus.bSuspended))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } // If the index is suspended, this thread should have been // shut down. The suspending thread will keep the database // locked until we exit. So, if we have the database locked, // the index better not be suspended. flmAssert( !pBackgroundIx->indexStatus.bSuspended && !(pIxd->uiFlags & IXD_SUSPENDED)); if (pIxd->uiContainerNum) { uiContainerNum = pIxd->uiContainerNum; if( uiFirstDrn == DRN_LAST_MARKER) { goto Exit; } } else { if( uiFirstDrn == DRN_LAST_MARKER && uiContainerNum == 0xFFFFFFFF) { goto Exit; } else { // The container number from the tracker record // may not be a real container. // Determine what the next actual container number is. if (uiContainerNum != FLM_DATA_CONTAINER) { while( uiContainerNum < pDb->pDict->uiIttCnt) { ITT * pItt = &pDb->pDict->pIttTbl [uiContainerNum]; if (ITT_IS_CONTAINER( pItt)) { break; } else { uiContainerNum++; } } if (uiContainerNum >= pDb->pDict->uiIttCnt) { uiContainerNum = FLM_DATA_CONTAINER; } } } } // Setup the DRN range we want to index. uiFirstDrn++; flmAssert( pIxd->uiLastDrnIndexed == uiFirstDrn - 1); // Set the thread's status pThread->setThreadStatus( "Indexing %u:%u", (unsigned)uiContainerNum, (unsigned)uiFirstDrn); // Read and index up to the highest drn (or record higher than uiEndDrn) // or until time runs out. The 500 is millisecs to take for the transaction. f_memcpy( &savedIxStatus, &pBackgroundIx->indexStatus, sizeof( FINDEX_STATUS)); if( RC_BAD( rc = flmIndexSetOfRecords( pDb, uiIndexNum, uiContainerNum, uiFirstDrn, DRN_LAST_MARKER, NULL, NULL, NULL, NULL, &pBackgroundIx->indexStatus, &bHitEnd, pThread, pReusableRec))) { // Lock the mutex while copying the saved index status back to // the main index status so that someone requesting the index status // won't see the status while the memcpy is in progress. f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy( &pBackgroundIx->indexStatus, &savedIxStatus, sizeof( FINDEX_STATUS)); f_mutexUnlock( gv_FlmSysData.hShareMutex); iErrorLine = (FLMINT)__LINE__; goto Exit; } if( pBackgroundIx->indexStatus.uiRecordsProcessed - savedIxStatus.uiRecordsProcessed) { if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSet( uiIndexNum, uiContainerNum, uiFirstDrn, pBackgroundIx->indexStatus.uiLastRecordIdIndexed))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } } // Commit the transaction (even if we didn't do any indexing work). if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } bStartedTrans = FALSE; pBackgroundIx->indexStatus.uiTransactions++; if( bHitEnd) { // flmIndexSetOfRecords brought the index on-line if( gv_FlmSysData.UpdateEvents.pEventCBList) { flmDoEventCallback( F_EVENT_UPDATES, F_EVENT_INDEXING_COMPLETE, (void *)uiIndexNum, (void *)0); } // Log a message flmLogIndexingProgress( uiIndexNum, 0); break; } } Exit: pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); if( bStartedTrans) { (void)flmAbortDbTrans( pDb); bStartedTrans = FALSE; } if( pDb && pDb->uiFlags & FDB_HAS_FILE_LOCK) { (void)pDb->pFile->pFileLockObj->unlock(); pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } if( bDbInitialized) { fdbExit( pDb); bDbInitialized = FALSE; } if( pDb) { (void)FlmDbClose( (HFDB *) &pDb); } if( RC_BAD(rc) && !bForcedShutdown) { if (rc == FERR_MEM || rc == FERR_IO_DISK_FULL || rc == FERR_MUST_WAIT_CHECKPOINT) { // Log the error f_sprintf( (char *)szMsg, "Background indexing thread %u (index %u)", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine); // Sleep a half second and try again. pThread->sleep( 500); goto Loop_Again; } else { f_sprintf( (char *)szMsg, "Background indexing thread %u (index %u) -- unrecoverable error.", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine); } } if( pReusableRec) { flmAssert( pReusableRec->getRefCount() == 1); pReusableRec->Release(); } if( bLimitedMode) { flmAssert( RC_OK( rc)); for (;;) { if( pThread->getShutdownFlag()) { break; } pThread->sleep( 1000); } } // Set the thread's app ID to 0, so that it will not // be found now that the thread is terminating (we don't // want flmBackgroundIndexGet to find the thread). pThread->setThreadAppId( 0); // Free the background index structure f_mutexLock( gv_FlmSysData.hShareMutex); f_free( &pBackgroundIx); pThread->setParm1( NULL); f_mutexUnlock( gv_FlmSysData.hShareMutex); return( rc); }
/**************************************************************************** Desc: Forces a checkpoint on the database. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbCheckpoint( HFDB hDb, FLMUINT uiTimeout) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bStartedTrans; bStartedTrans = FALSE; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_CHECKPOINT))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiTimeout))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } // Start an update transaction. Must not already be one going. if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, uiTimeout | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } // Commit the transaction, forcing it to be checkpointed. bStartedTrans = FALSE; pDb->bHadUpdOper = FALSE; if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) { goto Exit; } Exit: if (bStartedTrans) { (void)flmAbortDbTrans( pDb); } flmExit( FLM_DB_CHECKPOINT, pDb, rc); return( rc); }
/**************************************************************************** Desc: Starts a transaction. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbTransBegin( HFDB hDb, FLMUINT uiTransType, FLMUINT uiMaxLockWait, FLMBYTE * pucHeader) { RCODE rc = FERR_OK; FLMBOOL bIgnore; FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( uiTransType); FDB * pDb = (FDB *)hDb; uiTransType = FLM_GET_TRANS_TYPE( uiTransType); if (IsInCSMode( hDb)) { fdbInitCS( pDb); FCL_WIRE Wire( pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { if( RC_BAD( rc = Wire.doTransOp( FCS_OP_TRANSACTION_BEGIN, uiTransType, uiFlags, uiMaxLockWait, pucHeader))) { goto Exit; } } goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } // Verify the transaction type. if (( uiTransType != FLM_UPDATE_TRANS) && ( uiTransType != FLM_READ_TRANS)) { rc = RC_SET( FERR_ILLEGAL_TRANS); goto Exit; } // Verify the transaction flags if( (uiFlags & FLM_DONT_KILL_TRANS) && uiTransType != FLM_READ_TRANS) { rc = RC_SET( FERR_ILLEGAL_TRANS); goto Exit; } // Can't start an update transaction on a database that // is locked in shared mode. if ((uiTransType == FLM_UPDATE_TRANS) && (pDb->uiFlags & FDB_FILE_LOCK_SHARED)) { rc = RC_SET( FERR_PERMISSION); goto Exit; } // If the database has an invisible transaction going, abort it // before going any further - we don't want application transactions // to be nested under invisible transactions. Application transactions // take precedence over invisible transactions. if ((pDb->uiTransType != FLM_NO_TRANS) && (pDb->uiFlags & FDB_INVISIBLE_TRANS)) { if (RC_BAD( rc = flmAbortDbTrans( pDb))) { goto Exit; } } // If the database is not running a transaction, start one. // Otherwise, start a nested transaction - first verifying that // the transation type matches. if (pDb->uiTransType == FLM_NO_TRANS) { FLMUINT uiBytesRead; if( pucHeader) { if( RC_BAD( rc = pDb->pSFileHdl->readBlock( 0, 2048, pucHeader, &uiBytesRead))) { goto Exit; } } if (RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType, uiMaxLockWait, uiFlags, pucHeader ? &pucHeader [16] : NULL))) { goto Exit; } pDb->bHadUpdOper = FALSE; } else { // Cannot nest transactions. rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } Exit: flmExit( FLM_DB_TRANS_BEGIN, pDb, rc); return( rc); }
/**************************************************************************** Desc: This routine commits an active transaction for a particular database. If the database is open via a server, a message is sent to the server to commit the transaction. Otherwise, the transaction is committed locally. ****************************************************************************/ RCODE flmCommitDbTrans( FDB * pDb, FLMUINT uiNewLogicalEOF, FLMBOOL bForceCheckpoint, FLMBOOL * pbEmpty) { RCODE rc = FERR_OK; FLMBYTE * pucUncommittedLogHdr; FFILE * pFile = pDb->pFile; FLMUINT uiCPFileNum = 0; FLMUINT uiCPOffset = 0; FLMUINT uiTransId = 0; FLMBOOL bTransEndLogged; FLMBOOL bForceCloseOnError = FALSE; FLMBOOL bOkToLogAbort = TRUE; DB_STATS * pDbStats = pDb->pDbStats; FLMUINT uiTransType; FLMBOOL bInvisibleTrans = FALSE; FLMBOOL bIndexAfterCommit = FALSE; pDb->uiFlags |= FDB_COMMITTING_TRANS; // See if we even have a transaction going. if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS) { goto Exit; // Will return FERR_OK. } // See if we have a transaction going which should be aborted. if (flmCheckBadTrans( pDb)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } // If we are in a read transaction we can skip most of the stuff // below because no updates would have occurred. This will help // improve performance. if (uiTransType == FLM_READ_TRANS) { if( pDb->KrefCntrl.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( pDb); } goto Exit1; } // At this point, we know we have an update transaction. pFile->pRfl->clearLogHdrs(); #ifdef FLM_DBG_LOG flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, 0, 0, FERR_OK, "TCmit"); #endif uiTransId = pDb->LogHdr.uiCurrTransID; // If the transaction had no update operations, restore it // to its pre-transaction state - make it appear that no // transaction ever happened. if (!pDb->bHadUpdOper) { bOkToLogAbort = FALSE; rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, TRUE); // Even though we didn't have any update operations, there may have // been operations during the transaction (i.e., query operations) // that initialized the KREF in order to generate keys. KrefCntrlFree( pDb); // Restore everything as if the transaction never happened. f_mutexLock( gv_FlmSysData.hShareMutex); pFile->uiUpdateTransID = 0; f_mutexUnlock( gv_FlmSysData.hShareMutex); if (pbEmpty) { *pbEmpty = TRUE; } goto Exit1; } // Log commit record to roll-forward log bOkToLogAbort = FALSE; if (RC_BAD( rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged))) { goto Exit1; } bForceCloseOnError = TRUE; // Commit any keys in the KREF buffers. if (RC_BAD( rc = KYKeysCommit( pDb, TRUE))) { flmLogError( rc, "calling KYKeysCommit from flmCommitDbTrans"); goto Exit1; } if (RC_BAD( rc = FSCommitIxCounts( pDb))) { flmLogError( rc, "calling FSCommitIxCounts from flmCommitDbTrans"); goto Exit1; } // Reinitialize the log header. If the local dictionary was updated // during the transaction, increment the local dictionary ID so that // other concurrent users will know that it has been modified and // that they need to re-read it into memory. // If we are in recovery mode, see if we need to force // a checkpoint with what we have so far. We force a // checkpoint on one of two conditions: // 1. If it appears that we have a buildup of dirty cache // blocks. We force a checkpoint on this condition // because it will be more efficient than replacing // cache blocks one at a time. // We check for this condition by looking to see if // our LRU block is not used and it is dirty. That is // a pretty good indicator that we have a buildup // of dirty cache blocks. // 2. We are at the end of the roll-forward log. We // want to force a checkpoint here to complete the // recovery phase. if ( pDb->uiFlags & FDB_REPLAYING_RFL) { // If we are in the middle of upgrading, and are forcing // a checkpoint, use the file number and offset that were // set in the FDB. if ((pDb->uiFlags & FDB_UPGRADING) && bForceCheckpoint) { uiCPFileNum = pDb->uiUpgradeCPFileNum; uiCPOffset = pDb->uiUpgradeCPOffset; } else { SCACHE * pTmpSCache; F_Rfl * pRfl = pFile->pRfl; f_mutexLock( gv_FlmSysData.hShareMutex); pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache; // Test for buildup of dirty cache blocks. if( (pTmpSCache && !pTmpSCache->uiUseCount && (pTmpSCache->ui16Flags & (CA_DIRTY | CA_LOG_FOR_CP | CA_WRITE_TO_LOG))) || pRfl->atEndOfLog() || bForceCheckpoint) { bForceCheckpoint = TRUE; uiCPFileNum = pRfl->getCurrFileNum(); uiCPOffset = pRfl->getCurrReadOffset(); } f_mutexUnlock( gv_FlmSysData.hShareMutex); } } // Move information collected in the pDb->LogHdr into the // uncommitted log header. Other things that need to be // set have already been set in the uncommitted log header // at various places in the code. // Mutex does not have to be locked while we do this because // the update transaction is the only one that ever accesses // the uncommitted log header buffer. pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; // Set the new logical EOF if passed in. if( uiNewLogicalEOF) { pDb->LogHdr.uiLogicalEOF = uiNewLogicalEOF; } UD2FBA( (FLMUINT32)pDb->LogHdr.uiLogicalEOF, &pucUncommittedLogHdr [LOG_LOGICAL_EOF]); // Increment the commit counter. flmIncrUint( &pucUncommittedLogHdr [LOG_COMMIT_COUNT], 1); // Set the last committed transaction ID if( (bTransEndLogged || (pDb->uiFlags & FDB_REPLAYING_COMMIT)) && pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) { UD2FBA( (FLMUINT32)uiTransId, &pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); } // Write the header pFile->pRfl->commitLogHdrs( pucUncommittedLogHdr, pFile->ucCheckpointLogHdr); // Commit any record cache. flmRcaCommitTrans( pDb); // Push the IXD_FIXUP values back into the IXD if (pDb->pIxdFixups) { IXD_FIXUP * pIxdFixup; IXD_FIXUP * pDeleteIxdFixup; IXD * pIxd; pIxdFixup = pDb->pIxdFixups; while (pIxdFixup) { if( RC_BAD( fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE))) { flmAssert( 0); pIxd = NULL; } if( pIxd) { pIxd->uiLastContainerIndexed = pIxdFixup->uiLastContainerIndexed; pIxd->uiLastDrnIndexed = pIxdFixup->uiLastDrnIndexed; } pDeleteIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; f_free( &pDeleteIxdFixup); } pDb->pIxdFixups = NULL; } // Set the update transaction ID back to zero only // AFTER we know the transaction has safely committed. f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy( pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, LOG_HEADER_SIZE); pFile->uiUpdateTransID = 0; ScaReleaseLogBlocks( pFile); if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) { // Link the new local dictionary to its file. // Since the new local dictionary will be linked at the head // of the list of FDICT structures, see if the FDICT currently // at the head of the list is unused and can be unlinked. if ((pFile->pDictList) && (!pFile->pDictList->uiUseCount)) { flmUnlinkDict( pFile->pDictList); } flmLinkDictToFile( pFile, pDb->pDict); } f_mutexUnlock( gv_FlmSysData.hShareMutex); Exit1: // If the local dictionary was updated during this transaction, // link the new local dictionary structures to their file - or free // them if there was an error. if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) { if( RC_BAD( rc) && pDb->pDict) { // Unlink the FDB from the FDICT. - Shouldn't have // to lock semaphore, because the DICT is NOT linked // to the FFILE. flmAssert( pDb->pDict->pFile == NULL); flmUnlinkFdbFromDict( pDb); } } if (RC_BAD( rc)) { // Since we failed to commit, do an abort. We are purposely not // checking the return code from flmAbortDbTrans because we already // have an error return code. If we attempted to log the transaction // to the RFL and failed, we don't want to try to log an abort packet. // The RFL code has already reset the log back to the starting point // of the transaction, thereby discarding all operations. pDb->uiFlags &= ~FDB_COMMITTING_TRANS; (void)flmAbortDbTrans( pDb, bOkToLogAbort); uiTransType = FLM_NO_TRANS; // Do we need to force all handles to close? if( bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all FDBs linked to the FFILE // and set the FFILE's "must close" flag. This will cause any // subsequent operations on the database to fail until all // handles have been closed. flmSetMustCloseFlags( pFile, rc, FALSE); } } else { bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE; if (uiTransType == FLM_UPDATE_TRANS) { if (gv_FlmSysData.UpdateEvents.pEventCBList) { flmTransEventCallback( F_EVENT_COMMIT_TRANS, (HFDB)pDb, rc, uiTransId); } // Do the BLOB and indexing work before we unlock the db. FBListAfterCommit( pDb); if (pDb->pIxStopList || pDb->pIxStartList) { // Must not call flmIndexingAfterCommit until after // completeTransWrites. Otherwise, there is a potential // deadlock condition where flmIndexingAfterCommit is // waiting on an indexing thread to quit, but that // thread is waiting to be signaled by this thread that // writes are completed. However, flmIndexingAfterCommit // also must only be called while the database is still // locked. If we were to leave the database locked for // every call to completeTransWrites, however, we would // lose the group commit capability. Hence, we opt to // only lose it when there are actual indexing operations // to start or stop - which should be very few transactions. // That is what the bIndexAfterCommit flag is for. bIndexAfterCommit = TRUE; } } } // Unlock the database, if the update transaction is still going. // NOTE: We check uiTransType because it may have been reset // to FLM_NO_TRANS up above if flmAbortDbTrans was called. if (uiTransType == FLM_UPDATE_TRANS) { if (RC_BAD( rc)) { // SHOULD NEVER HAPPEN - because it would have been taken // care of above - flmAbortDbTrans would have been called and // uiTransType would no longer be FLM_UPDATE_TRANS. flmAssert( 0); (void)pFile->pRfl->completeTransWrites( pDb, FALSE, TRUE); } else if( !bForceCheckpoint) { if( bIndexAfterCommit) { rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE); flmIndexingAfterCommit( pDb); flmUnlinkDbFromTrans( pDb, TRUE); } else { rc = pFile->pRfl->completeTransWrites( pDb, TRUE, TRUE); } } else { // Do checkpoint, if forcing. Before doing the checkpoint // we have to make sure the roll-forward log writes // complete. We don't want to unlock the DB while the // writes are happening in this case - thus, the FALSE // parameter to completeTransWrites. if (RC_OK( rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE))) { bForceCloseOnError = FALSE; rc = ScaDoCheckpoint( pDbStats, pDb->pSFileHdl, pFile, (pDb->uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE, TRUE, CP_TIME_INTERVAL_REASON, uiCPFileNum, uiCPOffset); } if (bIndexAfterCommit) { flmIndexingAfterCommit( pDb); } flmUnlinkDbFromTrans( pDb, TRUE); } if (RC_BAD( rc) && bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all FDBs linked to the FFILE // and set the FFILE's "must close" flag. This will cause any // subsequent operations on the database to fail until all // handles have been closed. flmSetMustCloseFlags( pFile, rc, FALSE); } } else { // Unlink the database from the transaction // structure as well as from the FDICT structure. flmUnlinkDbFromTrans( pDb, FALSE); } if (pDbStats && uiTransType != FLM_NO_TRANS) { FLMUINT64 ui64ElapMilli = 0; flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli); pDbStats->bHaveStats = TRUE; if (uiTransType == FLM_READ_TRANS) { pDbStats->ReadTransStats.CommittedTrans.ui64Count++; pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; if (bInvisibleTrans) { pDbStats->ReadTransStats.InvisibleTrans.ui64Count++; pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli += ui64ElapMilli; } } else { pDbStats->UpdateTransStats.CommittedTrans.ui64Count++; pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; } } // Update stats if (pDb->pStats) { (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); } Exit: pDb->uiFlags &= ~FDB_COMMITTING_TRANS; return( rc); }