@ -4,8 +4,11 @@
// (found in the LICENSE.Apache file in the root directory).
# include <cstring>
# include <iomanip>
# include <memory>
# include <sstream>
# include <string>
# include "db/version_edit.h"
# include "db/version_set.h"
# include "logging/logging.h"
@ -80,13 +83,15 @@ class VersionBuilderTest : public testing::Test {
void AddBlob ( uint64_t blob_file_number , uint64_t total_blob_count ,
uint64_t total_blob_bytes , std : : string checksum_method ,
std : : string checksum_value , uint64_t garbage_blob_count ,
uint64_t garbage_blob_bytes ) {
std : : string checksum_value ,
BlobFileMetaData : : LinkedSsts linked_ssts ,
uint64_t garbage_blob_count , uint64_t garbage_blob_bytes ) {
auto shared_meta = SharedBlobFileMetaData : : Create (
blob_file_number , total_blob_count , total_blob_bytes ,
std : : move ( checksum_method ) , std : : move ( checksum_value ) ) ;
auto meta = BlobFileMetaData : : Create (
std : : move ( shared_meta ) , garbage_blob_count , garbage_blob_bytes ) ;
auto meta =
BlobFileMetaData : : Create ( std : : move ( shared_meta ) , std : : move ( linked_ssts ) ,
garbage_blob_count , garbage_blob_bytes ) ;
vstorage_ . AddBlobFile ( std : : move ( meta ) ) ;
}
@ -642,6 +647,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileAddition) {
ASSERT_EQ ( new_meta - > GetTotalBlobBytes ( ) , total_blob_bytes ) ;
ASSERT_EQ ( new_meta - > GetChecksumMethod ( ) , checksum_method ) ;
ASSERT_EQ ( new_meta - > GetChecksumValue ( ) , checksum_value ) ;
ASSERT_TRUE ( new_meta - > GetLinkedSsts ( ) . empty ( ) ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobCount ( ) , 0 ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobBytes ( ) , 0 ) ;
}
@ -658,7 +664,8 @@ TEST_F(VersionBuilderTest, ApplyBlobFileAdditionAlreadyInBase) {
constexpr uint64_t garbage_blob_bytes = 456789 ;
AddBlob ( blob_file_number , total_blob_count , total_blob_bytes , checksum_method ,
checksum_value , garbage_blob_count , garbage_blob_bytes ) ;
checksum_value , BlobFileMetaData : : LinkedSsts ( ) , garbage_blob_count ,
garbage_blob_bytes ) ;
EnvOptions env_options ;
constexpr TableCache * table_cache = nullptr ;
@ -717,7 +724,8 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileInBase) {
constexpr uint64_t garbage_blob_bytes = 456789 ;
AddBlob ( blob_file_number , total_blob_count , total_blob_bytes , checksum_method ,
checksum_value , garbage_blob_count , garbage_blob_bytes ) ;
checksum_value , BlobFileMetaData : : LinkedSsts ( ) , garbage_blob_count ,
garbage_blob_bytes ) ;
const auto meta =
GetBlobFileMetaData ( vstorage_ . GetBlobFiles ( ) , blob_file_number ) ;
@ -759,6 +767,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileInBase) {
ASSERT_EQ ( new_meta - > GetTotalBlobBytes ( ) , total_blob_bytes ) ;
ASSERT_EQ ( new_meta - > GetChecksumMethod ( ) , checksum_method ) ;
ASSERT_EQ ( new_meta - > GetChecksumValue ( ) , checksum_value ) ;
ASSERT_TRUE ( new_meta - > GetLinkedSsts ( ) . empty ( ) ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobCount ( ) ,
garbage_blob_count + new_garbage_blob_count ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobBytes ( ) ,
@ -816,6 +825,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileAdditionApplied) {
ASSERT_EQ ( new_meta - > GetTotalBlobBytes ( ) , total_blob_bytes ) ;
ASSERT_EQ ( new_meta - > GetChecksumMethod ( ) , checksum_method ) ;
ASSERT_EQ ( new_meta - > GetChecksumValue ( ) , checksum_value ) ;
ASSERT_TRUE ( new_meta - > GetLinkedSsts ( ) . empty ( ) ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobCount ( ) , garbage_blob_count ) ;
ASSERT_EQ ( new_meta - > GetGarbageBlobBytes ( ) , garbage_blob_bytes ) ;
}
@ -856,8 +866,8 @@ TEST_F(VersionBuilderTest, SaveBlobFilesTo) {
AddBlob ( blob_file_number , total_blob_count , total_blob_bytes ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) , garbage_blob_count ,
garbage_blob_bytes ) ;
/* checksum_value */ std : : string ( ) , BlobFileMetaData : : LinkedSsts ( ) ,
garbage_blob_count , garbage_blob_ bytes ) ;
}
EnvOptions env_options ;
@ -946,7 +956,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
AddBlob ( /* blob_file_number */ 16 , /* total_blob_count */ 1000 ,
/* total_blob_bytes */ 1000000 ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) , BlobFileMetaData : : LinkedSsts { 1 } ,
/* garbage_blob_count */ 500 , /* garbage_blob_bytes */ 300000 ) ;
UpdateVersionStorageInfo ( ) ;
@ -995,9 +1005,9 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
UnrefFilesInVersion ( & new_vstorage ) ;
}
TEST_F ( VersionBuilderTest , CheckConsistencyForBlobFilesNotInVersion ) {
// Initialize base version. The table file points to a blob file that is
// not in this version .
TEST_F ( VersionBuilderTest , CheckConsistencyForBlobFilesInconsistentLinks ) {
// Initialize base version. Links between the table file and the blob file
// are inconsistent .
Add ( /* level */ 1 , /* file_number */ 1 , /* smallest */ " 150 " ,
/* largest */ " 200 " , /* file_size */ 100 ,
@ -1009,7 +1019,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
AddBlob ( /* blob_file_number */ 16 , /* total_blob_count */ 1000 ,
/* total_blob_bytes */ 1000000 ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) , BlobFileMetaData : : LinkedSsts { 1 } ,
/* garbage_blob_count */ 500 , /* garbage_blob_bytes */ 300000 ) ;
UpdateVersionStorageInfo ( ) ;
@ -1029,8 +1039,9 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
const Status s = builder . SaveTo ( & new_vstorage ) ;
ASSERT_TRUE ( s . IsCorruption ( ) ) ;
ASSERT_TRUE (
std : : strstr ( s . getState ( ) , " Blob file #256 is not part of this version " ) ) ;
ASSERT_TRUE ( std : : strstr (
s . getState ( ) ,
" Links are inconsistent between table files and blob file #16 " ) ) ;
UnrefFilesInVersion ( & new_vstorage ) ;
}
@ -1049,7 +1060,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
AddBlob ( /* blob_file_number */ 16 , /* total_blob_count */ 1000 ,
/* total_blob_bytes */ 1000000 ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) , BlobFileMetaData : : LinkedSsts { 1 } ,
/* garbage_blob_count */ 1000 , /* garbage_blob_bytes */ 1000000 ) ;
UpdateVersionStorageInfo ( ) ;
@ -1075,6 +1086,224 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
UnrefFilesInVersion ( & new_vstorage ) ;
}
TEST_F ( VersionBuilderTest , CheckConsistencyForBlobFilesAllGarbageLinkedSsts ) {
// Initialize base version, with a table file pointing to a blob file
// that has no garbage at this point.
Add ( /* level */ 1 , /* file_number */ 1 , /* smallest */ " 150 " ,
/* largest */ " 200 " , /* file_size */ 100 ,
/* path_id */ 0 , /* smallest_seq */ 100 , /* largest_seq */ 100 ,
/* num_entries */ 0 , /* num_deletions */ 0 ,
/* sampled */ false , /* smallest_seqno */ 100 , /* largest_seqno */ 100 ,
/* oldest_blob_file_number */ 16 ) ;
AddBlob ( /* blob_file_number */ 16 , /* total_blob_count */ 1000 ,
/* total_blob_bytes */ 1000000 ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) , BlobFileMetaData : : LinkedSsts { 1 } ,
/* garbage_blob_count */ 0 , /* garbage_blob_bytes */ 0 ) ;
UpdateVersionStorageInfo ( ) ;
// Mark the entire blob file garbage but do not remove the linked SST.
EnvOptions env_options ;
constexpr TableCache * table_cache = nullptr ;
constexpr VersionSet * version_set = nullptr ;
VersionBuilder builder ( env_options , & ioptions_ , table_cache , & vstorage_ ,
version_set ) ;
VersionEdit edit ;
edit . AddBlobFileGarbage ( /* blob_file_number */ 16 ,
/* garbage_blob_count */ 1000 ,
/* garbage_blob_bytes */ 1000000 ) ;
ASSERT_OK ( builder . Apply ( & edit ) ) ;
// Save to a new version in order to trigger consistency checks.
constexpr bool force_consistency_checks = true ;
VersionStorageInfo new_vstorage ( & icmp_ , ucmp_ , options_ . num_levels ,
kCompactionStyleLevel , & vstorage_ ,
force_consistency_checks ) ;
const Status s = builder . SaveTo ( & new_vstorage ) ;
ASSERT_TRUE ( s . IsCorruption ( ) ) ;
ASSERT_TRUE (
std : : strstr ( s . getState ( ) , " Blob file #16 consists entirely of garbage " ) ) ;
UnrefFilesInVersion ( & new_vstorage ) ;
}
TEST_F ( VersionBuilderTest , MaintainLinkedSstsForBlobFiles ) {
// Initialize base version. Table files 1..10 are linked to blob files 1..5,
// while table files 11..20 are not linked to any blob files.
for ( uint64_t i = 1 ; i < = 10 ; + + i ) {
std : : ostringstream oss ;
oss < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < i ;
const std : : string key = oss . str ( ) ;
Add ( /* level */ 1 , /* file_number */ i , /* smallest */ key . c_str ( ) ,
/* largest */ key . c_str ( ) , /* file_size */ 100 ,
/* path_id */ 0 , /* smallest_seq */ i * 100 , /* largest_seq */ i * 100 ,
/* num_entries */ 0 , /* num_deletions */ 0 ,
/* sampled */ false , /* smallest_seqno */ i * 100 ,
/* largest_seqno */ i * 100 ,
/* oldest_blob_file_number */ ( ( i - 1 ) % 5 ) + 1 ) ;
}
for ( uint64_t i = 1 ; i < = 5 ; + + i ) {
AddBlob ( /* blob_file_number */ i , /* total_blob_count */ 2000 ,
/* total_blob_bytes */ 2000000 ,
/* checksum_method */ std : : string ( ) ,
/* checksum_value */ std : : string ( ) ,
BlobFileMetaData : : LinkedSsts { i , i + 5 } ,
/* garbage_blob_count */ 1000 , /* garbage_blob_bytes */ 1000000 ) ;
}
for ( uint64_t i = 11 ; i < = 20 ; + + i ) {
std : : ostringstream oss ;
oss < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < i ;
const std : : string key = oss . str ( ) ;
Add ( /* level */ 1 , /* file_number */ i , /* smallest */ key . c_str ( ) ,
/* largest */ key . c_str ( ) , /* file_size */ 100 ,
/* path_id */ 0 , /* smallest_seq */ i * 100 , /* largest_seq */ i * 100 ,
/* num_entries */ 0 , /* num_deletions */ 0 ,
/* sampled */ false , /* smallest_seqno */ i * 100 ,
/* largest_seqno */ i * 100 , kInvalidBlobFileNumber ) ;
}
UpdateVersionStorageInfo ( ) ;
{
const auto & blob_files = vstorage_ . GetBlobFiles ( ) ;
ASSERT_EQ ( blob_files . size ( ) , 5 ) ;
const std : : vector < BlobFileMetaData : : LinkedSsts > expected_linked_ssts {
{ 1 , 6 } , { 2 , 7 } , { 3 , 8 } , { 4 , 9 } , { 5 , 10 } } ;
for ( size_t i = 0 ; i < 5 ; + + i ) {
const auto meta =
GetBlobFileMetaData ( blob_files , /* blob_file_number */ i + 1 ) ;
ASSERT_NE ( meta , nullptr ) ;
ASSERT_EQ ( meta - > GetLinkedSsts ( ) , expected_linked_ssts [ i ] ) ;
}
}
VersionEdit edit ;
// Add an SST that references a blob file.
edit . AddFile (
/* level */ 1 , /* file_number */ 21 , /* path_id */ 0 ,
/* file_size */ 100 , /* smallest */ GetInternalKey ( " 21 " , 2100 ) ,
/* largest */ GetInternalKey ( " 21 " , 2100 ) , /* smallest_seqno */ 2100 ,
/* largest_seqno */ 2100 , /* marked_for_compaction */ false ,
/* oldest_blob_file_number */ 1 , kUnknownOldestAncesterTime ,
kUnknownFileCreationTime , kUnknownFileChecksum ,
kUnknownFileChecksumFuncName ) ;
// Add an SST that does not reference any blob files.
edit . AddFile (
/* level */ 1 , /* file_number */ 22 , /* path_id */ 0 ,
/* file_size */ 100 , /* smallest */ GetInternalKey ( " 22 " , 2200 ) ,
/* largest */ GetInternalKey ( " 22 " , 2200 ) , /* smallest_seqno */ 2200 ,
/* largest_seqno */ 2200 , /* marked_for_compaction */ false ,
kInvalidBlobFileNumber , kUnknownOldestAncesterTime ,
kUnknownFileCreationTime , kUnknownFileChecksum ,
kUnknownFileChecksumFuncName ) ;
// Delete a file that references a blob file.
edit . DeleteFile ( /* level */ 1 , /* file_number */ 6 ) ;
// Delete a file that does not reference any blob files.
edit . DeleteFile ( /* level */ 1 , /* file_number */ 16 ) ;
// Trivially move a file that references a blob file. Note that we save
// the original BlobFileMetaData object so we can check that no new object
// gets created.
auto meta3 =
GetBlobFileMetaData ( vstorage_ . GetBlobFiles ( ) , /* blob_file_number */ 3 ) ;
edit . DeleteFile ( /* level */ 1 , /* file_number */ 3 ) ;
edit . AddFile ( /* level */ 2 , /* file_number */ 3 , /* path_id */ 0 ,
/* file_size */ 100 , /* smallest */ GetInternalKey ( " 03 " , 300 ) ,
/* largest */ GetInternalKey ( " 03 " , 300 ) ,
/* smallest_seqno */ 300 ,
/* largest_seqno */ 300 , /* marked_for_compaction */ false ,
/* oldest_blob_file_number */ 3 , kUnknownOldestAncesterTime ,
kUnknownFileCreationTime , kUnknownFileChecksum ,
kUnknownFileChecksumFuncName ) ;
// Trivially move a file that does not reference any blob files.
edit . DeleteFile ( /* level */ 1 , /* file_number */ 13 ) ;
edit . AddFile ( /* level */ 2 , /* file_number */ 13 , /* path_id */ 0 ,
/* file_size */ 100 , /* smallest */ GetInternalKey ( " 13 " , 1300 ) ,
/* largest */ GetInternalKey ( " 13 " , 1300 ) ,
/* smallest_seqno */ 1300 ,
/* largest_seqno */ 1300 , /* marked_for_compaction */ false ,
kInvalidBlobFileNumber , kUnknownOldestAncesterTime ,
kUnknownFileCreationTime , kUnknownFileChecksum ,
kUnknownFileChecksumFuncName ) ;
// Add one more SST file that references a blob file, then promptly
// delete it in a second version edit before the new version gets saved.
// This file should not show up as linked to the blob file in the new version.
edit . AddFile ( /* level */ 1 , /* file_number */ 23 , /* path_id */ 0 ,
/* file_size */ 100 , /* smallest */ GetInternalKey ( " 23 " , 2300 ) ,
/* largest */ GetInternalKey ( " 23 " , 2300 ) ,
/* smallest_seqno */ 2300 ,
/* largest_seqno */ 2300 , /* marked_for_compaction */ false ,
/* oldest_blob_file_number */ 5 , kUnknownOldestAncesterTime ,
kUnknownFileCreationTime , kUnknownFileChecksum ,
kUnknownFileChecksumFuncName ) ;
VersionEdit edit2 ;
edit2 . DeleteFile ( /* level */ 1 , /* file_number */ 23 ) ;
EnvOptions env_options ;
constexpr TableCache * table_cache = nullptr ;
constexpr VersionSet * version_set = nullptr ;
VersionBuilder builder ( env_options , & ioptions_ , table_cache , & vstorage_ ,
version_set ) ;
ASSERT_OK ( builder . Apply ( & edit ) ) ;
ASSERT_OK ( builder . Apply ( & edit2 ) ) ;
constexpr bool force_consistency_checks = true ;
VersionStorageInfo new_vstorage ( & icmp_ , ucmp_ , options_ . num_levels ,
kCompactionStyleLevel , & vstorage_ ,
force_consistency_checks ) ;
ASSERT_OK ( builder . SaveTo ( & new_vstorage ) ) ;
{
const auto & blob_files = new_vstorage . GetBlobFiles ( ) ;
ASSERT_EQ ( blob_files . size ( ) , 5 ) ;
const std : : vector < BlobFileMetaData : : LinkedSsts > expected_linked_ssts {
{ 1 , 21 } , { 2 , 7 } , { 3 , 8 } , { 4 , 9 } , { 5 , 10 } } ;
for ( size_t i = 0 ; i < 5 ; + + i ) {
const auto meta =
GetBlobFileMetaData ( blob_files , /* blob_file_number */ i + 1 ) ;
ASSERT_NE ( meta , nullptr ) ;
ASSERT_EQ ( meta - > GetLinkedSsts ( ) , expected_linked_ssts [ i ] ) ;
}
// Make sure that no new BlobFileMetaData got created for the blob file
// affected by the trivial move.
ASSERT_EQ ( GetBlobFileMetaData ( blob_files , /* blob_file_number */ 3 ) , meta3 ) ;
}
UnrefFilesInVersion ( & new_vstorage ) ;
}
TEST_F ( VersionBuilderTest , CheckConsistencyForFileDeletedTwice ) {
Add ( 0 , 1U , " 150 " , " 200 " , 100U ) ;
UpdateVersionStorageInfo ( ) ;