@ -536,7 +536,7 @@ Status DBImpl::Recover(
std : : vector < std : : string > files_in_wal_dir ;
std : : vector < std : : string > files_in_wal_dir ;
if ( s . ok ( ) ) {
if ( s . ok ( ) ) {
// Initial max_total_in_memory_state_ before recovery log s. Log recovery
// Initial max_total_in_memory_state_ before recovery wa ls. Log recovery
// may check this value to decide whether to flush.
// may check this value to decide whether to flush.
max_total_in_memory_state_ = 0 ;
max_total_in_memory_state_ = 0 ;
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
@ -571,7 +571,7 @@ Status DBImpl::Recover(
return s ;
return s ;
}
}
std : : vector < uint64_t > log s;
std : : unordered_map < uint64_t , std : : string > wal_file s;
for ( const auto & file : files_in_wal_dir ) {
for ( const auto & file : files_in_wal_dir ) {
uint64_t number ;
uint64_t number ;
FileType type ;
FileType type ;
@ -582,21 +582,40 @@ Status DBImpl::Recover(
" existing log file: " ,
" existing log file: " ,
file ) ;
file ) ;
} else {
} else {
logs . push_back ( number ) ;
wal_files [ number ] =
LogFileName ( immutable_db_options_ . wal_dir , number ) ;
}
}
}
}
}
}
if ( logs . size ( ) > 0 ) {
if ( immutable_db_options_ . track_and_verify_wals_in_manifest ) {
// Verify WALs in MANIFEST.
s = versions_ - > GetWalSet ( ) . CheckWals ( env_ , wal_files ) ;
} else if ( ! versions_ - > GetWalSet ( ) . GetWals ( ) . empty ( ) ) {
// Tracking is disabled, clear previously tracked WALs from MANIFEST,
// otherwise, in the future, if WAL tracking is enabled again,
// since the WALs deleted when WAL tracking is disabled are not persisted
// into MANIFEST, WAL check may fail.
VersionEdit edit ;
for ( const auto & wal : versions_ - > GetWalSet ( ) . GetWals ( ) ) {
WalNumber number = wal . first ;
edit . DeleteWal ( number ) ;
}
s = versions_ - > LogAndApplyToDefaultColumnFamily ( & edit , & mutex_ ) ;
}
if ( ! s . ok ( ) ) {
return s ;
}
if ( ! wal_files . empty ( ) ) {
if ( error_if_wal_file_exists ) {
if ( error_if_wal_file_exists ) {
return Status : : Corruption (
return Status : : Corruption (
" The db was opened in readonly mode with error_if_wal_file_exists "
" The db was opened in readonly mode with error_if_wal_file_exists "
" flag but a WAL file already exists " ) ;
" flag but a WAL file already exists " ) ;
} else if ( error_if_data_exists_in_wals ) {
} else if ( error_if_data_exists_in_wals ) {
for ( auto & log : logs ) {
for ( auto & wal_file : wal_files ) {
std : : string fname = LogFileName ( immutable_db_options_ . wal_dir , log ) ;
uint64_t bytes ;
uint64_t bytes ;
s = env_ - > GetFileSize ( fname , & bytes ) ;
s = env_ - > GetFileSize ( wal_file . second , & bytes ) ;
if ( s . ok ( ) ) {
if ( s . ok ( ) ) {
if ( bytes > 0 ) {
if ( bytes > 0 ) {
return Status : : Corruption (
return Status : : Corruption (
@ -608,13 +627,19 @@ Status DBImpl::Recover(
}
}
}
}
if ( ! logs . empty ( ) ) {
if ( ! wal_files . empty ( ) ) {
// Recover in the order in which the logs were generated
// Recover in the order in which the wals were generated
std : : sort ( logs . begin ( ) , logs . end ( ) ) ;
std : : vector < uint64_t > wals ;
bool corrupted_log_found = false ;
wals . reserve ( wal_files . size ( ) ) ;
s = RecoverLogFiles ( logs , & next_sequence , read_only ,
for ( const auto & wal_file : wal_files ) {
& corrupted_log_found ) ;
wals . push_back ( wal_file . first ) ;
if ( corrupted_log_found & & recovered_seq ! = nullptr ) {
}
std : : sort ( wals . begin ( ) , wals . end ( ) ) ;
bool corrupted_wal_found = false ;
s = RecoverLogFiles ( wals , & next_sequence , read_only ,
& corrupted_wal_found ) ;
if ( corrupted_wal_found & & recovered_seq ! = nullptr ) {
* recovered_seq = next_sequence ;
* recovered_seq = next_sequence ;
}
}
if ( ! s . ok ( ) ) {
if ( ! s . ok ( ) ) {
@ -767,10 +792,10 @@ Status DBImpl::InitPersistStatsColumnFamily() {
return s ;
return s ;
}
}
// REQUIRES: log _numbers are sorted in ascending order
// REQUIRES: wa l_numbers are sorted in ascending order
Status DBImpl : : RecoverLogFiles ( const std : : vector < uint64_t > & log _numbers ,
Status DBImpl : : RecoverLogFiles ( const std : : vector < uint64_t > & wa l_numbers,
SequenceNumber * next_sequence , bool read_only ,
SequenceNumber * next_sequence , bool read_only ,
bool * corrupted_log _found ) {
bool * corrupted_wa l_found ) {
struct LogReporter : public log : : Reader : : Reporter {
struct LogReporter : public log : : Reader : : Reporter {
Env * env ;
Env * env ;
Logger * info_log ;
Logger * info_log ;
@ -800,10 +825,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
auto stream = event_logger_ . Log ( ) ;
auto stream = event_logger_ . Log ( ) ;
stream < < " job " < < job_id < < " event "
stream < < " job " < < job_id < < " event "
< < " recovery_started " ;
< < " recovery_started " ;
stream < < " log _files " ;
stream < < " wa l_files" ;
stream . StartArray ( ) ;
stream . StartArray ( ) ;
for ( auto log _number : log _numbers ) {
for ( auto wa l_number : wa l_numbers) {
stream < < log _number ;
stream < < wa l_number;
}
}
stream . EndArray ( ) ;
stream . EndArray ( ) ;
}
}
@ -826,25 +851,25 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
bool stop_replay_by_wal_filter = false ;
bool stop_replay_by_wal_filter = false ;
bool stop_replay_for_corruption = false ;
bool stop_replay_for_corruption = false ;
bool flushed = false ;
bool flushed = false ;
uint64_t corrupted_log _number = kMaxSequenceNumber ;
uint64_t corrupted_wa l_number = kMaxSequenceNumber ;
uint64_t min_log _number = MinLogNumberToKeep ( ) ;
uint64_t min_wa l_number = MinLogNumberToKeep ( ) ;
for ( auto log _number : log _numbers ) {
for ( auto wa l_number : wa l_numbers) {
if ( log _number < min_log _number ) {
if ( wa l_number < min_wa l_number ) {
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
" Skipping log #% " PRIu64
" Skipping log #% " PRIu64
" since it is older than min log to keep #% " PRIu64 ,
" since it is older than min log to keep #% " PRIu64 ,
log _number , min_log _number ) ;
wa l_number, min_wa l_number ) ;
continue ;
continue ;
}
}
// The previous incarnation may not have written any MANIFEST
// The previous incarnation may not have written any MANIFEST
// records after allocating this log number. So we manually
// records after allocating this log number. So we manually
// update the file number allocation counter in VersionSet.
// update the file number allocation counter in VersionSet.
versions_ - > MarkFileNumberUsed ( log _number ) ;
versions_ - > MarkFileNumberUsed ( wa l_number) ;
// Open the log file
// Open the log file
std : : string fname = LogFileName ( immutable_db_options_ . wal_dir , log _number ) ;
std : : string fname = LogFileName ( immutable_db_options_ . wal_dir , wa l_number) ;
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
" Recovering log #% " PRIu64 " mode %d " , log _number ,
" Recovering log #% " PRIu64 " mode %d " , wa l_number,
static_cast < int > ( immutable_db_options_ . wal_recovery_mode ) ) ;
static_cast < int > ( immutable_db_options_ . wal_recovery_mode ) ) ;
auto logFileDropped = [ this , & fname ] ( ) {
auto logFileDropped = [ this , & fname ] ( ) {
uint64_t bytes ;
uint64_t bytes ;
@ -897,7 +922,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
// to be skipped instead of propagating bad information (like overly
// to be skipped instead of propagating bad information (like overly
// large sequence numbers).
// large sequence numbers).
log : : Reader reader ( immutable_db_options_ . info_log , std : : move ( file_reader ) ,
log : : Reader reader ( immutable_db_options_ . info_log , std : : move ( file_reader ) ,
& reporter , true /*checksum*/ , log _number ) ;
& reporter , true /*checksum*/ , wa l_number) ;
// Determine if we should tolerate incomplete records at the tail end of the
// Determine if we should tolerate incomplete records at the tail end of the
// Read all the records and add to a memtable
// Read all the records and add to a memtable
@ -945,7 +970,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
WalFilter : : WalProcessingOption wal_processing_option =
WalFilter : : WalProcessingOption wal_processing_option =
immutable_db_options_ . wal_filter - > LogRecordFound (
immutable_db_options_ . wal_filter - > LogRecordFound (
log _number , fname , batch , & new_batch , & batch_changed ) ;
wa l_number, fname , batch , & new_batch , & batch_changed ) ;
switch ( wal_processing_option ) {
switch ( wal_processing_option ) {
case WalFilter : : WalProcessingOption : : kContinueProcessing :
case WalFilter : : WalProcessingOption : : kContinueProcessing :
@ -997,7 +1022,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
" mode %d log filter %s returned "
" mode %d log filter %s returned "
" more records (%d) than original (%d) which is not allowed. "
" more records (%d) than original (%d) which is not allowed. "
" Aborting recovery. " ,
" Aborting recovery. " ,
log _number ,
wa l_number,
static_cast < int > ( immutable_db_options_ . wal_recovery_mode ) ,
static_cast < int > ( immutable_db_options_ . wal_recovery_mode ) ,
immutable_db_options_ . wal_filter - > Name ( ) , new_count ,
immutable_db_options_ . wal_filter - > Name ( ) , new_count ,
original_count ) ;
original_count ) ;
@ -1024,7 +1049,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
bool has_valid_writes = false ;
bool has_valid_writes = false ;
status = WriteBatchInternal : : InsertInto (
status = WriteBatchInternal : : InsertInto (
& batch , column_family_memtables_ . get ( ) , & flush_scheduler_ ,
& batch , column_family_memtables_ . get ( ) , & flush_scheduler_ ,
& trim_history_scheduler_ , true , log _number , this ,
& trim_history_scheduler_ , true , wa l_number, this ,
false /* concurrent_memtable_writes */ , next_sequence ,
false /* concurrent_memtable_writes */ , next_sequence ,
& has_valid_writes , seq_per_batch_ , batch_per_txn_ ) ;
& has_valid_writes , seq_per_batch_ , batch_per_txn_ ) ;
MaybeIgnoreError ( & status ) ;
MaybeIgnoreError ( & status ) ;
@ -1044,7 +1069,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
cfd - > UnrefAndTryDelete ( ) ;
cfd - > UnrefAndTryDelete ( ) ;
// If this asserts, it means that InsertInto failed in
// If this asserts, it means that InsertInto failed in
// filtering updates to already-flushed column families
// filtering updates to already-flushed column families
assert ( cfd - > GetLogNumber ( ) < = log _number ) ;
assert ( cfd - > GetLogNumber ( ) < = wa l_number) ;
auto iter = version_edits . find ( cfd - > GetID ( ) ) ;
auto iter = version_edits . find ( cfd - > GetID ( ) ) ;
assert ( iter ! = version_edits . end ( ) ) ;
assert ( iter ! = version_edits . end ( ) ) ;
VersionEdit * edit = & iter - > second ;
VersionEdit * edit = & iter - > second ;
@ -1081,21 +1106,21 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
" seq #% " PRIu64
" seq #% " PRIu64
" . %s. This likely mean loss of synced WAL, "
" . %s. This likely mean loss of synced WAL, "
" thus recovery fails. " ,
" thus recovery fails. " ,
log _number , * next_sequence ,
wa l_number, * next_sequence ,
status . ToString ( ) . c_str ( ) ) ;
status . ToString ( ) . c_str ( ) ) ;
return status ;
return status ;
}
}
// We should ignore the error but not continue replaying
// We should ignore the error but not continue replaying
status = Status : : OK ( ) ;
status = Status : : OK ( ) ;
stop_replay_for_corruption = true ;
stop_replay_for_corruption = true ;
corrupted_log _number = log _number ;
corrupted_wa l_number = wa l_number;
if ( corrupted_log _found ! = nullptr ) {
if ( corrupted_wa l_found ! = nullptr ) {
* corrupted_log _found = true ;
* corrupted_wa l_found = true ;
}
}
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
ROCKS_LOG_INFO ( immutable_db_options_ . info_log ,
" Point in time recovered to log #% " PRIu64
" Point in time recovered to log #% " PRIu64
" seq #% " PRIu64 ,
" seq #% " PRIu64 ,
log _number , * next_sequence ) ;
wa l_number, * next_sequence ) ;
} else {
} else {
assert ( immutable_db_options_ . wal_recovery_mode = =
assert ( immutable_db_options_ . wal_recovery_mode = =
WALRecoveryMode : : kTolerateCorruptedTailRecords | |
WALRecoveryMode : : kTolerateCorruptedTailRecords | |
@ -1121,7 +1146,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
// corruption. This could during PIT recovery when the WAL is corrupted and
// corruption. This could during PIT recovery when the WAL is corrupted and
// some (but not all) CFs are flushed
// some (but not all) CFs are flushed
// Exclude the PIT case where no log is dropped after the corruption point.
// Exclude the PIT case where no log is dropped after the corruption point.
// This is to cover the case for empty log s after corrupted log, in which we
// This is to cover the case for empty wa ls after corrupted log, in which we
// don't reset stop_replay_for_corruption.
// don't reset stop_replay_for_corruption.
if ( stop_replay_for_corruption = = true & &
if ( stop_replay_for_corruption = = true & &
( immutable_db_options_ . wal_recovery_mode = =
( immutable_db_options_ . wal_recovery_mode = =
@ -1129,7 +1154,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
immutable_db_options_ . wal_recovery_mode = =
immutable_db_options_ . wal_recovery_mode = =
WALRecoveryMode : : kTolerateCorruptedTailRecords ) ) {
WALRecoveryMode : : kTolerateCorruptedTailRecords ) ) {
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
if ( cfd - > GetLogNumber ( ) > corrupted_log _number ) {
if ( cfd - > GetLogNumber ( ) > corrupted_wa l_number ) {
ROCKS_LOG_ERROR ( immutable_db_options_ . info_log ,
ROCKS_LOG_ERROR ( immutable_db_options_ . info_log ,
" Column family inconsistency: SST file contains data "
" Column family inconsistency: SST file contains data "
" beyond the point of corruption. " ) ;
" beyond the point of corruption. " ) ;
@ -1144,16 +1169,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
if ( ! read_only ) {
if ( ! read_only ) {
// no need to refcount since client still doesn't have access
// no need to refcount since client still doesn't have access
// to the DB and can not drop column families while we iterate
// to the DB and can not drop column families while we iterate
auto max_log _number = log _numbers . back ( ) ;
auto max_wa l_number = wa l_numbers. back ( ) ;
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
for ( auto cfd : * versions_ - > GetColumnFamilySet ( ) ) {
auto iter = version_edits . find ( cfd - > GetID ( ) ) ;
auto iter = version_edits . find ( cfd - > GetID ( ) ) ;
assert ( iter ! = version_edits . end ( ) ) ;
assert ( iter ! = version_edits . end ( ) ) ;
VersionEdit * edit = & iter - > second ;
VersionEdit * edit = & iter - > second ;
if ( cfd - > GetLogNumber ( ) > max_log _number ) {
if ( cfd - > GetLogNumber ( ) > max_wa l_number ) {
// Column family cfd has already flushed the data
// Column family cfd has already flushed the data
// from all log s. Memtable has to be empty because
// from all wa ls. Memtable has to be empty because
// we filter the updates based on log _number
// we filter the updates based on wa l_number
// (in WriteBatch::InsertInto)
// (in WriteBatch::InsertInto)
assert ( cfd - > mem ( ) - > GetFirstSequenceNumber ( ) = = 0 ) ;
assert ( cfd - > mem ( ) - > GetFirstSequenceNumber ( ) = = 0 ) ;
assert ( edit - > NumEntries ( ) = = 0 ) ;
assert ( edit - > NumEntries ( ) = = 0 ) ;
@ -1185,13 +1210,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
// Update the log number info in the version edit corresponding to this
// Update the log number info in the version edit corresponding to this
// column family. Note that the version edits will be written to MANIFEST
// column family. Note that the version edits will be written to MANIFEST
// together later.
// together later.
// writing log _number in the manifest means that any log file
// writing wa l_number in the manifest means that any log file
// with number strongly less than (log _number + 1) is already
// with number strongly less than (wa l_number + 1) is already
// recovered and should be ignored on next reincarnation.
// recovered and should be ignored on next reincarnation.
// Since we already recovered max_log _number, we want all log s
// Since we already recovered max_wa l_number, we want all wa ls
// with numbers `<= max_log _number` (includes this one) to be ignored
// with numbers `<= max_wa l_number` (includes this one) to be ignored
if ( flushed | | cfd - > mem ( ) - > GetFirstSequenceNumber ( ) = = 0 ) {
if ( flushed | | cfd - > mem ( ) - > GetFirstSequenceNumber ( ) = = 0 ) {
edit - > SetLogNumber ( max_log _number + 1 ) ;
edit - > SetLogNumber ( max_wa l_number + 1 ) ;
}
}
}
}
if ( status . ok ( ) ) {
if ( status . ok ( ) ) {
@ -1199,7 +1224,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
// not actually used. that is because VersionSet assumes
// not actually used. that is because VersionSet assumes
// VersionSet::next_file_number_ always to be strictly greater than any
// VersionSet::next_file_number_ always to be strictly greater than any
// log number
// log number
versions_ - > MarkFileNumberUsed ( max_log _number + 1 ) ;
versions_ - > MarkFileNumberUsed ( max_wa l_number + 1 ) ;
autovector < ColumnFamilyData * > cfds ;
autovector < ColumnFamilyData * > cfds ;
autovector < const MutableCFOptions * > cf_opts ;
autovector < const MutableCFOptions * > cf_opts ;
@ -1219,7 +1244,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
}
}
if ( status . ok ( ) & & data_seen & & ! flushed ) {
if ( status . ok ( ) & & data_seen & & ! flushed ) {
status = RestoreAliveLogFiles ( log _numbers ) ;
status = RestoreAliveLogFiles ( wa l_numbers) ;
}
}
event_logger_ . Log ( ) < < " job " < < job_id < < " event "
event_logger_ . Log ( ) < < " job " < < job_id < < " event "
@ -1228,8 +1253,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
return status ;
return status ;
}
}
Status DBImpl : : RestoreAliveLogFiles ( const std : : vector < uint64_t > & log _numbers ) {
Status DBImpl : : RestoreAliveLogFiles ( const std : : vector < uint64_t > & wa l_numbers) {
if ( log _numbers . empty ( ) ) {
if ( wa l_numbers. empty ( ) ) {
return Status : : OK ( ) ;
return Status : : OK ( ) ;
}
}
Status s ;
Status s ;
@ -1242,20 +1267,20 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers) {
// FindObsoleteFiles()
// FindObsoleteFiles()
total_log_size_ = 0 ;
total_log_size_ = 0 ;
log_empty_ = false ;
log_empty_ = false ;
for ( auto log _number : log _numbers ) {
for ( auto wa l_number : wa l_numbers) {
LogFileNumberSize log ( log _number ) ;
LogFileNumberSize log ( wa l_number) ;
std : : string fname = LogFileName ( immutable_db_options_ . wal_dir , log _number ) ;
std : : string fname = LogFileName ( immutable_db_options_ . wal_dir , wa l_number) ;
// This gets the appear size of the log s, not including preallocated space.
// This gets the appear size of the wa ls, not including preallocated space.
s = env_ - > GetFileSize ( fname , & log . size ) ;
s = env_ - > GetFileSize ( fname , & log . size ) ;
if ( ! s . ok ( ) ) {
if ( ! s . ok ( ) ) {
break ;
break ;
}
}
total_log_size_ + = log . size ;
total_log_size_ + = log . size ;
alive_log_files_ . push_back ( log ) ;
alive_log_files_ . push_back ( log ) ;
// We preallocate space for log s, but then after a crash and restart, those
// We preallocate space for wa ls, but then after a crash and restart, those
// preallocated space are not needed anymore. It is likely only the last
// preallocated space are not needed anymore. It is likely only the last
// log has such preallocated space, so we only truncate for the last log.
// log has such preallocated space, so we only truncate for the last log.
if ( log _number = = log _numbers . back ( ) ) {
if ( wa l_number = = wa l_numbers. back ( ) ) {
std : : unique_ptr < FSWritableFile > last_log ;
std : : unique_ptr < FSWritableFile > last_log ;
Status truncate_status = fs_ - > ReopenWritableFile (
Status truncate_status = fs_ - > ReopenWritableFile (
fname ,
fname ,
@ -1272,7 +1297,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers) {
// Not a critical error if fail to truncate.
// Not a critical error if fail to truncate.
if ( ! truncate_status . ok ( ) ) {
if ( ! truncate_status . ok ( ) ) {
ROCKS_LOG_WARN ( immutable_db_options_ . info_log ,
ROCKS_LOG_WARN ( immutable_db_options_ . info_log ,
" Failed to truncate log #% " PRIu64 " : %s " , log _number ,
" Failed to truncate log #% " PRIu64 " : %s " , wa l_number,
truncate_status . ToString ( ) . c_str ( ) ) ;
truncate_status . ToString ( ) . c_str ( ) ) ;
}
}
}
}
@ -1610,7 +1635,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
// In WritePrepared there could be gap in sequence numbers. This breaks
// In WritePrepared there could be gap in sequence numbers. This breaks
// the trick we use in kPointInTimeRecovery which assumes the first seq in
// the trick we use in kPointInTimeRecovery which assumes the first seq in
// the log right after the corrupted log is one larger than the last seq
// the log right after the corrupted log is one larger than the last seq
// we read from the log s. To let this trick keep working, we add a dummy
// we read from the wa ls. To let this trick keep working, we add a dummy
// entry with the expected sequence to the first log right after recovery.
// entry with the expected sequence to the first log right after recovery.
// In non-WritePrepared case also the new log after recovery could be
// In non-WritePrepared case also the new log after recovery could be
// empty, and thus missing the consecutive seq hint to distinguish
// empty, and thus missing the consecutive seq hint to distinguish