@@ -36,6 +36,35 @@ namespace engine {
3636
3737using rocksdb::Slice;
3838
39+ namespace {
40+
41+ bool IsExpiredHashFieldSubkey (const HashMetadata &metadata, Slice value, uint64_t now, bool *expired) {
42+ *expired = false ;
43+ if (!metadata.IsFieldExpirationEncoding ()) {
44+ return true ;
45+ }
46+
47+ uint64_t expire = 0 ;
48+ auto s = metadata.DecodeSubkeyValue (&value, &expire);
49+ if (!s.ok ()) {
50+ return false ;
51+ }
52+
53+ *expired = expire != 0 && expire < now;
54+ return true ;
55+ }
56+
57+ bool HasHashFieldExpirationCandidates (const HashMetadata &metadata) {
58+ return metadata.IsFieldExpirationEncoding () && metadata.size > metadata.persist ;
59+ }
60+
61+ bool IsHashFieldExpirationMetadataExpired (const HashMetadata &metadata, uint64_t now) {
62+ return HasHashFieldExpirationCandidates (metadata) && metadata.persist == 0 && metadata.upper != 0 &&
63+ metadata.upper < now;
64+ }
65+
66+ } // namespace
67+
3968bool MetadataFilter::Filter ([[maybe_unused]] int level, const Slice &key, const Slice &value,
4069 [[maybe_unused]] std::string *new_value, [[maybe_unused]] bool *modified) const {
4170 Metadata metadata (kRedisNone , false );
@@ -45,9 +74,22 @@ bool MetadataFilter::Filter([[maybe_unused]] int level, const Slice &key, const
4574 WARN (" [compact_filter/metadata] Failed to decode, namespace: {}, key: {}, err: {}" , ns, user_key, s.ToString ());
4675 return false ;
4776 }
77+
78+ bool expired = metadata.Expired ();
79+ if (!expired && metadata.Type () == kRedisHash ) {
80+ HashMetadata hash_metadata (false );
81+ Slice input (value);
82+ if (s = hash_metadata.Decode (&input); !s.ok ()) {
83+ WARN (" [compact_filter/metadata] Failed to decode hash metadata, namespace: {}, key: {}, err: {}" , ns, user_key,
84+ s.ToString ());
85+ return false ;
86+ }
87+ expired = IsHashFieldExpirationMetadataExpired (hash_metadata, util::GetTimeStampMS ());
88+ }
89+
4890 DEBUG (" [compact_filter/metadata] namespace: {}, key: {}, result: {}" , ns, user_key,
49- (metadata. Expired () ? " deleted" : " reserved" ));
50- return metadata. Expired () ;
91+ (expired ? " deleted" : " reserved" ));
92+ return expired ;
5193}
5294
5395Status SubKeyFilter::GetMetadata (const InternalKey &ikey, Metadata *metadata) const {
@@ -114,6 +156,26 @@ rocksdb::CompactionFilter::Decision SubKeyFilter::FilterBlobByKey([[maybe_unused
114156 if (metadata.Type () == kRedisBitmap || metadata.Type () == kRedisTimeSeries ) {
115157 return rocksdb::CompactionFilter::Decision::kUndetermined ;
116158 }
159+ if (metadata.Type () == kRedisHash ) {
160+ if (IsMetadataExpired (ikey, metadata)) {
161+ return rocksdb::CompactionFilter::Decision::kRemove ;
162+ }
163+
164+ HashMetadata hash_metadata (false );
165+ Slice input (cached_metadata_);
166+ if (auto s = hash_metadata.Decode (&input); !s.ok ()) {
167+ ERROR (" [compact_filter/subkey] Failed to decode hash metadata, namespace: {}, key: {}, err: {}" ,
168+ ikey.GetNamespace (), ikey.GetKey (), s.ToString ());
169+ return rocksdb::CompactionFilter::Decision::kKeep ;
170+ }
171+ if (IsHashFieldExpirationMetadataExpired (hash_metadata, util::GetTimeStampMS ())) {
172+ return rocksdb::CompactionFilter::Decision::kRemove ;
173+ }
174+ if (HasHashFieldExpirationCandidates (hash_metadata)) {
175+ return rocksdb::CompactionFilter::Decision::kUndetermined ;
176+ }
177+ return rocksdb::CompactionFilter::Decision::kKeep ;
178+ }
117179
118180 bool result = IsMetadataExpired (ikey, metadata);
119181 return result ? rocksdb::CompactionFilter::Decision::kRemove : rocksdb::CompactionFilter::Decision::kKeep ;
@@ -154,7 +216,38 @@ bool SubKeyFilter::Filter([[maybe_unused]] int level, const Slice &key, const Sl
154216 return expired;
155217 }
156218
157- return IsMetadataExpired (ikey, metadata) || (metadata.Type () == kRedisBitmap && redis::Bitmap::IsEmptySegment (value));
219+ bool metadata_expired = IsMetadataExpired (ikey, metadata);
220+ if (metadata_expired) {
221+ return true ;
222+ }
223+
224+ if (metadata.Type () == kRedisHash ) {
225+ HashMetadata hash_metadata (false );
226+ Slice input (cached_metadata_);
227+ auto s = hash_metadata.Decode (&input);
228+ if (!s.ok ()) {
229+ ERROR (" [compact_filter/subkey] Failed to decode hash metadata, namespace: {}, key: {}, err: {}" ,
230+ ikey.GetNamespace (), ikey.GetKey (), s.ToString ());
231+ return false ;
232+ }
233+ auto now = util::GetTimeStampMS ();
234+ if (IsHashFieldExpirationMetadataExpired (hash_metadata, now)) {
235+ return true ;
236+ }
237+ if (!HasHashFieldExpirationCandidates (hash_metadata)) {
238+ return false ;
239+ }
240+
241+ bool field_expired = false ;
242+ if (!IsExpiredHashFieldSubkey (hash_metadata, value, now, &field_expired)) {
243+ ERROR (" [compact_filter/subkey] Failed to decode hash subkey value, namespace: {}, key: {}" , ikey.GetNamespace (),
244+ ikey.GetKey ());
245+ return false ;
246+ }
247+ return field_expired;
248+ }
249+
250+ return metadata.Type () == kRedisBitmap && redis::Bitmap::IsEmptySegment (value);
158251}
159252
160253bool SearchFilter::Filter ([[maybe_unused]] int level, const Slice &key, [[maybe_unused]] const Slice &value,
0 commit comments