@@ -1010,6 +1010,7 @@ macro_rules! get_db_and_cfs {
10101010#[ cfg( test) ]
10111011mod lifecycle_tests {
10121012 use std:: sync:: { Arc , mpsc} ;
1013+ use std:: thread;
10131014 use std:: time:: Duration ;
10141015
10151016 use super :: {
@@ -1018,8 +1019,11 @@ mod lifecycle_tests {
10181019 use crate :: data_compaction_filter:: {
10191020 CompactionFilterTestGate , install_compaction_filter_test_gate,
10201021 } ;
1022+ use crate :: format_base_key:: BaseMetaKey ;
1023+ use crate :: format_base_meta_value:: ParsedSetsMetaValue ;
10211024 use crate :: { BgTaskHandler , StorageOptions , safe_cleanup_test_db, unique_test_db_path} ;
10221025 use kstd:: lock_mgr:: LockMgr ;
1026+ use rocksdb:: IteratorMode ;
10231027
10241028 fn open_compaction_test_redis ( path : & std:: path:: Path ) -> Redis {
10251029 let mut storage_options = StorageOptions :: default ( ) ;
@@ -1043,6 +1047,94 @@ mod lifecycle_tests {
10431047 redis
10441048 }
10451049
1050+ #[ test]
1051+ fn del_compaction_removes_old_generation_and_preserves_recreated_set ( ) {
1052+ let path = unique_test_db_path ( ) ;
1053+ safe_cleanup_test_db ( & path) ;
1054+
1055+ let mut storage_options = StorageOptions :: default ( ) ;
1056+ storage_options. options . set_disable_auto_compactions ( true ) ;
1057+ let ( bg_task_handler, _) = BgTaskHandler :: new ( ) ;
1058+ let mut redis = Redis :: new (
1059+ Arc :: new ( storage_options) ,
1060+ 0 ,
1061+ Arc :: new ( bg_task_handler) ,
1062+ Arc :: new ( kstd:: lock_mgr:: LockMgr :: new ( 64 ) ) ,
1063+ ) ;
1064+ redis
1065+ . open ( path. to_str ( ) . expect ( "test DB path should be valid UTF-8" ) )
1066+ . expect ( "compaction test Redis should open" ) ;
1067+ let redis = Arc :: new ( redis) ;
1068+
1069+ let key = b"del_compaction_set" ;
1070+ redis
1071+ . sadd ( key, & [ b"old-member" ] )
1072+ . expect ( "initial set write should succeed" ) ;
1073+ let db = redis. db ( ) . expect ( "Redis should own RocksDB" ) ;
1074+ db. flush ( ) . expect ( "initial data should be flushed" ) ;
1075+
1076+ let meta_key = BaseMetaKey :: new ( key) . encode ( ) . unwrap ( ) ;
1077+ let meta_cf = redis. get_cf_handle ( ColumnFamilyIndex :: MetaCF ) . unwrap ( ) ;
1078+ let original_meta = db. get_cf ( & meta_cf, & meta_key) . unwrap ( ) . unwrap ( ) ;
1079+ let original_version = ParsedSetsMetaValue :: new ( & original_meta[ ..] )
1080+ . unwrap ( )
1081+ . version ( ) ;
1082+ let data_cf = redis. get_cf_handle ( ColumnFamilyIndex :: SetsDataCF ) . unwrap ( ) ;
1083+ let old_data_keys: Vec < Vec < u8 > > = db
1084+ . iterator_cf ( & data_cf, IteratorMode :: Start )
1085+ . map ( |entry| entry. unwrap ( ) . 0 . to_vec ( ) )
1086+ . collect ( ) ;
1087+ assert_eq ! ( old_data_keys. len( ) , 1 ) ;
1088+
1089+ assert ! ( redis. del_key( key) . unwrap( ) ) ;
1090+ db. flush ( ) . expect ( "tombstone should be flushed" ) ;
1091+ let tombstone = db. get_cf ( & meta_cf, & meta_key) . unwrap ( ) . unwrap ( ) ;
1092+ let parsed_tombstone = ParsedSetsMetaValue :: new ( & tombstone[ ..] ) . unwrap ( ) ;
1093+ assert_eq ! ( parsed_tombstone. count( ) , 0 ) ;
1094+ assert_eq ! ( parsed_tombstone. etime( ) , 0 ) ;
1095+ assert ! ( parsed_tombstone. version( ) > original_version) ;
1096+
1097+ let gate = CompactionFilterTestGate :: new ( & [ ] ) ;
1098+ let _gate_guard = install_compaction_filter_test_gate ( Arc :: clone ( & gate) ) ;
1099+ let compactor = Arc :: clone ( & redis) ;
1100+ let compaction_thread = thread:: spawn ( move || {
1101+ compactor
1102+ . compact_range ( None , None )
1103+ . expect ( "manual compaction should succeed" ) ;
1104+ } ) ;
1105+ assert ! ( gate. wait_until_entered( Duration :: from_secs( 10 ) ) ) ;
1106+ gate. release ( ) ;
1107+ compaction_thread. join ( ) . unwrap ( ) ;
1108+
1109+ let removed_keys = gate. wait_until_removed ( Duration :: from_secs ( 10 ) ) ;
1110+ assert ! (
1111+ old_data_keys
1112+ . iter( )
1113+ . any( |old_key| removed_keys. contains( old_key) ) ,
1114+ "DataCompactionFilter did not remove the old generation key"
1115+ ) ;
1116+ let remaining_data_keys: Vec < Vec < u8 > > = db
1117+ . iterator_cf ( & data_cf, IteratorMode :: Start )
1118+ . map ( |entry| entry. unwrap ( ) . 0 . to_vec ( ) )
1119+ . collect ( ) ;
1120+ assert ! (
1121+ old_data_keys
1122+ . iter( )
1123+ . all( |old_key| !remaining_data_keys. contains( old_key) ) ,
1124+ "old generation key survived compaction"
1125+ ) ;
1126+
1127+ redis
1128+ . sadd ( key, & [ b"new-member" ] )
1129+ . expect ( "recreated set write should succeed" ) ;
1130+ assert_eq ! ( redis. smembers( key) . unwrap( ) , vec![ "new-member" . to_string( ) ] ) ;
1131+
1132+ drop ( data_cf) ;
1133+ drop ( meta_cf) ;
1134+ drop ( redis) ;
1135+ safe_cleanup_test_db ( & path) ;
1136+ }
1137+
10461138 #[ test]
10471139 fn dropping_last_owner_waits_for_active_compaction_filter_before_reopen ( ) {
10481140 let path = unique_test_db_path ( ) ;
0 commit comments