1509 lines
48 KiB
C++
1509 lines
48 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 2000.
|
|
//
|
|
// File: DQUEUE.CXX
|
|
//
|
|
// Contents: Document queue
|
|
//
|
|
// Classes: CDocQueue
|
|
// CExtractDocs
|
|
//
|
|
// History: 29-Mar-91 BartoszM Created
|
|
// 13-Sep-93 BartoszM Split from changes
|
|
// 11-Oct-93 BartoszM Rewrote
|
|
// 24-Feb-97 SitaramR Push filtering
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <doclist.hxx>
|
|
#include <cifailte.hxx>
|
|
|
|
#include "dqueue.hxx"
|
|
#include "resman.hxx"
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::CheckInvariants
|
|
//
|
|
// Synopsis: Check that the doc queue is internally self-consistent
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::CheckInvariants ( char * pTag ) const
|
|
{
|
|
#if DBG
|
|
Win4Assert( _state == eUseSerializedList ||
|
|
_state == eUseUnSerializedList );
|
|
|
|
Win4Assert( _oAppend <= cDocInChunk &&
|
|
_oRetrieve <= cDocInChunk );
|
|
|
|
if (_cDocuments == 0)
|
|
{
|
|
Win4Assert( _oAppend == _oRetrieve );
|
|
|
|
Win4Assert( _unserializedList.IsEmpty() ||
|
|
(_pFirstUnFiltered != 0 &&
|
|
0 == _pFirstUnFiltered->GetNext()) );
|
|
|
|
Win4Assert( _changeLog.IsEmpty() );
|
|
|
|
Win4Assert( _serializedList.IsEmpty() ||
|
|
_pFirstUnFiltered != 0 );
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( ! _unserializedList.IsEmpty() ||
|
|
! _serializedList.IsEmpty() ||
|
|
! _changeLog.IsEmpty() );
|
|
|
|
if ( _pFirstUnFiltered &&
|
|
0 == _pFirstUnFiltered->GetNext() &&
|
|
CDocQueue::eUseUnSerializedList == _state )
|
|
{
|
|
Win4Assert( _cDocuments == (_oAppend - _oRetrieve) );
|
|
}
|
|
}
|
|
|
|
if (_oAppend < cDocInChunk)
|
|
{
|
|
Win4Assert( ! _unserializedList.IsEmpty() );
|
|
}
|
|
if (_oRetrieve < cDocInChunk)
|
|
{
|
|
Win4Assert( _pFirstUnFiltered != 0 );
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Class: CExtractDocs
|
|
//
|
|
// Purpose: A class to "extract" documents from the CDocQueue in a
|
|
// transactioned fashion. If there is a failure while extracting
|
|
// the documents, the state of the CDocQueue will be left in
|
|
// such a way that it will appear no documents have been taken out
|
|
// of the CDocQueue.
|
|
//
|
|
// History: 5-16-94 srikants Created
|
|
//
|
|
// Notes: When we extract documents out of the CDocQueue, we may have
|
|
// to deserialize from the disk if the in-memory queue gets used
|
|
// up. There is a possibility that this may fail and in such a
|
|
// case an exception will be thrown. If we have already taken out
|
|
// some of the documents, these documents may never get filtered
|
|
// again. To prevent this situation, we use a "transactioned
|
|
// extract" provided by this class.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class CExtractDocs
|
|
{
|
|
public:
|
|
|
|
inline CExtractDocs( CDocQueue & docQueue, CDocList & docList );
|
|
inline ~CExtractDocs();
|
|
BOOL IsEmpty() const
|
|
{
|
|
Win4Assert( _cDocsRetrieved <= _cDocsTotal );
|
|
return _cDocsRetrieved == _cDocsTotal;
|
|
}
|
|
inline void Retrieve( WORKID & wid,
|
|
USN & usn,
|
|
VOLUMEID& volumeId,
|
|
ULONG & action,
|
|
ULONG & cRetries,
|
|
ULONG & cSecQRetries );
|
|
void Commit() { _fCommit = TRUE; }
|
|
|
|
private:
|
|
|
|
BOOL IsEndOfCurrentChunk()
|
|
{
|
|
Win4Assert( _oRetrieve <= cDocInChunk );
|
|
return( _oRetrieve == cDocInChunk );
|
|
}
|
|
|
|
CDocQueue & _docQueue;
|
|
CDocList & _docList;
|
|
BOOL _fCommit;
|
|
|
|
CDocQueue::ERetrieveState _state; // Current state of retrieval.
|
|
|
|
ULONG _oRetrieve; // Offset in the first unfiltered
|
|
// chunk to retrieve.
|
|
CDocChunk* _pFirstUnFiltered; // Ptr to first unfiltered chk
|
|
|
|
const unsigned &_cDocsTotal; // # of documents left.
|
|
unsigned _cDocsRetrieved; // # of documents retrieved.
|
|
unsigned _cFilteredChunks; // # of filtered chunks.
|
|
|
|
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CExtractDocs::CExtractDocs
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// History: 5-23-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline CExtractDocs::CExtractDocs( CDocQueue & docQueue, CDocList & docList )
|
|
: _docQueue(docQueue),
|
|
_docList(docList),
|
|
_fCommit(FALSE),
|
|
_state( docQueue._state ),
|
|
_oRetrieve( docQueue._oRetrieve),
|
|
_pFirstUnFiltered( docQueue._pFirstUnFiltered ),
|
|
_cDocsTotal( docQueue._cDocuments ),
|
|
_cDocsRetrieved(0),
|
|
_cFilteredChunks( docQueue._cFilteredChunks )
|
|
{
|
|
docQueue.CheckInvariants("CExtractDocs ctor");
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CExtractDocs::CExtractDocs
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 5-23-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline CExtractDocs::~CExtractDocs()
|
|
{
|
|
_docQueue.CheckInvariants("CExtractDocs pre-dtor");
|
|
if ( _fCommit )
|
|
{
|
|
_docQueue._oRetrieve = _oRetrieve;
|
|
_docQueue._pFirstUnFiltered = _pFirstUnFiltered;
|
|
_docQueue._cDocuments -= _cDocsRetrieved;
|
|
_docQueue._cFilteredChunks = _cFilteredChunks;
|
|
_docQueue._state = _state;
|
|
|
|
//
|
|
// Release the memory for filtered chunks that are
|
|
// serialized.
|
|
//
|
|
CDocChunk * pTemp = _state == CDocQueue::eUseSerializedList ?
|
|
_pFirstUnFiltered : 0;
|
|
|
|
_docQueue._serializedList.DestroyUpto( pTemp );
|
|
|
|
}
|
|
else
|
|
{
|
|
_docList.LokClear();
|
|
}
|
|
|
|
_docQueue.CheckInvariants("CExtractDocs post-dtor");
|
|
#if DBG
|
|
if ( _pFirstUnFiltered &&
|
|
0 == _pFirstUnFiltered->GetNext() &&
|
|
CDocQueue::eUseUnSerializedList == _state )
|
|
{
|
|
Win4Assert( _docQueue._cDocuments ==
|
|
(_docQueue._oAppend - _docQueue._oRetrieve) );
|
|
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CExtractDocs::Retrieve
|
|
//
|
|
// Synopsis: Retrieves a single document from the docqueue. It has been
|
|
// assumed that the docQueue is not empty when this method is
|
|
// called.
|
|
//
|
|
// Arguments: [wid] -- Output - Wid of the document
|
|
// [usn] -- Output - USN of the document
|
|
// [volumeId] -- Output - Volume id of the document
|
|
// [action] -- Output - Action performed on the document
|
|
// [cRetries] -- Output - Number of filtering retries.
|
|
// [cSecQRetries] -- Output - Number of Secondary Q filtering retries.
|
|
//
|
|
// History: 5-26-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline void CExtractDocs::Retrieve( WORKID & wid,
|
|
USN & usn,
|
|
VOLUMEID& volumeId,
|
|
ULONG & action,
|
|
ULONG & cRetries,
|
|
ULONG & cSecQRetries )
|
|
{
|
|
_docQueue.CheckInvariants("CExtractDocs::Retrieve pre");
|
|
|
|
Win4Assert( !IsEmpty() );
|
|
Win4Assert( 0 != _pFirstUnFiltered );
|
|
|
|
if ( IsEndOfCurrentChunk() )
|
|
{
|
|
if ( CDocQueue::eUseSerializedList == _state )
|
|
{
|
|
//
|
|
// We are currently retrieving from the serialized list.
|
|
// Check to see if there is another chunk in the serialized
|
|
// list that can be used next.
|
|
//
|
|
if ( 0 != _pFirstUnFiltered->GetNext() )
|
|
{
|
|
_pFirstUnFiltered = _pFirstUnFiltered->GetNext();
|
|
}
|
|
else if ( _docQueue._changeLog.IsEmpty() )
|
|
{
|
|
//
|
|
// The change log doesn't have any archived chunks to be
|
|
// read. In this case, the unserialized list cannot be
|
|
// empty.
|
|
//
|
|
Win4Assert( !_docQueue._unserializedList.IsEmpty() );
|
|
_pFirstUnFiltered = _docQueue._unserializedList.GetFirst();
|
|
_state = CDocQueue::eUseUnSerializedList;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// DeSerialize from the change log.
|
|
//
|
|
_docQueue.DeSerialize();
|
|
_pFirstUnFiltered = _pFirstUnFiltered->GetNext();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The state is UseUnSerializedList.
|
|
//
|
|
Win4Assert( 0 != _pFirstUnFiltered->GetNext() );
|
|
_pFirstUnFiltered = _pFirstUnFiltered->GetNext();
|
|
}
|
|
|
|
_oRetrieve = 0;
|
|
Win4Assert( 0 != _pFirstUnFiltered );
|
|
}
|
|
|
|
CDocNotification * pRetrieve = _pFirstUnFiltered->GetDoc( _oRetrieve );
|
|
|
|
wid = pRetrieve->Wid();
|
|
usn = pRetrieve->Usn();
|
|
volumeId = pRetrieve->VolumeId();
|
|
action = pRetrieve->Action() & ~CI_SCAN_UPDATE;
|
|
cRetries = pRetrieve->Retries();
|
|
cSecQRetries = pRetrieve->SecQRetries();
|
|
|
|
_oRetrieve++;
|
|
_cDocsRetrieved++;
|
|
|
|
if ( IsEndOfCurrentChunk() )
|
|
{
|
|
_cFilteredChunks++;
|
|
}
|
|
|
|
if ( CDocQueue::eUseUnSerializedList == _state &&
|
|
0 == _pFirstUnFiltered->GetNext() )
|
|
{
|
|
Win4Assert( (_cDocsTotal - _cDocsRetrieved) ==
|
|
(_docQueue._oAppend - _oRetrieve) );
|
|
}
|
|
|
|
Win4Assert( _cDocsRetrieved <= _cDocsTotal );
|
|
Win4Assert( _oRetrieve <= cDocInChunk );
|
|
|
|
Win4Assert( _docQueue._changeLog.AvailChunkCount() * cDocInChunk <=
|
|
(_cDocsTotal-_cDocsRetrieved) );
|
|
|
|
_docQueue.CheckInvariants("CExtractDocs::Retrieve post");
|
|
} //Retrieve
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::CDocQueue
|
|
//
|
|
// Synopsis: Initializes queue
|
|
//
|
|
// Arguments: [widChangeLog] -- Workid of changlog
|
|
// [storage] -- Storage
|
|
// [type] -- Type of change log -- Primary or Secondary
|
|
// [frmwrkParams] -- Framework parameters
|
|
//
|
|
// History: 11-Oct-93 BartoszM Created
|
|
// 10-Feb-94 DwightKr Added code to load from disk
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CDocQueue::CDocQueue( WORKID widChangeLog,
|
|
PStorage & storage,
|
|
PStorage::EChangeLogType type,
|
|
CCiFrameworkParams & frmwrkParams ) :
|
|
_frmwrkParams( frmwrkParams ),
|
|
_sigDocQueue(eSigDocQueue),
|
|
_changeLog( widChangeLog, storage, type ),
|
|
_pResManager(0)
|
|
{
|
|
LokInit();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokInit
|
|
//
|
|
// Synopsis: Initializes the doc queue members. Also, deserializes chunks
|
|
// from the changelog if it is not empty.
|
|
//
|
|
// History: 27-Dec-94 SrikantS Moved from ~ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::LokInit()
|
|
{
|
|
_state = eUseSerializedList;
|
|
_oAppend = _oRetrieve = cDocInChunk;
|
|
_pFirstUnFiltered = 0;
|
|
_cFilteredChunks = 0;
|
|
_cRefiledDocs = 0;
|
|
_cDocuments = 0;
|
|
|
|
if ( !_changeLog.IsEmpty() )
|
|
{
|
|
_cDocuments = _changeLog.AvailChunkCount() * cDocInChunk;
|
|
|
|
ULONG nChunksToRead = min( _frmwrkParams.GetMaxQueueChunks() / 2,
|
|
_changeLog.AvailChunkCount() );
|
|
|
|
ULONG nChunksRead = _changeLog.DeSerialize( _serializedList,
|
|
nChunksToRead );
|
|
Win4Assert( nChunksRead == nChunksToRead );
|
|
|
|
_pFirstUnFiltered = _serializedList.GetFirst();
|
|
Win4Assert( 0 != _pFirstUnFiltered );
|
|
_oRetrieve = 0;
|
|
}
|
|
CheckInvariants("LokInit post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::~CDocQueue, public
|
|
//
|
|
// History: 11-Oct-93 BartoszM Created
|
|
// 10-Feb-94 DwightKr Added code to save to disk
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CDocQueue::~CDocQueue()
|
|
{
|
|
CheckInvariants("CDocQueue dtor");
|
|
|
|
ciDebugOut (( DEB_ITRACE, "CDocQueue\n" ));
|
|
|
|
//
|
|
// _cRefiledDocs may be non-zero, but that isn't a problem, since
|
|
// the updates are still sitting on disk. They are likely here
|
|
// because FilterReady got an exception from WorkIdToPath() as CI
|
|
// is shutting down, and the docs were refiled.
|
|
//
|
|
//
|
|
// Win4Assert( 0 == _cRefiledDocs );
|
|
//
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokCleanup
|
|
//
|
|
// Synopsis: Cleans up the private data members.
|
|
//
|
|
// History: 27-Dec-94 SrikantS Moved from ~ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDocQueue::LokCleanup()
|
|
{
|
|
CheckInvariants("LokCleanup");
|
|
_serializedList.DestroyUpto( 0 );
|
|
_unserializedList.DestroyUpto( 0 );
|
|
_cDocuments = _cFilteredChunks = 0;
|
|
_oAppend = _oRetrieve = cDocInChunk;
|
|
_pFirstUnFiltered = 0;
|
|
_cRefiledDocs = 0;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokEmpty, public
|
|
//
|
|
// History: 15-Nov-94 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDocQueue::LokEmpty()
|
|
{
|
|
LokCleanup();
|
|
_changeLog.LokEmpty();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LokDismount
|
|
//
|
|
// Synopsis: Prepares for the dismount by serializing the changelog.
|
|
//
|
|
// History: 6-20-94 srikants Created
|
|
//
|
|
// Notes: This routine should not throw, since we may be looping
|
|
// to dismount multiple partitions on a single volume.
|
|
// This avoids an outer TRY/CATCH block, and we really can't
|
|
// do anything to correct a failed dismount.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
NTSTATUS CDocQueue::LokDismount( CChangeTrans & xact )
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TRY
|
|
{
|
|
|
|
#ifdef CI_FAILTEST
|
|
NTSTATUS failstatus = STATUS_NO_MEMORY ;
|
|
ciFAILTEST( failstatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
if ( _changeLog.AreUpdatesEnabled() )
|
|
{
|
|
if ( 0 != _cRefiledDocs )
|
|
LokAppendRefiledDocs( xact );
|
|
|
|
if ( !_unserializedList.IsEmpty() )
|
|
{
|
|
Serialize(0);
|
|
}
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"ChangeLog could not be written out due to error 0x%X\n",
|
|
e.GetErrorCode() ));
|
|
|
|
status = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
|
|
//
|
|
// Even if the dismount failed, we must clean up our internal
|
|
// data structures.
|
|
//
|
|
LokCleanup();
|
|
|
|
//
|
|
// Should not accept any more update notifications.
|
|
//
|
|
_changeLog.LokDisableUpdates();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::Append, public
|
|
//
|
|
// Synopsis: Appends documents to the queue
|
|
//
|
|
// Arguments: [wid] -- work id
|
|
// [usn] -- update sequence number
|
|
// [volumeId] -- volume id
|
|
// [action] -- update/delete
|
|
// [cRetries] -- number of retries on this item
|
|
// [cSecQRetries] -- number of Sec Q retries on this item
|
|
//
|
|
// History: 11-Oct-93 BartoszM Created
|
|
//
|
|
// Notes: The RESMAN lock was taken before this method was called.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
SCODE CDocQueue::Append ( CChangeTrans & xact,
|
|
WORKID wid,
|
|
USN usn,
|
|
VOLUMEID volumeId,
|
|
ULONG action,
|
|
ULONG cRetries,
|
|
ULONG cSecQRetries )
|
|
{
|
|
CheckInvariants("Append pre");
|
|
|
|
// The first try is 1. The first retry is 2
|
|
|
|
Win4Assert( cRetries <= ( _frmwrkParams.GetFilterRetries() + 1 ) );
|
|
Win4Assert( 0 != cRetries );
|
|
|
|
// check for corrupted values, either wid is invalid or action must make sense
|
|
Win4Assert( wid == widInvalid ||
|
|
(action & CI_UPDATE_OBJ) != 0 ||
|
|
(action & CI_UPDATE_PROPS) != 0 ||
|
|
(action & CI_DELETE_OBJ) != 0 );
|
|
|
|
Win4Assert( 0 != wid );
|
|
|
|
Win4Assert( ((wid & 0x80000000) == 0) || (wid == widInvalid) );
|
|
|
|
//
|
|
// If we have disabled updates, then don't bother saving this
|
|
// filter request. If updates are disabled, we will schedule an
|
|
// EnableUpdates at a later time to discover all of the lost updates.
|
|
// In push filtering, there is no internal refiling and so any appends
|
|
// are always from client.
|
|
// In pull filtering, the in-memory part of changelog is cleaned up,
|
|
// and the in-memory part of changelog is re-initialized on startup.
|
|
// Hence appends can be ignored for both push and pull filtering
|
|
// when _fUpdatesEnabled is false.
|
|
//
|
|
if ( !_changeLog.AreUpdatesEnabled() )
|
|
return CI_E_UPDATES_DISABLED;
|
|
|
|
//
|
|
// Check if the current append chunk is full.
|
|
//
|
|
if ( IsAppendChunkFull() )
|
|
{
|
|
//
|
|
// Before allocating a new one, see if we have exceeded the
|
|
// threshold of maximum chunks in memory and serialize
|
|
// the list appropriately.
|
|
//
|
|
ULONG cTotal = _serializedList.Count() + _unserializedList.Count();
|
|
|
|
ULONG MaxQueueChunks = _frmwrkParams.GetMaxQueueChunks();
|
|
|
|
ciDebugOut(( DEB_ITRACE, "ctotal %d, maxqueuechunks %d\n",
|
|
cTotal, MaxQueueChunks ));
|
|
|
|
if ( cTotal >= MaxQueueChunks )
|
|
{
|
|
Serialize( MaxQueueChunks / 2 );
|
|
}
|
|
|
|
//
|
|
// Allocate a new chunk and append to the unserialized list.
|
|
//
|
|
CDocChunk * pChunk = new CDocChunk();
|
|
_unserializedList.Append( pChunk );
|
|
_oAppend = 0;
|
|
}
|
|
|
|
//
|
|
// OPTIMIZATION to avoid processing the same wid (eg temp files)
|
|
// in the same chunk.
|
|
//
|
|
// Insert this record so that it overwrites the first entry with the
|
|
// same WID within this chunk. This results in the extraction procedure
|
|
// to process the WID with the last STATUS received in this chunk.
|
|
//
|
|
CDocChunk * pLastChunk = _unserializedList.GetLast();
|
|
Win4Assert( 0 != pLastChunk );
|
|
CDocNotification * pAppend = pLastChunk->GetDoc( _oAppend );
|
|
pAppend->Set ( wid, usn, volumeId, action, cRetries, cSecQRetries );
|
|
|
|
CDocNotification * pRec;
|
|
|
|
//
|
|
// A special case exists if we are have inserted a record into the chunk
|
|
// from which we are extracting records. We need to search from the
|
|
// current read point forward, rather than from the start of the chunk.
|
|
//
|
|
if ( _pFirstUnFiltered == pLastChunk )
|
|
{
|
|
//
|
|
// Note that if _oRetrieve is cDocInChunk, then we would have
|
|
// created a new chunk and _pFirstUnFiltered would not be same
|
|
// as pLastChunk.
|
|
//
|
|
pRec = pLastChunk->GetDoc(_oRetrieve); // Starting search location
|
|
}
|
|
else
|
|
{
|
|
pRec = pLastChunk->GetDoc(0); // Starting search location
|
|
}
|
|
|
|
//
|
|
// Since we have added the current wid as a sentinel value, it is
|
|
// guaranteed to terminate.
|
|
//
|
|
while (pRec->Wid() != wid )
|
|
pRec++;
|
|
|
|
if (pRec == pAppend) // This will be true 99% of the time
|
|
{
|
|
_oAppend++;
|
|
_cDocuments++;
|
|
}
|
|
else // Overwrite the WID found
|
|
{
|
|
Win4Assert( wid == pRec->Wid() );
|
|
pRec->Set ( wid, usn, volumeId, action, cRetries, cSecQRetries );
|
|
}
|
|
|
|
if ( 0 == _pFirstUnFiltered )
|
|
{
|
|
_pFirstUnFiltered = pLastChunk;
|
|
_state = eUseUnSerializedList;
|
|
_oRetrieve = 0;
|
|
Win4Assert( _cDocuments == 1 && _oAppend == 1 );
|
|
}
|
|
|
|
if ( pLastChunk == _pFirstUnFiltered )
|
|
{
|
|
Win4Assert( _cDocuments == (_oAppend - _oRetrieve) );
|
|
}
|
|
|
|
Win4Assert( _changeLog.AvailChunkCount() * cDocInChunk <= _cDocuments );
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( STATUS_DISK_FULL );
|
|
#endif // CI_FAILTEST
|
|
|
|
CheckInvariants("Append post");
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::Get
|
|
//
|
|
// Synopsis: Get top of the queue. Items are not removed from the queue.
|
|
//
|
|
// Arguments: [aWid] -- Array of wids
|
|
// [cWid] -- Count of wids in array, updated on return
|
|
//
|
|
// Returns: TRUE if all items in the queue were returned.
|
|
//
|
|
// History: 23-May-94 SrikantS Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CDocQueue::Get( WORKID * aWid, unsigned & cWid )
|
|
{
|
|
CheckInvariants("Get");
|
|
|
|
//
|
|
// We can't refile w/o a transaction, so just return FALSE,
|
|
// and if there are too many we won't bother to return any.
|
|
//
|
|
|
|
if ( (0 != _cRefiledDocs) ||
|
|
(_cDocuments > cWid) ||
|
|
!_changeLog.AreUpdatesEnabled() )
|
|
{
|
|
cWid = 0;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Get documents from in-memory list
|
|
//
|
|
|
|
CDocChunk * pChunk = _pFirstUnFiltered;
|
|
Win4Assert( 0 != _pFirstUnFiltered || 0 == _cDocuments );
|
|
|
|
unsigned iDocInChunk = _oRetrieve;
|
|
|
|
for ( cWid = 0; cWid < _cDocuments; cWid++ )
|
|
{
|
|
if ( iDocInChunk == cDocInChunk )
|
|
{
|
|
pChunk = pChunk->GetNext();
|
|
iDocInChunk = 0;
|
|
|
|
if ( 0 == pChunk )
|
|
break;
|
|
}
|
|
|
|
aWid[cWid] = pChunk->GetDoc(iDocInChunk)->Wid();
|
|
iDocInChunk++;
|
|
}
|
|
|
|
//
|
|
// We may not have got all the documents. This occurs when we're using
|
|
// the serialized list and don't have all the documents available in
|
|
// memory. (We do try to keep a reasonable number of docs in memory)
|
|
//
|
|
|
|
return ( cWid == _cDocuments );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::Extract, public
|
|
//
|
|
// Synopsis: Extract top of the queue
|
|
//
|
|
// Arguments: [xact] -- transaction
|
|
// [maxDocs] -- size of doclist
|
|
// [docList] -- doc list to be filled. It will NOT have any
|
|
// duplicate WID entries. The last entry for a wid
|
|
// will supercede all other entries before it.
|
|
//
|
|
// History: 11-Oct-93 BartoszM Created
|
|
// 16-May-94 SrikantS Modified to make it a
|
|
// transaction.
|
|
//
|
|
// Notes: This function must be called under resman lock.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDocQueue::Extract( CChangeTrans & xact,
|
|
unsigned maxDocs,
|
|
CDocList & docList)
|
|
{
|
|
CheckInvariants("Extract pre");
|
|
|
|
//
|
|
// If there are any documents that got requeued due to a failure
|
|
// after they were extracted, we must append to the list before
|
|
// extracting the documents.
|
|
//
|
|
if ( 0 != _cRefiledDocs )
|
|
{
|
|
LokAppendRefiledDocs( xact );
|
|
}
|
|
|
|
|
|
//
|
|
// If we are not saving updates, then don't return any docs to
|
|
// filter.
|
|
//
|
|
if ( !_changeLog.AreUpdatesEnabled() )
|
|
{
|
|
docList.LokSetCount(0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// BeginTransaction
|
|
//
|
|
CExtractDocs extractDocs( *this, docList );
|
|
|
|
unsigned i = 0;
|
|
while ( (i < maxDocs) && !extractDocs.IsEmpty() )
|
|
{
|
|
WORKID wid;
|
|
USN usn;
|
|
VOLUMEID volumeId;
|
|
ULONG action;
|
|
ULONG cRetries;
|
|
ULONG cSecQRetries;
|
|
|
|
extractDocs.Retrieve( wid, usn, volumeId, action, cRetries, cSecQRetries );
|
|
|
|
if (widInvalid != wid)
|
|
{
|
|
//
|
|
// If the wid already exists in the doclist, we should overwrite
|
|
// that with this one. The last entry in the changelog for the
|
|
// same wid always wins.
|
|
//
|
|
|
|
//
|
|
// This is currently an N^2 algorithm. See if there
|
|
// is a better algorithm if it shows up in profiling. It hasn't
|
|
// in the past. Note that there will only be 16 items here
|
|
// so the algorithm choice doesn't matter too much.
|
|
//
|
|
|
|
for ( unsigned j = 0; j < i; j++ )
|
|
{
|
|
if ( docList.Wid(j) == wid )
|
|
break;
|
|
}
|
|
|
|
// check for corrupted values
|
|
Win4Assert( action == CI_UPDATE_OBJ ||
|
|
action == CI_UPDATE_PROPS ||
|
|
action == CI_DELETE_OBJ );
|
|
|
|
docList.Set( j,
|
|
wid,
|
|
usn,
|
|
volumeId,
|
|
(action == CI_DELETE_OBJ) ? DELETED : PENDING,
|
|
cRetries,
|
|
cSecQRetries
|
|
);
|
|
|
|
if ( j == i )
|
|
{
|
|
// New Wid. Increment the count of entries
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_PENDING | DEB_ITRACE,
|
|
"Duplicate wid=0x%X overwritten by action=0x%X\n",
|
|
wid, action ));
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckInvariants("Extract pre-commit");
|
|
docList.LokSetCount(i);
|
|
extractDocs.Commit(); // EndTransaction
|
|
CheckInvariants("Extract post");
|
|
} //Extract
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MoveToSerializedList
|
|
//
|
|
// Synopsis: Skips the chunks in the listToCompact in the change log and
|
|
// appends them to the _serializedList, up to the maximum
|
|
// permitted by the "nMaxChunksInMem" parameter. This will
|
|
// prevent un-necessarily reading the serialized chunks that are
|
|
// already in memory.
|
|
//
|
|
// The rest of the chunks are deleted and will have to be
|
|
// de-serialized at a later time because they cannot be held in
|
|
// memory now.
|
|
//
|
|
// Effects: The listToCompact will be emptied when this returns.
|
|
//
|
|
// Arguments: [listToCompact] -- The list which needs to be compacted.
|
|
// [nChunksToMove] -- Maximum number of chunks that must be
|
|
// left in memory (suggested).
|
|
//
|
|
// History: 6-06-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::MoveToSerializedList( CChunkList & listToCompact,
|
|
ULONG nChunksToMove )
|
|
{
|
|
//
|
|
// Until the count in the serializedlist is less than the
|
|
// maximum allowed, we transfer from the listToCompact
|
|
// to the serialized list.
|
|
//
|
|
while ( ( nChunksToMove > 0 ) &&
|
|
(0 != listToCompact.GetFirst()) )
|
|
{
|
|
CDocChunk * pChunk = listToCompact.Pop();
|
|
Win4Assert( 0 != pChunk );
|
|
_serializedList.Append(pChunk);
|
|
_changeLog.SkipChunk();
|
|
nChunksToMove--;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SerializeInLHS
|
|
//
|
|
// Synopsis: Serializes the unserialized chunks when we are retrieving
|
|
// from the "Left Hand Side" of the change log, ie, in the
|
|
// "eUseSerializedList" state.
|
|
//
|
|
// Effects: The unserializedList is emptied and appropriate number of
|
|
// chunks appended to the end of the serialized list.
|
|
//
|
|
// Arguments: [nMaxChunksInMem] -- Suggested maximum number of chunks that
|
|
// must be left in memory after doing the
|
|
// serialization.
|
|
// [aUsnFlushInfo] -- Usn flush info is returned here
|
|
//
|
|
// History: 6-06-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::SerializeInLHS( ULONG nMaxChunksInMem,
|
|
CCountedDynArray<CUsnFlushInfo> & aUsnFlushInfo )
|
|
{
|
|
CheckInvariants("SerializeInLHS pre");
|
|
|
|
Win4Assert( eUseSerializedList == _state );
|
|
Win4Assert( _pFirstUnFiltered == _serializedList.GetFirst() );
|
|
|
|
//
|
|
// Delete all the filtered chunks.
|
|
//
|
|
|
|
ULONG cChunkInLogBefore = _changeLog.AvailChunkCount();
|
|
_changeLog.Serialize( _unserializedList, aUsnFlushInfo );
|
|
|
|
ULONG nChunksToMove = 0;
|
|
if ( _serializedList.Count() < nMaxChunksInMem )
|
|
{
|
|
nChunksToMove = nMaxChunksInMem - _serializedList.Count();
|
|
}
|
|
|
|
CChunkList listToCompact;
|
|
listToCompact.AcquireAndAppend( _unserializedList );
|
|
|
|
if ( 0 == cChunkInLogBefore )
|
|
{
|
|
//
|
|
// We can move as much of the listToCompact to the serialized
|
|
// list as possible.
|
|
//
|
|
MoveToSerializedList( listToCompact, nChunksToMove );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Optimization - instead of deleting the unserialized list and
|
|
// then reading them, we will see if we can fit the unserialized list
|
|
// chunks in memory without having to exceed the memory threshold.
|
|
//
|
|
if ( cChunkInLogBefore < nChunksToMove )
|
|
{
|
|
//
|
|
// We have to deserialize the change log because chunks
|
|
// have to be consumed in the FIFO order strictly.
|
|
// Since there can be a failure while doing the deserialization,
|
|
// we must not leave the docqueue in an inconsistent
|
|
// state - the unSerializedList must be empty.
|
|
//
|
|
_changeLog.DeSerialize( _serializedList, cChunkInLogBefore );
|
|
nChunksToMove -= cChunkInLogBefore;
|
|
MoveToSerializedList( listToCompact, nChunksToMove );
|
|
}
|
|
|
|
}
|
|
|
|
Win4Assert( _unserializedList.IsEmpty() );
|
|
|
|
//
|
|
// After we serialize, we must start using a new chunk for doing
|
|
// appends.
|
|
//
|
|
Win4Assert( _oAppend <= cDocInChunk );
|
|
_cDocuments += (cDocInChunk-_oAppend);
|
|
_oAppend = cDocInChunk;
|
|
|
|
Win4Assert( (_oAppend - _oRetrieve) == (_cDocuments % cDocInChunk) ||
|
|
((_oAppend - _oRetrieve) == cDocInChunk &&
|
|
(_cDocuments % cDocInChunk) == 0) );
|
|
Win4Assert( _changeLog.AvailChunkCount() * cDocInChunk <= _cDocuments );
|
|
Win4Assert( 0 != _pFirstUnFiltered || 0 == _cDocuments );
|
|
|
|
CheckInvariants("SerializeInLHS post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SerializeInRHS
|
|
//
|
|
// Synopsis: Serializes the un-serialized list when we are retrieving
|
|
// from the "Right Hand Side" of the change-log, ie, we are
|
|
// in the "eUseUnSerializedList" state.
|
|
//
|
|
// Effects: The _unSerializedList will be emptied and appropriate
|
|
// number of chunks appended to the _serializedList.
|
|
//
|
|
// Arguments: [nChunksToMove] -- Maximum number of chunks permitted
|
|
// in memory (suggested).
|
|
// [aUsnFlushInfo] -- Usn flush info is returned here
|
|
//
|
|
// History: 6-06-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline void CDocQueue::SerializeInRHS( ULONG nChunksToMove,
|
|
CCountedDynArray<CUsnFlushInfo> & aUsnFlushInfo )
|
|
{
|
|
CheckInvariants("SerializeInRHS pre");
|
|
|
|
//
|
|
// We are using the unserialized list. The change log must be
|
|
// empty and the serialized list must also be empty.
|
|
//
|
|
Win4Assert( eUseUnSerializedList == _state );
|
|
Win4Assert( _changeLog.IsEmpty() );
|
|
Win4Assert( _serializedList.IsEmpty() );
|
|
|
|
//
|
|
// Write the unserialized list to disk and keep as much of
|
|
// it as possible.
|
|
//
|
|
_changeLog.Serialize( _unserializedList, aUsnFlushInfo );
|
|
|
|
//
|
|
// Skip up to the _pFirstUnFiltered and delete them.
|
|
//
|
|
while ( _unserializedList.GetFirst() != _pFirstUnFiltered )
|
|
{
|
|
CDocChunk * pDelete = _unserializedList.Pop();
|
|
delete pDelete;
|
|
_changeLog.SkipChunk();
|
|
}
|
|
|
|
//
|
|
// Keep as much of the remaining unserialized list in memory
|
|
// as possible.
|
|
//
|
|
CChunkList listToCompact;
|
|
listToCompact.AcquireAndAppend( _unserializedList );
|
|
BOOL fInLastChunk = FALSE;
|
|
|
|
if ( 0 != _pFirstUnFiltered )
|
|
{
|
|
//
|
|
// We MUST have at least one chunk (the _pFirstUnFiltered) in memory.
|
|
//
|
|
Win4Assert( listToCompact.GetFirst() == _pFirstUnFiltered );
|
|
nChunksToMove = max(nChunksToMove, 1);
|
|
fInLastChunk = _pFirstUnFiltered == listToCompact.GetLast();
|
|
}
|
|
|
|
MoveToSerializedList( listToCompact, nChunksToMove );
|
|
|
|
_state = eUseSerializedList;
|
|
|
|
//
|
|
// After we serialize, we must start using a new chunk for doing
|
|
// appends.
|
|
//
|
|
Win4Assert( _oAppend <= cDocInChunk );
|
|
if ( fInLastChunk && (_oAppend == _oRetrieve) )
|
|
{
|
|
//
|
|
// We have filtered all the documents. After serialization, we will
|
|
// start using a new chunk for appending new documents. We should
|
|
// consider this chunk fully filtered.
|
|
//
|
|
Win4Assert( 0 == _cDocuments );
|
|
_serializedList.DestroyUpto(0);
|
|
if ( cDocInChunk != _oRetrieve )
|
|
{
|
|
//
|
|
// The filtered chunk count should be incremented because we have
|
|
// skipped a few documents in the current chunk without filtering
|
|
// but have considered them to be filtered.
|
|
//
|
|
_cFilteredChunks++;
|
|
}
|
|
_oAppend = _oRetrieve = cDocInChunk;
|
|
_pFirstUnFiltered = 0;
|
|
}
|
|
else
|
|
{
|
|
_cDocuments += (cDocInChunk-_oAppend);
|
|
_oAppend = cDocInChunk;
|
|
|
|
Win4Assert( _changeLog.AvailChunkCount() * cDocInChunk <= _cDocuments );
|
|
}
|
|
|
|
Win4Assert( (_oAppend - _oRetrieve) == (_cDocuments % cDocInChunk) ||
|
|
((_oAppend - _oRetrieve) == cDocInChunk &&
|
|
(_cDocuments % cDocInChunk) == 0) );
|
|
Win4Assert( 0 != _pFirstUnFiltered || 0 == _cDocuments );
|
|
CheckInvariants("SerializeInRHS post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Serialize
|
|
//
|
|
// Synopsis: Serializes the _unSerialized list to disk, moves some of the
|
|
// chunks (as permitted nChunksToMove) to the _serializedList
|
|
// and frees up the remaining chunks.
|
|
//
|
|
// Arguments: [nChunksToMove] -- Suggested maximum number of chunks in
|
|
// memory.
|
|
//
|
|
// History: 6-06-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::Serialize( ULONG nMaxChunksInMem )
|
|
{
|
|
CheckInvariants("Serialize pre");
|
|
//
|
|
// Get the current system time which will be used as the flush time
|
|
//
|
|
FILETIME ftFlush;
|
|
GetSystemTimeAsFileTime( &ftFlush );
|
|
|
|
CCountedDynArray<CUsnFlushInfo> aUsnFlushInfo;
|
|
|
|
if ( eUseSerializedList == _state )
|
|
{
|
|
//
|
|
// Delete the serialized list up to the first unfiltered chunk.
|
|
//
|
|
_serializedList.DestroyUpto( _pFirstUnFiltered );
|
|
SerializeInLHS( nMaxChunksInMem, aUsnFlushInfo );
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
if ( _oRetrieve != cDocInChunk )
|
|
{
|
|
Win4Assert( _pFirstUnFiltered != 0 );
|
|
Win4Assert( _pFirstUnFiltered != _serializedList.GetFirst() );
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// We don't need any of the chunks in the serialized list to be
|
|
// in memory.
|
|
//
|
|
_serializedList.DestroyUpto( 0 );
|
|
SerializeInRHS( nMaxChunksInMem, aUsnFlushInfo );
|
|
}
|
|
|
|
Win4Assert( _pResManager );
|
|
|
|
if ( _changeLog.IsPrimary() )
|
|
_pResManager->NotifyFlush( ftFlush,
|
|
aUsnFlushInfo.Count(),
|
|
(USN_FLUSH_INFO **) aUsnFlushInfo.GetPointer() );
|
|
|
|
Win4Assert( _unserializedList.IsEmpty() );
|
|
CheckInvariants("Serialize post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DeSerialize
|
|
//
|
|
// Synopsis: Reads the appropriate number of serialized chunks from
|
|
// change log into memory.
|
|
//
|
|
// History: 6-01-94 srikants Redesigned.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::DeSerialize()
|
|
{
|
|
CheckInvariants("DeSerialize pre");
|
|
|
|
Win4Assert( eUseSerializedList == _state );
|
|
Win4Assert( !_changeLog.IsEmpty() );
|
|
|
|
//
|
|
// Delete all the filtered chunks.
|
|
//
|
|
_serializedList.DestroyUpto( _pFirstUnFiltered );
|
|
|
|
//
|
|
// First figure out how many chunks can be deserialized.
|
|
//
|
|
ULONG MaxQueueChunks = _frmwrkParams.GetMaxQueueChunks();
|
|
ULONG nChunksToRead = min( MaxQueueChunks / 2,
|
|
_changeLog.AvailChunkCount() );
|
|
|
|
ULONG nChunksInMem = _serializedList.Count() + _unserializedList.Count();
|
|
const ULONG nMaxChunksInMem = MaxQueueChunks / 2;
|
|
|
|
if ( nChunksToRead + nChunksInMem > nMaxChunksInMem )
|
|
{
|
|
|
|
//
|
|
// If we go ahead and de-serialize nChunksToRead, we will exceed
|
|
// the maximum threshold. We will first serialize the
|
|
// un-serialized documents and free up some memory.
|
|
//
|
|
|
|
if ( 0 != _unserializedList.Count() )
|
|
{
|
|
//
|
|
// Get the current system time which will be used as the flush time
|
|
//
|
|
FILETIME ftFlush;
|
|
GetSystemTimeAsFileTime( &ftFlush );
|
|
|
|
CCountedDynArray<CUsnFlushInfo> aUsnFlushInfo;
|
|
|
|
ULONG nChunksToLeave = nMaxChunksInMem - nChunksToRead;
|
|
SerializeInLHS( nChunksToLeave, aUsnFlushInfo );
|
|
|
|
Win4Assert( _pResManager );
|
|
|
|
if ( _changeLog.IsPrimary() )
|
|
_pResManager->NotifyFlush( ftFlush,
|
|
aUsnFlushInfo.Count(),
|
|
(USN_FLUSH_INFO **) aUsnFlushInfo.GetPointer() );
|
|
}
|
|
|
|
Win4Assert( _unserializedList.Count() == 0 );
|
|
nChunksInMem = _serializedList.Count();
|
|
if ( nChunksInMem >= nMaxChunksInMem )
|
|
{
|
|
//
|
|
// This is the special case where the number of docs in CDocList
|
|
// is > the MaxQueueChunks and this is to allow progress.
|
|
//
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"CDocQueue: Too many chunks in memory: %d\n", nChunksInMem ));
|
|
nChunksToRead = 1;
|
|
}
|
|
else
|
|
{
|
|
nChunksToRead = min( nChunksToRead,
|
|
nMaxChunksInMem-nChunksInMem );
|
|
}
|
|
}
|
|
|
|
Win4Assert( nChunksToRead > 0 );
|
|
_changeLog.DeSerialize( _serializedList, nChunksToRead );
|
|
_state = eUseSerializedList;
|
|
|
|
CheckInvariants("DeSerialize post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LokDeleteWIDsInPersistentIndexes
|
|
//
|
|
// Synopsis: Deletes the workIds that have been filtered and have already
|
|
// made it into a persistent index. This causes the change log
|
|
// to be shrinked from the front.
|
|
//
|
|
// Arguments: [xact] -- Change transaction to use
|
|
// [freshTestLatest] -- Latest freshTest
|
|
// [freshTestAtMerge] -- Fresh test snapshoted at time of shadow
|
|
// merge (can be same as freshTestLatest)
|
|
// [docList] -- Doc list
|
|
// [notifTrans] -- Notification transaction
|
|
//
|
|
// Algorithm: Documents are always processed in the FIFO order in the
|
|
// change log. If a document is not successfully filtered, it
|
|
// is appened as a new entry to the change log. If the order
|
|
// in change log is say A, B, C, D .. then if D has made it into
|
|
// persistent index, we can be sure that either all of A, B
|
|
// and C have made it into persistent index or they have been
|
|
// re-added to the change log. In either case, it is fine to
|
|
// delete up to D.
|
|
//
|
|
// History: 6-01-94 srikants Created
|
|
// 24-Feb-97 SitaramR Push filtering
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::LokDeleteWIDsInPersistentIndexes( CChangeTrans & xact,
|
|
CFreshTest & freshTestLatest,
|
|
CFreshTest & freshTestAtMerge,
|
|
CDocList & docList,
|
|
CNotificationTransaction & notifTrans )
|
|
{
|
|
CheckInvariants("LokDeleteWIDsInPersistentIndexes pre");
|
|
|
|
//
|
|
// First serialize everything and leave at most MAX_QUEUE_CHUNKS in
|
|
// memory. Serialization is necessary to make sure that the docs which
|
|
// were re-added due to filter failures are persistently recorded in
|
|
// the change log.
|
|
//
|
|
Serialize( _frmwrkParams.GetMaxQueueChunks() );
|
|
|
|
ULONG cChunksTruncated =
|
|
_changeLog.LokDeleteWIDsInPersistentIndexes( _cFilteredChunks,
|
|
freshTestLatest,
|
|
freshTestAtMerge,
|
|
docList,
|
|
notifTrans );
|
|
|
|
Win4Assert( cChunksTruncated <= _cFilteredChunks );
|
|
_cFilteredChunks -= cChunksTruncated;
|
|
CheckInvariants("LokDeleteWIDsInPersistentIndexes post");
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LokRefileDocs
|
|
//
|
|
// Synopsis: Refiles the documents with the change list. This method
|
|
// is called to add documents that could not be successfully
|
|
// filtered back to the change list.
|
|
//
|
|
// Arguments: [docList] -- Contains the documents to be added back to
|
|
// the change list.
|
|
//
|
|
// Algorithm: All documents with status != WL_IGNORE will be added back
|
|
// to the change list. If the status is DELETED, then it will
|
|
// be considered to be a deleted document. O/W, it will be
|
|
// treated as a "Update".
|
|
//
|
|
// History: 10-24-94 srikants Created
|
|
// 24-Feb-97 SitaramR Push filtering
|
|
//
|
|
// Notes: When this method is called, there must be no outstanding
|
|
// documents to be refiled. DocQueue can deal with at most one
|
|
// outstanding set of refiled documents.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::LokRefileDocs( const CDocList & docList )
|
|
{
|
|
CheckInvariants("LokRefileDocs pre");
|
|
Win4Assert( 0 == _cRefiledDocs );
|
|
Win4Assert( docList.Count() <= CI_MAX_DOCS_IN_WORDLIST );
|
|
|
|
for ( unsigned i = 0 ; i < docList.Count(); i++ )
|
|
{
|
|
//
|
|
// If the document has already made into a wordlist, DON'T
|
|
// refile it. The current code has potential for infinite looping
|
|
// behavior. We must NEVER refile a "deleted" document. Consider
|
|
// the following sequence for a document
|
|
//
|
|
// ---------------------------------------------
|
|
// | chunk1 | chunk2 | refiled |
|
|
// ---------------------------------------------
|
|
// | D1 | U2 | ? |
|
|
// ---------------------------------------------
|
|
|
|
// The document got deleted and then recreated. If we refile D1
|
|
// as a deletion, the U2 will be superceded by the deletion.
|
|
// Instead, we will just mark the refiled deletion as an update.
|
|
// If the document is not recreated when we go to filter it,
|
|
// the "Wid->Path" will fail and will then be considered (correctly)
|
|
// as a deletion. O/W, we will filter it as an update.
|
|
//
|
|
|
|
_aRefiledDocs[_cRefiledDocs].Set( docList.Wid(i),
|
|
0, // 0 usn for refiled docs
|
|
docList.VolumeId(i),
|
|
CI_UPDATE_OBJ,
|
|
docList.Retries(i),
|
|
docList.SecQRetries(i)
|
|
);
|
|
_cRefiledDocs++;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LokAppendRefiledDocs
|
|
//
|
|
// Synopsis: Appends the documents in the refiled set to the change list.
|
|
//
|
|
// Arguments: [xact] -- Transaction for this operation
|
|
//
|
|
// History: 10-24-94 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::LokAppendRefiledDocs( CChangeTrans & xact )
|
|
{
|
|
CheckInvariants("LokAppendRefiledDocs pre");
|
|
|
|
//
|
|
// If the refiled doclist is not empty, we must append those
|
|
// documents.
|
|
//
|
|
const unsigned cDocsToRefile = _cRefiledDocs;
|
|
_cRefiledDocs = 0;
|
|
|
|
for ( unsigned i = 0; i < cDocsToRefile ; i++ )
|
|
{
|
|
const CDocNotification & docInfo = _aRefiledDocs[i];
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"CDocQueue: Refiling WID: 0x%X Action: %d\n",
|
|
docInfo.Wid(),
|
|
docInfo.Action() ));
|
|
|
|
Win4Assert( docInfo.Usn() == 0 );
|
|
|
|
Append( xact,
|
|
docInfo.Wid(),
|
|
docInfo.Usn(),
|
|
docInfo.VolumeId(),
|
|
docInfo.Action(),
|
|
docInfo.Retries(),
|
|
docInfo.SecQRetries() );
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokDisableUpdates
|
|
//
|
|
// Synopsis: Disables further updates
|
|
//
|
|
// History: 27-Dec-94 SrikantS Created
|
|
// 24-Feb-97 SitaramR Push filtering
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDocQueue::LokDisableUpdates()
|
|
{
|
|
if ( !_changeLog.FPushFiltering() )
|
|
{
|
|
//
|
|
// In pull (i.e. not push) filtering, clean up the in-memory datastructure
|
|
// because they will be re-notified by the client when updates are
|
|
// enabled
|
|
//
|
|
LokCleanup();
|
|
}
|
|
|
|
_changeLog.LokDisableUpdates();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokEnableUpdate
|
|
//
|
|
// Synopsis: Enables updates and change log processing.
|
|
//
|
|
// Arguments: [fFirstTimeUpdatesAreEnabled] -- Is this being called for the
|
|
// first time ?
|
|
//
|
|
// History: 27-Dec-94 SrikantS Created
|
|
// 24-Feb-97 SitaramR Push filtering
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDocQueue::LokEnableUpdates( BOOL fFirstTimeUpdatesAreEnabled )
|
|
{
|
|
_changeLog.LokEnableUpdates( fFirstTimeUpdatesAreEnabled );
|
|
|
|
if ( fFirstTimeUpdatesAreEnabled || !_changeLog.FPushFiltering() )
|
|
{
|
|
//
|
|
// In pull (i.e. not push) filtering, re-initialize the in-memory
|
|
// datastructure from the persistent changlog. Also, if EnableUpdates is
|
|
// being called for the first time, then initialize the in-memory
|
|
// datastructure (i.e. do it in the case of push filtering also).
|
|
//
|
|
LokInit();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocQueue::LokFlushUpdates
|
|
//
|
|
// Synopsis: Serializes the changlog
|
|
//
|
|
// History: 27-Jun-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CDocQueue::LokFlushUpdates()
|
|
{
|
|
Serialize( _frmwrkParams.GetMaxQueueChunks() );
|
|
}
|
|
|