windows-nt/Source/XPSP1/NT/inetsrv/query/cindex/resman.cxx
2020-09-26 16:20:57 +08:00

5108 lines
150 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: RESMAN.CXX
//
// Contents: Resource Manager
//
// Classes: CResManager
//
// History: 08-Apr-91 BartoszM Created
// 4-Jan-95 BartoszM Separated Filter Manager
// Jan-08-97 mohamedn CFwEventItem and CDmFwEventItem
// 24-Feb-97 SitaramR Push filtering
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <cci.hxx>
#include <xact.hxx>
#include <pstore.hxx>
#include <cifailte.hxx>
#include <ciole.hxx>
#include <regevent.hxx>
#include <ciregkey.hxx>
#include <eventlog.hxx>
#include <cievtmsg.h>
#include <cifrmcom.hxx>
#include <fwevent.hxx>
#include <pidmap.hxx>
#include <identran.hxx>
#include <psavtrak.hxx>
#include "resman.hxx"
#include "ci.hxx"
#include "partn.hxx"
#include "pindex.hxx"
#include "mindex.hxx"
#include "idxids.hxx"
#include "indxact.hxx"
#include "merge.hxx"
#include "mmerglog.hxx"
#include "pendcur.hxx"
#include "fltstate.hxx"
#include "idle.hxx"
#include "notxact.hxx"
#include "lowres.hxx"
const ULONG lowDiskWaterMark = 3 * 512 * 1024; // 1.5 MB
const ULONG highDiskWaterMark = lowDiskWaterMark + 512 * 1024; // 2.0 MB
const ULONG minDiskFreeForMerge = lowDiskWaterMark;
class CRevertBoolValue {
public:
CRevertBoolValue( BOOL & rfValue, BOOL fValue ) :
_rfVal( rfValue ),
_fRevert( TRUE )
{
Win4Assert( rfValue != fValue );
_fPrevVal = _rfVal;
_rfVal = fValue;
}
~CRevertBoolValue()
{
if (_fRevert)
_rfVal = _fPrevVal;
}
void Revert()
{
_rfVal = _fPrevVal;
_fRevert = FALSE;
}
private:
BOOL & _rfVal;
BOOL _fPrevVal;
BOOL _fRevert;
};
CMergeThreadKiller::CMergeThreadKiller( CResManager & resman )
: _resman(resman), _fKill(TRUE)
{
}
CMergeThreadKiller::~CMergeThreadKiller()
{
if ( _fKill )
{
_resman._fStopMerge = TRUE;
_resman._thrMerge.Resume();
_resman.StopMerges();
}
}
//+---------------------------------------------------------------------------
//
// Class: CPendQueueTrans
//
// Purpose: Transaction for flushing the pending queue if there is a
// failure while extracting entries out of the pending queue
// and adding them to the changelog.
//
// History: 9-11-95 srikants Created
//
//----------------------------------------------------------------------------
class CPendQueueTrans : public CTransaction
{
public:
CPendQueueTrans( CPendingQueue & pendQueue ) : _pendQueue( pendQueue )
{
}
~CPendQueueTrans()
{
if ( CTransaction::XActAbort == _status )
{
CLock lock( _pendQueue.GetMutex() );
_pendQueue.LokFlushCompletedEntries();
}
}
private:
CPendingQueue & _pendQueue;
};
//+---------------------------------------------------------------------------
//
// Member: CResManager::CResManager, public
//
// Arguments: [cat] -- catalog
// [xact] -- transaction
//
// History: 08-Apr-91 BartoszM Created
// Jan-07-96 mohamedn CFwEventItem
//
//----------------------------------------------------------------------------
CResManager::CResManager(
PStorage &storage,
CCiFrameworkParams & params,
ICiCDocStore * pDocStore,
CI_STARTUP_INFO const & startupInfo,
IPropertyMapper * pPropMapper,
CTransaction& xact,
XInterface<CIndexNotificationTable> & xIndexNotifTable )
: _sigResman(eSigResman),
_frmwrkParams( params ),
_mergeTime(0),
_storage ( storage ),
_sKeyList(0),
_idxTab( _storage.QueryIndexTable( xact ) ),
_partList ( storage, *_idxTab, _sKeyList, xact, params ),
_fresh ( storage, xact, _partList ),
_partidToMerge ( partidInvalid ),
_fStopMerge(FALSE),
_pMerge(0),
#pragma warning( disable : 4355 ) // this used in base initialization
_thrMerge ( MergeThread, this, TRUE ), // create suspended
_mergeKiller( *this ),
#pragma warning( default : 4355 )
_activeDeletedIndex( _idxTab->GetDeletedIndex() ),
_cQuery(0),
_cFilteredDocuments(0),
_pBackupWorkItem(0),
_isBeingEmptied(FALSE),
_isLowOnDiskSpace(FALSE),
_isDismounted(FALSE),
_isOutOfDate(FALSE),
_isCorrupt(FALSE),
_fFirstTimeUpdatesAreEnabled( TRUE ),
_fPushFiltering( FALSE ),
_fFlushWorkerActive( FALSE ),
_configFlags( startupInfo.startupFlags ),
_xIndexNotifTable( xIndexNotifTable.Acquire() ),
_dwFilteringState( 0 ),
_pFilterAgent( 0 )
{
Win4Assert( 0 != pDocStore );
//
// Look for a resource monitor. If none exists, use the default.
//
_xLowResDefault.Set( new CLowRes(_frmwrkParams) );
SCODE sc = pDocStore->QueryInterface( IID_ICiCResourceMonitor,
_xLowRes.GetQIPointer() );
if ( FAILED(sc) )
{
_xLowResDefault->AddRef();
_xLowRes.Set( _xLowResDefault.GetPointer() );
}
//
//
// The presence or absence of an actual pointer in _xIndexNotifTable,
// indicates whether the client has specified pull filtering or push
// filtering.
//
_fPushFiltering = !_xIndexNotifTable.IsNull();
//
// Initialize the resman in all the partitions.
//
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition * pPart = iter.LokGet();
pPart->SetResMan( this, FPushFiltering() );
}
if ( _fPushFiltering )
{
//
// In push filtering, an identity translator is used
//
CIdentityNameTranslator *pIdentityTrans = new CIdentityNameTranslator();
sc = pIdentityTrans->QueryInterface( IID_ICiCDocNameToWorkidTranslator,
_translator.GetQIPointer() );
Win4Assert( SUCCEEDED( sc ) );
_translator->Release(); // QI does an AddRef
}
else
{
//
// In pull filtering, the client provides the translator, which may
// be the Ex version.
//
sc = pDocStore->QueryInterface( IID_ICiCDocNameToWorkidTranslatorEx,
_translator2.GetQIPointer() );
if ( SUCCEEDED(sc) )
sc = _translator2->QueryInterface( IID_ICiCDocNameToWorkidTranslator,
_translator.GetQIPointer() );
else
sc = pDocStore->QueryInterface( IID_ICiCDocNameToWorkidTranslator,
_translator.GetQIPointer() );
}
if ( S_OK != sc )
THROW( CException(sc) );
//
// Create a workIdToDocName converter object.
//
_xWorkidToDocName.Set( new CWorkIdToDocName( _translator.GetPointer(), _translator2.GetPointer() ) );
ICiCAdviseStatus * pAdviseStatus;
sc = pDocStore->QueryInterface( IID_ICiCAdviseStatus,
(void **) & pAdviseStatus );
if ( S_OK != sc )
THROW( CException(sc) );
_adviseStatus.Set( pAdviseStatus );
_prfCounter.SetAdviseStatus( pAdviseStatus );
ICiCPropertyStorage * pPropStore;
sc = pDocStore->QueryInterface( IID_ICiCPropertyStorage,
(void **) & pPropStore );
if ( S_OK != sc )
THROW( CException(sc) );
_propStore.Set( pPropStore );
pDocStore->AddRef();
_docStore.Set( pDocStore );
pPropMapper->AddRef();
_mapper.Set( pPropMapper );
ULONG mergeTime = _frmwrkParams.GetMasterMergeTime();
_mergeTime = CMasterMergePolicy::ComputeMidNightMergeTime(mergeTime);
if ( -1 == _mergeTime )
{
CFwEventItem item(EVENTLOG_ERROR_TYPE,
MSG_CI_BAD_SYSTEM_TIME,
0);
item.ReportEvent( _adviseStatus.GetReference() );
THROW( CException( STATUS_INVALID_PARAMETER ) );
}
//
// Restore the information associated with any master merge that
// was stopped when the drive was dismounted.
//
_partList.RestoreMMergeState(*this, _storage);
_partIter.LokInit( _partList );
_isLowOnDiskSpace = LokCheckIfDiskLow( *this,
_storage,
_isLowOnDiskSpace,
_adviseStatus.GetReference() );
_thrMerge.SetPriority ( _frmwrkParams.GetThreadPriorityMerge() );
_mergeKiller.Defuse();
_thrMerge.Resume();
//
// Set the merge progress indicator to 0, and refresh other counters
//
_prfCounter.Update( CI_PERF_MERGE_PROGRESS, 0 );
LokUpdateCounters();
//
// Enable or disables updates/notifications based on disk space available
// and whether the catalog is readonly
//
if ( _isLowOnDiskSpace || _storage.IsReadOnly() )
{
DisableUpdates( partidDefault );
_state.LokSetState( eUpdatesDisabled );
}
else
{
EnableUpdates( partidDefault );
SCODE sc = _docStore->EnableUpdates();
if ( SUCCEEDED( sc ) )
_state.LokSetState( eSteady );
else
_state.LokSetState( eUpdatesToBeEnabled );
}
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::~CResManager, public
//
// Synopsis: Shuts down merge(s)
//
// History: 13-Aug-93 KyleP Created
//
//--------------------------------------------------------------------------
CResManager::~CResManager()
{
_workMan.AbortWorkItems();
LokDeleteFlushWorkItems();
_workMan.WaitForDeath();
StopMerges();
#if CIDBG==1
// ======================================
{
CPriLock lock(_mutex);
Win4Assert( 0 == _pBackupWorkItem );
}
// ======================================
#endif // CIDBG==1
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::MergeThread, public
//
// Synopsis: Entry point for the thread responsible
// for asynchronous merges
//
// History: 05-Mar-92 BartoszM Created
//
//----------------------------------------------------------------------------
DWORD WINAPI CResManager::MergeThread( void* self )
{
if ( !((CResManager*)self)->_fStopMerge )
((CResManager*)self)->DoMerges();
return 0;
}
//==============
// STATE CHANGES
//==============
//+---------------------------------------------------------------------------
//
// Function: Dismount
//
// Synopsis: Dismounts the catalog by stopping any in-progress merge
// and also finishing off any pending writes ( eg. ChangeLog).
//
// History: 6-20-94 srikants Created
//
//----------------------------------------------------------------------------
NTSTATUS CResManager::Dismount()
{
ciDebugOut(( DEB_ITRACE, "*** CI: Initiating DisMount ***\n" ));
NTSTATUS status = STATUS_SUCCESS;
TRY
{
//
// Inform all the partitions to cleanup. We have to abort any
// in-progress merges without having to take a lock. There could
// be another thread (like the filter thread) with the lock doing
// a long operation. In one case we had it trying to create a new
// fresh test and the memory writer could not make any progress
// because the merge was continuing to dirty the pages.
//
// There is no need to take a lock because the partitions are not
// going away and the operation we are about to do is just turning
// on a one-way flag.
//
{
PARTITIONID partid = 1;
CPartition* pPart = _partList.LokGetPartition ( partid );
pPart->PrepareForCleanup();
}
//
// Stop in progress merges.
//
StopMerges();
//
// Dismount each of the partitions.
//
// ================================================
{
CPriLock lock( _mutex );
Win4Assert( !_isDismounted );
_isDismounted = TRUE;
CKeyList * pKeyList = _sKeyList.Acquire();
delete pKeyList;
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition * pPart = iter.LokGet();
// =======================================================
CChangeTrans xact( *this, pPart );
if ( STATUS_SUCCESS == pPart->LokDismount( xact ) )
{
xact.LokCommit();
}
//=========================================================
}
if ( _pBackupWorkItem )
_pBackupWorkItem->GetSaveProgressTracker().SetAbort();
}
// ================================================
_workMan.WaitForDeath();
}
CATCH( CException, e )
{
status = e.GetErrorCode();
ciDebugOut(( DEB_ERROR,
"ContentIndex Dismount Failed. Error Code 0x%X\n", status ));
}
END_CATCH
ciDebugOut(( DEB_ITRACE, "*** CI: Completed DisMount ***\n" ));
return status;
}
//+---------------------------------------------------------------------------
//
// Function: CResManager::Empty, public
//
// Synopsis: Releases resources associated with the resman object. This
// includes all indexes, the persistent freshlog, the persistant
// changelog, the freshtest, and the master merge log.
//
// History: 15-Aug-94 DwightKr Created
//
//----------------------------------------------------------------------------
void CResManager::Empty()
{
CPriLock lock( _mutex );
_isBeingEmptied = TRUE;
//
// Cancel any connections to the CI Filter service.
//
_pFilterAgent->LokCancel();
//
// Delete everything from the index table here first.
//
_idxTab->LokEmpty();
//
// If anything fails after this point, chkdsk /f or autochk will
// release the disk storage associated with the leaked objects
// since they are no longer part of the persistent index list.
//
//
// For each partition, zombify all indexes.
//
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition* pPart = iter.LokGet();
//
// Zombify all indexes in this partition, and delete them if their
// ref-count is 0.
//
CIndex ** aIndex = 0;
TRY
{
unsigned cIndex;
aIndex = pPart->LokZombify( cIndex );
ReleaseIndexes( cIndex, aIndex, NULL );
}
CATCH ( CException, e )
{
}
END_CATCH
delete [] aIndex;
//
// Delete the change log associated with this partition. These
// routines do not throw exceptions.
//
WORKID widChangeLog = _partList.GetChangeLogObjectId( pPart->GetId() );
if ( widChangeLog != widInvalid)
_storage.RemoveObject( widChangeLog );
WORKID widMMergeLog;
WORKID widDummy;
pPart->GetMMergeObjectIds( widMMergeLog, widDummy, widDummy );
if ( widMMergeLog != widInvalid)
_storage.RemoveObject( widMMergeLog );
}
_fresh.LokEmpty(); // Release storage associated with fresh test
WORKID widFreshLog = _storage.GetSpecialItObjectId( itFreshLog );
#ifdef KEYLIST_ENABLED
WORKID widKeyList = _storage.GetSpecialItObjectId( itMMKeyList );
#else
WORKID widKeyList = widInvalid;
#endif //
WORKID widPhrLat = _storage.GetSpecialItObjectId( itPhraseLat );
if ( widFreshLog != widInvalid)
_storage.RemoveObject( widFreshLog );
if ( widKeyList != widInvalid)
_storage.RemoveObject( widKeyList );
if ( widPhrLat != widInvalid)
_storage.RemoveObject( widPhrLat );
}
//+---------------------------------------------------------------------------
//
// Function: StopCurrentMerge
//
// Synopsis: Aborts any in-progress merge.
//
// History: 5-04-94 srikants Created
//
//----------------------------------------------------------------------------
NTSTATUS CResManager::StopCurrentMerge()
{
{
CPriLock lock( _mutex );
//
// If we're doing a merge right now then kill it off.
//
if ( 0 != _pMerge )
_pMerge->LokAbort();
}
return STATUS_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::DisableUpdates, public
//
// Arguments: [partid] -- partition id
//
// History: 24-Dec-94 Anonymous Created
//
//----------------------------------------------------------------------------
void CResManager::DisableUpdates( PARTITIONID partid )
{
CPriLock lock( _mutex );
CPartition * pPart = _partList.LokGetPartition( partid );
Win4Assert( 0 != pPart );
pPart->LokDisableUpdates();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::EnableUpdates, public
//
// Arguments: [partid] -- partition id
//
// History: 24-Dec-94 Anonymous Created
//
//----------------------------------------------------------------------------
void CResManager::EnableUpdates( PARTITIONID partid )
{
CPriLock lock( _mutex );
CPartition * pPart = _partList.LokGetPartition( partid );
Win4Assert( 0 != pPart );
pPart->LokEnableUpdates( _fFirstTimeUpdatesAreEnabled );
_fFirstTimeUpdatesAreEnabled = FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: LokCheckIfDiskLow
//
// Synopsis: Checks if we are running low on free disk space. It has
// a "LowWaterMark" and a "HighWaterMark".
//
// If current state is "not full", then it will check to
// see if the free disk space is < the LowWaterMark.
//
// if current state is "full", then it will check to see if
// the free disk space is < the HighWaterMark.
//
// The HighWaterMark is > the LowWaterMark to prevent hysterisis.
//
// Arguments: [resman] -- Resource manager
// [storage] -- Storage object.
// [fCurrent] -- Current state.
// [adviseStatus] -- reference to ICiCAdviseStatus
//
// Returns: TRUE if we are running low on disk space.
// FALSE otherwise.
//
// History: 10-11-94 srikants Created
// Jan-07-96 mohamedn CFwEventItem
//
//----------------------------------------------------------------------------
BOOL LokCheckIfDiskLow( CResManager & resman,
PStorage & storage,
BOOL fIsCurrentFull,
ICiCAdviseStatus & adviseStatus )
{
BOOL fLowOnDisk = fIsCurrentFull;
TRY
{
__int64 diskTotal, diskRemaining;
storage.GetDiskSpace( diskTotal, diskRemaining );
if ( !fIsCurrentFull )
fLowOnDisk = diskRemaining < lowDiskWaterMark;
else
fLowOnDisk = diskRemaining < highDiskWaterMark;
//
// It is okay to read it without mutex as it is only a heuristic.
//
if ( fLowOnDisk && !fIsCurrentFull && !storage.IsReadOnly() )
{
ciDebugOut(( DEB_WARN, "****YOU ARE RUNNING LOW ON DISK SPACE****\n"));
ciDebugOut(( DEB_WARN, "****PLEASE FREE UP SOME SPACE ****\n"));
ULONG mbToFree = highDiskWaterMark/(1024*1024);
ULONG mbIndex;
resman.IndexSize( mbIndex );
mbToFree = max( mbToFree, mbIndex );
mbToFree = min( mbToFree, 50 ); // don't ask for more than 50 MB
CFwEventItem item( EVENTLOG_AUDIT_FAILURE,
MSG_CI_LOW_DISK_SPACE,
2);
item.AddArg(storage.GetVolumeName() );
item.AddArg(mbToFree);
item.ReportEvent(adviseStatus);
}
else if ( fIsCurrentFull && !fLowOnDisk )
{
ciDebugOut(( DEB_WARN, "****DISK SPACE FREED UP ****\n"));
}
}
CATCH( CException,e )
{
ciDebugOut(( DEB_ERROR,
"Error 0x%X while getting disk space info\n",
e.GetErrorCode() ));
}
END_CATCH
return ( fLowOnDisk );
}
//+---------------------------------------------------------------------------
//
// Function: NoFailFreeResources
//
// Synopsis: deletes a big wordlist and schedules refiltering
//
// History: 6-Jan-95 BartoszM Created
//
//----------------------------------------------------------------------------
void CResManager::NoFailFreeResources()
{
ciDebugOut(( DEB_WARN, "Running out of resources. " ));
TRY
{
PARTITIONID partId = 1;
CPriLock lock(_mutex);
CPartition * pPart = LokGetPartition( partId );
//
// Before calling remove word list, we must append any refiled
// documents to the doc queue. This is because, the docqueue
// can handle only one set of refiled documents.
//
{
// =========================================
CChangeTrans xact( *this, pPart );
pPart->LokAppendRefiledDocs( xact );
xact.LokCommit();
// =========================================
}
LokNoFailRemoveWordlist( pPart );
}
CATCH( CException, e )
{
}
END_CATCH
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::IsMemoryLow, private
//
// Returns: TRUE if we're in a low memory situation
//
// History: 10-May-93 AmyA Created from old DoUpdates
// 3-May-96 dlee #if 0'ed it out because the memory
// load # isn't reliable, and we've
// got LOTS of other allocations anyway.
// 05-Nov-97 KyleP New approach
//
//--------------------------------------------------------------------------
BOOL CResManager::IsMemoryLow()
{
SCODE sc = _xLowRes->IsMemoryLow();
if ( E_NOTIMPL == sc )
sc = _xLowResDefault->IsMemoryLow();
return ( S_OK == sc );
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::IsIoHigh, private
//
// Returns: TRUE if the system is performing a 'lot' of I/O
//
// History: 10-Dec-97 KyleP Created
//
// Notes: This call takes time (~ 5 sec) to complete.
//
//--------------------------------------------------------------------------
BOOL CResManager::IsIoHigh()
{
SCODE sc = _xLowRes->IsIoHigh( &_fStopMerge );
if ( E_NOTIMPL == sc )
sc = _xLowResDefault->IsIoHigh( &_fStopMerge );
return ( S_OK == sc );
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::IsBatteryLow, private
//
// Returns: TRUE if the system is running low on battery power.
//
// History: 16-Jul-98 KyleP Created
//
// Notes: By default, even 100% battery (as opposed to A/C) may be
// considered low.
//
//--------------------------------------------------------------------------
BOOL CResManager::IsBatteryLow()
{
SCODE sc = _xLowRes->IsBatteryLow();
if ( E_NOTIMPL == sc )
sc = _xLowResDefault->IsBatteryLow();
return ( S_OK == sc );
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::IsOnBatteryPower, private
//
// Returns: TRUE if the system is running on battery power.
//
// History: 01-Oct-00 dlee Created
//
//--------------------------------------------------------------------------
BOOL CResManager::IsOnBatteryPower()
{
SCODE sc = _xLowRes->IsOnBatteryPower();
if ( E_NOTIMPL == sc )
sc = _xLowResDefault->IsOnBatteryPower();
return ( S_OK == sc );
} //IsOnBatteryPower
//+-------------------------------------------------------------------------
//
// Member: CResManager::SampleUserActivity, private
//
// Returns: - nothing -
//
// History: 31 Jul 98 AlanW Created
//
//--------------------------------------------------------------------------
void CResManager::SampleUserActivity()
{
SCODE sc = _xLowRes->SampleUserActivity();
if ( E_NOTIMPL == sc )
_xLowResDefault->SampleUserActivity();
return;
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::IsUserActive, private
//
// Arguments: [fCheckLongTerm] - TRUE if long-term activity should be checked.
//
// Returns: TRUE if the interactive user is using the keyboard or mouse
//
// History: 28 Jul 98 AlanW Created
//
//--------------------------------------------------------------------------
BOOL CResManager::IsUserActive(BOOL fCheckLongTerm)
{
SCODE sc = _xLowRes->IsUserActive(fCheckLongTerm);
if ( E_NOTIMPL == sc )
sc = _xLowResDefault->IsUserActive(fCheckLongTerm);
return ( S_OK == sc );
}
//======================
// STATE AND STATISTICS
//======================
//+-------------------------------------------------------------------------
//
// Member: CResManager::CountPendingUpdates, public
//
// Arguments: [secCount (output)] -- Count of secondary Q Updates
//
// Returns: Count of pending updates.
//
// History: 14-Dec-92 KyleP Created
//
// Notes: If wordlists are being constructed the count may be low.
//
//--------------------------------------------------------------------------
unsigned CResManager::CountPendingUpdates(unsigned& secCount)
{
CPriLock lock( _mutex );
unsigned count = 0;
CPartIter iter;
secCount = 0;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
count += iter.LokGet()->LokCountUpdates();
secCount += iter.LokGet()->LokCountSecUpdates();
}
return(count + secCount);
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::UpdateCounters, public
//
// Returns: Updates performance counters.
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CResManager::LokUpdateCounters()
{
if ( !_isDismounted )
{
_prfCounter.Update( CI_PERF_NUM_WORDLIST, _partList.LokWlCount() );
unsigned cSecQDocuments;
unsigned cUnfilteredDocs = CountPendingUpdates( cSecQDocuments );
_prfCounter.Update( CI_PERF_FILES_TO_BE_FILTERED, cUnfilteredDocs );
_prfCounter.Update( CI_PERF_DEFERRED_FILTER_FILES, cSecQDocuments );
_prfCounter.Update( CI_PERF_NUM_UNIQUE_KEY, _sKeyList->MaxKeyIdInUse() );
_prfCounter.Update( CI_PERF_DOCUMENTS_FILTERED, _cFilteredDocuments );
ULONG cDocuments;
GetClientState( cDocuments );
_prfCounter.Update( CI_PERF_NUM_DOCUMENTS, cDocuments );
}
}
//====================
// UPDATES AND QUERIES
//====================
//+---------------------------------------------------------------------------
//
// Member: CResManager::ReserveUpdate, public
//
// Arguments: [wid] -- Used to confirm hint.
//
// Returns: Hint to position of reserved slot.
//
// History: 30-Aug-95 KyleP Created
//
//----------------------------------------------------------------------------
unsigned CResManager::ReserveUpdate( WORKID wid )
{
CPriLock lock ( _pendQueue.GetMutex() );
return _pendQueue.LokPrepare( wid );
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::UpdateDocument, public
//
// Arguments: [iHint] -- Positional hint
// [wid] -- wid to add/delete
// [partid] -- partition containing this wid
// [usn] -- USN associated with change
// [volumeId] -- Volume Id
// [action] -- addition / deletion
//
// History: 08-Apr-91 BartoszM Created
// 08-Oct-93 BartoszM Rewrote to accept single document
// 30-Aug-95 KyleP Use reserved slots
//
//----------------------------------------------------------------------------
SCODE CResManager::UpdateDocument( unsigned iHint,
WORKID wid,
PARTITIONID partid,
USN usn,
VOLUMEID volumeId,
ULONG action )
{
if ( !IsIndexingEnabled() )
return CI_E_FILTERING_DISABLED;
Win4Assert ( partid == 1 );
//
// Try to avoid lock contention
// with filter agent
//
_pFilterAgent->SlowDown();
CPriLock lock ( _mutex );
BOOL fComplete;
{
CPriLock lock2( _pendQueue.GetMutex() );
if ( _isBeingEmptied ) // The content index is empty
{
//
// Just get rid of any pending entries in the queue.
//
if ( !_pendQueue.LokComplete( iHint, wid, usn, volumeId, partid, action ) )
{
while ( _pendQueue.LokRemove( wid, usn, volumeId, partid, action ) )
;
}
return CI_E_SHUTDOWN;
}
//
// The if clause triggers when this document is the only one
// on the queue.
//
fComplete = _pendQueue.LokComplete( iHint, wid, usn, volumeId, partid, action );
}
SCODE sc = S_OK;
if ( fComplete )
{
CPartition* pPart = _partList.LokGetPartition ( partid );
CChangeTrans xact( *this, pPart );
sc = pPart->LokUpdateDocument ( xact, wid, usn, volumeId, action, 1, 0 );
xact.LokCommit();
_pFilterAgent->LokWakeUp();
}
else
{
BOOL fGotOne = FALSE;
CPendQueueTrans pendQueueTrans( _pendQueue );
while ( TRUE )
{
BOOL fGotAnother;
{
CLock lock2( _pendQueue.GetMutex() );
fGotAnother = _pendQueue.LokRemove( wid, usn, volumeId, partid, action );
}
if ( fGotAnother )
{
fGotOne = TRUE;
CPartition* pPart = _partList.LokGetPartition ( partid );
CChangeTrans xact( *this, pPart );
sc = pPart->LokUpdateDocument ( xact, wid, usn, volumeId, action, 1, 0 );
xact.LokCommit();
}
else
break;
}
pendQueueTrans.Commit();
if ( fGotOne )
_pFilterAgent->LokWakeUp();
}
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::FlushUpdates
//
// Synopsis: Flushes all update notifications to disk
//
// History: 27-Jun-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CResManager::FlushUpdates()
{
CPriLock lock( _mutex );
CPartition * pPart = _partList.LokGetPartition( partidDefault );
Win4Assert( 0 != pPart );
pPart->LokFlushUpdates();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::QueryIndexes, public
//
// Arguments: [cPartitions] -- count of partitions
// [aPartID] -- array of partition id's
// [freshTest] -- return arg.: fresh test
// [cInd] -- return arg: count of indexes
// [cPendingUpdates] -- Pending update 'threshold'. If fewer
// pending updates, the pending wids are
// returned in *pcurPending.
// [pcurPending] -- Pending cursors stored here.
// [pFlags] -- return arg: status of indexes
//
// Returns: Array of pointers to indexes
//
// History: 08-Oct-91 BartoszM Created
//
// Notes: Called by Query
// Indexes and fresh test have ref count increased
// Flags may change to indicate status of indexes
//
//----------------------------------------------------------------------------
CIndex** CResManager::QueryIndexes (
unsigned cPartitions,
PARTITIONID aPartID[],
CFreshTest** freshTest,
unsigned& cInd,
ULONG cPendingUpdates,
CCurStack * pcurPending,
ULONG* pFlags )
{
unsigned count = 0;
CPartition* pPart;
CPriLock lock ( _mutex );
if ( _isCorrupt )
THROW( CException( CI_CORRUPT_DATABASE ) );
if ( _isBeingEmptied ) // Content index is empty or corrupt
{
cInd = 0;
cPartitions = 0;
return 0;
}
for ( unsigned i = 0; i < cPartitions; i++ )
{
pPart = _partList.LokGetPartition( aPartID [i] );
count += pPart->LokIndexCount();
}
XArray<CIndex *> apIndex( count );
#if CIDBG || DBG
unsigned j = 0;
#endif
for ( i = 0; i < cPartitions; i++ )
{
pPart = _partList.LokGetPartition(aPartID [i]);
#if CIDBG || DBG
j +=
#endif // CIDBG || DBG
pPart->LokGetIndexes ( &apIndex[i] );
if ( LokIsScanNeeded() && !_storage.IsReadOnly() )
*pFlags |= CI_NOT_UP_TO_DATE;
//
// If we're looking for pending updates, get them. Otherwise just
// set the out-of-date flag.
//
unsigned cPending = pPart->LokCountUpdates();
cPending += pPart->LokCountSecUpdates();
if ( cPending != 0 )
{
ULONG fFlags = CI_NOT_UP_TO_DATE; // assume non-success
if ( cPendingUpdates > 0 )
{
Win4Assert( 0 != pcurPending );
if ( cPending <= cPendingUpdates )
{
XArray<WORKID> pWid( cPending );
BOOL fSucceeded = pPart->LokGetPendingUpdates(
pWid.GetPointer(),
cPending );
if ( fSucceeded )
{
Win4Assert( cPending > 0 );
XPtr<CPendingCursor> xCursor( new CPendingCursor( pWid, cPending ) );
pcurPending->Push( xCursor.GetPointer() );
xCursor.Acquire();
fFlags = 0;
}
}
}
*pFlags |= fFlags;
}
}
//
// Also check to see if we are actually in the process of filtering some
// documents.
//
if ( _docList.Count() > 0 )
{
if ( _docList.Count() <= cPendingUpdates )
{
Win4Assert( 0 != pcurPending );
XArray<WORKID> pWid( _docList.Count() );
ciDebugOut((DEB_FILTERWIDS,
"CResManager::QueryIndexes - pending documents: %d %x\n",
_docList.Count(), pWid.GetPointer()));
_docList.LokGetWids( pWid );
XPtr<CPendingCursor> xCursor( new CPendingCursor( pWid, _docList.Count() ) );
pcurPending->Push( xCursor.GetPointer() );
xCursor.Acquire();
}
else
*pFlags |= CI_NOT_UP_TO_DATE;
}
//
// Also check documents pending notifcation that may be complete but
// out-of-order.
//
{
CPriLock lock( _pendQueue.GetMutex() );
unsigned cwidPending = _pendQueue.LokCountCompleted();
if ( cwidPending > 0 )
{
if ( cwidPending <= cPendingUpdates )
{
Win4Assert( 0 != pcurPending );
XArray<WORKID> pWid( cwidPending );
_pendQueue.LokGetCompleted( pWid.GetPointer() );
XPtr<CPendingCursor> xCursor( new CPendingCursor( pWid, cwidPending ) );
pcurPending->Push( xCursor.GetPointer() );
xCursor.Acquire();
}
else
*pFlags |= CI_NOT_UP_TO_DATE;
}
}
//
// Finally, check for pending scans.
//
if ( _isOutOfDate )
*pFlags |= CI_NOT_UP_TO_DATE;
Win4Assert ( j == count );
*freshTest = _fresh.LokGetFreshTest();
cInd = count;
return apIndex.Acquire();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::ReleaseIndexes, public
//
// Synopsis: Decrements ref counts, deletes if indexes
// marked to be deleted.
//
// Arguments: [cInd] -- count of indexes
// [apIndex] -- array of indexes
// [freshTest] -- fresh test
//
// History: 08-Oct-91 BartoszM Created
//
// Notes: Takes ResMan lock
//
//----------------------------------------------------------------------------
void CResManager::ReleaseIndexes ( unsigned cInd, CIndex** apIndex,
CFreshTest* pFreshTest )
{
ciDebugOut (( DEB_ITRACE, "Release Indexes\n" ));
CPriLock lock ( _mutex );
for ( unsigned i = 0; i < cInd; i++ )
{
if ( apIndex[i] != 0 )
LokReleaseIndex ( apIndex[i] );
}
if ( pFreshTest)
_fresh.LokReleaseFreshTest (pFreshTest);
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::BackupContentIndexData
//
// Synopsis: Private method called by the merge thread to backup the
// content index data. If successful, it sets the status of
// the backup work item to indicate success. Otherwise, it
// must be considered to have failed.
//
// History: 3-18-97 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::BackupContentIndexData()
{
Win4Assert( 0 != _pBackupWorkItem );
ciDebugOut(( DEB_WARN, "Starting Backup of Ci Data\n" ));
//
// Create the backup object.
//
CPartition *pPartition =_partList.LokGetPartition ( partidDefault );
CBackupCiPersData backup( *_pBackupWorkItem,
*this,
*pPartition );
// =========================================
{
CPriLock lock(_mutex);
backup.LokGrabResources();
}
// =========================================
backup.BackupIndexes();
// =========================================
{
CPriLock lock(_mutex);
backup.LokBackupMetaInfo();
ciDebugOut(( DEB_WARN, "Completed Backup of Ci Data\n" ));
_pBackupWorkItem->LokSetStatus( STATUS_SUCCESS );
}
// =========================================
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokUpdateBackupMergeProgress
//
// Synopsis: Updates the backup progress during the merge.
//
// History: 3-20-97 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokUpdateBackupMergeProgress()
{
Win4Assert( 0 != _pBackupWorkItem );
CCiFrmPerfCounter counter( _adviseStatus.GetPointer(),
CI_PERF_MERGE_PROGRESS );
DWORD dwMergeProgress = counter.GetCurrValue();
//
// Assuming Merge is half of the work, we will assume half
// progress. So make the denominator 200 instead of 100.
//
if ( _pMerge )
{
const cShadowMergePart = 5;
if ( _pBackupWorkItem->IsFullSave() )
{
if ( _pMerge->GetMergeType() != mtMaster )
{
//
// The shadow merge preceding a master is approx. 5%
// say.
//
dwMergeProgress = cShadowMergePart;
}
else
{
dwMergeProgress = min(dwMergeProgress + cShadowMergePart, 100);
}
}
_pBackupWorkItem->LokUpdateMergeProgress( (ULONG) dwMergeProgress, 100 );
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::BackupCIData
//
// Synopsis: External method to backup content index data. It creates
// a backup work item and waits for the backup to complete.
//
// Arguments: [storage] - Destination storage
// [fFull] - [in/out] Set to TRUE if a full merge is
// needed on input; On output will be set to TRUE if a full
// merge was done.
// [progressTracker] - Progress tracker.
//
// History: 3-18-97 srikants Created
//
//----------------------------------------------------------------------------
SCODE CResManager::BackupCIData( PStorage & storage,
BOOL & fFull,
XInterface<ICiEnumWorkids> & xEnumWorkids,
PSaveProgressTracker & progressTracker )
{
if ( !IsIndexMigrationEnabled() || !IsIndexingEnabled() )
return CI_E_INVALID_STATE;
SCODE sc = S_OK;
Win4Assert( fFull );
TRY
{
// ===================================================
{
CLock lock(_mutex);
if ( _isDismounted )
THROW( CException( CI_E_SHUTDOWN ) );
// There can be only one operation going on at a time.
if ( 0 != _pBackupWorkItem )
THROW( CException( CI_E_INVALID_STATE ) );
_pBackupWorkItem = new CBackupCiWorkItem( storage,
fFull,
progressTracker );
_eventMerge.Set();
}
// ===================================================
ciDebugOut(( DEB_WARN, "Waiting for backup to complete\n" )) ;
//
// Wait for the work-item to be completed.
//
while ( TRUE )
{
DWORD const dwWaitTime = 1 * 60 * 1000; // 1 minute in millisecs
_pBackupWorkItem->WaitForCompletion( dwWaitTime );
//============================================================
{
CPriLock lock(_mutex);
if ( _pBackupWorkItem->LokIsDone() )
break;
//
// If there is a merge going on, we must update the progress.
//
if ( _pBackupWorkItem->LokIsDoingMerge() )
LokUpdateBackupMergeProgress();
_pBackupWorkItem->LokReset();
}
//============================================================
} // of while
sc= _pBackupWorkItem->GetStatus();
ciDebugOut(( DEB_WARN, "Backup completed with code 0x%X\n", sc ));
if ( S_OK == sc )
{
fFull = _pBackupWorkItem->IsFullSave();
xEnumWorkids.Set( _pBackupWorkItem->AcquireWorkIdEnum() );
}
// ====================================
{
CLock lock(_mutex);
delete _pBackupWorkItem;
_pBackupWorkItem = 0;
}
// ====================================
}
CATCH( CException,e )
{
sc = HRESULT_FROM_WIN32( e.GetErrorCode() );
Win4Assert( !"How Did we Come Here" );
}
END_CATCH
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::CiState, public
//
// Arguments: [state] -- internal state of the CI
//
// History: 01-Nov-95 DwightKr Created
//
//
//----------------------------------------------------------------------------
#define setState( field, value ) \
if ( state.cbStruct >= ( offsetof( CIF_STATE, field) + \
sizeof( state.field ) ) ) \
{ \
state.field = ( value ); \
}
#define roundup(a, b) ((a%b) ? (a/b + 1) : (a/b))
NTSTATUS CResManager::CiState(CIF_STATE & state)
{
CPriLock lock( _mutex );
if ( _isDismounted )
return CI_E_SHUTDOWN;
if ( _isCorrupt )
return CI_CORRUPT_DATABASE;
setState( cWordList, _partList.LokWlCount() );
setState( cPersistentIndex, _partList.LokIndexCount() - state.cWordList );
// Get the perf counter for the # of queries executed
Win4Assert( !_adviseStatus.IsNull() );
long cQ;
_adviseStatus->GetPerfCounterValue( CI_PERF_TOTAL_QUERIES, &cQ );
setState( cQueries, cQ );
unsigned cSecQDocuments;
ULONG cUnfilteredDocs = CountPendingUpdates(cSecQDocuments) + _docList.Count();
setState( cDocuments, cUnfilteredDocs );
setState( cSecQDocuments, cSecQDocuments );
setState( cFreshTest, LokGetFreshCount() );
CCiFrmPerfCounter counter( _adviseStatus.GetPointer(),
CI_PERF_MERGE_PROGRESS );
setState( dwMergeProgress, (ULONG) counter.GetCurrValue() );
setState( cFilteredDocuments, _cFilteredDocuments );
unsigned size = roundup(_partList.LokIndexSize(), ((1024*1024)/CI_PAGE_SIZE));
setState( dwIndexSize, size);
setState( cUniqueKeys, _sKeyList->MaxKeyIdInUse() );
state.cbStruct = min( state.cbStruct, sizeof(state) );
DWORD eCiState = _dwFilteringState;
//
// Set each of the state bits independently.
//
//
// Are we in a shadow or master merge?
//
if ( 0 != _pMerge )
{
switch (_pMerge->GetMergeType() )
{
case mtShadow:
eCiState |= CIF_STATE_SHADOW_MERGE;
break;
case mtAnnealing:
eCiState |= CIF_STATE_ANNEALING_MERGE;
break;
case mtIncrBackup:
eCiState |= CIF_STATE_INDEX_MIGRATION_MERGE;
break;
default:
eCiState |= CIF_STATE_MASTER_MERGE;
break;
}
}
else
{
CPartition *pPartition = _partList.LokGetPartition ( partidDefault );
if ( pPartition->InMasterMerge() )
eCiState |= CIF_STATE_MASTER_MERGE_PAUSED;
}
if ( LokIsScanNeeded() )
eCiState |= CIF_STATE_CONTENT_SCAN_REQUIRED;
setState( eState, (CIF_STATE_FLAGS) eCiState );
return STATUS_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::IndexSize
//
// Synopsis: Computes the size of the indexes in all partitions
//
// Arguments: [mbIndex] - On output, will have the size of all indexes
// in MBs.
//
// History: 4-15-96 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::IndexSize( ULONG & mbIndex )
{
CLock lock(_mutex);
mbIndex = _partList.LokIndexSize() / ((1024*1024)/CI_PAGE_SIZE);
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokCheckWordlistQuotas, public
//
// Synopsis: Determine if we've reached wordlist capacity.
//
// Returns: TRUE if we have as much wordlist data in memory as
// parameters allow.
//
// History: 14-Jan-1999 KyleP Created
//
//----------------------------------------------------------------------------
BOOL CResManager::LokCheckWordlistQuotas()
{
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList) )
{
CPartition* pPart = iter.LokGet();
if ( pPart->LokCheckWordlistMerge() )
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::MarkCorruptIndex
//
// Synopsis: Marks the index as corrupt, disables updates in all partitions
// and wakes up the merge thread to notify the framework client
// about the corruption.
//
// History: 1-30-97 srikants Created
//
//----------------------------------------------------------------------------
NTSTATUS CResManager::MarkCorruptIndex()
{
CPriLock lock(_mutex);
//
// Disable updates in all partitions
//
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition * pPart = iter.LokGet();
pPart->LokDisableUpdates();
}
if ( _state.FLokCorruptionNotified() )
return S_OK;
_isCorrupt = TRUE;
StopCurrentMerge();
_eventMerge.Set();
return S_OK;
}
//=====================
// KEYINDEX
//=====================
#ifdef KEYLIST_ENABLED
//+---------------------------------------------------------------------------
//
// Member: CResManager::_AddRefKeyList, private
//
// Synopsis: Adds to refcount on the keylist
//
// History: 07-Jul-94 dlee Created
//
//----------------------------------------------------------------------------
CKeyList * CResManager::_AddRefKeyList()
{
CPriLock lock( _mutex );
_sKeyList->Reference();
return _sKeyList.GetPointer();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::_ReleaseKeyList, private
//
// Synopsis: Release a refcount on the keylist
//
// History: 07-Jul-94 dlee Created
//
//----------------------------------------------------------------------------
void CResManager::_ReleaseKeyList()
{
CPriLock lock( _mutex );
_sKeyList->Release();
if ( !_sKeyList->InUse() && _sKeyList->IsZombie() )
{
ciDebugOut(( DEB_ITRACE,
"Keylist %x unreferenced zombie. Deleting\n",
_sKeyList->GetId() ));
CKeyList * pKeyList = _sKeyList.Acquire();
pKeyList->Remove();
delete pKeyList;
}
}
#endif // KEYLIST_ENABLED
//+---------------------------------------------------------------------------
//
// Member: CResManager::KeyToId, public
//
// Synopsis: Maps from a key to an id.
//
// Arguments: [pkey] -- pointer to the key to be mapped to ULONG
//
// Returns: key id - a ULONG
//
// History: 03-Nov-93 w-Patg Created.
// 17-Feb-94 KyleP Initial version
//
//----------------------------------------------------------------------------
ULONG CResManager::KeyToId( CKey const * pkey )
{
#ifdef KEYLIST_ENABLED
CKeyList * pKeyList = _AddRefKeyList();
ULONG kid = pKeyList->KeyToId( pkey );
_ReleaseKeyList();
return( kid );
#else
return widInvalid;
#endif // KEYLIST_ENABLED
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::IdToKey, public
//
// Synopsis: Maps from an id to a key.
//
// Arguments: [ulKid] -- key id to be mapped to a key
// [rkey] -- reference to the returned key
//
// Returns: void
//
// History: 03-Nov-93 w-Patg Created.
// 17-Feb-94 KyleP Initial version
//
//----------------------------------------------------------------------------
void CResManager::IdToKey( ULONG ulKid, CKey & rkey )
{
#ifdef KEYLIST_ENABLED
CKeyList * pKeyList = _AddRefKeyList();
pKeyList->IdToKey( ulKid, rkey );
_ReleaseKeyList();
#else
rkey.FillMin();
#endif // KEYLIST_ENABLED
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::ComputeRelevantWords, public
//
// Synopsis: Computes and returns relevant words for a set of wids
//
// Arguments: [cRows] -- # of rows to compute and in pwid array
// [cRW] -- # of rw per wid
// [pwid] -- array of wids to work over
// [partid] -- partition
//
// History: 10-May-94 v-dlee Created
//
//--------------------------------------------------------------------------
CRWStore * CResManager::ComputeRelevantWords(ULONG cRows,ULONG cRW,
WORKID *pwids,
PARTITIONID partid)
{
CRWStore *prws = 0;
#ifdef KEYLIST_ENABLED
CPriLock lock( _mutex );
CPartition *pPart = LokGetPartition(partid);
if (pPart != 0)
{
CPersIndex *pIndex= pPart->GetCurrentMasterIndex();
if (pIndex != 0)
{
prws = pIndex->ComputeRelevantWords(cRows,cRW,pwids,
_sKeyList.GetPointer() );
}
}
#endif // KEYLIST_ENABLED
return prws;
} //ComputeRelevantWords
//+-------------------------------------------------------------------------
//
// Member: CRWStore::RetrieveRelevantWords, public
//
// Synopsis: Retrieves relevant words already computed
//
// Arguments: [fAcquire] -- TRUE if ownership is transferred.
// [partid] -- partition
//
// History: 10-May-94 v-dlee Created
//
//--------------------------------------------------------------------------
CRWStore * CResManager::RetrieveRelevantWords(BOOL fAcquire,
PARTITIONID partid)
{
CRWStore *prws = 0;
#ifdef KEYLIST_ENABLED
CPriLock lock( _mutex );
CPartition *pPart = LokGetPartition(partid);
if (pPart != 0)
{
prws = pPart->RetrieveRelevantWords(fAcquire);
}
#endif // KEYLIST_ENABLED
return prws;
} //RetrieveRelevantWords
//================
// PRIVATE METHODS
//================
//+-------------------------------------------------------------------------
//
// Member: CResManager::CompactResources
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CResManager::CompactResources()
{
CPriLock lock( _mutex );
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
iter.LokGet()->LokCompact();
}
}
//=================================//
// MERGES //
//=================================//
//+---------------------------------------------------------------------------
//
// Member: CResManager::DoMerges, private
//
// History: 08-Apr-91 BartoszM Created
// 25-Feb-92 BartoszM Rewrote to use thread
// 30-Jun-97 KrishnaN Calling docstore.FlushPropertyStore
// before a shadow merge.
// 03-Mar-98 KitmanH Don't merge if _storage is read-only
//
// Notes: Entry point for captive threads
//
//----------------------------------------------------------------------------
void CResManager::DoMerges()
{
if ( _storage.IsReadOnly() )
return;
CCiFrmPerfCounter pPerIndexCounter( _adviseStatus.GetPointer(), CI_PERF_NUM_PERSISTENT_INDEXES );
CCiFrmPerfCounter pIndexSizeCounter( _adviseStatus.GetPointer(), CI_PERF_INDEX_SIZE );
CCiFrmPerfCounter pPendingCounter( _adviseStatus.GetPointer(), CI_PERF_FILES_TO_BE_FILTERED );
CCiFrmPerfCounter pNumKeysCounter( _adviseStatus.GetPointer(), CI_PERF_NUM_UNIQUE_KEY);
CCiFrmPerfCounter pWordListCounter(_adviseStatus.GetPointer(), CI_PERF_NUM_WORDLIST);
CCiFrmPerfCounter pDeferredFilesCounter( _adviseStatus.GetPointer(), CI_PERF_DEFERRED_FILTER_FILES );
XPtr<CIdleTime> xIdle( new CIdleTime() );
//
// Allow mount to complete before checking for merges. O/W we will
// start a merge before the mount/startup is complete and it might
// prevent a system from coming up.
//
const lowDiskPollTime = 1; // 1 minute
DWORD dwWaitTime = _isLowOnDiskSpace ? lowDiskPollTime : _frmwrkParams.GetMaxMergeInterval();
_eventMerge.Wait( dwWaitTime * 1000 * 60 );
BOOL fAnnealing = FALSE;
while ( !_fStopMerge )
{
BOOL fCorrupt = FALSE;
ciDebugOut (( DEB_ITRACE, "\t|Merge Wakeup!\n" ));
//
// Book keeping chores.
//
BOOL fBackupCiData = FALSE;
BOOL fBackupStarted = FALSE;
TRY
{
// ===============================================
CPriLock lock(_mutex);
if ( _isCorrupt )
{
if ( !_state.FLokCorruptionNotified() )
LokNotifyCorruptionToClient();
}
else
{
//
// Use low disk condition plus the current state to decide if
// updates should be enabled or not
//
_isLowOnDiskSpace = LokCheckIfDiskLow( *this,
_storage,
_isLowOnDiskSpace,
_adviseStatus.GetReference() );
if ( _isLowOnDiskSpace )
{
if ( _state.LokGetState() != eUpdatesToBeDisabled
&& _state.LokGetState() != eUpdatesDisabled )
{
_state.LokSetState( eUpdatesToBeDisabled );
_state.LokSetUpdateType( eIncremental );
}
}
else
{
if ( _state.LokGetState() == eUpdatesDisabled )
_state.LokSetState( eUpdatesToBeEnabled );
}
if ( _state.LokGetState() == eUpdatesToBeDisabled )
LokNotifyDisableUpdatesToClient();
else if ( _state.LokGetState() == eUpdatesToBeEnabled )
LokNotifyEnableUpdatesToClient();
}
fBackupCiData = 0 != _pBackupWorkItem;
// ===============================================
}
CATCH( CException, e )
{
// ignore and try again the next time through the loop.
}
END_CATCH
TRY
{
if ( !_isCorrupt && _partidToMerge != partidInvalid )
{
ciDebugOut (( DEB_ITRACE, "Forced merge in %ld\n",
_partidToMerge ));
switch ( _mtForceMerge )
{
case CI_MASTER_MERGE:
MasterMerge ( _partidToMerge );
break;
case CI_SHADOW_MERGE:
Merge( mtShadow, _partidToMerge );
break;
case CI_ANNEALING_MERGE:
Merge( mtAnnealing, _partidToMerge );
break;
case CI_ANY_MERGE:
break; // Do nothing. Let normal mechanism work it out.
default:
Win4Assert( !"Invalid ForceMerge type" );
}
}
if ( !_isLowOnDiskSpace && !_isCorrupt )
{
//
// Refile the documents from the secondary queue to the primary
// queue in pull filtering.
//
// ======================= lock ======================
if ( !_fPushFiltering )
{
CLock lock(_mutex);
LokRefileSecQueueDocs();
}
// ======================= unlock ======================
if ( fBackupCiData )
{
BOOL fIsMasterPresent = FALSE;
BOOL fIsMMergeInProgress = FALSE;
//================================
{
CPriLock lock(_mutex);
_pBackupWorkItem->LokSetDoingMerge();
fIsMasterPresent = LokIsMasterIndexPresent( partidDefault );
fIsMMergeInProgress = LokIsMasterMergeInProgress( partidDefault );
}
//================================
if ( _pBackupWorkItem->IsFullSave() ||
fIsMMergeInProgress ||
!fIsMasterPresent )
{
_pBackupWorkItem->SetDoingFullSave();
MasterMerge( partidDefault );
}
else
{
Merge( mtIncrBackup, partidDefault );
}
fBackupStarted = TRUE;
BackupContentIndexData();
}
else
{
if ( fAnnealing )
{
fAnnealing = FALSE;
CPartIdStack partitionIds;
CheckAndDoMerge( mtAnnealing, partitionIds );
while ( partitionIds.Count() > 0 )
Merge( mtAnnealing, partitionIds.Pop() );
}
{
CPartIdStack partitionIds;
CheckAndDoMerge( mtShadow, partitionIds );
while ( partitionIds.Count() > 0 )
{
// Call FlushPropertyStore on DocStore to give it
// a chance to persist changes before a shadow merge.
_docStore->FlushPropertyStore();
Merge( mtShadow, partitionIds.Pop() );
}
}
{
if ( CheckDeleteMerge( _partidToMerge != partidInvalid ) )
{
// Call FlushPropertyStore on DocStore to give it
// a chance to persist changes before a shadow merge.
_docStore->FlushPropertyStore();
Merge( mtDeletes, 1 );
}
}
BOOL fIsMasterPresent = FALSE;
BOOL fIsMMergeInProgress = FALSE;
//================================
{
CPriLock lock(_mutex);
fIsMasterPresent = LokIsMasterIndexPresent( partidDefault );
fIsMMergeInProgress = LokIsMasterMergeInProgress( partidDefault );
}
//================================
{
CPartIdStack partitionIds;
CheckAndDoMerge( mtMaster, partitionIds );
while ( partitionIds.Count() > 0 )
MasterMerge( partitionIds.Pop() );
}
}
}
}
CATCH ( CException, e )
{
ciDebugOut (( DEB_ERROR,
"ResMan::DoMerges -- merge failed with 0x%x\n",
e.GetErrorCode() ));
if ( IsDiskFull(e.GetErrorCode()) )
{
ciDebugOut(( DEB_ERROR, "***** DISK IS FULL *****\n" ));
CPriLock lock(_mutex);
_isLowOnDiskSpace = LokCheckIfDiskLow( *this,
_storage,
_isLowOnDiskSpace,
_adviseStatus.GetReference() );
}
else if ( IsCorrupt( e.GetErrorCode() ) )
{
Win4Assert( "!Merge failed, but not for low disk. Why?" );
fCorrupt = TRUE;
}
//
// We have to prevent a master merge from being restarted
// immediately. The reason for failure could be something like
// out of disk space (typical reason) or log full. In either
// case we will only be exacerbating the situation if we
// restart immediately.
//
_eventMerge.Reset();
}
END_CATCH
// ==========================================================
{
CPriLock lock( _mutex );
Win4Assert( _partList.LokIndexCount() >= _partList.LokWlCount() );
pPerIndexCounter.Update(_partList.LokIndexCount()-_partList.LokWlCount());
pIndexSizeCounter.Update(_partList.LokIndexSize() / ((1024*1024)/CI_PAGE_SIZE) );
pNumKeysCounter.Update(_sKeyList->MaxKeyIdInUse());
pWordListCounter.Update(_partList.LokWlCount());
_partidToMerge = partidInvalid;
if ( _fStopMerge )
{
ciDebugOut(( DEB_ITRACE, "Stopping Merge\n" ));
break;
}
if ( !_isCorrupt && ( _state.LokGetState() == eSteady ) && ( 0 != _pFilterAgent ) )
_pFilterAgent->LokWakeUp();
_eventMerge.Reset(); // sleep
if ( fBackupStarted || _isCorrupt )
{
//
// Set that backup to be complete even if it failed. Once
// Backup starts, it either succeeds or fails but in either
// case the operation is considered done.
//
if ( 0 != _pBackupWorkItem )
{
if ( _isCorrupt )
_pBackupWorkItem->LokSetStatus( CI_CORRUPT_DATABASE );
_pBackupWorkItem->LokSetDone();
}
}
}
// ==========================================================
BOOL fSteadyState = TRUE;
if ( fCorrupt )
{
CPriLock lock(_mutex);
_isCorrupt = TRUE;
fSteadyState = _state.LokGetState() == eSteady;
}
unsigned cSecQCount;
unsigned cPending = CountPendingUpdates( cSecQCount );
//
// Wait for either the merge event to be signaled or the deadman timeout.
//
dwWaitTime = ( ( _isLowOnDiskSpace ) ||
( !fSteadyState ) ||
( 0 != cPending ) ) ?
lowDiskPollTime : _frmwrkParams.GetMaxMergeInterval();
NTSTATUS Status = _eventMerge.Wait( dwWaitTime * 60 * 1000 );
if ( !_isCorrupt )
{
unsigned PercentIdle = xIdle->PercentIdle();
//
// Are we idle enough to consider an annealing merge?
//
if ( STATUS_TIMEOUT == Status && PercentIdle >= _frmwrkParams.GetMinMergeIdleTime() )
{
ciDebugOut(( DEB_ITRACE, "Idle time for period: %u percent\n", PercentIdle ));
fAnnealing = TRUE;
}
}
if ( _fStopMerge )
{
ciDebugOut(( DEB_ITRACE, "Stopping Merge\n" ));
break;
}
cPending = CountPendingUpdates( cSecQCount );
pPendingCounter.Update(cPending);
pDeferredFilesCounter.Update(cSecQCount);
ciDebugOut (( DEB_ITRACE | DEB_PENDING,
"%d updates pending\n", cPending ));
}
// ===========================================
{
CPriLock lock2(_mutex);
if ( _pBackupWorkItem )
{
ciDebugOut(( DEB_WARN, "Forcing the backup to be done\n" ));
_pBackupWorkItem->LokSetDone();
}
}
// ===========================================
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::StopMerges, private
//
// Synopsis: Aborts merge-in-progress (if any) and sets merge flag.
//
// History: 13-Aug-93 KyleP Created
//
//--------------------------------------------------------------------------
void CResManager::StopMerges()
{
{
CPriLock lock( _mutex );
_fStopMerge = TRUE;
_eventMerge.Set(); // wake up
//
// If we're doing a merge right now then kill it off.
//
if ( 0 != _pMerge )
{
_pMerge->LokAbort();
}
}
//
// Wait for merge thread to die.
//
_thrMerge.WaitForDeath();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::CheckAndDoMerge, private
//
// History: 08-Apr-91 BartoszM Created
//
// Notes: Called by captive thread.
//
//----------------------------------------------------------------------------
void CResManager::CheckAndDoMerge( MergeType mt, CPartIdStack & partitionIds)
{
if ( !IsBatteryLow() )
{
CPriLock lock( _mutex );
ciDebugOut (( DEB_ITRACE, "check merge\n" ));
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition* pPart = iter.LokGet();
if ( mt == mtMaster )
{
__int64 shadowIndexSize = pPart->LokIndexSize();
CPersIndex * pMasterIndex = pPart->GetCurrentMasterIndex();
if ( pMasterIndex )
shadowIndexSize -= pMasterIndex->Size();
shadowIndexSize *= CI_PAGE_SIZE;
CMasterMergePolicy mergePolicy( _storage, shadowIndexSize,
LokGetFreshCount(),
_mergeTime,
_frmwrkParams,
_adviseStatus.GetReference() );
if ( mergePolicy.IsTimeToMasterMerge() ||
( mergePolicy.IsMidNightMergeTime() &&
!IsOnBatteryPower() ) )
partitionIds.Push( pPart->GetId() );
}
else
{
if ( pPart->LokCheckMerge(mt) ||
( IsMemoryLow() && pPart->LokCheckLowMemoryMerge() ) )
{
#if CIDBG == 1
if ( !pPart->LokCheckMerge(mt) )
{
ciDebugOut(( DEB_WARN, "Merge due to low memory. %u wordlists, %u shadow.\n",
pPart->WordListCount(), pPart->LokIndexCount() ));
}
#endif
partitionIds.Push( pPart->GetId() );
}
}
}
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::CheckDeleteMerge, private
//
// Synopsis: Merge check for delete merge
//
// Arguments: [fForce] -- If TRUE, even 1 pending delete will force merge.
//
// Returns: TRUE if merge should be performed.
//
// History: 12-Jun-97 KyleP Created
//
// Notes: Called by captive thread.
//
//----------------------------------------------------------------------------
BOOL CResManager::CheckDeleteMerge( BOOL fForce )
{
CPriLock lock( _mutex );
ciDebugOut (( DEB_ITRACE, "check delete merge\n" ));
//
// A 'delete merge' occurs when enough documents have been deleted, and no
// documents have been added or modified (which would cause a shadow merge).
//
return ( ( _fresh.LokDeleteCount() > _frmwrkParams.GetMaxFreshDeletes() ) ||
( fForce && _fresh.LokDeleteCount() > 0 ) );
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::Merge, private
//
// Synopsis: Perform a merge in partition
//
// Arguments: [partid] -- partition id
// [mt] -- Merge type
//
// Signals: CExcetpion
//
// History: 08-Apr-91 BartoszM Created
// 13-Nov-91 BartoszM Rewrote using CMerge
//
//----------------------------------------------------------------------------
void CResManager::Merge( MergeType mt, PARTITIONID partid )
{
ciDebugOut (( DEB_ITRACE, "%s", (mtDeletes == mt) ? "Delete merging: Have a quick stretch" :
"Shadow merging: Get a cup of coffee\n" ));
CMerge Merge( *this, partid, mt );
//
// Perform resource acquisition under storage transaction. If
// aborted all persistent storage will be deleted by the storage
// transaction.
//
{
CPriLock lock ( _mutex );
Win4Assert( _pMerge == 0 );
if (_fStopMerge)
{
ciDebugOut(( DEB_ITRACE, "Stopping Merge\n" ));
return;
}
Merge.LokGrabResources(); // may throw an exception
//
// Optimization: Don't merge zero indexes.
//
if ( 0 != Merge.LokCountOld() )
{
_pMerge = &Merge;
}
else if ( mtDeletes != mt )
{
ciDebugOut(( DEB_ITRACE, "Shadow merge of zero indexes. Cancelled.\n" ));
return;
}
}
XPtr<CNotificationTransaction> xNotifyTrans;
// Perform the merge under simple transaction
// No lock held.
{
TRY
{
//
// Perform merge, if necessary.
//
if ( 0 != Merge.LokCountOld() )
{
CCiFrmPerfCounter counter( _adviseStatus.GetPointer(), CI_PERF_MERGE_PROGRESS );
Merge.Do( counter );
}
//
// NotifTrans should be outside resman lock and the resman lock
// is acquired in notifTran's destructor
//
xNotifyTrans.Set( new CNotificationTransaction( this,
_xIndexNotifTable.GetPointer() ) );
}
CATCH ( CException, e )
{
CPriLock lock ( _mutex );
Merge.LokRollBack();
_pMerge = 0;
// Really bad errors indicate the index is corrupt.
SCODE scE = e.GetErrorCode();
if ( STATUS_INTEGER_DIVIDE_BY_ZERO == scE ||
STATUS_ACCESS_VIOLATION == scE ||
STATUS_IN_PAGE_ERROR == scE )
{
ciDebugOut(( DEB_ERROR,
"Corrupt index, caught 0x%x\n", scE ));
_storage.ReportCorruptComponent( L"ShadowMerge" );
THROW( CException( CI_CORRUPT_DATABASE ) );
}
RETHROW();
}
END_CATCH
}
// nb: no failures allowed until CMergeTrans is constructed!
CDiskIndex* pIndexNew = 0;
{ //=================== begin notification transaction =========================
{
CLock lock( _mutex );
XPtr<CFreshTest> xFreshTestAtMerge;
{
_pMerge = 0;
CMergeTrans Xact( *this, _storage, Merge );
unsigned cIndOld = Merge.LokCountOld();
INDEXID* aIidOld = Merge.LokGetIidList();
pIndexNew = Merge.LokGetNewIndex();
//
// Write the new fresh test persistently to disk.
//
CPersFresh newFreshLog( _storage, _partList );
CShadowMergeSwapInfo swapInfo;
swapInfo._widNewFreshLog = _fresh.LokUpdate( Merge,
Xact,
newFreshLog,
pIndexNew ? pIndexNew->GetId() : iidInvalid,
cIndOld,
aIidOld,
xFreshTestAtMerge );
swapInfo._widOldFreshLog =
_storage.GetSpecialItObjectId( itFreshLog );
swapInfo._cIndexOld = cIndOld;
swapInfo._aIidOld = aIidOld;
_partList.LokSwapIndexes( Xact,
partid,
pIndexNew,
swapInfo );
//
// No failures allowed after this point on until the transaction
// is committed.
//
Merge.ReleaseNewIndex();
ciDebugOut (( DEB_ITRACE, "done merging\n" ));
Xact.LokCommit();
#if CIDBG == 1
if ( pIndexNew )
pIndexNew->Reference();
#endif
//==========================================
}
ciFAILTEST( STATUS_NO_MEMORY );
CPartition *pPartition =_partList.LokGetPartition ( partid );
//
// Delete the wids in the change log that have made it to
// the new shadow index created in this merge. The algorithm
// needs the latest fresh test, fresh test snapshoted at the
// start of shadow merge and the doc list. If the latest fresh
// test is the same as the fresh test at merge, then as an
// optimization, the fresh test at merge is null. So, in the
// optimized case, we use the latest fresh test as the fresh
// test at merge.
//
CFreshTest *pFreshTestLatest = _fresh.LokGetFreshTest();
CFreshTestLock freshTestLock( pFreshTestLatest );
CFreshTest *pFreshTestAtMerge;
if ( xFreshTestAtMerge.IsNull() )
pFreshTestAtMerge = pFreshTestLatest;
else
pFreshTestAtMerge = xFreshTestAtMerge.GetPointer();
//==========================================
CChangeTrans xact( *this, pPartition );
pPartition->LokDeleteWIDsInPersistentIndexes( xact,
*pFreshTestLatest,
*pFreshTestAtMerge,
_docList,
xNotifyTrans.GetReference() );
xact.Commit();
}
xNotifyTrans.Free();
} //=================== end notification transaction =========================
#if CIDBG == 1
if ( pIndexNew )
{
pIndexNew->VerifyContents();
{
CPriLock lock ( _mutex );
LokReleaseIndex( pIndexNew );
}
}
#endif
_storage.CheckPoint();
} //Merge
//+---------------------------------------------------------------------------
//
// Member: CResManager::LogMMergeStartFailure
//
// Synopsis: Writes an eventlog message to indicate a MasterMerge could
// not be started.
//
// Arguments: [fRestart] - Indicates if this is a restarted master merge.
// [storage] - Storage reference
// [error] - Error code.
// [adviseStatus] - reference to ICiCAdviseStatus
//
// History: 5-27-96 srikants Created
// Jan-07-96 mohamedn CDmFwEventItem
//
//----------------------------------------------------------------------------
void CResManager::LogMMergeStartFailure( BOOL fRestart,
PStorage & storage,
ULONG error,
ICiCAdviseStatus & adviseStatus)
{
TRY
{
DWORD dwMsgCode = fRestart ?
MSG_CI_MASTER_MERGE_CANT_RESTART :
MSG_CI_MASTER_MERGE_CANT_START;
CDmFwEventItem Item( EVENTLOG_AUDIT_FAILURE,
dwMsgCode,
2,
sizeof(error),
(void *)&error );
Item.AddArg( storage.GetVolumeName() );
Item.AddArg( error );
Item.ReportEvent( adviseStatus );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR, "Error 0x%X in LogMMergeStartFailure\n",
e.GetErrorCode() ));
}
END_CATCH
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LogMMergePaused
//
// Synopsis: Writes an eventlog message that master merge was paused.
//
// Arguments: [storage] - Storage
// [error] - Error which caused the merge to be paused.
// [adviseStatus] - Advise status to use for notifying about the
// event.
//
// History: 1-24-97 srikants Added header
//
//----------------------------------------------------------------------------
void CResManager::LogMMergePaused( PStorage & storage,
ULONG error,
ICiCAdviseStatus &adviseStatus)
{
if ( IsCorrupt( error ) )
{
// don't log "paused" when there is corruption
return;
}
TRY
{
CDmFwEventItem Item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_ABORTED,
2,
sizeof(error),
(void *)&error );
Item.AddArg( storage.GetVolumeName() );
Item.AddArg( error );
Item.ReportEvent( adviseStatus );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR, "Error 0x%X in LogMMergePaused\n",
e.GetErrorCode() ));
}
END_CATCH
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::MasterMerge, private
//
// Synopsis: Perform a MasterMerge in partition
//
// Arguments: [partid] -- partition id
//
// Signals: CException
//
// History: 08-Apr-91 BartoszM Created
// 13-Nov-91 BartoszM Rewrote using CMerge
// 17-Feb-94 KyleP Added keylist merge
// Jan-07-96 mohamedn CDmFwEventItem
//
//----------------------------------------------------------------------------
void CResManager::MasterMerge ( PARTITIONID partid )
{
ciDebugOut (( DEB_ITRACE, "\n===MASTER MERGE===\n" ));
//
// Force a shadow merge if we are not restarting a master merge. This
// will force all word lists into persistent indexes.
//
BOOL fMasterMergePaused = FALSE;
//
// Before starting the master merge, we have to swap the deleted index
// iid. However, if there is a failure before we commit the beginning
// of the master merge, we have to roll back this change. The
// CDeletedIIDTrans object does this.
//
CDeletedIIDTrans delIIDTrans( *this );
INDEXID iidDelPreMerge = iidInvalid;
{
CPriLock lock ( _mutex );
CPartition *pPartition = _partList.LokGetPartition ( partid );
fMasterMergePaused = pPartition->InMasterMerge();
if ( !fMasterMergePaused )
{
if ( 0 == LokGetFreshCount() )
{
//
// There is no benefit in doing a master merge. Just return
//
return;
}
iidDelPreMerge = _activeDeletedIndex.GetIndex();
delIIDTrans.LokLogNewDeletedIid( iidDelPreMerge,
_activeDeletedIndex.GetNewIndex() );
_activeDeletedIndex.SwapIndex();
}
}
if ( !fMasterMergePaused )
{
//
// First do a delete merge to finish merging all outstanding
// word-lists into a persistent index.
//
// A delete merge will force an optimized null merge if there are
// outstanding deletes in the fresh test which need to be logged.
// This is an important case for Push filtering. Since it doesn't
// hurt in the other model, just keep the code identical.
//
Merge( mtDeletes, partid );
}
else
{
//
// This is a restarted master merge. The _activeDeletedIndex contains
// the "Post" master merge deleted index. The "New" one will be same
// as the "Prev" one.
//
iidDelPreMerge = _activeDeletedIndex.GetNewIndex();
CDmFwEventItem Item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_RESTARTED,
1 );
Item.AddArg( _storage.GetVolumeName() );
Item.ReportEvent( _adviseStatus.GetReference() );
}
CMasterMerge Merge( *this, partid );
TRY
{
CPriLock lock ( _mutex );
Win4Assert( _pMerge == 0 );
if ( _fStopMerge )
{
ciDebugOut(( DEB_ITRACE, "Stopping Merge\n" ));
return;
}
ciFAILTEST( STATUS_NO_MEMORY );
CPartition *pPartition =_partList.LokGetPartition ( partid );
if ( !pPartition->InMasterMerge() )
{
Win4Assert( delIIDTrans.IsTransLogged() );
Merge.LokGrabResources( delIIDTrans ); // may throw an exception
if ( 0 == Merge.LokCountOld() )
{
ciDebugOut(( DEB_ITRACE, "Master merge of 0 indexes cancelled\n"));
return;
}
}
else
{
Merge.LokLoadRestartResources();
}
//
// The deletionIIDTrans must be either committed or not logged at
// all.
//
Win4Assert( !delIIDTrans.IsRollBackTrans() );
_pMerge = &Merge;
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR, "Error 0x%X while starting a MasterMerge\n",
e.GetErrorCode() ));
LogMMergeStartFailure( fMasterMergePaused,
_storage,
e.GetErrorCode(),
_adviseStatus.GetReference() );
RETHROW();
}
END_CATCH
// Perform the merge under simple transaction
// No lock held.
{
TRY
{
// Perform merge
CCiFrmPerfCounter counter( _adviseStatus.GetPointer(), CI_PERF_MERGE_PROGRESS );
Merge.Do( counter );
}
CATCH ( CException, e )
{
CPriLock lock ( _mutex );
Merge.LokRollBack();
_pMerge = 0;
LogMMergePaused( _storage,
e.GetErrorCode(),
_adviseStatus.GetReference() );
RETHROW();
}
END_CATCH
}
CMasterMergeIndex* pMMergeIndex = 0;
{
CPriLock lock ( _mutex );
{
_pMerge = 0;
unsigned cIndOld = Merge.LokCountOld();
INDEXID* aIidOld = Merge.LokGetIidList();
pMMergeIndex = Merge.LokGetMasterMergeIndex();
CMergeTrans MergeTrans( *this, _storage, Merge );
ciFAILTEST( STATUS_NO_MEMORY );
//==========================================
CMasterMergeTrans xact( *this,
_storage,
MergeTrans,
pMMergeIndex,
Merge );
CPartition *pPart = LokGetPartition(partid);
#ifdef KEYLIST_ENABLED
pPart->SetRelevantWords(pMMergeIndex->AcquireRelevantWords());
#endif // KEYLIST_ENABLED
//
// Remove old indexes from fresh list after master merge
//
CPersFresh newFreshLog( _storage, _partList );
CMasterMergeSwapInfo SwapInfo;
SwapInfo._widNewFreshLog = _fresh.LokRemoveIndexes (
MergeTrans,
newFreshLog,
cIndOld, aIidOld, iidDelPreMerge );
SwapInfo._widOldFreshLog = _storage.GetSpecialItObjectId( itFreshLog );
SwapInfo._cIndexOld = cIndOld;
SwapInfo._aIidOld = aIidOld;
SwapInfo._partid = partid;
CKeyList const * pOldKeyList = _sKeyList.GetPointer();
CKeyList const * pNewKeyList = Merge.LokGetNewKeyList();
_partList.LokSwapIndexes( MergeTrans,
partid,
pMMergeIndex,
SwapInfo,
pOldKeyList,
pNewKeyList );
//
// After this point, we must not resume the master merge even
// if there is a soft failure.
//
Merge.ReleaseNewIndex();
#ifdef KEYLIST_ENABLED
//
// Get rid of the current KeyList and replace it with the
// new KeyList
//
_storage.SetSpecialItObjectId( itMMKeyList, widInvalid );
CKeyList * pKeyList = _sKeyList.Acquire();
Win4Assert( 0 != pKeyList );
_sKeyList.Set( Merge.LokGetNewKeyList() );
Merge.LokReleaseNewKeyList();
pKeyList->Zombify();
if ( !pKeyList->InUse() )
{
pKeyList->Remove();
delete pKeyList;
}
#else
CKeyList * pKeyList = _sKeyList.Acquire();
Win4Assert( 0 != pKeyList );
_sKeyList.Set( Merge.LokGetNewKeyList() );
Merge.LokReleaseNewKeyList();
delete pKeyList;
#endif // KEYLIST_ENABLED
#if CIDBG == 1
pMMergeIndex->Reference();
#endif
xact.LokCommit();
//==========================================
ciDebugOut (( DEB_ITRACE, "done merging\n" ));
}
//
// There is no need to compact the change log here because the
// participants in the master merge are persistent indexes and
// there will be no wids to be deleted from the change log when
// persistent indexes are merged into another persistent index.
//
}
#if CIDBG == 1
pMMergeIndex->VerifyContents();
{
CPriLock lock ( _mutex );
LokReleaseIndex( pMMergeIndex );
}
#endif
_storage.CheckPoint();
CDmFwEventItem Item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_COMPLETED,
1 );
Item.AddArg( _storage.GetVolumeName() );
Item.ReportEvent( _adviseStatus.GetReference() );
}
//+---------------------------------------------------------------------------
//
// Class: CReleaseMMergeIndex
//
// Purpose: Class to acquire the ownernship of the target and current
// master indexes during the destruction of a mastermerge index
// and release the target and current indexes.
//
// History: 9-29-94 srikants Created
//
//----------------------------------------------------------------------------
class CReleaseMMergeIndex
{
public:
CReleaseMMergeIndex( CResManager & resman, CMasterMergeIndex * mmIndex );
~CReleaseMMergeIndex();
private:
CResManager & _resman;
CMasterMergeIndex * _pmmIndex;
CPersIndex * _pTargetMaster;
CPersIndex * _pCurrentMaster;
};
CReleaseMMergeIndex::CReleaseMMergeIndex( CResManager & resman,
CMasterMergeIndex * mmIndex ) :
_resman(resman),
_pmmIndex(mmIndex),
_pTargetMaster(0),
_pCurrentMaster(0)
{
Win4Assert( _pmmIndex );
Win4Assert( _pmmIndex->IsZombie() && !_pmmIndex->InUse() );
_pmmIndex->AcquireCurrentAndTarget( &_pCurrentMaster, &_pTargetMaster );
Win4Assert( 0 == _pCurrentMaster || _pCurrentMaster->IsZombie() );
if ( 0 != _pCurrentMaster )
{
_pCurrentMaster->Reference();
}
Win4Assert( 0 != _pTargetMaster );
_pTargetMaster->Reference();
}
CReleaseMMergeIndex::~CReleaseMMergeIndex()
{
delete _pmmIndex;
if ( 0 != _pCurrentMaster )
_resman.LokReleaseIndex( _pCurrentMaster );
_resman.LokReleaseIndex( _pTargetMaster );
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokReleaseIndex, private
//
// Synopsis: Decrements ref counts, deletes if index
// marked to be deleted.
//
// Arguments: [pIndex] -- index to be released
//
// History: 08-Oct-91 BartoszM Created
//
// Notes: ResMan LOCKED
//
//----------------------------------------------------------------------------
void CResManager::LokReleaseIndex ( CIndex* pIndex )
{
pIndex->Release();
if ( pIndex->IsZombie() && !pIndex->InUse() )
{
CIndexId iid = pIndex->GetId();
//
// If it is not a master index, it has to be removed from the
// Partition object. A master index would have been already removed
// when merge completed.
//
// If we are in the Empty path, then we want to force all indexes
// to be deleted if their ref-count is 0.
//
if ( !pIndex->IsMaster() || _isBeingEmptied )
{
if ( !_isDismounted )
{
TRY
{
_partList.LokRemoveIndex ( iid );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR,
"Index 0x%08x could not be deleted from index table\n",
iid ));
}
END_CATCH
TRY
{
// Must impersonate as system since query threads
// impersonating as a query user can't delete the
// files sometimes deleted when an index is released
// after a merge. Impersonation can fail, which will
// leak the file on disk.
CImpersonateSystem impersonate;
if ( iid.IsPersistent() )
pIndex->Remove();
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR,
"Index 0x%08x could not be deleted from disk, %#x\n",
iid, e.GetErrorCode() ));
}
END_CATCH
}
else
{
//
// Zombie indexes are cleaned up after reboot.
//
//
// After a dismount, we should not have any dirty streams
// and releasing a persistent zombie index will update the
// index table on disk. That is not allowed. This situation
// happens only when queries have not been released and
// shutdown is happening.
//
//
ciDebugOut(( DEB_WARN,
"Index with iid 0x%X being released AFTER CI dismount\n",
iid ));
}
delete pIndex;
}
else
{
//
// This must be a master merge index as no other master index
// can be a zombie.
//
Win4Assert( pIndex->IsMasterMergeIndex() );
CReleaseMMergeIndex mmIndexRel( *this,
(CMasterMergeIndex *)pIndex );
}
}
}
//+---------------------------------------------------------------------------
//
// Function: LokReplaceMasterIndex
//
// Synopsis: Replaces the CMasterMergeIndex with the CPersIndex incarnation
// of the target master index in the index list. Also prepares
// the CMasterMergeIndex for removal from the in-memory index
// list by zombifying it.
//
// Arguments: [pMMergeIndex] -- The target index in the CMasterMergeIndex
// form.
// [pNewMaster] -- The CPersIndex form of the target index.
//
// History: 6-30-94 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokReplaceMasterIndex ( CMasterMergeIndex* pMMergeIndex )
{
Win4Assert( pMMergeIndex->IsMaster() && pMMergeIndex->IsZombie() );
CPersIndex * pNewMaster = pMMergeIndex->GetTargetMaster();
Win4Assert( pNewMaster->IsMaster() && !pNewMaster->IsZombie() );
//
// Delete the master merge index from the partition.
//
CIndexId iid = pMMergeIndex->GetId();
CPartition * pPart = _partList.LokGetPartition( iid.PartId() );
pPart->LokRemoveIndex ( iid );
//
// Check if there is a query in progress. If not, we can
// go ahead and delete it. If a query is in progress, the
// indexsnapshot of the query will delete it.
//
if ( !pMMergeIndex->InUse() )
{
Win4Assert( !pNewMaster->InUse() );
pMMergeIndex->ReleaseTargetAndCurrent();
delete pMMergeIndex;
}
//
// Add the new master index to the partition object. This will
// now be the only master accessible to new queries.
//
pPart->AddIndex( pNewMaster );
}
//
//+---------------------------------------------------------------------------
//
// Function: RollBackDeletedIIDChange
//
// Synopsis: Rolls back the changed made to the deleted iid during the
// beginning of a master merge.
//
// Arguments: [xact] - The transaction object which is rolling back the
// change to the deleted iid.
//
// History: 7-17-95 srikants Created
//
// Notes: Consider the following sequence of events:
//
// 1. Pre-MasterMerge preparations : Change Deleleted IID to iidDeleted2 from
// iidDeleted1.
//
// 2. Start a shadow merge.
//
// 3. Commit the beginning of a new MasterMerge
//
// 4. Complete the master merge .....
//
// If there is a failure between steps 2 and 3, we just revert the deleted
// IID back to iidDeleted2. Even if some entries were created in the fresh
// list between steps 2 & 3 of the form, WID->iidDeleted2, it is okay. They
// will get deleted from the fresh list on the second MasterMerge.
//
//----------------------------------------------------------------------------
void CResManager::RollBackDeletedIIDChange( CDeletedIIDTrans & xact )
{
CPriLock lock(_mutex);
Win4Assert( _activeDeletedIndex.GetIndex() == xact.GetNewDelIID() );
Win4Assert( _activeDeletedIndex.GetNewIndex() == xact.GetOldDelIID() );
_activeDeletedIndex.SwapIndex();
}
//========================
// FILTER RELATED METHODS
//========================
//+---------------------------------------------------------------------------
//
// Function: LokGetFilterDocs
//
// Synopsis: Gets documents from the change log for filtering. _docList
// will be filled with the documents.
//
// Arguments: [cMaxDocs] -- [in] Maximum number of docs to retrieve
// [cDocs] -- [out] Number of documents retrieved
// [state] -- [in/out] State of the document retrieval
// progress.
//
// Returns: Number of documents in the docBuffer.
//
// History: 6-16-94 srikants Separated for making kernel mode call
// Asynchronous.
//
// Notes: This function has two goals:
//
// 1. Select documents from different partitions in a round
// robin fashion.
//
//----------------------------------------------------------------------------
BOOL CResManager::LokGetFilterDocs ( ULONG cMaxDocs,
ULONG & cDocs,
CGetFilterDocsState & state )
{
ciDebugOut(( DEB_ITRACE, "# CResManager::LokGetFilterDocs.\n" ));
Win4Assert( state.GetTriesLeft() > 0 && state.GetTriesLeft() <=2 );
cDocs = 0;
Win4Assert ( !_isBeingEmptied );
unsigned maxDocs = min( cMaxDocs, CI_MAX_DOCS_IN_WORDLIST);
if ( _partIter.LokAtEnd() )
return FALSE; // There are no valid partitions
// we are sure that the partition is valid
CPartition* pPart = _partIter.LokGet();
Win4Assert ( pPart != 0 );
//
// Don't bother looking for more documents to filter if there
// are too many wordlists in memory -- wait until they are
// merged.
//
if ( pPart->WordListCount() < _frmwrkParams.GetMaxWordlists() )
{
CChangeTrans xact( *this, pPart );
pPart->LokQueryPendingUpdates ( xact, maxDocs, _docList );
xact.LokCommit();
}
else
{
ciDebugOut(( DEB_ITRACE,
"Too many wordlists. Waking merge\n" ));
_eventMerge.Set();
}
_docList.SetPartId ( pPart->GetId() );
//==================================
//
// If there is a failure after this point, we have to re-add
// the documents to the change log.
//
cDocs = _docList.Count();
for (unsigned i = 0; i < cDocs; i++)
{
if (_docList.Status(i) != DELETED && _docList.Status(i) != WL_NULL)
break;
}
BOOL fRetry = FALSE;
if ( i == cDocs && i != 0 )
{
CIndexTrans xact( *this );
_fresh.LokDeleteDocuments ( xact, _docList, _activeDeletedIndex.GetIndex() );
xact.LokCommit();
cDocs = 0;
_docList.LokClear();
state.Reset();
//
// If we are doing a bunch of deletions in a "delnode" case,
// we should see if the number of changed documents has exceeded
// the max fresh test count and wake up the merge thread. Note
// that we are not starting a merge here - the merge thread will
// deal with the policy issues.
//
if ( LokGetFreshCount() > _frmwrkParams.GetMaxFreshCount() ||
CheckDeleteMerge( FALSE ) )
{
_eventMerge.Set();
}
LokUpdateCounters();
}
_partIter.LokAdvance( _partList );
if ( _partIter.LokAtEnd() )
{
_partIter.LokInit( _partList );
state.DecrementTries();
fRetry = (0 == cDocs) && (state.GetTriesLeft() > 0);
}
else
{
fRetry = ( 0 != cDocs );
}
Win4Assert( cDocs == _docList.Count() );
return fRetry;
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::FillDocBuffer, public
//
// Synopsis: Fills buffer with strings for paths and properties of docs
// to be filtered
//
// Arguments: [docBuffer] -- (in, out) buffer to be filled with document
// names.
// [cb] -- (in, out) count of bytes in docBuffer
// [cDocs] -- (out) count of # documents to filter
// [state] -- [in/out] State of the document retrieval
// progress.
//
// History: 08-Apr-1991 BartoszM Created
// 24-Nov-1992 KyleP Retry pending documents
// 22-Mar-1993 AmyA Split into several functions
// 22-Jan-1997 SrikantS Changed doc. names to bytes for frame
// work.
// 18-May-2000 KitmanH Changed to push retried docs to filter
// one by one
//
//----------------------------------------------------------------------------
void CResManager::FillDocBuffer( BYTE * docBuffer,
ULONG & cb, ULONG & cDocs,
CGetFilterDocsState & state )
{
Win4Assert( docBuffer != 0 );
// =========================================
{
CPriLock lock(_mutex);
_docList.LokSortOnWid();
}
// ==========================================
unsigned cDocuments = _docList.Count();
//
// Format of the data
// 4 Bytes remaining files count, 2 Bytes number of files. Following
// that is (2 Bytes of Name Length in bytes, Name)*
//
if ( _fPushFiltering )
{
//
// We can't take an exception while growing the aborted wids
// array while doing exception processing in the filter manager.
// So, pre-allocate the array now to insure we won't run out of
// room later. Now is a good time to die; later isn't.
//
CPriLock lock( _mutex );
_aAbortedWids.LokReserve( cDocuments );
//
// In push filtering there are no refiles, and hence the buffer
// cannot overflow. Check that the buffer can fit 16 documents.
// A document name is the serialized form of the wid, i.e. it's
// 4 bytes long. The buffer size is 4K.
//
Win4Assert( cb > 4 + 2 + 16 * (2 + 4) );
}
const cbLenField = sizeof(ULONG);
Win4Assert( cb >= cbLenField );
Win4Assert( sizeof(ULONG) == 4 );
if ( cb < cbLenField )
THROW( CException( STATUS_INVALID_PARAMETER ) );
//
// Fill the number of documents left in the changelog in the first
// 4 bytes of the buffer.
//
// =========================================================
{
CPriLock lock( _mutex );
ULONG partid = 1;
CPartition * pPart = _partList.LokGetPartition( partid );
ULONG cDocsLeft = pPart->LokCountUpdates();
RtlCopyMemory( docBuffer, &cDocsLeft, sizeof(ULONG) );
}
// =========================================================
RtlZeroMemory( docBuffer + cbLenField, sizeof USHORT );
const cbUSHORT = sizeof USHORT;
BYTE * pCount = docBuffer + cbLenField;
*pCount = 0;
BYTE * pBuf = pCount + cbUSHORT;
BYTE * pEnd = docBuffer + cb;
cDocs = 0;
// copy path/property pairs into buffer until we run out of either
// buffer space or pairs. We stop filling the buffer if we encounter a
// document that has a retry count > 1, we will fill the docBuffer with
// these docs one at a time
ciDebugOut((DEB_FILTERWIDS | DEB_ITRACE,
"CResManager::FillDocBuffer..."));
unsigned cbExtraSpaceNeeded = 0;
for (unsigned cSrcDocs = 0; cSrcDocs < cDocuments; cSrcDocs++)
{
//
// Leave the first 2 bytes for the length field
//
unsigned cbPath = (unsigned)(pEnd - pBuf);
if ( cbPath <= cbUSHORT )
{
// We would definitely need more space than a USHORT, but
// we don't know how much right now. But since the filter
// increases space by a PAGE, I guess we should do fine.
cbExtraSpaceNeeded = cbUSHORT - cbPath + 1;
break;
}
if (_docList.Status(cSrcDocs) == DELETED)
{
cbPath = 0;
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, " %dX",
_docList.Wid(cSrcDocs) ));
}
else
{
if ( _docList.Retries(cSrcDocs) > 1 )
{
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME,
"************FillDocBuffer: Retry count is %d. Wid is %d\n",
_docList.Retries(cSrcDocs),
_docList.Wid(cSrcDocs) ));
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, "cDocs is %d\n", cDocs ));
if ( cDocs > 0 )
{
//
// There is at least one document in the docBuffer. Stop
// now so we can file the doc(s) being retried one by one.
//
break;
}
else
{
//
// Add the currenct doc. Stop if the current add is successful
//
//
// Make room for the length field at the beginning.
//
cbPath -= cbUSHORT;
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, " %d",
_docList.Wid(cSrcDocs) ));
unsigned cbPathIn = cbPath;
//
// If this is not the first try, then attempt to get a more
// accurate path. We may be suffering from NTBUG: 270566
//
// changes cbPath.
if ( !WorkidToDocName( _docList.Wid(cSrcDocs),
pBuf+cbUSHORT,
cbPath,
TRUE ) )
{
if ( cbPathIn < cbPath)
{
cbExtraSpaceNeeded = cbPath - cbPathIn;
}
break;
}
// cbPath = 0 if object has been deleted (not added to docBuffer)
if ( cbPath == 0 )
{
_docList.SetStatus( cSrcDocs, DELETED );
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, "X" ));
}
else
{
ClearNonStoragePropertiesForWid( _docList.Wid(cSrcDocs) );
cSrcDocs++;
cDocs++;
USHORT uscb = (USHORT) cbPath;
RtlCopyMemory( pBuf, &uscb, cbUSHORT );
pBuf += (cbPath+cbUSHORT);
break;
}
}
}
else // not retries
{
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME,
"FillDocBuffer: Not retry for wid %d\n",
_docList.Wid(cSrcDocs) ));
//
// Make room for the length field at the beginning.
//
cbPath -= cbUSHORT;
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, " %d",
_docList.Wid(cSrcDocs) ));
unsigned cbPathIn = cbPath;
// changes cbPath.
if ( !WorkidToDocName( _docList.Wid(cSrcDocs),
pBuf+cbUSHORT,
cbPath,
FALSE ) )
{
if ( cbPathIn < cbPath)
{
cbExtraSpaceNeeded = cbPath - cbPathIn;
}
break;
}
// cbPath = 0 if object has been deleted
if ( cbPath == 0 )
{
_docList.SetStatus( cSrcDocs, DELETED );
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, "X" ));
}
else
{
cDocs++;
ClearNonStoragePropertiesForWid( _docList.Wid(cSrcDocs) );
}
}
}
USHORT uscb = (USHORT) cbPath;
RtlCopyMemory( pBuf, &uscb, cbUSHORT );
pBuf += (cbPath+cbUSHORT);
}
ciDebugOut (( DEB_FILTERWIDS | DEB_NOCOMPNAME, "\n" ));
//
// Fill in the count of the number of documents.
//
USHORT usFileCount = (USHORT) cSrcDocs;
RtlCopyMemory( pCount, &usFileCount, cbUSHORT );
if ( cSrcDocs < cDocuments )
{
if ( 0 == cSrcDocs && cbExtraSpaceNeeded > 0 )
{
// We need a larger buffer
cb += cbExtraSpaceNeeded;
}
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
ciDebugOut (( DEB_ITRACE, "Putting back documents\n" ));
CDocList docListExtra;
docListExtra.SetPartId( _docList.PartId() );
for ( unsigned i = 0, j = cSrcDocs; j < cDocuments; i++, j++ )
{
ciDebugOut(( DEB_ITRACE | DEB_NOCOMPNAME, " %d", _docList.Wid(j) ));
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME, "Refiling Wid %d\n", _docList.Wid(j) ));
docListExtra.Set( i,
_docList.Wid(j),
_docList.Usn(j),
_docList.VolumeId(j) );
}
ciDebugOut (( DEB_ITRACE | DEB_NOCOMPNAME, "\n" ));
CPriLock lock2(_mutex);
docListExtra.LokSetCount(i);
_docList.LokSetCount( cSrcDocs );
ReFileCommit ( docListExtra );
}
//
// If we were unable to convert any of the wids to paths, then all of
// them have been deleted.
//
if ( 0 == cDocs )
{
ciDebugOut (( DEB_ITRACE, "All docs were deleted\n" ));
CPriLock lock3(_mutex);
_docList.LokClear();
state.Reset();
}
else
{
cb = (ULONG)(pBuf - docBuffer);
}
}
//+---------------------------------------------------------------------------
//
// Function: NoFailReFile
//
// Synopsis: re-adds the documents to the change log witout committing
//
// History: 6-16-94 srikants Separated from FilterReady for making
// kernel mode call Asynchronous.
//
//----------------------------------------------------------------------------
BOOL CResManager::NoFailReFile ( CDocList& docList )
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
CPriLock lock ( _mutex );
if ( _isBeingEmptied )
return FALSE;
LokNoFailReFileChanges( docList );
docList.LokClear();
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: ReFileCommit
//
// Synopsis: re-adds the documents to the change log and commits
// them to the change list.
//
// History: 6-16-94 srikants Separated from FilterReady for making
// kernel mode call Asynchronous.
//
//----------------------------------------------------------------------------
BOOL CResManager::ReFileCommit ( CDocList& docList )
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
CPriLock lock ( _mutex );
if ( _isBeingEmptied )
return FALSE;
LokNoFailReFileChanges( docList );
docList.LokClear();
//
// The docqueue can handle only one set of refiled documents.
// we MUST complete processing of the
// refiled documents.
//
PARTITIONID partId = 1;
CPartition * pPart = LokGetPartition( partId );
{
// =========================================
CChangeTrans xact( *this, pPart );
pPart->LokAppendRefiledDocs( xact );
xact.LokCommit();
// =========================================
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: LokNoFailReFileChanges
//
// Synopsis: Refiles the updates with the change log.
//
// Arguments: [docList] -- contains the documents to be added back to the
// change log.
//
// History: 5-16-94 srikants Modified to deal with failures during
// append of docs to change log.
//
// Notes: This CANNOT throw
//
//----------------------------------------------------------------------------
void CResManager::LokNoFailReFileChanges( CDocList& docList )
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
Win4Assert ( docList.PartId() == 1 );
CPartition* pPart = _partList.LokGetPartition ( docList.PartId() );
pPart->LokRefileDocs( docList );
_pFilterAgent->LokWakeUp ();
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::LokReFileDocument, public
//
// Returns: Put a document back on the quueue to be refiltered
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CResManager::LokReFileDocument ( PARTITIONID partid,
WORKID wid,
USN usn,
VOLUMEID volumeId,
ULONG retries,
ULONG secQRetries )
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
CPartition* pPart = _partList.LokGetPartition(partid);
//==========================================
CChangeTrans xact( *this, pPart );
//
// A usn of 0 is used for all refiled documents
//
pPart->LokUpdateDocument (xact, wid, 0, volumeId, CI_UPDATE_OBJ, retries, secQRetries);
xact.LokCommit();
//==========================================
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokAddToSecQueue
//
// Synopsis: Adds the given document to the secondary change log of the
// partition.
//
// Arguments: [partid] - PartitionId
// [wid] - WorkId of the document
// [volumeId] - Volume id
// [cSecQRetries] - Sec Q Retry Count
//
// History: 5-09-96 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokAddToSecQueue( PARTITIONID partid,
WORKID wid,
VOLUMEID volumeId,
ULONG cSecQRetries )
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
CPartition* pPart = _partList.LokGetPartition(partid);
//==========================================
CChangeTrans xact( *this, pPart );
pPart->LokAddToSecQueue (xact, wid, volumeId, cSecQRetries);
xact.LokCommit();
//==========================================
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokRefileSecQueueDocs
//
// Synopsis: Transfers the documents from the secondary queue to the primary
// change queue.
//
// History: 5-09-96 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokRefileSecQueueDocs()
{
//
// No refiling in push filtering
//
Win4Assert( !_fPushFiltering );
CPartIter iter;
for ( iter.LokInit(_partList); !iter.LokAtEnd(); iter.LokAdvance(_partList))
{
CPartition * pPart = iter.LokGet();
//==========================================
CChangeTrans xact( *this, pPart );
pPart->LokRefileSecQueue( xact );
xact.LokCommit();
//==========================================
}
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::LokTransferWordlist, public
//
// Returns: Transfer a wordlist from the filter manager
// to the content index
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
BOOL CResManager::LokTransferWordlist ( PWordList& pWordList )
{
BOOL fSuccess = TRUE;
PARTITIONID partid = _docList.PartId();
CPartition* pPart = _partList.LokGetPartition(partid);
INDEXID iid = pWordList->GetId();
TRY
{
//==========================================
CIndexTrans xact( *this );
//
// LokDone can fail while trying to add unfinished docs to the
// change log. In that case we must disable further USN updates.
// We should commit the change transaction immediately after the
// LokDone succeeds.
//
CChangeTrans changeXact( *this, pPart );
pPart->LokDone ( changeXact, iid, _docList );
ciFAILTEST( STATUS_NO_MEMORY );
changeXact.LokCommit();
_fresh.LokAddIndex ( xact, iid, _activeDeletedIndex.GetIndex(),
_docList, *pWordList );
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
"Success: transfering wordlist\n" ));
pWordList->Done();
//
// This may be an empty wordlist: all documents were deleted or in error.
// If so, just get rid of the wordlist.
//
if ( pWordList->IsEmpty() )
{
#if CIDBG == 1
unsigned cShouldHaveData = 0;
unsigned cDocuments = _docList.Count();
for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
{
switch ( _docList.Status(iDoc) )
{
case TOO_MANY_RETRIES:
case SUCCESS:
case TOO_MANY_BLOCKS_TO_FILTER:
cShouldHaveData++;
break;
default:
break;
}
}
if ( cShouldHaveData != 0 )
{
for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
{
ciDebugOut(( DEB_ERROR, "WID: 0x%x STATUS: %d\n",
_docList.Wid(iDoc), _docList.Status(iDoc) ));
}
}
Win4Assert( cShouldHaveData == 0 );
#endif
pWordList.Delete();
}
else
pWordList.Transfer ( pPart );
xact.LokCommit();
}
CATCH ( CException, e )
{
ciDebugOut(( DEB_ERROR,
"Exception 0x%x caught commiting wordlist.\n"
"delete wordlist and clear doclist\n",
e.GetErrorCode() ));
fSuccess = FALSE;
}
END_CATCH
_eventMerge.Set(); // wake up
return fSuccess;
}
//+---------------------------------------------------------------------------
//
// Function: LokNoFailRemoveWordlist
//
// Synopsis: Removes the biggest wordlist from memory and adds the
// documents back to the change log.
//
// Requires: _docList.Count() == 0 when this method is called. This
// relies on the fact that there are no other outstanding
// wordlists being constructed.
//
// History: 10-20-94 srikants Added the header section and modified
// to deal with the change in refiling
// documents.
//
//----------------------------------------------------------------------------
void CResManager::LokNoFailRemoveWordlist( CPartition * pPart )
{
//
// In push filtering, we don't delete completed wordlist to simplify
// the logic of aborting the notification transaction on the client
//
if ( _isBeingEmptied || _fPushFiltering )
return;
//
// Look for biggest wordlist
//
unsigned cInd;
CIndex ** apIndex = pPart->LokQueryMergeIndexes( cInd, mtWordlist );
XPtrST<CIndex *> xIndex( apIndex );
unsigned size = 0;
CWordList * pWordList = 0;
for ( unsigned i = 0; i < cInd; i++ )
{
apIndex[i]->Release();
if ( apIndex[i]->Size() > size )
{
Win4Assert( apIndex[i]->IsWordlist() );
pWordList = (CWordList *)apIndex[i];
size = pWordList->Size();
}
}
if ( pWordList )
{
//
// Reschedule contents for later filtering.
//
INDEXID iid = pWordList->GetId();
ciDebugOut(( DEB_ITRACE,
"Removing wordlist %x to reduce resource consumption.\n",
iid ));
CDocList _docList;
_docList.SetPartId ( pPart->GetId() );
pWordList->GetDocuments( _docList );
#if CIDBG == 1
for ( unsigned cWid = 0; cWid < _docList.Count(); cWid++ )
{
ciDebugOut(( DEB_ITRACE, "Reschedule filtering of wid %d\n",
_docList.Wid( cWid ) ));
}
#endif // CIDBG == 1
LokNoFailReFileChanges ( _docList );
//
// Remove it
//
ciDebugOut(( DEB_WARN, "Deleting wordlist with USN (%x, %x).\n",
lltoHighPart(pWordList->Usn()), (ULONG) pWordList->Usn() ));
pPart->LokRemoveIndex( iid );
if ( !pWordList->InUse() )
{
_partList.LokRemoveIndex ( iid );
delete pWordList;
}
else
{
pWordList->Zombify();
}
}
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::LokMakeWordlistId, public
//
// Returns: Makes unique wordlist id
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
INDEXID CResManager::LokMakeWordlistId (PARTITIONID partid)
{
CPartition* pPart = _partList.LokGetPartition(partid);
if ( pPart == 0 )
return iidInvalid;
// Get unique index id
INDEXID iid = pPart->LokMakeWlstId ();
ciDebugOut((DEB_FILTERWIDS,
"CResManager::LokMakeWorkListId - new wordlist iid: %x\n", iid));
return iid;
}
//+-------------------------------------------------------------------------
//
// Member: CResManager::ReportFilterFailure, public
//
// Synopsis: Write to event log about unsuccessful filtering
//
// Arguments: [wid] -- workid of the file that couldn't be filtered
//
// History: 05-Jan-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CResManager::ReportFilterFailure (WORKID wid)
{
PROPVARIANT var;
var.vt = VT_UI4;
var.ulVal = wid;
_adviseStatus->NotifyStatus( CI_NOTIFY_FILTERING_FAILURE,
1,
&var );
}
//=========
// SCANNING
//=========
//+---------------------------------------------------------------------------
//
// Member: CResManager::SetUpdateLost
//
// Synopsis: Sets that an update got lost and wakes up the merge thread
// to notify the client about it.
//
// Arguments: [partId] - Partition id
//
// History: 1-24-97 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::SetUpdatesLost( PARTITIONID partId )
{
CPriLock lock(_mutex);
if ( _state.LokGetState() != eUpdatesToBeDisabled )
{
_state.LokSetState( eUpdatesToBeDisabled );
_state.LokSetUpdateType( eIncremental );
StopCurrentMerge();
_eventMerge.Set();
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokCleanupForFullCiScan, public
//
// History: 10-Nov-94 DwightKr Created
//
// Notes: This routine performs the disk I/O and other operations
// which does the cleanup after a corruption is detected.
//
// This routine is similar to the Empty() method, except
// that we do not delete the change log object or the
// freshlog object, and the index list must contain
// the change log and fresh log after the cleanup is complete.
//
//----------------------------------------------------------------------------
void CResManager::LokCleanupForFullCiScan()
{
if ( 0 != _pMerge ) // Stop in progress merges.
_pMerge->LokAbort();
//
// Delete everything from the index table here first.
//
_idxTab->LokEmpty();
//
// If anything fails after this point, chkdsk /f or autochk will
// release the disk storage associated with the leaked objects
// since they are no longer part of the persistent index list.
//
//
// For each partition, zombify all indexes, and re-add the change
// log to the index table on disk.
//
CPartIter iter;
for ( iter.LokInit(_partList);
!iter.LokAtEnd();
iter.LokAdvance(_partList))
{
CPartition* pPart = iter.LokGet();
Win4Assert( pPart != NULL );
pPart->LokDisableUpdates();
//
// Zombify all indexes in this partition, and delete them if
// their ref-count is 0.
//
unsigned cIndex;
CIndex ** aIndex = pPart->LokZombify( cIndex );
ReleaseIndexes( cIndex, aIndex, NULL );
delete [] aIndex;
WORKID widMMergeLog;
WORKID widDummy;
pPart->GetMMergeObjectIds( widMMergeLog, widDummy, widDummy );
if ( widMMergeLog != widInvalid)
_storage.RemoveObject( widMMergeLog );
//
// Empty the fresh test and the fresh log, and add it to the
// index table on disk.
//
pPart->LokEmpty(); // Release change list/log storage
WORKID oidChangeLog = pPart->GetChangeLogObjectId();
Win4Assert( oidChangeLog != widInvalid );
_partList.LokAddIt( oidChangeLog, itChangeLog, pPart->GetId() );
}
//
// Empty the fresh test, fresh log, and add the fresh log to
// the index table on disk.
//
_fresh.LokInit(); // Release fresh test/log storage
WORKID oidFreshLog = _storage.GetSpecialItObjectId(itFreshLog);
Win4Assert( oidFreshLog != widInvalid );
_partList.LokAddIt( oidFreshLog, itFreshLog, partidFresh1 );
#ifdef KEYLIST_ENABLED
WORKID widKeyList = _storage.GetSpecialItObjectId( itMMKeyList );
#else
WORKID widKeyList = widInvalid;
#endif // KEYLIST_ENABLED
WORKID widPhrLat = _storage.GetSpecialItObjectId( itPhraseLat );
if ( widKeyList != widInvalid)
{
_storage.RemoveObject( widKeyList );
_storage.SetSpecialItObjectId( itMMKeyList, widInvalid );
}
if ( widPhrLat != widInvalid)
{
_storage.RemoveObject( widPhrLat );
_storage.SetSpecialItObjectId( itPhraseLat, widInvalid );
}
_isBeingEmptied = FALSE; // Allow new queries
}
// ===========================================================
// Content Index Frame Work Interfacing.
// ===========================================================
//+---------------------------------------------------------------------------
//
// Member: CResManager::WorkidToDocName
//
// Synopsis: Converts the given workid to path and fill it in the buffer
// given.
//
// Arguments: [wid] -- Workid to convert.
// [pbBuf] -- Destination buffer;
// [cb] -- On input the total number of bytes that can be
// put. On output, the actual number of bytes copied.
// For a deleted file, the cb will be 0.
// [fAccurate] -- TRUE --> Attempt to get a more accurate
// workid-to-path translation.
//
// Returns: TRUE if successfully converted; FALSE means the buffer was
// not big enough.
//
// History: 12-04-96 srikants Created
//
// Notes: WorkidToDocName is called outside the resman lock. It is a
// potentially long operation and taking resman lock is not
// advisable. Also, creation of a CWorkidToDocName object once
// for each wid->docName conversion is expensive. So, we create
// one in resman constructor and protect it with a separate
// lock.
//
//----------------------------------------------------------------------------
BOOL CResManager::WorkidToDocName( WORKID wid,
BYTE * pbBuf,
unsigned & cb,
BOOL fAccurate )
{
CLock lock(_workidToDocNameMutex);
ULONG cbDocName;
BYTE const * pbDocName = _xWorkidToDocName->GetDocName( wid, cbDocName, fAccurate );
BOOL fStatus = TRUE;
if ( pbDocName )
{
ciDebugOut(( DEB_FILTERWIDS | DEB_NOCOMPNAME,
"WorkidToDocName: wid is %d and DocName is %ws\n",
wid,
pbDocName ));
if ( cbDocName > cb )
fStatus = FALSE;
else
RtlCopyMemory( pbBuf, pbDocName, cbDocName );
cb = cbDocName;
}
else
{
cb = 0;
}
return fStatus;
} // WorkidToDocName
//+---------------------------------------------------------------------------
//
// Member: CResManager::GetClientState
//
// Synopsis: Gets the client specific status information like total
// number of documents.
//
// Arguments: [cDocuments] - [out] Total # of documents
//
// History: 12-04-96 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::GetClientState( ULONG & cDocuments )
{
CI_CLIENT_STATUS status;
SCODE sc = _docStore->GetClientStatus( &status );
if ( S_OK == sc )
{
cDocuments = status.cDocuments;
}
else
{
cDocuments = 0;
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokNotifyCorruptionToClient
//
// Synopsis: Informs the client that the catalog is corrupt
//
// History: 12-04-96 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokNotifyCorruptionToClient()
{
Win4Assert( _isCorrupt );
DisableUpdates( partidDefault );
SCODE sc = _docStore->DisableUpdates( FALSE, CI_CORRUPT_INDEX );
if ( SUCCEEDED( sc ) )
_state.LokSetCorruptionNotified();
//
// Disabled following assert so that the general NT 5 user is not
// bothered by this assert.
//
//Win4Assert( ! "We must empty the Content Index" );
// Empty();
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokNotifyDisableUpdatesToClient
//
// Synopsis: Informs the client that updates should be disabled
//
// History: 07-05-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CResManager::LokNotifyDisableUpdatesToClient()
{
Win4Assert( _state.LokGetState() == eUpdatesToBeDisabled );
DisableUpdates( partidDefault );
BOOL fIncremental = _state.LokGetUpdateType() == eIncremental;
SCODE sc = _docStore->DisableUpdates( fIncremental, CI_LOST_UPDATE );
if ( SUCCEEDED(sc) )
{
ciDebugOut(( DEB_WARN, "Notified disabled updates to DocStore\n" ));
_state.LokSetState( eUpdatesDisabled );
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::LokNotifyEnableUpdatesToClient
//
// Synopsis: Informs the client that updates should be enabled
//
// History: 07-05-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CResManager::LokNotifyEnableUpdatesToClient()
{
Win4Assert( _state.LokGetState() == eUpdatesToBeEnabled );
EnableUpdates( partidDefault );
SCODE sc = _docStore->EnableUpdates();
if ( SUCCEEDED(sc) )
{
ciDebugOut(( DEB_WARN, "Notified enable updates to DocStore\n" ));
_state.LokSetState( eSteady );
}
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::StoreValue
//
// Synopsis: Stores the given value in the document store
//
// Arguments: [wid] - WorkId of the document
// [ps] - The FULLPROPSPEC of the property
// [var] - Value of the property
// [fInSchema] - Returns TRUE if the property is in the schema
//
// Returns: SCODE result
//
// History: 12-18-96 srikants Created
//
//----------------------------------------------------------------------------
SCODE CResManager::StoreValue( WORKID wid,
CFullPropSpec const & ps,
CStorageVariant const & var,
BOOL & fInSchema )
{
FULLPROPSPEC const * fps = ps.CastToStruct();
PROPVARIANT const * pVar = ConvertToPropVariant( &var );
// Default to TRUE since the return code isn't checked!
fInSchema = TRUE;
SCODE sc = _propStore->StoreProperty( wid, fps, pVar );
if ( CI_E_PROPERTY_NOT_CACHED == sc )
fInSchema = FALSE;
else if ( FAILED( sc ) )
return sc;
return S_OK;
}
BOOL CResManager::StoreSecurity( WORKID wid,
PSECURITY_DESCRIPTOR pSD,
ULONG cbSD )
{
return S_OK == _docStore->StoreSecurity( wid, (BYTE *) pSD, cbSD );
}
//+---------------------------------------------------------------------------
//
// Member: CResManager::FPSToPROPID, public
//
// Synopsis: Converts FULLPROPSPEC property to PROPID
//
// Arguments: [fps] -- FULLPROPSPEC representation of property
// [pid] -- PROPID written here on success
//
// Returns: S_OK on success
//
// History: 29-Dec-1997 KyleP Created.
//
//----------------------------------------------------------------------------
SCODE CResManager::FPSToPROPID( CFullPropSpec const & fps, PROPID & pid )
{
return _mapper->PropertyToPropid( fps.CastToStruct(), TRUE, &pid );
}
//
// Flush notify support
//
//+---------------------------------------------------------------------------
//
// Member: CResManager::NotifyFlush
//
// Synopsis: Notifies the client that changes got flushed and gives the
// flush information.
//
// Arguments: [ft] - FileTime of the last successful flush.
// [cEntries] - Number of entries in the aInfo
// [aInfo] - Array of USN information for the flush.
//
// History: 1-27-97 srikants Created
//
// Note: We should not call directly into framework client because
// it violates resman->client lock hierarchy. We should not call
// into client with resman lock held.
//
//----------------------------------------------------------------------------
void CResManager::NotifyFlush( FILETIME const & ft,
ULONG cEntries,
USN_FLUSH_INFO ** ppInfo )
{
if ( !_fPushFiltering )
{
//
// Notification of flushes are sent in pull filtering only.
// For push filtering, the ICiCIndexNotificationStatus
// interface is used to notify clients.
//
CPriLock lock(_mutex);
CChangesFlushNotifyItem * pItem =
new CChangesFlushNotifyItem( ft, cEntries, ppInfo );
_flushList.Queue( pItem );
if (! _fFlushWorkerActive)
{
CFwFlushNotify * pWorkItem = new CFwFlushNotify( _workMan, *this );
_workMan.AddToWorkList( pWorkItem );
pWorkItem->AddToWorkQueue();
pWorkItem->Release();
}
}
} //NotifyFlush
//+---------------------------------------------------------------------------
//
// Member: CResManager::ProcessFlushNotifies
//
// Synopsis: Processes the queued flush notifies in a FIFO order. This
// method is called only by the asynchronous worker thread
// responsible for notifying the client about flushes.
//
// History: 1-27-97 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::ProcessFlushNotifies()
{
//
// Only one worker thread allowed to operate on the list at a time.
//
CLock lock (_mtxChangeFlush );
CRevertBoolValue RevertFlushActive( _fFlushWorkerActive, TRUE );
while ( TRUE )
{
CChangesFlushNotifyItem * pItem = 0;
//
// Remove the first flush item from the list under resman lock.
//
//===========================================
{
CPriLock lock(_mutex);
pItem = _flushList.GetFirst();
if ( pItem )
_flushList.Pop();
if (_flushList.IsEmpty())
RevertFlushActive.Revert();
}
//===========================================
if ( pItem )
{
SCODE sc = _docStore->CheckPointChangesFlushed(
pItem->GetFlushTime(),
pItem->GetCount(),
pItem->GetFlushInfo() );
if ( FAILED(sc) )
{
//
// We don't have to requeue this item. The client's restart
// work will just increase.
//
ciDebugOut(( DEB_ERROR,
"CheckPointChangesFlushed failed. Error (0x%X)\n",
sc ));
}
pItem->Close();
delete pItem;
}
else
{
break;
}
} // While (TRUE)
} //ProcessFlushNotifies
//+---------------------------------------------------------------------------
//
// Member: CResManager::_LokDeleteFlushWorkItems
//
// Synopsis: Deletes the flush notifies in the queue. This is called
// during shutdown (dismount) to clean up any asynchronous
// worker threads referring to resman.
//
// History: 1-27-97 srikants Created
//
//----------------------------------------------------------------------------
void CResManager::LokDeleteFlushWorkItems()
{
for ( CChangesFlushNotifyItem * pItem = _flushList.Pop();
0 != pItem;
pItem = _flushList.Pop() )
{
pItem->Close();
delete pItem;
}
} //LokDeleteFlushWorkItems
//+---------------------------------------------------------------------------
//
// Member: CResManager::NoFailAbortWidsInDocList
//
// Synopsis: Used in push filtering to abort all wids in the current
// doclist
//
// History: 24-Feb-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CResManager::NoFailAbortWidsInDocList()
{
CPriLock lock ( _mutex );
if ( _isBeingEmptied )
return ;
for ( unsigned i=0; i<_docList.Count(); i++ )
{
WORKID wid = _docList.Wid(i);
USN usn = _docList.Usn(i);
//
// Abort the client transaction
//
_xIndexNotifTable->AbortWid( wid, usn );
//
// Keep track of aborted wids for use during
// changlog shrink from front.
//
_aAbortedWids.NoFailLokAddWid( wid, usn );
}
_docList.LokClear();
} //NoFailAbortWidsInDocList
//+---------------------------------------------------------------------------
//
// Member: CResManager::NoFailAbortWid
//
// Synopsis: Used in push filtering to abort a wid
//
// Arguments: [wid] -- Workid
// [usn] -- Usn
//
// History: 24-Feb-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CResManager::NoFailAbortWid( WORKID wid, USN usn )
{
CPriLock lock ( _mutex );
if ( _isBeingEmptied )
return ;
//
// Abort the client transaction
//
_xIndexNotifTable->AbortWid( wid, usn );
//
// Keep track of aborted wid for use during
// changlog shrink from front.
//
_aAbortedWids.NoFailLokAddWid( wid, usn );
} //NoFailAbortWid
//+---------------------------------------------------------------------------
//
// Member: CResManager::ClearNonStoragePropertiesForWid
//
// Synopsis: Clear non-storage properties from the property storage for wid
//
// Arguments: [wid] -- Workid
//
// History: 10-Oct-2000 KitmanH Created
//
//----------------------------------------------------------------------------
SCODE CResManager::ClearNonStoragePropertiesForWid( WORKID wid )
{
return _propStore->ClearNonStoragePropertiesForWid( wid );
}
//====================
// MASTER MERGE POLICY
//====================
//+---------------------------------------------------------------------------
//
// Member: CMasterMergePolicy::IsTimeToMasterMerge, public
//
// Synopsis: Determines if the system should start a master merge
// immediately, and not wait until the master merge time.
//
// History: 4-Aug-94 DwightKr Created
//
// Notes: If new tests are added simply add them here.
//
//----------------------------------------------------------------------------
BOOL CMasterMergePolicy::IsTimeToMasterMerge() const
{
return IsMaxShadowIndexSize() ||
IsMaxFreshListCount() ||
IsMinDiskFreeForceMerge();
}
//+---------------------------------------------------------------------------
//
// Member: CMasterMergePolicy::IsMinDiskFreeForceMerge, private
//
// Synopsis: If the amount of free disk space is lower than a threshold,
// then we should master merge, since it is likely that disk
// space will be released.
//
// History: 4-Aug-94 DwightKr Created
// Jan-07-96 mohamedn CDmFwEventItem
//
//----------------------------------------------------------------------------
BOOL CMasterMergePolicy::IsMinDiskFreeForceMerge() const
{
//
// If we've exceeded the MIN_DISKFREE_FORCE_MERGE space used on
// the system, and a master merge has the potential to make some
// useful progress, then it's time to force a master merge. The
// values of MIN_DISKFREE_FORCE_MERGE and MAX_SHADOW_FREESPACE
// are expressed as a percentage.
//
__int64 diskTotal, diskRemaining;
_storage.GetDiskSpace( diskTotal, diskRemaining );
if ( diskRemaining < minDiskFreeForMerge )
return FALSE;
if (diskRemaining*100/diskTotal < _frmwrkParams.GetMinDiskFreeForceMerge() &&
_shadowIndexSize*100/diskRemaining > _frmwrkParams.GetMaxShadowFreeForceMerge() )
{
CDmFwEventItem item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_STARTED_TOTAL_DISK_SPACE_USED,
2);
item.AddArg( _storage.GetVolumeName() );
item.AddArg( _frmwrkParams.GetMinDiskFreeForceMerge() );
item.ReportEvent(_adviseStatus);
ciDebugOut(( DEB_ITRACE | DEB_PENDING,
"Master merge, reason: Low on disk space\n" ));
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CMasterMergePolicy::IsMaxFreshListCount, private
//
// Synopsis: If the number of entries in the fresh list is great than
// a threshold, we should master merge, and free up memory
// occupied by the fresh hash.
//
// History: 4-Aug-94 DwightKr Created
// Jan-07-96 mohamedn CDmFwEventItem
//
//----------------------------------------------------------------------------
BOOL CMasterMergePolicy::IsMaxFreshListCount() const
{
if ( _freshCount > _frmwrkParams.GetMaxFreshCount() )
{
CDmFwEventItem item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_STARTED_FRESH_LIST_COUNT,
2);
item.AddArg( _storage.GetVolumeName() );
item.AddArg( _frmwrkParams.GetMaxFreshCount() );
item.ReportEvent(_adviseStatus);
ciDebugOut(( DEB_ITRACE | DEB_PENDING,
"Master merge, reason: More than %d entries in freshhash\n",
_frmwrkParams.GetMaxFreshCount() ));
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CMasterMergePolicy::IsMaxShadowIndexSize, private
//
// Synopsis: If the combined size of all shadow indexes is greater than
// a threahold, we should master merge, and free up disk space.
// The content index should not take up more than a given
// percentage of disk space.
//
// History: 4-Aug-94 DwightKr Created
// Jan-07-96 mohamedn CDmFwEventItem
//
//----------------------------------------------------------------------------
BOOL CMasterMergePolicy::IsMaxShadowIndexSize() const
{
__int64 diskTotal, diskRemaining;
_storage.GetDiskSpace( diskTotal, diskRemaining );
if ( diskRemaining < minDiskFreeForMerge )
return FALSE;
if ( _shadowIndexSize*100/diskTotal > _frmwrkParams.GetMaxShadowIndexSize() )
{
CDmFwEventItem item( EVENTLOG_INFORMATION_TYPE,
MSG_CI_MASTER_MERGE_STARTED_SHADOW_INDEX_SIZE,
2);
item.AddArg( _storage.GetVolumeName() );
item.AddArg( _frmwrkParams.GetMaxShadowIndexSize() );
item.ReportEvent(_adviseStatus);
ciDebugOut(( DEB_ITRACE | DEB_PENDING,
"Master merge, reason: Combined shadow indexes > %d%% of disk\n",
_frmwrkParams.GetMaxShadowIndexSize() ));
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Method: CMasterMergePolicy::ComputeMidNightMergeTime
//
// Synopsis: Function to compute the time (in hundreds of nanoseconds
// from midnight Dec 31, 1969) for the next master merge based on
// the value given from registry.
//
// Arguments: [mergeTime] - The time read from the registry control params.
// It's an offset in minutes after midnight.
//
// Returns: LONG - time for next merge
//
// History: 19-Feb-1996 srikants Moved the code from svcfilt.cxx
// 17-Nov-1999 KyleP Stop using CRT
//
// Notes: If the time is before January 1, 1970 it will return -1.
//
//----------------------------------------------------------------------------
ULONGLONG CMasterMergePolicy::ComputeMidNightMergeTime( LONG mergeTime )
{
Win4Assert( mergeTime < 24 * 60 );
mergeTime *= 60;
//
// What time is it here?
//
SYSTEMTIME stLocal;
GetLocalTime( &stLocal );
LONG lNow = stLocal.wHour*60*60 + stLocal.wMinute*60 + stLocal.wSecond;
//
// Have we passed the daily merge time?
//
LONGLONG llHNSTilMerge; // Hundred-Nano-Second-Til-Merge
LONGLONG const llHNSOneDay = 24*60*60*10000000i64;
if ( lNow < mergeTime )
llHNSTilMerge = mergeTime * 10000000i64;
else
llHNSTilMerge = mergeTime * 10000000i64 + llHNSOneDay;
//
// Create the *local* filetime for the next merge
//
stLocal.wHour = 0;
stLocal.wMinute = 0;
stLocal.wSecond = 0;
stLocal.wMilliseconds = 1;
FILETIME ftLocal;
if ( !SystemTimeToFileTime( &stLocal, &ftLocal ) )
return -1;
((ULARGE_INTEGER *)&ftLocal)->QuadPart += llHNSTilMerge;
//
// Now, adjust to UTC and return
//
FILETIME ft;
if ( !LocalFileTimeToFileTime( &ftLocal, &ft ) )
return -1;
#if CIDBG == 1
FILETIME ft2;
GetSystemTimeAsFileTime( &ft2 );
Win4Assert( ((ULARGE_INTEGER *)&ft2)->QuadPart < ((ULARGE_INTEGER *)&ft)->QuadPart );
#endif // CIDBG
return ((ULARGE_INTEGER *)&ft)->QuadPart;
} //ComputeMidNightMergeTime
//+---------------------------------------------------------------------------
//
// Member: CMasterMergePolicy::IsMidNightMergeTime, public
//
// Synopsis: Determines if it's been 24 hours since the last merge time
//
// History: 4-Aug-1994 DwightKr Created
// 17-Nov-1999 KyleP Stop using CRT
//
//----------------------------------------------------------------------------
BOOL CMasterMergePolicy::IsMidNightMergeTime()
{
FILETIME ft;
GetSystemTimeAsFileTime( &ft );
if ( ((ULARGE_INTEGER *)&ft)->QuadPart > _mergeTime )
{
ULONG mergeTime = _frmwrkParams.GetMasterMergeTime();
ULONGLONG oldMergeTime = _mergeTime;
_mergeTime = ComputeMidNightMergeTime( mergeTime );
//
// If it's been more than 30 minutes since the master merge time, don't
// do it. This can happen if we were doing a master merge as part of
// indexing documents or if you turn on a machine in the morning and it
// was off at the master merge time.
//
LONGLONG const llHNSHalfHour = 30*60*10000000i64;
if ( ((ULARGE_INTEGER *)&ft)->QuadPart > ( oldMergeTime + llHNSHalfHour ) )
return FALSE;
return TRUE;
}
return FALSE;
} //IsMidNightMergeTime