@ -13,11 +13,13 @@
# include <atomic>
# include <cinttypes>
# include <cstdlib>
# include <exception>
# include <functional>
# include <future>
# include <limits>
# include <map>
# include <mutex>
# include <optional>
# include <sstream>
# include <string>
# include <thread>
@ -155,16 +157,10 @@ class BackupEngineImpl {
void GetCorruptedBackups ( std : : vector < BackupID > * corrupt_backup_ids ) const ;
IOStatus RestoreDBFromBackup ( const RestoreOptions & options ,
BackupID backup_id , const std : : string & db_dir ,
const std : : string & wal_dir ) const ;
IOStatus RestoreDBFromLatestBackup ( const RestoreOptions & options ,
const std : : string & db_dir ,
const std : : string & wal_dir ) const {
// Note: don't read latest_valid_backup_id_ outside of lock
return RestoreDBFromBackup ( options , kLatestBackupIDMarker , db_dir , wal_dir ) ;
}
IOStatus RestoreDBFromBackup (
const RestoreOptions & options , BackupID backup_id ,
const std : : string & db_dir , const std : : string & wal_dir ,
const std : : list < const BackupEngineImpl * > & locked_restore_from_dirs ) const ;
IOStatus VerifyBackup ( BackupID backup_id ,
bool verify_with_checksum = false ) const ;
@ -220,6 +216,7 @@ class BackupEngineImpl {
FileInfo & operator = ( const FileInfo & ) = delete ;
int refs ;
// Relative path from backup dir
const std : : string filename ;
const uint64_t size ;
// crc32c checksum as hex. empty == unknown / unavailable
@ -233,7 +230,7 @@ class BackupEngineImpl {
const std : : string db_session_id ;
Temperature temp ;
std : : string GetDbFileName ( ) {
std : : string GetDbFileName ( ) const {
std : : string rv ;
// extract the filename part
size_t slash = filename . find_last_of ( ' / ' ) ;
@ -415,6 +412,10 @@ class BackupEngineImpl {
IOStatus AddFile ( std : : shared_ptr < FileInfo > file_info ) ;
void AddExcludedFile ( const std : : string & relative_file ) {
excluded_files_ . emplace_back ( relative_file ) ;
}
IOStatus Delete ( bool delete_meta = true ) ;
bool Empty ( ) const { return files_ . empty ( ) ; }
@ -431,6 +432,10 @@ class BackupEngineImpl {
return files_ ;
}
const std : : vector < BackupExcludedFileInfo > & GetExcludedFiles ( ) const {
return excluded_files_ ;
}
// @param abs_path_to_size Pre-fetched file sizes (bytes).
IOStatus LoadFromFile (
const std : : string & backup_dir ,
@ -488,6 +493,7 @@ class BackupEngineImpl {
std : : string const meta_tmp_filename_ ;
// files with relative paths (without "/" prefix!!)
std : : vector < std : : shared_ptr < FileInfo > > files_ ;
std : : vector < BackupExcludedFileInfo > excluded_files_ ;
std : : unordered_map < std : : string , std : : shared_ptr < FileInfo > > * file_infos_ ;
Env * env_ ;
mutable std : : shared_ptr < Env > env_for_open_ ;
@ -681,17 +687,19 @@ class BackupEngineImpl {
return * this ;
}
CopyOrCreateWorkItem (
std : : string _src_path , std : : string _dst_path ,
const Temperature _src_temperature , const Temperature _dst_temperature ,
CopyOrCreateWorkItem ( std : : string _src_path , std : : string _dst_path ,
const Temperature _src_temperature ,
const Temperature _dst_temperature ,
std : : string _contents , Env * _src_env , Env * _dst_env ,
EnvOptions _src_env_options , bool _sync , RateLimiter * _rate_limiter ,
uint64_t _size_limit , Statistics * _stats ,
std : : function < void ( ) > _progress_callback = [ ] ( ) { } ,
EnvOptions _src_env_options , bool _sync ,
RateLimiter * _rate_limiter , uint64_t _size_limit ,
Statistics * _stats ,
std : : function < void ( ) > _progress_callback = { } ,
const std : : string & _src_checksum_func_name =
kUnknownFileChecksumFuncName ,
const std : : string & _src_checksum_hex = " " ,
const std : : string & _db_id = " " , const std : : string & _db_session_id = " " )
const std : : string & _db_id = " " ,
const std : : string & _db_session_id = " " )
: src_path ( std : : move ( _src_path ) ) ,
dst_path ( std : : move ( _dst_path ) ) ,
src_temperature ( _src_temperature ) ,
@ -758,6 +766,9 @@ class BackupEngineImpl {
dst_relative ( std : : move ( _dst_relative ) ) { }
} ;
using BackupWorkItemPair =
std : : pair < CopyOrCreateWorkItem , BackupAfterCopyOrCreateWorkItem > ;
struct RestoreAfterCopyOrCreateWorkItem {
std : : future < CopyOrCreateResult > result ;
std : : string from_file ;
@ -807,13 +818,14 @@ class BackupEngineImpl {
// @param contents If non-empty, the file will be created with these contents.
IOStatus AddBackupFileWorkItem (
std : : unordered_set < std : : string > & live_dst_paths ,
std : : vector < BackupAfterCopyOrCreateWorkItem > & backup_items_to_finish ,
BackupID backup_id , bool shared , const std : : string & src_dir ,
std : : deque < BackupAfterCopyOrCreateWorkItem > & backup_items_to_finish ,
std : : deque < BackupWorkItemPair > * excludable_items , BackupID backup_id ,
bool shared , const std : : string & src_dir ,
const std : : string & fname , // starts with "/"
const EnvOptions & src_env_options , RateLimiter * rate_limiter ,
FileType file_type , uint64_t size_bytes , Statistics * stats ,
uint64_t size_limit = 0 , bool shared_checksum = false ,
std : : function < void ( ) > progress_callback = [ ] ( ) { } ,
std : : function < void ( ) > progress_callback = { } ,
const std : : string & contents = std : : string ( ) ,
const std : : string & src_checksum_func_name = kUnknownFileChecksumFuncName ,
const std : : string & src_checksum_str = kUnknownFileChecksum ,
@ -923,8 +935,37 @@ class BackupEngineImplThreadSafe : public BackupEngine,
IOStatus RestoreDBFromBackup ( const RestoreOptions & options ,
BackupID backup_id , const std : : string & db_dir ,
const std : : string & wal_dir ) const override {
ReadLock lock ( & mutex_ ) ;
return impl_ . RestoreDBFromBackup ( options , backup_id , db_dir , wal_dir ) ;
// TSAN reports a lock inversion (potential deadlock) if we acquire read
// locks in different orders. Assuming the implementation of RWMutex
// allows simultaneous read locks, there should be no deadlock, because
// there is no write lock involved here. Nevertheless, to appease TSAN and
// in case of degraded RWMutex implementation, we lock the BackupEngines
// including this one and those in options.alternate_dirs in a consistent
// order.
// However, locked_restore_from_dirs is kept in "search" order.
std : : list < const BackupEngineImpl * > locked_restore_from_dirs ;
std : : vector < port : : RWMutex * > mutexes ;
// Add `this`
locked_restore_from_dirs . emplace_back ( & impl_ ) ;
mutexes . push_back ( & mutex_ ) ;
// Add alternates
for ( BackupEngineReadOnlyBase * be : options . alternate_dirs ) {
BackupEngineImplThreadSafe * bets =
static_cast_with_check < BackupEngineImplThreadSafe > (
be - > AsBackupEngine ( ) ) ;
locked_restore_from_dirs . emplace_back ( & bets - > impl_ ) ;
mutexes . push_back ( & bets - > mutex_ ) ;
}
// Acquire read locks in pointer order
std : : sort ( mutexes . begin ( ) , mutexes . end ( ) ) ;
std : : vector < ReadLock > locks ( mutexes . begin ( ) , mutexes . end ( ) ) ;
// Impl
return impl_ . RestoreDBFromBackup ( options , backup_id , db_dir , wal_dir ,
locked_restore_from_dirs ) ;
}
using BackupEngine : : RestoreDBFromLatestBackup ;
@ -941,6 +982,8 @@ class BackupEngineImplThreadSafe : public BackupEngine,
return impl_ . VerifyBackup ( backup_id , verify_with_checksum ) ;
}
BackupEngine * AsBackupEngine ( ) override { return this ; }
// Not public API but needed
IOStatus Initialize ( ) {
// No locking needed
@ -966,6 +1009,7 @@ class BackupEngineImplThreadSafe : public BackupEngine,
mutable port : : RWMutex mutex_ ;
BackupEngineImpl impl_ ;
} ;
} // namespace
IOStatus BackupEngine : : Open ( const BackupEngineOptions & options , Env * env ,
@ -1300,6 +1344,12 @@ IOStatus BackupEngineImpl::CreateNewBackupWithMetadata(
return IOStatus : : InvalidArgument ( " App metadata too large " ) ;
}
bool maybe_exclude_items = bool { options . exclude_files_callback } ;
if ( maybe_exclude_items & & options_ . schema_version < 2 ) {
return IOStatus : : InvalidArgument (
" exclude_files_callback requires schema_version >= 2 " ) ;
}
if ( options . decrease_background_thread_cpu_priority ) {
if ( options . background_thread_cpu_priority < threads_cpu_priority_ ) {
threads_cpu_priority_ . store ( options . background_thread_cpu_priority ) ;
@ -1364,7 +1414,8 @@ IOStatus BackupEngineImpl::CreateNewBackupWithMetadata(
// live file.
std : : unordered_set < std : : string > live_dst_paths ;
std : : vector < BackupAfterCopyOrCreateWorkItem > backup_items_to_finish ;
std : : deque < BackupWorkItemPair > excludable_items ;
std : : deque < BackupAfterCopyOrCreateWorkItem > backup_items_to_finish ;
// Add a CopyOrCreateWorkItem to the channel for each live file
Status disabled = db - > DisableFileDeletions ( ) ;
DBOptions db_options = db - > GetDBOptions ( ) ;
@ -1436,7 +1487,8 @@ IOStatus BackupEngineImpl::CreateNewBackupWithMetadata(
break ;
}
io_st = AddBackupFileWorkItem (
live_dst_paths , backup_items_to_finish , new_backup_id ,
live_dst_paths , backup_items_to_finish ,
maybe_exclude_items ? & excludable_items : nullptr , new_backup_id ,
options_ . share_table_files & &
( type = = kTableFile | | type = = kBlobFile ) ,
src_dirname , fname , src_env_options , rate_limiter , type ,
@ -1451,7 +1503,8 @@ IOStatus BackupEngineImpl::CreateNewBackupWithMetadata(
FileType type ) {
Log ( options_ . info_log , " add file for backup %s " , fname . c_str ( ) ) ;
return AddBackupFileWorkItem (
live_dst_paths , backup_items_to_finish , new_backup_id ,
live_dst_paths , backup_items_to_finish ,
maybe_exclude_items ? & excludable_items : nullptr , new_backup_id ,
false /* shared */ , " " /* src_dir */ , fname ,
EnvOptions ( ) /* src_env_options */ , rate_limiter , type ,
contents . size ( ) , db_options . statistics . get ( ) , 0 /* size_limit */ ,
@ -1464,7 +1517,46 @@ IOStatus BackupEngineImpl::CreateNewBackupWithMetadata(
new_backup - > SetSequenceNumber ( sequence_number ) ;
}
}
ROCKS_LOG_INFO ( options_ . info_log , " add files for backup done, wait finish. " ) ;
ROCKS_LOG_INFO ( options_ . info_log , " add files for backup done. " ) ;
if ( io_s . ok ( ) & & maybe_exclude_items ) {
assert ( options . exclude_files_callback ) ;
size_t count = excludable_items . size ( ) ;
std : : vector < MaybeExcludeBackupFile > maybe_exclude_files ;
maybe_exclude_files . reserve ( count ) ;
for ( auto & e : excludable_items ) {
maybe_exclude_files . emplace_back (
BackupExcludedFileInfo ( e . second . dst_relative ) ) ;
}
if ( count > 0 ) {
try {
options . exclude_files_callback (
& maybe_exclude_files . front ( ) ,
/*end pointer*/ & maybe_exclude_files . back ( ) + 1 ) ;
} catch ( const std : : exception & exn ) {
io_s = IOStatus : : Aborted ( " Exception in exclude_files_callback: " +
std : : string ( exn . what ( ) ) ) ;
} catch ( . . . ) {
io_s = IOStatus : : Aborted ( " Unknown exception in exclude_files_callback " ) ;
}
}
if ( io_s . ok ( ) ) {
for ( size_t i = 0 ; i < count ; + + i ) {
auto & e = excludable_items [ i ] ;
if ( maybe_exclude_files [ i ] . exclude_decision ) {
new_backup . get ( ) - > AddExcludedFile ( e . second . dst_relative ) ;
} else {
files_to_copy_or_create_ . write ( std : : move ( e . first ) ) ;
backup_items_to_finish . push_back ( std : : move ( e . second ) ) ;
}
}
}
excludable_items . clear ( ) ;
} else {
assert ( ! options . exclude_files_callback ) ;
assert ( excludable_items . empty ( ) ) ;
}
ROCKS_LOG_INFO ( options_ . info_log ,
" dispatch files for backup done, wait for finish. " ) ;
IOStatus item_io_status ;
for ( auto & item : backup_items_to_finish ) {
item . result . wait ( ) ;
@ -1699,7 +1791,7 @@ void BackupEngineImpl::SetBackupInfoFromBackupMeta(
auto & file_details = backup_info - > file_details ;
file_details . reserve ( meta . GetFiles ( ) . size ( ) ) ;
for ( auto & file_ptr : meta . GetFiles ( ) ) {
BackupFileInfo & finfo = * file_details . emplace ( file_details . end ( ) ) ;
BackupFileInfo & finfo = file_details . emplace_back ( ) ;
finfo . relative_filename = file_ptr - > filename ;
finfo . size = file_ptr - > size ;
finfo . directory = dir ;
@ -1711,7 +1803,10 @@ void BackupEngineImpl::SetBackupInfoFromBackupMeta(
finfo . file_type = type ;
}
// TODO: temperature, file_checksum, file_checksum_func_name
// finfo.temperature = file_ptr->temp;
}
backup_info - > excluded_files = meta . GetExcludedFiles ( ) ;
backup_info - > name_for_open = GetAbsolutePath ( GetPrivateFileRel ( id ) ) ;
backup_info - > name_for_open . pop_back ( ) ; // remove trailing '/'
backup_info - > env_for_open = meta . GetEnvForOpen ( ) ;
@ -1769,7 +1864,8 @@ void BackupEngineImpl::GetCorruptedBackups(
IOStatus BackupEngineImpl : : RestoreDBFromBackup (
const RestoreOptions & options , BackupID backup_id ,
const std : : string & db_dir , const std : : string & wal_dir ) const {
const std : : string & db_dir , const std : : string & wal_dir ,
const std : : list < const BackupEngineImpl * > & locked_restore_from_dirs ) const {
assert ( initialized_ ) ;
if ( backup_id = = kLatestBackupIDMarker ) {
// Note: Read latest_valid_backup_id_ inside of lock
@ -1829,6 +1925,37 @@ IOStatus BackupEngineImpl::RestoreDBFromBackup(
DeleteChildren ( db_dir ) ;
}
// Files to restore, and from where (taking into account excluded files)
std : : vector < std : : pair < const BackupEngineImpl * , const FileInfo * > >
restore_file_infos ;
restore_file_infos . reserve ( backup - > GetFiles ( ) . size ( ) +
backup - > GetExcludedFiles ( ) . size ( ) ) ;
for ( const auto & ef : backup - > GetExcludedFiles ( ) ) {
const std : : string & file = ef . relative_file ;
bool found = false ;
for ( auto be : locked_restore_from_dirs ) {
auto it = be - > backuped_file_infos_ . find ( file ) ;
if ( it ! = backuped_file_infos_ . end ( ) ) {
restore_file_infos . emplace_back ( be , & * it - > second ) ;
found = true ;
break ;
}
}
if ( ! found ) {
return IOStatus : : InvalidArgument (
" Excluded file " + file + " not found in other backups nor in " +
std : : to_string ( locked_restore_from_dirs . size ( ) - 1 ) +
" alternate backup directories " ) ;
}
}
// Non-excluded files
for ( const auto & file_info_shared : backup - > GetFiles ( ) ) {
restore_file_infos . emplace_back ( this , & * file_info_shared ) ;
}
IOStatus io_s ;
std : : vector < RestoreAfterCopyOrCreateWorkItem > restore_items_to_finish ;
std : : string temporary_current_file ;
@ -1836,8 +1963,13 @@ IOStatus BackupEngineImpl::RestoreDBFromBackup(
std : : unique_ptr < FSDirectory > db_dir_for_fsync ;
std : : unique_ptr < FSDirectory > wal_dir_for_fsync ;
for ( const auto & file_info : backup - > GetFiles ( ) ) {
for ( const auto & engine_and_file_info : restore_file_infos ) {
const FileInfo * file_info = engine_and_file_info . second ;
const std : : string & file = file_info - > filename ;
std : : string absolute_file =
engine_and_file_info . first - > GetAbsolutePath ( file ) ;
Env * src_env = engine_and_file_info . first - > backup_env_ ;
// 1. get DB filename
std : : string dst = file_info - > GetDbFileName ( ) ;
@ -1881,8 +2013,8 @@ IOStatus BackupEngineImpl::RestoreDBFromBackup(
ROCKS_LOG_INFO ( options_ . info_log , " Restoring %s to %s \n " , file . c_str ( ) ,
dst . c_str ( ) ) ;
CopyOrCreateWorkItem copy_or_create_work_item (
GetAbsolutePath ( file ) , dst , Temperature : : kUnknown /* src_temp */ ,
file_info - > temp , " " /* contents */ , backup_env_ , db_env_ ,
absolute_file , dst , Temperature : : kUnknown /* src_temp */ ,
file_info - > temp , " " /* contents */ , src_env , db_env_ ,
EnvOptions ( ) /* src_env_options */ , options_ . sync ,
options_ . restore_rate_limiter . get ( ) , file_info - > size ,
nullptr /* stats */ ) ;
@ -2122,8 +2254,19 @@ IOStatus BackupEngineImpl::CopyOrCreateFile(
while ( * bytes_toward_next_callback > =
options_ . callback_trigger_interval_size ) {
* bytes_toward_next_callback - = options_ . callback_trigger_interval_size ;
if ( progress_callback ) {
std : : lock_guard < std : : mutex > lock ( byte_report_mutex_ ) ;
try {
progress_callback ( ) ;
} catch ( const std : : exception & exn ) {
io_s = IOStatus : : Aborted ( " Exception in progress_callback: " +
std : : string ( exn . what ( ) ) ) ;
break ;
} catch ( . . . ) {
io_s = IOStatus : : Aborted ( " Unknown exception in progress_callback " ) ;
break ;
}
}
}
} while ( io_s . ok ( ) & & contents . empty ( ) & & data . size ( ) > 0 & & size_limit > 0 ) ;
@ -2144,11 +2287,12 @@ IOStatus BackupEngineImpl::CopyOrCreateFile(
// fname will always start with "/"
IOStatus BackupEngineImpl : : AddBackupFileWorkItem (
std : : unordered_set < std : : string > & live_dst_paths ,
std : : vector < BackupAfterCopyOrCreateWorkItem > & backup_items_to_finish ,
BackupID backup_id , bool shared , const std : : string & src_dir ,
const std : : string & fname , const EnvOptions & src_env_options ,
RateLimiter * rate_limiter , FileType file_type , uint64_t size_bytes ,
Statistics * stats , uint64_t size_limit , bool shared_checksum ,
std : : deque < BackupAfterCopyOrCreateWorkItem > & backup_items_to_finish ,
std : : deque < BackupWorkItemPair > * excludable_items , BackupID backup_id ,
bool shared , const std : : string & src_dir , const std : : string & fname ,
const EnvOptions & src_env_options , RateLimiter * rate_limiter ,
FileType file_type , uint64_t size_bytes , Statistics * stats ,
uint64_t size_limit , bool shared_checksum ,
std : : function < void ( ) > progress_callback , const std : : string & contents ,
const std : : string & src_checksum_func_name ,
const std : : string & src_checksum_str , const Temperature src_temperature ) {
@ -2342,8 +2486,6 @@ IOStatus BackupEngineImpl::AddBackupFileWorkItem(
// Step 3: Add work item
if ( ! contents . empty ( ) | | need_to_copy ) {
ROCKS_LOG_INFO ( options_ . info_log , " Copying %s to %s " , fname . c_str ( ) ,
copy_dest_path - > c_str ( ) ) ;
CopyOrCreateWorkItem copy_or_create_work_item (
src_dir . empty ( ) ? " " : src_path , * copy_dest_path , src_temperature ,
Temperature : : kUnknown /*dst_temp*/ , contents , db_env_ , backup_env_ ,
@ -2353,8 +2495,21 @@ IOStatus BackupEngineImpl::AddBackupFileWorkItem(
BackupAfterCopyOrCreateWorkItem after_copy_or_create_work_item (
copy_or_create_work_item . result . get_future ( ) , shared , need_to_copy ,
backup_env_ , temp_dest_path , final_dest_path , dst_relative ) ;
if ( excludable_items ! = nullptr & & shared & & shared_checksum & &
need_to_copy ) {
ROCKS_LOG_INFO ( options_ . info_log , " Copying (if not excluded) %s to %s " ,
fname . c_str ( ) , copy_dest_path - > c_str ( ) ) ;
excludable_items - > emplace_back ( std : : move ( copy_or_create_work_item ) ,
std : : move ( after_copy_or_create_work_item ) ) ;
} else {
// For files known not excluded, can start copying even before finishing
// the checkpoint
ROCKS_LOG_INFO ( options_ . info_log , " Copying %s to %s " , fname . c_str ( ) ,
copy_dest_path - > c_str ( ) ) ;
files_to_copy_or_create_ . write ( std : : move ( copy_or_create_work_item ) ) ;
backup_items_to_finish . push_back ( std : : move ( after_copy_or_create_work_item ) ) ;
backup_items_to_finish . push_back (
std : : move ( after_copy_or_create_work_item ) ) ;
}
} else {
std : : promise < CopyOrCreateResult > promise_result ;
BackupAfterCopyOrCreateWorkItem after_copy_or_create_work_item (
@ -2745,6 +2900,7 @@ const std::string kAppMetaDataFieldName{"metadata"};
const std : : string kFileCrc32cFieldName { " crc32 " } ;
const std : : string kFileSizeFieldName { " size " } ;
const std : : string kTemperatureFieldName { " temp " } ;
const std : : string kExcludedFieldName { " ni::excluded " } ;
// Marks a (future) field that should cause failure if not recognized.
// Other fields are assumed to be ignorable. For example, in the future
@ -2765,8 +2921,7 @@ const std::string kNonIgnorableFieldPrefix{"ni::"};
// ...
//----------------------------------------------------------
//
// For schema version 2.x (not in public APIs, but
// forward-compatibility started):
// For schema version 2.x:
//----------------------------------------------------------
// schema_version <ver>
// <timestamp>
@ -2909,20 +3064,6 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
const std : : string & filename = components [ 0 ] ;
uint64_t actual_size ;
const std : : shared_ptr < FileInfo > file_info = GetFile ( filename ) ;
if ( file_info ) {
actual_size = file_info - > size ;
} else {
std : : string abs_path = backup_dir + " / " + filename ;
auto e = abs_path_to_size . find ( abs_path ) ;
if ( e = = abs_path_to_size . end ( ) ) {
return IOStatus : : Corruption (
" Pathname in meta file not found on disk: " + abs_path ) ;
}
actual_size = e - > second ;
}
if ( schema_major_version > = 2 ) {
if ( components . size ( ) % 2 ! = 1 ) {
return IOStatus : : Corruption (
@ -2944,8 +3085,10 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
}
}
std : : optional < uint64_t > expected_size { } ;
std : : string checksum_hex ;
Temperature temp = Temperature : : kUnknown ;
bool excluded = false ;
for ( unsigned i = 1 ; i < components . size ( ) ; i + = 2 ) {
const std : : string & field_name = components [ i ] ;
const std : : string & field_data = components [ i + 1 ] ;
@ -2959,14 +3102,7 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
}
checksum_hex = ChecksumInt32ToHex ( checksum_value ) ;
} else if ( field_name = = kFileSizeFieldName ) {
uint64_t ex_size =
std : : strtoull ( field_data . c_str ( ) , nullptr , /*base*/ 10 ) ;
if ( ex_size ! = actual_size ) {
return IOStatus : : Corruption (
" For file " + filename + " expected size " +
std : : to_string ( ex_size ) + " but found size " +
std : : to_string ( actual_size ) ) ;
}
expected_size = std : : strtoull ( field_data . c_str ( ) , nullptr , /*base*/ 10 ) ;
} else if ( field_name = = kTemperatureFieldName ) {
auto iter = temperature_string_map . find ( field_data ) ;
if ( iter ! = temperature_string_map . end ( ) ) {
@ -2977,6 +3113,15 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
// be safe.
temp = Temperature : : kUnknown ;
}
} else if ( field_name = = kExcludedFieldName ) {
if ( field_data = = " true " ) {
excluded = true ;
} else if ( field_data = = " false " ) {
excluded = false ;
} else {
return IOStatus : : NotSupported ( " Unrecognized value \" " + field_data +
" \" for field " + field_name ) ;
}
} else if ( StartsWith ( field_name , kNonIgnorableFieldPrefix ) ) {
return IOStatus : : NotSupported ( " Unrecognized non-ignorable file field " +
field_name + " (from future version?) " ) ;
@ -2989,9 +3134,30 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
}
}
files . emplace_back ( new FileInfo ( filename , actual_size , checksum_hex ,
if ( excluded ) {
excluded_files_ . emplace_back ( filename ) ;
} else {
// Verify file exists, with expected size
std : : string abs_path = backup_dir + " / " + filename ;
auto e = abs_path_to_size . find ( abs_path ) ;
if ( e = = abs_path_to_size . end ( ) ) {
return IOStatus : : Corruption (
" Pathname in meta file not found on disk: " + abs_path ) ;
}
uint64_t actual_size = e - > second ;
if ( expected_size . has_value ( ) & & * expected_size ! = actual_size ) {
return IOStatus : : Corruption ( " For file " + filename + " expected size " +
std : : to_string ( * expected_size ) +
" but found size " +
std : : to_string ( actual_size ) ) ;
}
// NOTE: FileInfo will be coalesced for sharing later (AddFile below)
files . emplace_back (
std : : make_shared < FileInfo > ( filename , actual_size , checksum_hex ,
/*id*/ " " , /*sid*/ " " , temp ) ) ;
}
}
if ( footer_present ) {
assert ( schema_major_version > = 2 ) ;
@ -3046,7 +3212,7 @@ IOStatus BackupEngineImpl::BackupMeta::LoadFromFile(
const std : : vector < std : : string > minor_version_strings {
" " , // invalid major version 0
" " , // implicit major version 1
" 2.0 " ,
" 2.1 " ,
} ;
IOStatus BackupEngineImpl : : BackupMeta : : StoreToFile (
@ -3124,6 +3290,11 @@ IOStatus BackupEngineImpl::BackupMeta::StoreToFile(
buf < < " \n " ;
}
for ( const auto & file : excluded_files_ ) {
assert ( schema_version > = 2 ) ;
buf < < file . relative_file < < " " < < kExcludedFieldName < < " true \n " ;
}
if ( schema_test_options & & ! schema_test_options - > footer_fields . empty ( ) ) {
buf < < kFooterMarker < < " \n " ;
for ( auto & e : schema_test_options - > footer_fields ) {