|
30 | 30 | #include "storage/redis_metadata.h" |
31 | 31 | #include "time_util.h" |
32 | 32 | #include "types/redis_bitmap.h" |
| 33 | +#include "types/redis_hash.h" |
33 | 34 | #include "types/redis_timeseries.h" |
34 | 35 |
|
35 | 36 | namespace engine { |
@@ -152,6 +153,48 @@ bool SubKeyFilter::Filter([[maybe_unused]] int level, const Slice &key, const Sl |
152 | 153 | return false; |
153 | 154 | } |
154 | 155 | return expired; |
| 156 | + } else if (metadata.Type() == kRedisHash) { |
| 157 | + auto md_expired = IsMetadataExpired(ikey, metadata); |
| 158 | + if (md_expired) { |
| 159 | + return true; |
| 160 | + } |
| 161 | + |
| 162 | + auto db = stor_->GetDB(); |
| 163 | + |
| 164 | + // Hash field subkeys come in pairs: |
| 165 | + // 1. Field value key with version = metadata.version |
| 166 | + // 2. Field expire key with version = metadata.version + ExpireVersionOffset (8) |
| 167 | + // They are processed independently in compaction, so we must handle both in Filter to ensure consistency. |
| 168 | + // To avoid orphaning, we check expire time when processing key, but compaction filter handle key one by one, so we |
| 169 | + // have to left the orphaned key exists here. |
| 170 | + if (ikey.GetVersion() == metadata.version) { // when there is an hash field |
| 171 | + std::string ns_key = ComposeNamespaceKey(ikey.GetNamespace(), ikey.GetKey(), stor_->IsSlotIdEncoded()); |
| 172 | + InternalKey expire_ikey(ns_key, ikey.GetSubKey(), metadata.version + ExpireVersionOffset, |
| 173 | + stor_->IsSlotIdEncoded()); |
| 174 | + std::string expire_key = expire_ikey.Encode(); |
| 175 | + std::string expire_value; |
| 176 | + auto expire_status = |
| 177 | + db->Get(rocksdb::ReadOptions(), stor_->GetCFHandle(ColumnFamilyID::PrimarySubkey), expire_key, &expire_value); |
| 178 | + if (expire_status.ok() && expire_value.size() >= sizeof(uint64_t)) { |
| 179 | + uint64_t expire_at = DecodeFixed64(expire_value.data()); |
| 180 | + uint64_t now = util::GetTimeStampMS(); |
| 181 | + if (expire_at < now) { |
| 182 | + return true; |
| 183 | + } |
| 184 | + } |
| 185 | + return false; |
| 186 | + } else if (ikey.GetVersion() == metadata.version + ExpireVersionOffset) { // when there is an hash field expire key |
| 187 | + std::string ns_key = ComposeNamespaceKey(ikey.GetNamespace(), ikey.GetKey(), stor_->IsSlotIdEncoded()); |
| 188 | + InternalKey origin_ikey(ns_key, ikey.GetSubKey(), metadata.version, stor_->IsSlotIdEncoded()); |
| 189 | + std::string origin_key = origin_ikey.Encode(); |
| 190 | + std::string origin_value; |
| 191 | + auto origin_status = |
| 192 | + db->Get(rocksdb::ReadOptions(), stor_->GetCFHandle(ColumnFamilyID::PrimarySubkey), origin_key, &origin_value); |
| 193 | + if (origin_status.IsNotFound()) { |
| 194 | + return true; |
| 195 | + } |
| 196 | + } |
| 197 | + return false; |
155 | 198 | } |
156 | 199 |
|
157 | 200 | return IsMetadataExpired(ikey, metadata) || (metadata.Type() == kRedisBitmap && redis::Bitmap::IsEmptySegment(value)); |
|
0 commit comments