@ -39,7 +39,8 @@ class ExternalSSTFileBasicTest : public DBTestBase {
Status GenerateAndAddExternalFile (
const Options options , std : : vector < int > keys ,
const std : : vector < ValueType > & value_types , int file_id ,
const std : : vector < ValueType > & value_types ,
std : : vector < std : : pair < int , int > > range_deletions , int file_id ,
std : : map < std : : string , std : : string > * true_data ) {
assert ( value_types . size ( ) = = 1 | | keys . size ( ) = = value_types . size ( ) ) ;
std : : string file_path = sst_files_dir_ + ToString ( file_id ) ;
@ -49,6 +50,29 @@ class ExternalSSTFileBasicTest : public DBTestBase {
if ( ! s . ok ( ) ) {
return s ;
}
for ( size_t i = 0 ; i < range_deletions . size ( ) ; i + + ) {
// Account for the effect of range deletions on true_data before
// all point operators, even though sst_file_writer.DeleteRange
// must be called before other sst_file_writer methods. This is
// because point writes take precedence over range deletions
// in the same ingested sst.
std : : string start_key = Key ( range_deletions [ i ] . first ) ;
std : : string end_key = Key ( range_deletions [ i ] . second ) ;
s = sst_file_writer . DeleteRange ( start_key , end_key ) ;
if ( ! s . ok ( ) ) {
sst_file_writer . Finish ( ) ;
return s ;
}
auto start_key_it = true_data - > find ( start_key ) ;
if ( start_key_it = = true_data - > end ( ) ) {
start_key_it = true_data - > upper_bound ( start_key ) ;
}
auto end_key_it = true_data - > find ( end_key ) ;
if ( end_key_it = = true_data - > end ( ) ) {
end_key_it = true_data - > upper_bound ( end_key ) ;
}
true_data - > erase ( start_key_it , end_key_it ) ;
}
for ( size_t i = 0 ; i < keys . size ( ) ; i + + ) {
std : : string key = Key ( keys [ i ] ) ;
std : : string value = Key ( keys [ i ] ) + ToString ( file_id ) ;
@ -86,6 +110,14 @@ class ExternalSSTFileBasicTest : public DBTestBase {
return s ;
}
Status GenerateAndAddExternalFile (
const Options options , std : : vector < int > keys ,
const std : : vector < ValueType > & value_types , int file_id ,
std : : map < std : : string , std : : string > * true_data ) {
return GenerateAndAddExternalFile ( options , keys , value_types , { } , file_id ,
true_data ) ;
}
Status GenerateAndAddExternalFile (
const Options options , std : : vector < int > keys , const ValueType value_type ,
int file_id , std : : map < std : : string , std : : string > * true_data ) {
@ -126,9 +158,14 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
ASSERT_EQ ( file1_info . num_entries , 100 ) ;
ASSERT_EQ ( file1_info . smallest_key , Key ( 0 ) ) ;
ASSERT_EQ ( file1_info . largest_key , Key ( 99 ) ) ;
ASSERT_EQ ( file1_info . num_range_del_entries , 0 ) ;
ASSERT_EQ ( file1_info . smallest_range_del_key , " " ) ;
ASSERT_EQ ( file1_info . largest_range_del_key , " " ) ;
// sst_file_writer already finished, cannot add this value
s = sst_file_writer . Put ( Key ( 100 ) , " bad_val " ) ;
ASSERT_FALSE ( s . ok ( ) ) < < s . ToString ( ) ;
s = sst_file_writer . DeleteRange ( Key ( 100 ) , Key ( 200 ) ) ;
ASSERT_FALSE ( s . ok ( ) ) < < s . ToString ( ) ;
DestroyAndReopen ( options ) ;
// Add file using file path
@ -189,6 +226,7 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
ASSERT_EQ ( file3_info . num_entries , 15 ) ;
ASSERT_EQ ( file3_info . smallest_key , Key ( 110 ) ) ;
ASSERT_EQ ( file3_info . largest_key , Key ( 124 ) ) ;
s = DeprecatedAddFile ( { file1 } , true /* move file */ ) ;
ASSERT_TRUE ( s . ok ( ) ) < < s . ToString ( ) ;
ASSERT_EQ ( Status : : NotFound ( ) , env_ - > FileExists ( file1 ) ) ;
@ -197,8 +235,8 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
ASSERT_TRUE ( s . ok ( ) ) < < s . ToString ( ) ;
ASSERT_OK ( env_ - > FileExists ( file2 ) ) ;
// This file have overlapping values with the existing data
s = DeprecatedAddFile ( { file2 } , true /* move file */ ) ;
// This file has overlapping values with the existing data
s = DeprecatedAddFile ( { file3 } , true /* move file */ ) ;
ASSERT_FALSE ( s . ok ( ) ) < < s . ToString ( ) ;
ASSERT_OK ( env_ - > FileExists ( file3 ) ) ;
@ -218,34 +256,33 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 1 , 2 , 3 , 4 , 5 , 6 } ,
ValueType : : kTypeValue , file_id + + ,
& true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 10 , 11 , 12 , 13 } ,
ValueType : : kTypeValue , file_id + + ,
& true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 4 , 6 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 11 , 15 , 19 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 120 , 130 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 130 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 3 ) ;
// Write some keys through normal write path
@ -257,17 +294,17 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 60 , 61 , 62 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 40 , 41 , 42 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 20 , 30 , 40 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 2 ) ;
const Snapshot * snapshot = db_ - > GetSnapshot ( ) ;
@ -314,37 +351,54 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 1 , 2 , 3 , 4 , 5 , 6 } ,
ValueType : : kTypeValue , file_id + + ,
& true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 10 , 11 , 12 , 13 } ,
ValueType : : kTypeValue , file_id + + ,
& true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 4 , 6 } , ValueType : : kTypeMerge , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 11 , 15 , 19 } ,
ValueType : : kTypeDeletion , file_id + + ,
& true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 120 , 130 } , ValueType : : kTypeMerge , file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 130 } , ValueType : : kTypeDeletion , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 3 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 120 } ,
{ ValueType : : kTypeValue } , { { 120 , 135 } } ,
file_id + + , & true_data ) ) ;
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 4 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { } , { } , { { 110 , 120 } } ,
file_id + + , & true_data ) ) ;
// The range deletion ends on a key, but it doesn't actually delete
// this key because the largest key in the range is exclusive. Still,
// it counts as an overlap so a new seqno will be assigned.
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 5 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { } , { } , { { 100 , 109 } } ,
file_id + + , & true_data ) ) ;
// File doesn't overwrite any keys, no seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 5 ) ;
// Write some keys through normal write path
for ( int i = 0 ; i < 50 ; i + + ) {
ASSERT_OK ( Put ( Key ( i ) , " memtable " ) ) ;
@ -354,18 +408,18 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 60 , 61 , 62 } , ValueType : : kTypeValue , file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 40 , 41 , 42 } , ValueType : : kTypeMerge , file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile ( options , { 20 , 30 , 40 } ,
ValueType : : kTypeDeletion , file_id + + ,
& true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 2 ) ;
const Snapshot * snapshot = db_ - > GetSnapshot ( ) ;
@ -414,7 +468,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
{ ValueType : : kTypeValue , ValueType : : kTypeMerge , ValueType : : kTypeValue ,
ValueType : : kTypeMerge , ValueType : : kTypeValue , ValueType : : kTypeMerge } ,
file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
@ -422,35 +476,58 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
{ ValueType : : kTypeValue , ValueType : : kTypeMerge , ValueType : : kTypeValue ,
ValueType : : kTypeMerge } ,
file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 0 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 4 , 6 } , { ValueType : : kTypeDeletion , ValueType : : kTypeValue ,
ValueType : : kTypeMerge } ,
file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 11 , 15 , 19 } , { ValueType : : kTypeDeletion , ValueType : : kTypeMerge ,
ValueType : : kTypeValue } ,
file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 120 , 130 } , { ValueType : : kTypeValue , ValueType : : kTypeMerge } ,
file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 2 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 1 , 130 } , { ValueType : : kTypeMerge , ValueType : : kTypeDeletion } ,
file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 3 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 150 , 151 , 152 } ,
{ ValueType : : kTypeValue , ValueType : : kTypeMerge ,
ValueType : : kTypeDeletion } ,
{ { 150 , 160 } , { 180 , 190 } } , file_id + + , & true_data ) ) ;
// File doesn't overwrite any keys, no seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 3 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 150 , 151 , 152 } ,
{ ValueType : : kTypeValue , ValueType : : kTypeMerge , ValueType : : kTypeValue } ,
{ { 200 , 250 } } , file_id + + , & true_data ) ) ;
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 4 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 300 , 301 , 302 } ,
{ ValueType : : kTypeValue , ValueType : : kTypeMerge ,
ValueType : : kTypeDeletion } ,
{ { 1 , 2 } , { 152 , 154 } } , file_id + + , & true_data ) ) ;
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , 5 ) ;
// Write some keys through normal write path
for ( int i = 0 ; i < 50 ; i + + ) {
ASSERT_OK ( Put ( Key ( i ) , " memtable " ) ) ;
@ -462,14 +539,15 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
options , { 60 , 61 , 62 } ,
{ ValueType : : kTypeValue , ValueType : : kTypeMerge , ValueType : : kTypeValue } ,
file_id + + , & true_data ) ) ;
// File dont overwrite any keys, N o seqno needed
// File doesn't overwrite any keys, n o seqno needed
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 40 , 41 , 42 } , { ValueType : : kTypeValue , ValueType : : kTypeDeletion ,
options , { 40 , 41 , 42 } ,
{ ValueType : : kTypeValue , ValueType : : kTypeDeletion ,
ValueType : : kTypeDeletion } ,
file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 1 ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
@ -477,7 +555,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
{ ValueType : : kTypeDeletion , ValueType : : kTypeDeletion ,
ValueType : : kTypeDeletion } ,
file_id + + , & true_data ) ) ;
// File overwrite some keys, a seqno will be assigned
// File overwrites some keys, a seqno will be assigned
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno + 2 ) ;
const Snapshot * snapshot = db_ - > GetSnapshot ( ) ;
@ -589,7 +667,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
SequenceNumber last_seqno = dbfull ( ) - > GetLatestSequenceNumber ( ) ;
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 60 , 90 } , { ValueType : : kTypeValue , ValueType : : kTypeValue } ,
file_id + + , & true_data ) ) ;
{ { 65 , 70 } , { 70 , 85 } } , file_id + + , & true_data ) ) ;
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , + + last_seqno ) ;
ASSERT_EQ ( 2 , NumTableFilesAtLevel ( 0 ) ) ;
ASSERT_EQ ( 0 , NumTableFilesAtLevel ( kNumLevels - 2 ) ) ;
@ -605,6 +683,16 @@ TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( kNumLevels - 2 ) ) ;
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( options . num_levels - 1 ) ) ;
// overlaps with L5 file but not memtable or L0 file, so flush is skipped and
// file is ingested into L4
ASSERT_OK ( GenerateAndAddExternalFile ( options , { } , { } , { { 5 , 15 } } , file_id + + ,
& true_data ) ) ;
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , + + last_seqno ) ;
ASSERT_EQ ( 2 , NumTableFilesAtLevel ( 0 ) ) ;
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( kNumLevels - 2 ) ) ;
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( options . num_levels - 2 ) ) ;
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( options . num_levels - 1 ) ) ;
// ingested file overlaps with memtable, so flush is triggered before the file
// is ingested such that the ingested data is considered newest. So L0 file
// count increases by two.
@ -623,7 +711,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
// seqnum.
ASSERT_OK ( GenerateAndAddExternalFile (
options , { 151 , 175 } , { ValueType : : kTypeValue , ValueType : : kTypeValue } ,
file_id + + , & true_data ) ) ;
{ { 160 , 200 } } , file_id + + , & true_data ) ) ;
ASSERT_EQ ( dbfull ( ) - > GetLatestSequenceNumber ( ) , last_seqno ) ;
ASSERT_EQ ( 4 , NumTableFilesAtLevel ( 0 ) ) ;
ASSERT_EQ ( 1 , NumTableFilesAtLevel ( kNumLevels - 2 ) ) ;