@ -9,6 +9,7 @@
# include "db/seqno_to_time_mapping.h"
# include "port/stack_trace.h"
# include "rocksdb/iostats_context.h"
# include "rocksdb/utilities/debug.h"
# include "test_util/mock_time_env.h"
# ifndef ROCKSDB_LITE
@ -37,7 +38,7 @@ class SeqnoTimeTest : public DBTestBase {
}
// make sure the file is not in cache, otherwise it won't have IO info
void AssertKet Temperature ( int key_id , Temperature expected_temperature ) {
void AssertKey Temperature ( int key_id , Temperature expected_temperature ) {
get_iostats_context ( ) - > Reset ( ) ;
IOStatsContext * iostats = get_iostats_context ( ) ;
std : : string result = Get ( Key ( key_id ) ) ;
@ -101,7 +102,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
ASSERT_EQ ( GetSstSizeHelper ( Temperature : : kCold ) , 0 ) ;
// read a random key, which should be hot (kUnknown)
AssertKet Temperature ( 20 , Temperature : : kUnknown ) ;
AssertKey Temperature ( 20 , Temperature : : kUnknown ) ;
// Write more data, but still all hot until the 10th SST, as:
// write a key every 10 seconds, 100 keys per SST, each SST takes 1000 seconds
@ -139,7 +140,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
ASSERT_GT ( hot_data_size , 0 ) ;
ASSERT_GT ( cold_data_size , 0 ) ;
// the first a few key should be cold
AssertKet Temperature ( 20 , Temperature : : kCold ) ;
AssertKey Temperature ( 20 , Temperature : : kCold ) ;
for ( int i = 0 ; i < 30 ; i + + ) {
dbfull ( ) - > TEST_WaitForPeridicTaskRun ( [ & ] {
@ -148,8 +149,8 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
ASSERT_OK ( db_ - > CompactRange ( cro , nullptr , nullptr ) ) ;
// the hot/cold data cut off range should be between i * 20 + 200 -> 250
AssertKet Temperature ( i * 20 + 250 , Temperature : : kUnknown ) ;
AssertKet Temperature ( i * 20 + 200 , Temperature : : kCold ) ;
AssertKey Temperature ( i * 20 + 250 , Temperature : : kUnknown ) ;
AssertKey Temperature ( i * 20 + 200 , Temperature : : kCold ) ;
}
ASSERT_LT ( GetSstSizeHelper ( Temperature : : kUnknown ) , hot_data_size ) ;
@ -166,7 +167,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
}
// any random data close to the end should be cold
AssertKet Temperature ( 1000 , Temperature : : kCold ) ;
AssertKey Temperature ( 1000 , Temperature : : kCold ) ;
// close explicitly, because the env is local variable which will be released
// first.
@ -215,7 +216,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
ASSERT_EQ ( GetSstSizeHelper ( Temperature : : kCold ) , 0 ) ;
// read a random key, which should be hot (kUnknown)
AssertKet Temperature ( 20 , Temperature : : kUnknown ) ;
AssertKey Temperature ( 20 , Temperature : : kUnknown ) ;
// Adding more data to have mixed hot and cold data
for ( ; sst_num < 14 ; sst_num + + ) {
@ -237,7 +238,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
ASSERT_GT ( hot_data_size , 0 ) ;
ASSERT_GT ( cold_data_size , 0 ) ;
// the first a few key should be cold
AssertKet Temperature ( 20 , Temperature : : kCold ) ;
AssertKey Temperature ( 20 , Temperature : : kCold ) ;
// Wait some time, with each wait, the cold data is increasing and hot data is
// decreasing
@ -253,8 +254,8 @@ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
ASSERT_GT ( cold_data_size , pre_cold ) ;
// the hot/cold cut_off key should be around i * 20 + 400 -> 450
AssertKet Temperature ( i * 20 + 450 , Temperature : : kUnknown ) ;
AssertKet Temperature ( i * 20 + 400 , Temperature : : kCold ) ;
AssertKey Temperature ( i * 20 + 450 , Temperature : : kUnknown ) ;
AssertKey Temperature ( i * 20 + 400 , Temperature : : kCold ) ;
}
// Wait again, the most of the data should be cold after that
@ -267,14 +268,53 @@ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
}
// any random data close to the end should be cold
AssertKet Temperature ( 1000 , Temperature : : kCold ) ;
AssertKey Temperature ( 1000 , Temperature : : kCold ) ;
Close ( ) ;
}
TEST_F ( SeqnoTimeTest , BasicSeqnoToTimeMapping ) {
enum class SeqnoTimeTestType : char {
kTrackInternalTimeSeconds = 0 ,
kPrecludeLastLevel = 1 ,
kBothSetTrackSmaller = 2 ,
} ;
class SeqnoTimeTablePropTest
: public SeqnoTimeTest ,
public : : testing : : WithParamInterface < SeqnoTimeTestType > {
public :
SeqnoTimeTablePropTest ( ) : SeqnoTimeTest ( ) { }
void SetTrackTimeDurationOptions ( uint64_t track_time_duration ,
Options & options ) const {
// either option set will enable the time tracking feature
switch ( GetParam ( ) ) {
case SeqnoTimeTestType : : kTrackInternalTimeSeconds :
options . preclude_last_level_data_seconds = 0 ;
options . preserve_internal_time_seconds = track_time_duration ;
break ;
case SeqnoTimeTestType : : kPrecludeLastLevel :
options . preclude_last_level_data_seconds = track_time_duration ;
options . preserve_internal_time_seconds = 0 ;
break ;
case SeqnoTimeTestType : : kBothSetTrackSmaller :
options . preclude_last_level_data_seconds = track_time_duration ;
options . preserve_internal_time_seconds = track_time_duration / 10 ;
break ;
}
}
} ;
INSTANTIATE_TEST_CASE_P (
SeqnoTimeTablePropTest , SeqnoTimeTablePropTest ,
: : testing : : Values ( SeqnoTimeTestType : : kTrackInternalTimeSeconds ,
SeqnoTimeTestType : : kPrecludeLastLevel ,
SeqnoTimeTestType : : kBothSetTrackSmaller ) ) ;
TEST_P ( SeqnoTimeTablePropTest , BasicSeqnoToTimeMapping ) {
Options options = CurrentOptions ( ) ;
options . preclude_last_level_data_seconds = 10000 ;
SetTrackTimeDurationOptions ( 10000 , options ) ;
options . env = mock_env_ . get ( ) ;
options . disable_auto_compactions = true ;
DestroyAndReopen ( options ) ;
@ -297,6 +337,8 @@ TEST_F(SeqnoTimeTest, BasicSeqnoToTimeMapping) {
ASSERT_OK ( tp_mapping . Sort ( ) ) ;
ASSERT_FALSE ( tp_mapping . Empty ( ) ) ;
auto seqs = tp_mapping . TEST_GetInternalMapping ( ) ;
// about ~20 seqs->time entries, because the sample rate is 10000/100, and it
// passes 2k time.
ASSERT_GE ( seqs . size ( ) , 19 ) ;
ASSERT_LE ( seqs . size ( ) , 21 ) ;
SequenceNumber seq_end = dbfull ( ) - > GetLatestSequenceNumber ( ) ;
@ -444,7 +486,8 @@ TEST_F(SeqnoTimeTest, BasicSeqnoToTimeMapping) {
ASSERT_LE ( seqs . size ( ) , 101 ) ;
for ( auto i = start_seq ; i < seq_end - 99 ; i + + ) {
// likely the first 100 entries reports 0
ASSERT_LE ( tp_mapping . GetOldestApproximateTime ( i ) , ( i - start_seq ) + 3000 ) ;
ASSERT_LE ( tp_mapping . GetOldestApproximateTime ( i ) ,
( i - start_seq ) * 100 + 50000 ) ;
}
start_seq + = 101 ;
@ -457,9 +500,10 @@ TEST_F(SeqnoTimeTest, BasicSeqnoToTimeMapping) {
ASSERT_OK ( db_ - > Close ( ) ) ;
}
TEST_F ( SeqnoTimeTest , MultiCFs ) {
TEST_P ( SeqnoTimeTableProp Test , MultiCFs ) {
Options options = CurrentOptions ( ) ;
options . preclude_last_level_data_seconds = 0 ;
options . preserve_internal_time_seconds = 0 ;
options . env = mock_env_ . get ( ) ;
options . stats_dump_period_sec = 0 ;
options . stats_persist_period_sec = 0 ;
@ -485,7 +529,7 @@ TEST_F(SeqnoTimeTest, MultiCFs) {
ASSERT_TRUE ( dbfull ( ) - > TEST_GetSeqnoToTimeMapping ( ) . Empty ( ) ) ;
Options options_1 = options ;
options_1 . preclude_last_level_data_seconds = 10000 ; // 10k
SetTrackTimeDurationOptions ( 10000 , options_1 ) ;
CreateColumnFamilies ( { " one " } , options_1 ) ;
ASSERT_TRUE ( scheduler . TEST_HasTask ( PeriodicTaskType : : kRecordSeqnoTime ) ) ;
@ -514,11 +558,11 @@ TEST_F(SeqnoTimeTest, MultiCFs) {
ASSERT_FALSE ( tp_mapping . Empty ( ) ) ;
auto seqs = tp_mapping . TEST_GetInternalMapping ( ) ;
ASSERT_GE ( seqs . size ( ) , 1 ) ;
ASSERT_LE ( seqs . size ( ) , 3 ) ;
ASSERT_LE ( seqs . size ( ) , 4 ) ;
// Create one more CF with larger preclude_last_level time
Options options_2 = options ;
options_2 . preclude_last_level_data_seconds = 1000000 ; // 1m
SetTrackTimeDurationOptions ( 1000000 , options_2 ) ; // 1m
CreateColumnFamilies ( { " two " } , options_2 ) ;
// Add more data to CF "two" to fill the in memory mapping
@ -618,11 +662,11 @@ TEST_F(SeqnoTimeTest, MultiCFs) {
Close ( ) ;
}
TEST_F ( SeqnoTimeTest , MultiInstancesBasic ) {
TEST_P ( SeqnoTimeTableProp Test , MultiInstancesBasic ) {
const int kInstanceNum = 2 ;
Options options = CurrentOptions ( ) ;
options . preclude_last_level_data_seconds = 10000 ;
SetTrackTimeDurationOptions ( 10000 , options ) ;
options . env = mock_env_ . get ( ) ;
options . stats_dump_period_sec = 0 ;
options . stats_persist_period_sec = 0 ;
@ -650,17 +694,32 @@ TEST_F(SeqnoTimeTest, MultiInstancesBasic) {
}
}
TEST_F ( SeqnoTimeTest , SeqnoToTimeMappingUniversal ) {
TEST_P ( SeqnoTimeTablePropTest , SeqnoToTimeMappingUniversal ) {
const int kNumTrigger = 4 ;
const int kNumLevels = 7 ;
const int kNumKeys = 100 ;
Options options = CurrentOptions ( ) ;
SetTrackTimeDurationOptions ( 10000 , options ) ;
options . compaction_style = kCompactionStyleUniversal ;
options . preclude_last_level_data_seconds = 10000 ;
options . num_levels = kNumLevels ;
options . env = mock_env_ . get ( ) ;
DestroyAndReopen ( options ) ;
for ( int j = 0 ; j < 3 ; j + + ) {
for ( int i = 0 ; i < 100 ; i + + ) {
ASSERT_OK ( Put ( Key ( i ) , " value " ) ) ;
std : : atomic_uint64_t num_seqno_zeroing { 0 } ;
SyncPoint : : GetInstance ( ) - > DisableProcessing ( ) ;
SyncPoint : : GetInstance ( ) - > ClearAllCallBacks ( ) ;
SyncPoint : : GetInstance ( ) - > SetCallBack (
" CompactionIterator::PrepareOutput:ZeroingSeq " ,
[ & ] ( void * /*arg*/ ) { num_seqno_zeroing + + ; } ) ;
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
int sst_num = 0 ;
for ( ; sst_num < kNumTrigger - 1 ; sst_num + + ) {
for ( int i = 0 ; i < kNumKeys ; i + + ) {
ASSERT_OK ( Put ( Key ( sst_num * ( kNumKeys - 1 ) + i ) , " value " ) ) ;
dbfull ( ) - > TEST_WaitForPeridicTaskRun (
[ & ] { mock_clock_ - > MockSleepForSeconds ( static_cast < int > ( 10 ) ) ; } ) ;
}
@ -681,11 +740,12 @@ TEST_F(SeqnoTimeTest, SeqnoToTimeMappingUniversal) {
}
// Trigger a compaction
for ( int i = 0 ; i < 100 ; i + + ) {
ASSERT_OK ( Put ( Key ( i ) , " value " ) ) ;
for ( int i = 0 ; i < kNumKeys ; i + + ) {
ASSERT_OK ( Put ( Key ( sst_num * ( kNumKeys - 1 ) + i ) , " value " ) ) ;
dbfull ( ) - > TEST_WaitForPeridicTaskRun (
[ & ] { mock_clock_ - > MockSleepForSeconds ( static_cast < int > ( 10 ) ) ; } ) ;
}
sst_num + + ;
ASSERT_OK ( Flush ( ) ) ;
ASSERT_OK ( dbfull ( ) - > TEST_WaitForCompact ( ) ) ;
tables_props . clear ( ) ;
@ -696,6 +756,73 @@ TEST_F(SeqnoTimeTest, SeqnoToTimeMappingUniversal) {
SeqnoToTimeMapping tp_mapping ;
ASSERT_FALSE ( it - > second - > seqno_to_time_mapping . empty ( ) ) ;
ASSERT_OK ( tp_mapping . Add ( it - > second - > seqno_to_time_mapping ) ) ;
// compact to the last level
CompactRangeOptions cro ;
cro . bottommost_level_compaction = BottommostLevelCompaction : : kForce ;
ASSERT_OK ( db_ - > CompactRange ( cro , nullptr , nullptr ) ) ;
// make sure the data is all compacted to penultimate level if the feature is
// on, otherwise, compacted to the last level.
if ( options . preclude_last_level_data_seconds > 0 ) {
ASSERT_GT ( NumTableFilesAtLevel ( 5 ) , 0 ) ;
ASSERT_EQ ( NumTableFilesAtLevel ( 6 ) , 0 ) ;
} else {
ASSERT_EQ ( NumTableFilesAtLevel ( 5 ) , 0 ) ;
ASSERT_GT ( NumTableFilesAtLevel ( 6 ) , 0 ) ;
}
// regardless the file is on the last level or not, it should keep the time
// information and sequence number are not set
tables_props . clear ( ) ;
tp_mapping . Clear ( ) ;
ASSERT_OK ( dbfull ( ) - > GetPropertiesOfAllTables ( & tables_props ) ) ;
ASSERT_EQ ( tables_props . size ( ) , 1 ) ;
ASSERT_EQ ( num_seqno_zeroing , 0 ) ;
it = tables_props . begin ( ) ;
ASSERT_FALSE ( it - > second - > seqno_to_time_mapping . empty ( ) ) ;
ASSERT_OK ( tp_mapping . Add ( it - > second - > seqno_to_time_mapping ) ) ;
// make half of the data expired
mock_clock_ - > MockSleepForSeconds ( static_cast < int > ( 8000 ) ) ;
ASSERT_OK ( db_ - > CompactRange ( cro , nullptr , nullptr ) ) ;
tables_props . clear ( ) ;
tp_mapping . Clear ( ) ;
ASSERT_OK ( dbfull ( ) - > GetPropertiesOfAllTables ( & tables_props ) ) ;
if ( options . preclude_last_level_data_seconds > 0 ) {
ASSERT_EQ ( tables_props . size ( ) , 2 ) ;
} else {
ASSERT_EQ ( tables_props . size ( ) , 1 ) ;
}
ASSERT_GT ( num_seqno_zeroing , 0 ) ;
std : : vector < KeyVersion > key_versions ;
ASSERT_OK ( GetAllKeyVersions ( db_ , Slice ( ) , Slice ( ) ,
std : : numeric_limits < size_t > : : max ( ) ,
& key_versions ) ) ;
// make sure there're more than 300 keys and first 100 keys are having seqno
// zeroed out, the last 100 key seqno not zeroed out
ASSERT_GT ( key_versions . size ( ) , 300 ) ;
for ( int i = 0 ; i < 100 ; i + + ) {
ASSERT_EQ ( key_versions [ i ] . sequence , 0 ) ;
}
auto rit = key_versions . rbegin ( ) ;
for ( int i = 0 ; i < 100 ; i + + ) {
ASSERT_GT ( rit - > sequence , 0 ) ;
rit + + ;
}
// make all data expired and compact again to push it to the last level
// regardless if the tiering feature is enabled or not
mock_clock_ - > MockSleepForSeconds ( static_cast < int > ( 20000 ) ) ;
ASSERT_OK ( db_ - > CompactRange ( cro , nullptr , nullptr ) ) ;
ASSERT_GT ( num_seqno_zeroing , 0 ) ;
ASSERT_GT ( NumTableFilesAtLevel ( 6 ) , 0 ) ;
Close ( ) ;
}