diff --git a/src/types/redis_hash.cc b/src/types/redis_hash.cc index ccceb04caf3..ef35b216f1c 100644 --- a/src/types/redis_hash.cc +++ b/src/types/redis_hash.cc @@ -47,31 +47,8 @@ rocksdb::Status Hash::Size(engine::Context &ctx, const Slice &user_key, uint64_t HashMetadata metadata(false); rocksdb::Status s = GetMetadata(ctx, ns_key, &metadata); if (!s.ok()) return s; - // if field expiration is disabled, - // the size field in metadata is the length of hash - if (!metadata.IsFieldExpirationEnabled()) { - *size = metadata.size; - return rocksdb::Status::OK(); - } - - // otherwise, we have to check each field to calc the length - std::string prefix_key = InternalKey(ns_key, "", metadata.version, storage_->IsSlotIdEncoded()).Encode(); - std::string next_version_prefix_key = - InternalKey(ns_key, "", metadata.version + 1, storage_->IsSlotIdEncoded()).Encode(); - rocksdb::ReadOptions read_options = ctx.DefaultScanOptions(); - rocksdb::Slice upper_bound(next_version_prefix_key); - read_options.iterate_upper_bound = &upper_bound; - - auto iter = util::UniqueIterator(ctx, read_options); - for (iter->Seek(prefix_key); iter->Valid() && iter->key().starts_with(prefix_key); iter->Next()) { - uint64_t expire = 0; - auto value = iter->value().ToString(); - if (!decodeExpireFromValue(metadata, &value, expire).ok()) { - continue; - } - *size += 1; - } + size += GetValidFieldCount(ctx, ns_key, metadata); return rocksdb::Status::OK(); } @@ -734,6 +711,37 @@ bool Hash::IsFieldExpired(const Slice &metadata_key, const Slice &value) { return expire != 0 && expire < util::GetTimeStampMS(); } +uint64_t Hash::GetValidFieldCount(engine::Context &ctx, const Slice &ns_key, const HashMetadata &metadata) { + if (metadata.Expired()) { + return 0; + } + // if field expiration is disabled, + // the size field in metadata is the length of hash + if (!metadata.IsFieldExpirationEnabled()) { + return metadata.size; + } + + std::string prefix_key = InternalKey(ns_key, "", metadata.version, storage_->IsSlotIdEncoded()).Encode(); + std::string next_version_prefix_key = + InternalKey(ns_key, "", metadata.version + 1, storage_->IsSlotIdEncoded()).Encode(); + + rocksdb::ReadOptions read_options = ctx.DefaultScanOptions(); + rocksdb::Slice upper_bound(next_version_prefix_key); + read_options.iterate_upper_bound = &upper_bound; + + // count the num of expired field + uint64_t expired_field_num = 0; + auto iter = util::UniqueIterator(ctx, read_options); + for (iter->Seek(prefix_key); iter->Valid() && iter->key().starts_with(prefix_key); iter->Next()) { + uint64_t expire = 0; + auto value = iter->value().ToString(); + if (!decodeExpireFromValue(metadata, &value, expire).ok()) { + expired_field_num += 1; + } + } + return metadata.size - expired_field_num; +} + bool Hash::ExistValidField(engine::Context &ctx, const Slice &ns_key, const HashMetadata &metadata) { if (metadata.Expired()) { return false; diff --git a/src/types/redis_hash.h b/src/types/redis_hash.h index b1a19845bf2..ef44e0ba84f 100644 --- a/src/types/redis_hash.h +++ b/src/types/redis_hash.h @@ -76,6 +76,7 @@ class Hash : public SubKeyScanner { std::vector *ret); rocksdb::Status TTLFields(engine::Context &ctx, const Slice &user_key, const std::vector &fields, std::vector *ret); + uint64_t GetValidFieldCount(engine::Context &ctx, const Slice &ns_key, const HashMetadata &metadata); bool ExistValidField(engine::Context &ctx, const Slice &ns_key, const HashMetadata &metadata); static bool IsFieldExpired(const Slice &metadata_key, const Slice &value);