@ -338,6 +338,163 @@ TEST_F(DBWALTest, RecoverWithTableHandle) {
} while ( ChangeWalOptions ( ) ) ;
}
TEST_F ( DBWALTest , RecoverWithBlob ) {
// Write a value that's below the prospective size limit for blobs and another
// one that's above. Note that blob files are not actually enabled at this
// point.
constexpr uint64_t min_blob_size = 10 ;
constexpr char short_value [ ] = " short " ;
static_assert ( sizeof ( short_value ) - 1 < min_blob_size ,
" short_value too long " ) ;
constexpr char long_value [ ] = " long_value " ;
static_assert ( sizeof ( long_value ) - 1 > = min_blob_size ,
" long_value too short " ) ;
ASSERT_OK ( Put ( " key1 " , short_value ) ) ;
ASSERT_OK ( Put ( " key2 " , long_value ) ) ;
// There should be no files just yet since we haven't flushed.
{
VersionSet * const versions = dbfull ( ) - > TEST_GetVersionSet ( ) ;
assert ( versions ) ;
ColumnFamilyData * const cfd = versions - > GetColumnFamilySet ( ) - > GetDefault ( ) ;
assert ( cfd ) ;
Version * const current = cfd - > current ( ) ;
assert ( current ) ;
const VersionStorageInfo * const storage_info = current - > storage_info ( ) ;
assert ( storage_info ) ;
ASSERT_EQ ( storage_info - > num_non_empty_levels ( ) , 0 ) ;
ASSERT_TRUE ( storage_info - > GetBlobFiles ( ) . empty ( ) ) ;
}
// Reopen the database with blob files enabled. A new table file/blob file
// pair should be written during recovery.
Options options ;
options . enable_blob_files = true ;
options . min_blob_size = min_blob_size ;
options . avoid_flush_during_recovery = false ;
options . disable_auto_compactions = true ;
Reopen ( options ) ;
ASSERT_EQ ( Get ( " key1 " ) , short_value ) ;
// TODO: enable once Get support is implemented for blobs
// ASSERT_EQ(Get("key2"), long_value);
VersionSet * const versions = dbfull ( ) - > TEST_GetVersionSet ( ) ;
assert ( versions ) ;
ColumnFamilyData * const cfd = versions - > GetColumnFamilySet ( ) - > GetDefault ( ) ;
assert ( cfd ) ;
Version * const current = cfd - > current ( ) ;
assert ( current ) ;
const VersionStorageInfo * const storage_info = current - > storage_info ( ) ;
assert ( storage_info ) ;
const auto & l0_files = storage_info - > LevelFiles ( 0 ) ;
ASSERT_EQ ( l0_files . size ( ) , 1 ) ;
const FileMetaData * const table_file = l0_files [ 0 ] ;
assert ( table_file ) ;
const auto & blob_files = storage_info - > GetBlobFiles ( ) ;
ASSERT_EQ ( blob_files . size ( ) , 1 ) ;
const auto & blob_file = blob_files . begin ( ) - > second ;
assert ( blob_file ) ;
ASSERT_EQ ( table_file - > smallest . user_key ( ) , " key1 " ) ;
ASSERT_EQ ( table_file - > largest . user_key ( ) , " key2 " ) ;
ASSERT_EQ ( table_file - > fd . smallest_seqno , 1 ) ;
ASSERT_EQ ( table_file - > fd . largest_seqno , 2 ) ;
ASSERT_EQ ( table_file - > oldest_blob_file_number ,
blob_file - > GetBlobFileNumber ( ) ) ;
ASSERT_EQ ( blob_file - > GetTotalBlobCount ( ) , 1 ) ;
# ifndef ROCKSDB_LITE
const InternalStats * const internal_stats = cfd - > internal_stats ( ) ;
assert ( internal_stats ) ;
const uint64_t expected_bytes =
table_file - > fd . GetFileSize ( ) + blob_file - > GetTotalBlobBytes ( ) ;
const auto & compaction_stats = internal_stats - > TEST_GetCompactionStats ( ) ;
ASSERT_FALSE ( compaction_stats . empty ( ) ) ;
ASSERT_EQ ( compaction_stats [ 0 ] . bytes_written , expected_bytes ) ;
ASSERT_EQ ( compaction_stats [ 0 ] . num_output_files , 2 ) ;
const uint64_t * const cf_stats_value = internal_stats - > TEST_GetCFStatsValue ( ) ;
ASSERT_EQ ( cf_stats_value [ InternalStats : : BYTES_FLUSHED ] , expected_bytes ) ;
# endif // ROCKSDB_LITE
}
class DBRecoveryTestBlobError
: public DBWALTest ,
public testing : : WithParamInterface < std : : string > {
public :
DBRecoveryTestBlobError ( ) : fault_injection_env_ ( env_ ) { }
~ DBRecoveryTestBlobError ( ) { Close ( ) ; }
FaultInjectionTestEnv fault_injection_env_ ;
} ;
INSTANTIATE_TEST_CASE_P ( DBRecoveryTestBlobError , DBRecoveryTestBlobError ,
: : testing : : ValuesIn ( std : : vector < std : : string > {
" BlobFileBuilder::WriteBlobToFile:AddRecord " ,
" BlobFileBuilder::WriteBlobToFile:AppendFooter " } ) ) ;
TEST_P ( DBRecoveryTestBlobError , RecoverWithBlobError ) {
// Write a value. Note that blob files are not actually enabled at this point.
ASSERT_OK ( Put ( " key " , " blob " ) ) ;
// Reopen with blob files enabled but make blob file writing fail during
// recovery.
SyncPoint : : GetInstance ( ) - > SetCallBack ( GetParam ( ) , [ this ] ( void * /* arg */ ) {
fault_injection_env_ . SetFilesystemActive ( false , Status : : IOError ( ) ) ;
} ) ;
SyncPoint : : GetInstance ( ) - > SetCallBack (
" BuildTable:BeforeFinishBuildTable " , [ this ] ( void * /* arg */ ) {
fault_injection_env_ . SetFilesystemActive ( true ) ;
} ) ;
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
Options options ;
options . enable_blob_files = true ;
options . avoid_flush_during_recovery = false ;
options . disable_auto_compactions = true ;
options . env = & fault_injection_env_ ;
ASSERT_NOK ( TryReopen ( options ) ) ;
SyncPoint : : GetInstance ( ) - > DisableProcessing ( ) ;
SyncPoint : : GetInstance ( ) - > ClearAllCallBacks ( ) ;
// Make sure the files generated by the failed recovery have been deleted.
std : : vector < std : : string > files ;
ASSERT_OK ( env_ - > GetChildren ( dbname_ , & files ) ) ;
for ( const auto & file : files ) {
uint64_t number = 0 ;
FileType type = kTableFile ;
if ( ! ParseFileName ( file , & number , & type ) ) {
continue ;
}
ASSERT_NE ( type , kTableFile ) ;
ASSERT_NE ( type , kBlobFile ) ;
}
}
TEST_F ( DBWALTest , IgnoreRecoveredLog ) {
std : : string backup_logs = dbname_ + " /backup_logs " ;