@ -24,13 +24,37 @@ class DBWALTestBase : public DBTestBase {
# if defined(ROCKSDB_PLATFORM_POSIX)
public :
# if defined(ROCKSDB_FALLOCATE_PRESENT)
bool IsFallocateSupported ( ) {
// Test fallocate support of running file system.
// Skip this test if fallocate is not supported.
std : : string fname_test_fallocate = dbname_ + " /preallocate_testfile " ;
int fd = - 1 ;
do {
fd = open ( fname_test_fallocate . c_str ( ) , O_CREAT | O_RDWR | O_TRUNC , 0644 ) ;
} while ( fd < 0 & & errno = = EINTR ) ;
assert ( fd > 0 ) ;
int alloc_status = fallocate ( fd , 0 , 0 , 1 ) ;
int err_number = errno ;
close ( fd ) ;
assert ( env_ - > DeleteFile ( fname_test_fallocate ) = = Status : : OK ( ) ) ;
if ( err_number = = ENOSYS | | err_number = = EOPNOTSUPP ) {
fprintf ( stderr , " Skipped preallocated space check: %s \n " ,
errnoStr ( err_number ) . c_str ( ) ) ;
return false ;
}
assert ( alloc_status = = 0 ) ;
return true ;
}
# endif // ROCKSDB_FALLOCATE_PRESENT
uint64_t GetAllocatedFileSize ( std : : string file_name ) {
struct stat sbuf ;
int err = stat ( file_name . c_str ( ) , & sbuf ) ;
assert ( err = = 0 ) ;
return sbuf . st_blocks * 512 ;
}
# endif
# endif // ROCKSDB_PLATFORM_POSIX
} ;
class DBWALTest : public DBWALTestBase {
@ -1849,24 +1873,9 @@ TEST_F(DBWALTest, TruncateLastLogAfterRecoverWithoutFlush) {
ROCKSDB_GTEST_SKIP ( " Test requires non-mem environment " ) ;
return ;
}
// Test fallocate support of running file system.
// Skip this test if fallocate is not supported.
std : : string fname_test_fallocate = dbname_ + " /preallocate_testfile " ;
int fd = - 1 ;
do {
fd = open ( fname_test_fallocate . c_str ( ) , O_CREAT | O_RDWR | O_TRUNC , 0644 ) ;
} while ( fd < 0 & & errno = = EINTR ) ;
ASSERT_GT ( fd , 0 ) ;
int alloc_status = fallocate ( fd , 0 , 0 , 1 ) ;
int err_number = errno ;
close ( fd ) ;
ASSERT_OK ( options . env - > DeleteFile ( fname_test_fallocate ) ) ;
if ( err_number = = ENOSYS | | err_number = = EOPNOTSUPP ) {
fprintf ( stderr , " Skipped preallocated space check: %s \n " ,
errnoStr ( err_number ) . c_str ( ) ) ;
if ( ! IsFallocateSupported ( ) ) {
return ;
}
ASSERT_EQ ( 0 , alloc_status ) ;
DestroyAndReopen ( options ) ;
size_t preallocated_size =
@ -1889,6 +1898,120 @@ TEST_F(DBWALTest, TruncateLastLogAfterRecoverWithoutFlush) {
ASSERT_LT ( GetAllocatedFileSize ( dbname_ + file_before - > PathName ( ) ) ,
preallocated_size ) ;
}
// Tests that we will truncate the preallocated space of the last log from
// previous.
TEST_F ( DBWALTest , TruncateLastLogAfterRecoverWithFlush ) {
constexpr size_t kKB = 1024 ;
Options options = CurrentOptions ( ) ;
options . env = env_ ;
options . avoid_flush_during_recovery = false ;
options . avoid_flush_during_shutdown = true ;
if ( mem_env_ ) {
ROCKSDB_GTEST_SKIP ( " Test requires non-mem environment " ) ;
return ;
}
if ( ! IsFallocateSupported ( ) ) {
return ;
}
DestroyAndReopen ( options ) ;
size_t preallocated_size =
dbfull ( ) - > TEST_GetWalPreallocateBlockSize ( options . write_buffer_size ) ;
ASSERT_OK ( Put ( " foo " , " v1 " ) ) ;
VectorLogPtr log_files_before ;
ASSERT_OK ( dbfull ( ) - > GetSortedWalFiles ( log_files_before ) ) ;
ASSERT_EQ ( 1 , log_files_before . size ( ) ) ;
auto & file_before = log_files_before [ 0 ] ;
ASSERT_LT ( file_before - > SizeFileBytes ( ) , 1 * kKB ) ;
ASSERT_GE ( GetAllocatedFileSize ( dbname_ + file_before - > PathName ( ) ) ,
preallocated_size ) ;
// The log file has preallocated space.
Close ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > LoadDependency (
{ { " DBImpl::PurgeObsoleteFiles:Begin " ,
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterRecover " } ,
{ " DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterTruncate " ,
" DBImpl::DeleteObsoleteFileImpl::BeforeDeletion " } } ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
port : : Thread reopen_thread ( [ & ] ( ) { Reopen ( options ) ; } ) ;
TEST_SYNC_POINT (
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterRecover " ) ;
// After the flush during Open, the log file should get deleted. However,
// if the process is in a crash loop, the log file may not get
// deleted and thte preallocated space will keep accumulating. So we need
// to ensure it gets trtuncated.
EXPECT_LT ( GetAllocatedFileSize ( dbname_ + file_before - > PathName ( ) ) ,
preallocated_size ) ;
TEST_SYNC_POINT (
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterTruncate " ) ;
reopen_thread . join ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > DisableProcessing ( ) ;
}
TEST_F ( DBWALTest , TruncateLastLogAfterRecoverWALEmpty ) {
Options options = CurrentOptions ( ) ;
options . env = env_ ;
options . avoid_flush_during_recovery = false ;
if ( mem_env_ | | encrypted_env_ ) {
ROCKSDB_GTEST_SKIP ( " Test requires non-mem/non-encrypted environment " ) ;
return ;
}
if ( ! IsFallocateSupported ( ) ) {
return ;
}
DestroyAndReopen ( options ) ;
size_t preallocated_size =
dbfull ( ) - > TEST_GetWalPreallocateBlockSize ( options . write_buffer_size ) ;
Close ( ) ;
std : : vector < std : : string > filenames ;
std : : string last_log ;
uint64_t last_log_num = 0 ;
ASSERT_OK ( env_ - > GetChildren ( dbname_ , & filenames ) ) ;
for ( auto fname : filenames ) {
uint64_t number ;
FileType type ;
if ( ParseFileName ( fname , & number , & type , nullptr ) ) {
if ( type = = kWalFile & & number > last_log_num ) {
last_log = fname ;
}
}
}
ASSERT_NE ( last_log , " " ) ;
last_log = dbname_ + ' / ' + last_log ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > LoadDependency (
{ { " DBImpl::PurgeObsoleteFiles:Begin " ,
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterRecover " } ,
{ " DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterTruncate " ,
" DBImpl::DeleteObsoleteFileImpl::BeforeDeletion " } } ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > SetCallBack (
" PosixWritableFile::Close " ,
[ ] ( void * arg ) { * ( reinterpret_cast < size_t * > ( arg ) ) = 0 ; } ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
// Preallocate space for the empty log file. This could happen if WAL data
// was buffered in memory and the process crashed.
std : : unique_ptr < WritableFile > log_file ;
ASSERT_OK ( env_ - > ReopenWritableFile ( last_log , & log_file , EnvOptions ( ) ) ) ;
log_file - > SetPreallocationBlockSize ( preallocated_size ) ;
log_file - > PrepareWrite ( 0 , 4096 ) ;
log_file . reset ( ) ;
ASSERT_GE ( GetAllocatedFileSize ( last_log ) , preallocated_size ) ;
port : : Thread reopen_thread ( [ & ] ( ) { Reopen ( options ) ; } ) ;
TEST_SYNC_POINT (
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterRecover " ) ;
// The preallocated space should be truncated.
EXPECT_LT ( GetAllocatedFileSize ( last_log ) , preallocated_size ) ;
TEST_SYNC_POINT (
" DBWALTest::TruncateLastLogAfterRecoverWithFlush:AfterTruncate " ) ;
reopen_thread . join ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > DisableProcessing ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > ClearAllCallBacks ( ) ;
}
# endif // ROCKSDB_FALLOCATE_PRESENT
# endif // ROCKSDB_PLATFORM_POSIX