@ -14,7 +14,7 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
const EnvOptions & soptions ,
const EnvOptions & soptions ,
const SequenceNumber seq ,
const SequenceNumber seq ,
std : : unique_ptr < VectorLogPtr > files ,
std : : unique_ptr < VectorLogPtr > files ,
SequenceNumber const * const lastFlushedSequence ) :
DBImpl const * const dbimpl ) :
dir_ ( dir ) ,
dir_ ( dir ) ,
options_ ( options ) ,
options_ ( options ) ,
soptions_ ( soptions ) ,
soptions_ ( soptions ) ,
@ -24,10 +24,10 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
isValid_ ( false ) ,
isValid_ ( false ) ,
currentFileIndex_ ( 0 ) ,
currentFileIndex_ ( 0 ) ,
currentBatchSeq_ ( 0 ) ,
currentBatchSeq_ ( 0 ) ,
currentBatchCount_ ( 0 ) ,
currentLastSeq_ ( 0 ) ,
lastFlushedSequence_ ( lastFlushedSequence ) {
dbimpl_ ( dbimpl ) {
assert ( startingSequenceNumber_ < = * lastFlushedSequence_ ) ;
assert ( files_ ! = nullptr ) ;
assert ( files_ ! = nullptr ) ;
assert ( dbimpl_ ! = nullptr ) ;
reporter_ . env = options_ - > env ;
reporter_ . env = options_ - > env ;
reporter_ . info_log = options_ - > info_log . get ( ) ;
reporter_ . info_log = options_ - > info_log . get ( ) ;
@ -77,7 +77,7 @@ bool TransactionLogIteratorImpl::RestrictedRead(
Slice * record ,
Slice * record ,
std : : string * scratch ) {
std : : string * scratch ) {
// Don't read if no more complete entries to read from logs
// Don't read if no more complete entries to read from logs
if ( currentBatchSeq_ > = * lastFlushedSequence_ ) {
if ( currentLastSeq_ > = dbimpl_ - > GetLatestSequenceNumber ( ) ) {
return false ;
return false ;
}
}
return currentLogReader_ - > ReadRecord ( record , scratch ) ;
return currentLogReader_ - > ReadRecord ( record , scratch ) ;
@ -90,11 +90,6 @@ void TransactionLogIteratorImpl::SeekToStartSequence(
Slice record ;
Slice record ;
started_ = false ;
started_ = false ;
isValid_ = false ;
isValid_ = false ;
if ( startingSequenceNumber_ > * lastFlushedSequence_ ) {
currentStatus_ = Status : : IOError ( " Looking for a sequence, "
" which is not flushed yet. " ) ;
return ;
}
if ( files_ - > size ( ) < = startFileIndex ) {
if ( files_ - > size ( ) < = startFileIndex ) {
return ;
return ;
}
}
@ -110,8 +105,7 @@ void TransactionLogIteratorImpl::SeekToStartSequence(
continue ;
continue ;
}
}
UpdateCurrentWriteBatch ( record ) ;
UpdateCurrentWriteBatch ( record ) ;
if ( currentBatchSeq_ + currentBatchCount_ - 1 > =
if ( currentLastSeq_ > = startingSequenceNumber_ ) {
startingSequenceNumber_ ) {
if ( strict & & currentBatchSeq_ ! = startingSequenceNumber_ ) {
if ( strict & & currentBatchSeq_ ! = startingSequenceNumber_ ) {
currentStatus_ = Status : : Corruption ( " Gap in sequence number. Could not "
currentStatus_ = Status : : Corruption ( " Gap in sequence number. Could not "
" seek to required sequence number " ) ;
" seek to required sequence number " ) ;
@ -128,38 +122,57 @@ void TransactionLogIteratorImpl::SeekToStartSequence(
isValid_ = false ;
isValid_ = false ;
}
}
}
}
// Could not find start sequence in first file. Normally this must be the
// Could not find start sequence in first file. Normally this must be the
// only file. Otherwise log the error and let the iterator return next entry
// only file. Otherwise log the error and let the iterator return next entry
if ( files_ - > size ( ) ! = 1 ) {
// If strict is set, we want to seek exactly till the start sequence and it
// should have been present in the file we scanned above
if ( strict ) {
currentStatus_ = Status : : Corruption ( " Gap in sequence number. Could not "
" seek to required sequence number " ) ;
reporter_ . Info ( currentStatus_ . ToString ( ) . c_str ( ) ) ;
} else if ( files_ - > size ( ) ! = 1 ) {
currentStatus_ = Status : : Corruption ( " Start sequence was not found, "
currentStatus_ = Status : : Corruption ( " Start sequence was not found, "
" skipping to the next available " ) ;
" skipping to the next available " ) ;
reporter_ . Corruption ( 0 , currentStatus_ ) ;
reporter_ . Info ( currentStatus_ . ToString ( ) . c_str ( ) ) ;
started_ = true ; // Let Next find next available entry
// Let NextImpl find the next available entry. started_ remains false
Next ( ) ;
// because we don't want to check for gaps while moving to start sequence
NextImpl ( true ) ;
}
}
}
}
void TransactionLogIteratorImpl : : Next ( ) {
void TransactionLogIteratorImpl : : Next ( ) {
return NextImpl ( false ) ;
}
void TransactionLogIteratorImpl : : NextImpl ( bool internal ) {
std : : string scratch ;
std : : string scratch ;
Slice record ;
Slice record ;
isValid_ = false ;
isValid_ = false ;
if ( ! started_ ) { // Runs every time until we can seek to the start sequence
if ( ! internal & & ! started_ ) {
// Runs every time until we can seek to the start sequence
return SeekToStartSequence ( ) ;
return SeekToStartSequence ( ) ;
}
}
while ( true ) {
while ( true ) {
assert ( currentLogReader_ ) ;
assert ( currentLogReader_ ) ;
if ( currentBatchSeq_ < * lastFlushedSequence_ ) {
if ( currentLogReader_ - > IsEOF ( ) ) {
if ( currentLogReader_ - > IsEOF ( ) ) {
currentLogReader_ - > UnmarkEOF ( ) ;
currentLogReader_ - > UnmarkEOF ( ) ;
}
}
while ( RestrictedRead ( & record , & scratch ) ) {
while ( currentLogReader_ - > ReadRecord ( & record , & scratch ) ) {
if ( record . size ( ) < 12 ) {
if ( record . size ( ) < 12 ) {
reporter_ . Corruption (
reporter_ . Corruption (
record . size ( ) , Status : : Corruption ( " very small log record " ) ) ;
record . size ( ) , Status : : Corruption ( " very small log record " ) ) ;
continue ;
continue ;
} else {
} else {
// started_ should be true if called by application
return UpdateCurrentWriteBatch ( record ) ;
assert ( internal | | started_ ) ;
// started_ should be false if called internally
assert ( ! internal | | ! started_ ) ;
UpdateCurrentWriteBatch ( record ) ;
if ( internal & & ! started_ ) {
started_ = true ;
}
}
return ;
}
}
}
}
@ -174,27 +187,27 @@ void TransactionLogIteratorImpl::Next() {
}
}
} else {
} else {
isValid_ = false ;
isValid_ = false ;
if ( currentBatchSeq_ = = * lastFlushedSequence_ ) {
if ( currentLastSeq_ = = dbimpl_ - > GetLatestSequenceNumber ( ) ) {
currentStatus_ = Status : : OK ( ) ;
currentStatus_ = Status : : OK ( ) ;
} else {
} else {
currentStatus_ = Status : : IOError ( " NO MORE DATA LEFT" ) ;
currentStatus_ = Status : : IOError ( " NO MORE DATA LEFT " ) ;
}
}
return ;
return ;
}
}
}
}
}
}
bool TransactionLogIteratorImpl : : IsBatchContinuous (
bool TransactionLogIteratorImpl : : IsBatchExpected (
const WriteBatch * batch ,
const WriteBatch * batch ,
const SequenceNumber expectedSeq ) {
const SequenceNumber expectedSeq ) {
assert ( batch ) ;
assert ( batch ) ;
SequenceNumber batchSeq = WriteBatchInternal : : Sequence ( batch ) ;
SequenceNumber batchSeq = WriteBatchInternal : : Sequence ( batch ) ;
if ( started_ & & batchSeq ! = expectedSeq ) {
if ( batchSeq ! = expectedSeq ) {
char buf [ 200 ] ;
char buf [ 200 ] ;
snprintf ( buf , sizeof ( buf ) ,
snprintf ( buf , sizeof ( buf ) ,
" Discontinuity in log records. Got seq=%lu, Expected seq=%lu, "
" Discontinuity in log records. Got seq=%lu, Expected seq=%lu, "
" Last flushed seq=%lu. Log iterator will seek the correct batch. " ,
" Last flushed seq=%lu.Log iterator will re seek the correct batch. " ,
batchSeq , expectedSeq , * lastFlushedSequence_ ) ;
batchSeq , expectedSeq , dbimpl_ - > GetLatestSequenceNumber ( ) ) ;
reporter_ . Info ( buf ) ;
reporter_ . Info ( buf ) ;
return false ;
return false ;
}
}
@ -205,8 +218,9 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
WriteBatch * batch = new WriteBatch ( ) ;
WriteBatch * batch = new WriteBatch ( ) ;
WriteBatchInternal : : SetContents ( batch , record ) ;
WriteBatchInternal : : SetContents ( batch , record ) ;
SequenceNumber expectedSeq = currentBatchSeq_ + currentBatchCount_ ;
SequenceNumber expectedSeq = currentLastSeq_ + 1 ;
if ( ! IsBatchContinuous ( batch , expectedSeq ) ) {
// If the iterator has started, then confirm that we get continuous batches
if ( started_ & & ! IsBatchExpected ( batch , expectedSeq ) ) {
// Seek to the batch having expected sequence number
// Seek to the batch having expected sequence number
if ( expectedSeq < files_ - > at ( currentFileIndex_ ) - > StartSequence ( ) ) {
if ( expectedSeq < files_ - > at ( currentFileIndex_ ) - > StartSequence ( ) ) {
// Expected batch must lie in the previous log file
// Expected batch must lie in the previous log file
@ -214,13 +228,15 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
currentFileIndex_ = ( currentFileIndex_ > = 0 ) ? currentFileIndex_ : 0 ;
currentFileIndex_ = ( currentFileIndex_ > = 0 ) ? currentFileIndex_ : 0 ;
}
}
startingSequenceNumber_ = expectedSeq ;
startingSequenceNumber_ = expectedSeq ;
// currentStatus_ will be set to Ok if reseek succeeds
currentStatus_ = Status : : NotFound ( " Gap in sequence numbers " ) ;
return SeekToStartSequence ( currentFileIndex_ , true ) ;
return SeekToStartSequence ( currentFileIndex_ , true ) ;
}
}
currentBatchSeq_ = WriteBatchInternal : : Sequence ( batch ) ;
currentBatchSeq_ = WriteBatchInternal : : Sequence ( batch ) ;
currentBatchCount_ = WriteBatchInternal : : Count ( batch ) ;
currentLastSeq_ = currentBatchSeq_ + WriteBatchInternal : : Count ( batch ) - 1 ;
// currentBatchSeq_ can only change here
// currentBatchSeq_ can only change here
assert ( currentBatchSeq_ < = * lastFlushedSequence_ ) ;
assert ( currentLastSeq_ < = dbimpl_ - > GetLatestSequenceNumber ( ) ) ;
currentBatch_ . reset ( batch ) ;
currentBatch_ . reset ( batch ) ;
isValid_ = true ;
isValid_ = true ;