@ -43,33 +43,36 @@ namespace ROCKSDB_NAMESPACE {
static constexpr int kValueSize = 1000 ;
static constexpr int kValueSize = 1000 ;
namespace {
namespace {
// A wrapper that allows injection of errors.
// A wrapper that allows injection of errors.
class ErrorEnv : public Env Wrapper {
class ErrorFS : public FileSystem Wrapper {
public :
public :
bool writable_file_error_ ;
bool writable_file_error_ ;
int num_writable_file_errors_ ;
int num_writable_file_errors_ ;
explicit ErrorEnv ( Env * _target )
explicit ErrorFS ( const std : : shared_ptr < FileSystem > & _target )
: Env Wrapper( _target ) ,
: FileSystem Wrapper( _target ) ,
writable_file_error_ ( false ) ,
writable_file_error_ ( false ) ,
num_writable_file_errors_ ( 0 ) { }
num_writable_file_errors_ ( 0 ) { }
const char * Name ( ) const override { return " ErrorEnv " ; }
const char * Name ( ) const override { return " ErrorEnv " ; }
virtual Status NewWritableFile ( const std : : string & fname ,
virtual IOStatus NewWritableFile ( const std : : string & fname ,
std : : unique_ptr < WritableFile > * result ,
const FileOptions & opts ,
const EnvOptions & soptions ) override {
std : : unique_ptr < FSWritableFile > * result ,
IODebugContext * dbg ) override {
result - > reset ( ) ;
result - > reset ( ) ;
if ( writable_file_error_ ) {
if ( writable_file_error_ ) {
+ + num_writable_file_errors_ ;
+ + num_writable_file_errors_ ;
return Status : : IOError ( fname , " fake error " ) ;
return IO Status: : IOError ( fname , " fake error " ) ;
}
}
return target ( ) - > NewWritableFile ( fname , result , soptions ) ;
return target ( ) - > NewWritableFile ( fname , opts , result , dbg ) ;
}
}
} ;
} ;
} // anonymous namespace
} // anonymous namespace
class CorruptionTest : public testing : : Test {
class CorruptionTest : public testing : : Test {
public :
public :
std : : shared_ptr < Env > env_guard_ ;
std : : shared_ptr < Env > env_guard_ ;
ErrorEnv * env_ ;
std : : shared_ptr < ErrorFS > fs_ ;
std : : unique_ptr < Env > env_ ;
Env * base_env_ ;
std : : string dbname_ ;
std : : string dbname_ ;
std : : shared_ptr < Cache > tiny_cache_ ;
std : : shared_ptr < Cache > tiny_cache_ ;
Options options_ ;
Options options_ ;
@ -80,14 +83,15 @@ class CorruptionTest : public testing::Test {
// set it to 0), test SequenceNumberRecovery will fail, likely because of a
// set it to 0), test SequenceNumberRecovery will fail, likely because of a
// bug in recovery code. Keep it 4 for now to make the test passes.
// bug in recovery code. Keep it 4 for now to make the test passes.
tiny_cache_ = NewLRUCache ( 100 , 4 ) ;
tiny_cache_ = NewLRUCache ( 100 , 4 ) ;
Env * base_env = Env : : Default ( ) ;
base_env_ = Env : : Default ( ) ;
EXPECT_OK (
EXPECT_OK (
test : : CreateEnvFromSystem ( ConfigOptions ( ) , & base_env , & env_guard_ ) ) ;
test : : CreateEnvFromSystem ( ConfigOptions ( ) , & base_env_ , & env_guard_ ) ) ;
EXPECT_NE ( base_env , nullptr ) ;
EXPECT_NE ( base_env_ , nullptr ) ;
env_ = new ErrorEnv ( base_env ) ;
fs_ . reset ( new ErrorFS ( base_env_ - > GetFileSystem ( ) ) ) ;
env_ = NewCompositeEnv ( fs_ ) ;
options_ . wal_recovery_mode = WALRecoveryMode : : kTolerateCorruptedTailRecords ;
options_ . wal_recovery_mode = WALRecoveryMode : : kTolerateCorruptedTailRecords ;
options_ . env = env_ ;
options_ . env = env_ . get ( ) ;
dbname_ = test : : PerThreadDBPath ( env_ , " corruption_test " ) ;
dbname_ = test : : PerThreadDBPath ( env_ . get ( ) , " corruption_test " ) ;
Status s = DestroyDB ( dbname_ , options_ ) ;
Status s = DestroyDB ( dbname_ , options_ ) ;
EXPECT_OK ( s ) ;
EXPECT_OK ( s ) ;
@ -110,10 +114,9 @@ class CorruptionTest : public testing::Test {
fprintf ( stdout , " db is still at %s \n " , dbname_ . c_str ( ) ) ;
fprintf ( stdout , " db is still at %s \n " , dbname_ . c_str ( ) ) ;
} else {
} else {
Options opts ;
Options opts ;
opts . env = env_ - > target ( ) ;
opts . env = base_ env_;
EXPECT_OK ( DestroyDB ( dbname_ , opts ) ) ;
EXPECT_OK ( DestroyDB ( dbname_ , opts ) ) ;
}
}
delete env_ ;
}
}
void CloseDb ( ) {
void CloseDb ( ) {
@ -128,7 +131,7 @@ class CorruptionTest : public testing::Test {
if ( opt . env = = Options ( ) . env ) {
if ( opt . env = = Options ( ) . env ) {
// If env is not overridden, replace it with ErrorEnv.
// If env is not overridden, replace it with ErrorEnv.
// Otherwise, the test already uses a non-default Env.
// Otherwise, the test already uses a non-default Env.
opt . env = env_ ;
opt . env = env_ . get ( ) ;
}
}
opt . arena_block_size = 4096 ;
opt . arena_block_size = 4096 ;
BlockBasedTableOptions table_options ;
BlockBasedTableOptions table_options ;
@ -223,7 +226,7 @@ class CorruptionTest : public testing::Test {
}
}
ASSERT_TRUE ( ! fname . empty ( ) ) < < filetype ;
ASSERT_TRUE ( ! fname . empty ( ) ) < < filetype ;
ASSERT_OK ( test : : CorruptFile ( env_ , fname , offset , bytes_to_corrupt ,
ASSERT_OK ( test : : CorruptFile ( env_ . get ( ) , fname , offset , bytes_to_corrupt ,
/*verify_checksum*/ filetype = = kTableFile ) ) ;
/*verify_checksum*/ filetype = = kTableFile ) ) ;
}
}
@ -234,7 +237,7 @@ class CorruptionTest : public testing::Test {
db_ - > GetLiveFilesMetaData ( & metadata ) ;
db_ - > GetLiveFilesMetaData ( & metadata ) ;
for ( const auto & m : metadata ) {
for ( const auto & m : metadata ) {
if ( m . level = = level ) {
if ( m . level = = level ) {
ASSERT_OK ( test : : CorruptFile ( env_ , dbname_ + " / " + m . name , offset ,
ASSERT_OK ( test : : CorruptFile ( env_ . get ( ) , dbname_ + " / " + m . name , offset ,
bytes_to_corrupt ) ) ;
bytes_to_corrupt ) ) ;
return ;
return ;
}
}
@ -308,7 +311,7 @@ class CorruptionTest : public testing::Test {
if ( bytes_to_truncate = = 0 ) {
if ( bytes_to_truncate = = 0 ) {
new_size = 0 ;
new_size = 0 ;
}
}
ASSERT_OK ( test : : TruncateFile ( env_ , path , new_size ) ) ;
ASSERT_OK ( test : : TruncateFile ( env_ . get ( ) , path , new_size ) ) ;
}
}
} ;
} ;
@ -402,14 +405,14 @@ TEST_F(CorruptionTest, PostPITRCorruptionWALsRetained) {
}
}
TEST_F ( CorruptionTest , RecoverWriteError ) {
TEST_F ( CorruptionTest , RecoverWriteError ) {
env _- > writable_file_error_ = true ;
fs _- > writable_file_error_ = true ;
Status s = TryReopen ( ) ;
Status s = TryReopen ( ) ;
ASSERT_TRUE ( ! s . ok ( ) ) ;
ASSERT_TRUE ( ! s . ok ( ) ) ;
}
}
TEST_F ( CorruptionTest , NewFileErrorDuringWrite ) {
TEST_F ( CorruptionTest , NewFileErrorDuringWrite ) {
// Do enough writing to force minor compaction
// Do enough writing to force minor compaction
env _- > writable_file_error_ = true ;
fs _- > writable_file_error_ = true ;
const int num =
const int num =
static_cast < int > ( 3 + ( Options ( ) . write_buffer_size / kValueSize ) ) ;
static_cast < int > ( 3 + ( Options ( ) . write_buffer_size / kValueSize ) ) ;
std : : string value_storage ;
std : : string value_storage ;
@ -425,8 +428,8 @@ TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
ASSERT_TRUE ( ! failed | | ! s . ok ( ) ) ;
ASSERT_TRUE ( ! failed | | ! s . ok ( ) ) ;
}
}
ASSERT_TRUE ( ! s . ok ( ) ) ;
ASSERT_TRUE ( ! s . ok ( ) ) ;
ASSERT_GE ( env _- > num_writable_file_errors_ , 1 ) ;
ASSERT_GE ( fs _- > num_writable_file_errors_ , 1 ) ;
env _- > writable_file_error_ = false ;
fs _- > writable_file_error_ = false ;
Reopen ( ) ;
Reopen ( ) ;
}
}
@ -444,7 +447,7 @@ TEST_F(CorruptionTest, TableFile) {
TEST_F ( CorruptionTest , VerifyChecksumReadahead ) {
TEST_F ( CorruptionTest , VerifyChecksumReadahead ) {
Options options ;
Options options ;
SpecialEnv senv ( env_ - > target ( ) ) ;
SpecialEnv senv ( base_ env_) ;
options . env = & senv ;
options . env = & senv ;
// Disable block cache as we are going to check checksum for
// Disable block cache as we are going to check checksum for
// the same file twice and measure number of reads.
// the same file twice and measure number of reads.
@ -587,8 +590,8 @@ TEST_F(CorruptionTest, TableFileWrongSize) {
// Make the file smaller with truncation.
// Make the file smaller with truncation.
// First leaving a partial footer, and then completely removing footer.
// First leaving a partial footer, and then completely removing footer.
for ( size_t bytes_lost : { 8 , 100 } ) {
for ( size_t bytes_lost : { 8 , 100 } ) {
ASSERT_OK (
ASSERT_OK ( test : : TruncateFile ( env_ . get ( ) , filename ,
test : : TruncateFile ( env_ , filename , metadata [ 0 ] . size - bytes_lost ) ) ;
metadata [ 0 ] . size - bytes_lost ) ) ;
// Reported well with paranoid checks
// Reported well with paranoid checks
options_ . paranoid_checks = true ;
options_ . paranoid_checks = true ;
@ -653,7 +656,7 @@ TEST_F(CorruptionTest, CorruptedDescriptor) {
TEST_F ( CorruptionTest , CompactionInputError ) {
TEST_F ( CorruptionTest , CompactionInputError ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
Reopen ( & options ) ;
Reopen ( & options ) ;
Build ( 10 ) ;
Build ( 10 ) ;
DBImpl * dbi = static_cast_with_check < DBImpl > ( db_ ) ;
DBImpl * dbi = static_cast_with_check < DBImpl > ( db_ ) ;
@ -674,7 +677,7 @@ TEST_F(CorruptionTest, CompactionInputError) {
TEST_F ( CorruptionTest , CompactionInputErrorParanoid ) {
TEST_F ( CorruptionTest , CompactionInputErrorParanoid ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . paranoid_checks = true ;
options . paranoid_checks = true ;
options . write_buffer_size = 131072 ;
options . write_buffer_size = 131072 ;
options . max_write_buffer_number = 2 ;
options . max_write_buffer_number = 2 ;
@ -761,7 +764,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
ImmutableOptions ( options_ ) , kRangeDelBlockName , & range_del_handle ) ) ;
ImmutableOptions ( options_ ) , kRangeDelBlockName , & range_del_handle ) ) ;
ASSERT_OK ( TryReopen ( ) ) ;
ASSERT_OK ( TryReopen ( ) ) ;
ASSERT_OK ( test : : CorruptFile ( env_ , filename ,
ASSERT_OK ( test : : CorruptFile ( env_ . get ( ) , filename ,
static_cast < int > ( range_del_handle . offset ( ) ) , 1 ) ) ;
static_cast < int > ( range_del_handle . offset ( ) ) , 1 ) ) ;
ASSERT_TRUE ( TryReopen ( ) . IsCorruption ( ) ) ;
ASSERT_TRUE ( TryReopen ( ) . IsCorruption ( ) ) ;
}
}
@ -769,7 +772,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
TEST_F ( CorruptionTest , FileSystemStateCorrupted ) {
TEST_F ( CorruptionTest , FileSystemStateCorrupted ) {
for ( int iter = 0 ; iter < 2 ; + + iter ) {
for ( int iter = 0 ; iter < 2 ; + + iter ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . paranoid_checks = true ;
options . paranoid_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
Reopen ( & options ) ;
Reopen ( & options ) ;
@ -808,7 +811,7 @@ static const auto& corruption_modes = {
TEST_F ( CorruptionTest , ParanoidFileChecksOnFlush ) {
TEST_F ( CorruptionTest , ParanoidFileChecksOnFlush ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
options . paranoid_file_checks = true ;
options . paranoid_file_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
@ -836,7 +839,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
TEST_F ( CorruptionTest , ParanoidFileChecksOnCompact ) {
TEST_F ( CorruptionTest , ParanoidFileChecksOnCompact ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . paranoid_file_checks = true ;
options . paranoid_file_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
@ -869,7 +872,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRangeFirst ) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRangeFirst ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
options . paranoid_file_checks = true ;
options . paranoid_file_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
@ -905,7 +908,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRange ) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRange ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
options . paranoid_file_checks = true ;
options . paranoid_file_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
@ -944,7 +947,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRangeLast ) {
TEST_F ( CorruptionTest , ParanoidFileChecksWithDeleteRangeLast ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
options . paranoid_file_checks = true ;
options . paranoid_file_checks = true ;
options . create_if_missing = true ;
options . create_if_missing = true ;
@ -980,7 +983,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
TEST_F ( CorruptionTest , LogCorruptionErrorsInCompactionIterator ) {
TEST_F ( CorruptionTest , LogCorruptionErrorsInCompactionIterator ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . allow_data_in_errors = true ;
options . allow_data_in_errors = true ;
auto mode = mock : : MockTableFactory : : kCorruptKey ;
auto mode = mock : : MockTableFactory : : kCorruptKey ;
@ -1009,7 +1012,7 @@ TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
TEST_F ( CorruptionTest , CompactionKeyOrderCheck ) {
TEST_F ( CorruptionTest , CompactionKeyOrderCheck ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . paranoid_file_checks = false ;
options . paranoid_file_checks = false ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . check_flush_compaction_key_order = false ;
options . check_flush_compaction_key_order = false ;
@ -1036,7 +1039,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
TEST_F ( CorruptionTest , FlushKeyOrderCheck ) {
TEST_F ( CorruptionTest , FlushKeyOrderCheck ) {
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . paranoid_file_checks = false ;
options . paranoid_file_checks = false ;
options . create_if_missing = true ;
options . create_if_missing = true ;
ASSERT_OK ( db_ - > SetOptions ( { { " check_flush_compaction_key_order " , " true " } } ) ) ;
ASSERT_OK ( db_ - > SetOptions ( { { " check_flush_compaction_key_order " , " true " } } ) ) ;
@ -1089,7 +1092,7 @@ TEST_F(CorruptionTest, DisableKeyOrderCheck) {
TEST_F ( CorruptionTest , VerifyWholeTableChecksum ) {
TEST_F ( CorruptionTest , VerifyWholeTableChecksum ) {
CloseDb ( ) ;
CloseDb ( ) ;
Options options ;
Options options ;
options . env = env_ ;
options . env = env_ . get ( ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . file_checksum_gen_factory =
options . file_checksum_gen_factory =
@ -1178,7 +1181,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
track_and_verify_wals_in_manifest_ ;
track_and_verify_wals_in_manifest_ ;
options . wal_recovery_mode = WALRecoveryMode : : kPointInTimeRecovery ;
options . wal_recovery_mode = WALRecoveryMode : : kPointInTimeRecovery ;
options . avoid_flush_during_recovery = false ;
options . avoid_flush_during_recovery = false ;
options . env = env_ ;
options . env = env_ . get ( ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . max_write_buffer_number = 8 ;
options . max_write_buffer_number = 8 ;
@ -1350,7 +1353,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
options . track_and_verify_wals_in_manifest =
options . track_and_verify_wals_in_manifest =
track_and_verify_wals_in_manifest_ ;
track_and_verify_wals_in_manifest_ ;
options . avoid_flush_during_recovery = false ;
options . avoid_flush_during_recovery = false ;
options . env = env_ ;
options . env = env_ . get ( ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
options . create_if_missing = true ;
options . create_if_missing = true ;
options . max_write_buffer_number = 3 ;
options . max_write_buffer_number = 3 ;
@ -1545,7 +1548,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
Options options ;
Options options ;
options . wal_recovery_mode = WALRecoveryMode : : kPointInTimeRecovery ;
options . wal_recovery_mode = WALRecoveryMode : : kPointInTimeRecovery ;
options . avoid_flush_during_recovery = false ;
options . avoid_flush_during_recovery = false ;
options . env = env_ ;
options . env = env_ . get ( ) ;
options . create_if_missing = true ;
options . create_if_missing = true ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;
ASSERT_OK ( DestroyDB ( dbname_ , options ) ) ;