This is an automated email from the ASF dual-hosted git repository.

hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 5b54f9cd Record and export the keyspace hit/miss count to INFO command 
(#1971)
5b54f9cd is described below

commit 5b54f9cdb1aa3b7b789ef2f05bb1b198e09c9da6
Author: hulk <[email protected]>
AuthorDate: Wed Dec 27 09:45:47 2023 +0800

    Record and export the keyspace hit/miss count to INFO command (#1971)
    
    Currently, Redis supports inspecting the keyspace hit/miss count via
    INFO command to see if the hit ratio is expected. So we also export
    the same metric to align with Redis metrics.
---
 src/commands/cmd_replication.cc     |  6 ++--
 src/server/redis_connection.cc      |  2 +-
 src/server/redis_request.cc         |  6 ++--
 src/server/server.cc                | 22 +++++++++------
 src/stats/stats.h                   | 16 +++++------
 src/storage/event_listener.cc       |  4 +--
 src/storage/redis_db.cc             | 48 ++++++++------------------------
 src/storage/redis_db.h              |  4 +--
 src/storage/storage.cc              | 55 ++++++++++++++++++++++++++++++-------
 src/storage/storage.h               | 27 +++++++++++++-----
 src/types/redis_string.cc           | 30 ++++----------------
 tests/gocase/unit/info/info_test.go | 41 +++++++++++++++++++++++++++
 12 files changed, 154 insertions(+), 107 deletions(-)

diff --git a/src/commands/cmd_replication.cc b/src/commands/cmd_replication.cc
index 2a4f074a..0a86a9cc 100644
--- a/src/commands/cmd_replication.cc
+++ b/src/commands/cmd_replication.cc
@@ -84,7 +84,7 @@ class CommandPSync : public Commander {
     }
 
     if (need_full_sync) {
-      srv->stats.IncrPSyncErrCounter();
+      srv->stats.IncrPSyncErrCount();
       return {Status::RedisExecErr, *output};
     }
 
@@ -98,7 +98,7 @@ class CommandPSync : public Commander {
       return s.Prefixed("failed to set blocking mode on socket");
     }
 
-    srv->stats.IncrPSyncOKCounter();
+    srv->stats.IncrPSyncOKCount();
     s = srv->AddSlave(conn, next_repl_seq_);
     if (!s.IsOK()) {
       std::string err = "-ERR " + s.Msg() + "\r\n";
@@ -216,7 +216,7 @@ class CommandFetchMeta : public Commander {
 
     conn->NeedNotFreeBufferEvent();
     conn->EnableFlag(redis::Connection::kCloseAsync);
-    srv->stats.IncrFullSyncCounter();
+    srv->stats.IncrFullSyncCount();
 
     // Feed-replica-meta thread
     auto t = GET_OR_RET(util::CreateThread("feed-repl-info", [srv, repl_fd, 
ip, bev = conn->GetBufferEvent()] {
diff --git a/src/server/redis_connection.cc b/src/server/redis_connection.cc
index 73ed3a80..ae80e950 100644
--- a/src/server/redis_connection.cc
+++ b/src/server/redis_connection.cc
@@ -126,7 +126,7 @@ void Connection::OnEvent(bufferevent *bev, int16_t events) {
 }
 
 void Connection::Reply(const std::string &msg) {
-  owner_->srv->stats.IncrOutbondBytes(msg.size());
+  owner_->srv->stats.IncrOutboundBytes(msg.size());
   redis::Reply(bufferevent_get_output(bev_), msg);
 }
 
diff --git a/src/server/redis_request.cc b/src/server/redis_request.cc
index f6faa7b1..4c796b41 100644
--- a/src/server/redis_request.cc
+++ b/src/server/redis_request.cc
@@ -67,7 +67,7 @@ Status Request::Tokenize(evbuffer *input) {
         }
 
         pipeline_size++;
-        srv_->stats.IncrInbondBytes(line.length);
+        srv_->stats.IncrInboundBytes(line.length);
         if (line[0] == '*') {
           auto parse_result = ParseInt<int64_t>(std::string(line.get() + 1, 
line.length - 1), 10);
           if (!parse_result) {
@@ -101,7 +101,7 @@ Status Request::Tokenize(evbuffer *input) {
         UniqueEvbufReadln line(input, EVBUFFER_EOL_CRLF_STRICT);
         if (!line || line.length <= 0) return Status::OK();
 
-        srv_->stats.IncrInbondBytes(line.length);
+        srv_->stats.IncrInboundBytes(line.length);
         if (line[0] != '$') {
           return {Status::NotOK, "Protocol error: expected '$'"};
         }
@@ -125,7 +125,7 @@ Status Request::Tokenize(evbuffer *input) {
         char *data = reinterpret_cast<char *>(evbuffer_pullup(input, 
static_cast<ssize_t>(bulk_len_ + 2)));
         tokens_.emplace_back(data, bulk_len_);
         evbuffer_drain(input, bulk_len_ + 2);
-        srv_->stats.IncrInbondBytes(bulk_len_ + 2);
+        srv_->stats.IncrInboundBytes(bulk_len_ + 2);
         --multi_bulk_len_;
         if (multi_bulk_len_ == 0) {
           state_ = ArrayLen;
diff --git a/src/server/server.cc b/src/server/server.cc
index ab7d311f..231d532d 100644
--- a/src/server/server.cc
+++ b/src/server/server.cc
@@ -801,8 +801,8 @@ void Server::GetRocksDBInfo(std::string *info) {
                   << "]:" << cf_stats_map["memtable-limit-stops"] << "\r\n";
   }
 
-  auto db_stats = storage->GetDB()->GetDBOptions().statistics;
-  if (db_stats) {
+  auto rocksdb_stats = storage->GetDB()->GetDBOptions().statistics;
+  if (rocksdb_stats) {
     std::map<std::string, uint32_t> block_cache_stats = {
         {"block_cache_hit", rocksdb::Tickers::BLOCK_CACHE_HIT},
         {"block_cache_index_hit", rocksdb::Tickers::BLOCK_CACHE_INDEX_HIT},
@@ -814,7 +814,7 @@ void Server::GetRocksDBInfo(std::string *info) {
         {"block_cache_data_miss", rocksdb::Tickers::BLOCK_CACHE_DATA_MISS},
     };
     for (const auto &iter : block_cache_stats) {
-      string_stream << iter.first << ":" << 
db_stats->getTickerCount(iter.second) << "\r\n";
+      string_stream << iter.first << ":" << 
rocksdb_stats->getTickerCount(iter.second) << "\r\n";
     }
   }
 
@@ -829,8 +829,9 @@ void Server::GetRocksDBInfo(std::string *info) {
   string_stream << "num_live_versions:" << num_live_versions << "\r\n";
   string_stream << "num_super_version:" << num_super_version << "\r\n";
   string_stream << "num_background_errors:" << num_background_errors << "\r\n";
-  string_stream << "flush_count:" << storage->GetFlushCount() << "\r\n";
-  string_stream << "compaction_count:" << storage->GetCompactionCount() << 
"\r\n";
+  auto db_stats = storage->GetDBStats();
+  string_stream << "flush_count:" << db_stats->flush_count << "\r\n";
+  string_stream << "compaction_count:" << db_stats->compaction_count << "\r\n";
   string_stream << "put_per_sec:" << 
stats.GetInstantaneousMetric(STATS_METRIC_ROCKSDB_PUT) << "\r\n";
   string_stream << "get_per_sec:"
                 << stats.GetInstantaneousMetric(STATS_METRIC_ROCKSDB_GET) +
@@ -1027,9 +1028,14 @@ void Server::GetStatsInfo(std::string *info) {
                 << 
static_cast<float>(stats.GetInstantaneousMetric(STATS_METRIC_NET_INPUT) / 1024) 
<< "\r\n";
   string_stream << "instantaneous_output_kbps:"
                 << 
static_cast<float>(stats.GetInstantaneousMetric(STATS_METRIC_NET_OUTPUT) / 
1024) << "\r\n";
-  string_stream << "sync_full:" << stats.fullsync_counter << "\r\n";
-  string_stream << "sync_partial_ok:" << stats.psync_ok_counter << "\r\n";
-  string_stream << "sync_partial_err:" << stats.psync_err_counter << "\r\n";
+  string_stream << "sync_full:" << stats.fullsync_count << "\r\n";
+  string_stream << "sync_partial_ok:" << stats.psync_ok_count << "\r\n";
+  string_stream << "sync_partial_err:" << stats.psync_err_count << "\r\n";
+
+  auto db_stats = storage->GetDBStats();
+  string_stream << "keyspace_hits:" << db_stats->keyspace_hits << "\r\n";
+  string_stream << "keyspace_misses:" << db_stats->keyspace_misses << "\r\n";
+
   {
     std::lock_guard<std::mutex> lg(pubsub_channels_mu_);
     string_stream << "pubsub_channels:" << pubsub_channels_.size() << "\r\n";
diff --git a/src/stats/stats.h b/src/stats/stats.h
index 114ac667..88ab2108 100644
--- a/src/stats/stats.h
+++ b/src/stats/stats.h
@@ -64,19 +64,19 @@ class Stats {
   mutable std::shared_mutex inst_metrics_mutex;
   std::vector<InstMetric> inst_metrics;
 
-  std::atomic<uint64_t> fullsync_counter = {0};
-  std::atomic<uint64_t> psync_err_counter = {0};
-  std::atomic<uint64_t> psync_ok_counter = {0};
+  std::atomic<uint64_t> fullsync_count = {0};
+  std::atomic<uint64_t> psync_err_count = {0};
+  std::atomic<uint64_t> psync_ok_count = {0};
   std::map<std::string, CommandStat> commands_stats;
 
   Stats();
   void IncrCalls(const std::string &command_name);
   void IncrLatency(uint64_t latency, const std::string &command_name);
-  void IncrInbondBytes(uint64_t bytes) { in_bytes.fetch_add(bytes, 
std::memory_order_relaxed); }
-  void IncrOutbondBytes(uint64_t bytes) { out_bytes.fetch_add(bytes, 
std::memory_order_relaxed); }
-  void IncrFullSyncCounter() { fullsync_counter.fetch_add(1, 
std::memory_order_relaxed); }
-  void IncrPSyncErrCounter() { psync_err_counter.fetch_add(1, 
std::memory_order_relaxed); }
-  void IncrPSyncOKCounter() { psync_ok_counter.fetch_add(1, 
std::memory_order_relaxed); }
+  void IncrInboundBytes(uint64_t bytes) { in_bytes.fetch_add(bytes, 
std::memory_order_relaxed); }
+  void IncrOutboundBytes(uint64_t bytes) { out_bytes.fetch_add(bytes, 
std::memory_order_relaxed); }
+  void IncrFullSyncCount() { fullsync_count.fetch_add(1, 
std::memory_order_relaxed); }
+  void IncrPSyncErrCount() { psync_err_count.fetch_add(1, 
std::memory_order_relaxed); }
+  void IncrPSyncOKCount() { psync_ok_count.fetch_add(1, 
std::memory_order_relaxed); }
   static int64_t GetMemoryRSS();
   void TrackInstantaneousMetric(int metric, uint64_t current_reading);
   uint64_t GetInstantaneousMetric(int metric) const;
diff --git a/src/storage/event_listener.cc b/src/storage/event_listener.cc
index 6e054945..8bc204f7 100644
--- a/src/storage/event_listener.cc
+++ b/src/storage/event_listener.cc
@@ -84,7 +84,7 @@ void EventListener::OnCompactionCompleted(rocksdb::DB *db, 
const rocksdb::Compac
             << ", input bytes: " << ci.stats.total_input_bytes << ", output 
bytes:" << ci.stats.total_output_bytes
             << ", is_manual_compaction:" << (ci.stats.is_manual_compaction ? 
"yes" : "no")
             << ", elapsed(micro): " << ci.stats.elapsed_micros;
-  storage_->IncrCompactionCount(1);
+  storage_->RecordStat(engine::StatType::CompactionCount, 1);
   storage_->CheckDBSizeLimit();
 }
 
@@ -94,7 +94,7 @@ void EventListener::OnFlushBegin(rocksdb::DB *db, const 
rocksdb::FlushJobInfo &f
 }
 
 void EventListener::OnFlushCompleted(rocksdb::DB *db, const 
rocksdb::FlushJobInfo &fi) {
-  storage_->IncrFlushCount(1);
+  storage_->RecordStat(engine::StatType::FlushCount, 1);
   storage_->CheckDBSizeLimit();
   LOG(INFO) << "[event_listener/flush_completed] column family: " << 
fi.cf_name << ", thread_id: " << fi.thread_id
             << ", job_id: " << fi.job_id << ", file: " << fi.file_path
diff --git a/src/storage/redis_db.cc b/src/storage/redis_db.cc
index a3734730..bdbd9789 100644
--- a/src/storage/redis_db.cc
+++ b/src/storage/redis_db.cc
@@ -25,6 +25,7 @@
 #include <utility>
 
 #include "cluster/redis_slot.h"
+#include "common/scope_exit.h"
 #include "db_util.h"
 #include "parse_util.h"
 #include "rocksdb/iterator.h"
@@ -44,6 +45,15 @@ rocksdb::Status Database::ParseMetadata(RedisTypes types, 
Slice *bytes, Metadata
   std::string old_metadata;
   metadata->Encode(&old_metadata);
 
+  bool is_keyspace_hit = false;
+  ScopeExit se([this, &is_keyspace_hit] {
+    if (is_keyspace_hit) {
+      storage_->RecordStat(engine::StatType::KeyspaceHits, 1);
+    } else {
+      storage_->RecordStat(engine::StatType::KeyspaceMisses, 1);
+    }
+  });
+
   auto s = metadata->Decode(bytes);
   if (!s.ok()) return s;
 
@@ -64,6 +74,7 @@ rocksdb::Status Database::ParseMetadata(RedisTypes types, 
Slice *bytes, Metadata
     auto _ [[maybe_unused]] = metadata->Decode(old_metadata);
     return rocksdb::Status::NotFound("no element found");
   }
+  is_keyspace_hit = true;
   return s;
 }
 
@@ -597,43 +608,6 @@ rocksdb::Status Database::ClearKeysOfSlot(const 
rocksdb::Slice &ns, int slot) {
   return rocksdb::Status::OK();
 }
 
-rocksdb::Status Database::GetSlotKeysInfo(int slot, std::map<int, uint64_t> 
*slotskeys, std::vector<std::string> *keys,
-                                          int count) {
-  LatestSnapShot ss(storage_);
-  rocksdb::ReadOptions read_options = storage_->DefaultScanOptions();
-  read_options.snapshot = ss.GetSnapShot();
-
-  auto iter = util::UniqueIterator(storage_, read_options, 
metadata_cf_handle_);
-  bool end = false;
-  for (int i = 0; i < HASH_SLOTS_SIZE; i++) {
-    std::string prefix = ComposeSlotKeyPrefix(namespace_, i);
-    uint64_t total = 0;
-    int cnt = 0;
-    if (slot != -1 && i != slot) {
-      (*slotskeys)[i] = total;
-      continue;
-    }
-    for (iter->Seek(prefix); iter->Valid(); iter->Next()) {
-      if (!iter->key().starts_with(prefix)) {
-        break;
-      }
-      total++;
-      if (slot != -1 && count > 0 && !end) {
-        // Get user key
-        if (cnt < count) {
-          auto [_, user_key] = ExtractNamespaceKey(iter->key(), true);
-          keys->emplace_back(user_key.ToString());
-          cnt++;
-        }
-      }
-    }
-    // Maybe cnt < count
-    if (cnt > 0) end = true;
-    (*slotskeys)[i] = total;
-  }
-  return rocksdb::Status::OK();
-}
-
 rocksdb::Status Database::KeyExist(const std::string &key) {
   int cnt = 0;
   std::vector<rocksdb::Slice> keys;
diff --git a/src/storage/redis_db.h b/src/storage/redis_db.h
index 7258923c..65804441 100644
--- a/src/storage/redis_db.h
+++ b/src/storage/redis_db.h
@@ -34,7 +34,7 @@ class Database {
   static constexpr uint64_t RANDOM_KEY_SCAN_LIMIT = 60;
 
   explicit Database(engine::Storage *storage, std::string ns = "");
-  [[nodiscard]] static rocksdb::Status ParseMetadata(RedisTypes types, Slice 
*bytes, Metadata *metadata);
+  [[nodiscard]] rocksdb::Status ParseMetadata(RedisTypes types, Slice *bytes, 
Metadata *metadata);
   [[nodiscard]] rocksdb::Status GetMetadata(RedisTypes types, const Slice 
&ns_key, Metadata *metadata);
   [[nodiscard]] rocksdb::Status GetMetadata(RedisTypes types, const Slice 
&ns_key, std::string *raw_value,
                                             Metadata *metadata, Slice *rest);
@@ -60,8 +60,6 @@ class Database {
                                                        std::string *begin, 
std::string *end,
                                                        
rocksdb::ColumnFamilyHandle *cf_handle = nullptr);
   [[nodiscard]] rocksdb::Status ClearKeysOfSlot(const rocksdb::Slice &ns, int 
slot);
-  [[nodiscard]] rocksdb::Status GetSlotKeysInfo(int slot, std::map<int, 
uint64_t> *slotskeys,
-                                                std::vector<std::string> 
*keys, int count);
   [[nodiscard]] rocksdb::Status KeyExist(const std::string &key);
 
  protected:
diff --git a/src/storage/storage.cc b/src/storage/storage.cc
index a0e53c0b..c176db1b 100644
--- a/src/storage/storage.cc
+++ b/src/storage/storage.cc
@@ -200,12 +200,6 @@ Status Storage::SetOptionForAllColumnFamilies(const 
std::string &key, const std:
   return Status::OK();
 }
 
-Status Storage::SetOption(const std::string &key, const std::string &value) {
-  auto s = db_->SetOptions({{key, value}});
-  if (!s.ok()) return {Status::NotOK, s.ToString()};
-  return Status::OK();
-}
-
 Status Storage::SetDBOption(const std::string &key, const std::string &value) {
   auto s = db_->SetDBOptions({{key, value}});
   if (!s.ok()) return {Status::NotOK, s.ToString()};
@@ -525,10 +519,15 @@ rocksdb::Status Storage::Get(const rocksdb::ReadOptions 
&options, const rocksdb:
 
 rocksdb::Status Storage::Get(const rocksdb::ReadOptions &options, 
rocksdb::ColumnFamilyHandle *column_family,
                              const rocksdb::Slice &key, std::string *value) {
+  rocksdb::Status s;
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
-    return txn_write_batch_->GetFromBatchAndDB(db_.get(), options, 
column_family, key, value);
+    s = txn_write_batch_->GetFromBatchAndDB(db_.get(), options, column_family, 
key, value);
+  } else {
+    s = db_->Get(options, column_family, key, value);
   }
-  return db_->Get(options, column_family, key, value);
+
+  recordKeyspaceStat(column_family, s);
+  return s;
 }
 
 rocksdb::Status Storage::Get(const rocksdb::ReadOptions &options, const 
rocksdb::Slice &key,
@@ -538,16 +537,31 @@ rocksdb::Status Storage::Get(const rocksdb::ReadOptions 
&options, const rocksdb:
 
 rocksdb::Status Storage::Get(const rocksdb::ReadOptions &options, 
rocksdb::ColumnFamilyHandle *column_family,
                              const rocksdb::Slice &key, rocksdb::PinnableSlice 
*value) {
+  rocksdb::Status s;
   if (is_txn_mode_ && txn_write_batch_->GetWriteBatch()->Count() > 0) {
-    return txn_write_batch_->GetFromBatchAndDB(db_.get(), options, 
column_family, key, value);
+    s = txn_write_batch_->GetFromBatchAndDB(db_.get(), options, column_family, 
key, value);
+  } else {
+    s = db_->Get(options, column_family, key, value);
   }
-  return db_->Get(options, column_family, key, value);
+
+  recordKeyspaceStat(column_family, s);
+  return s;
 }
 
 rocksdb::Iterator *Storage::NewIterator(const rocksdb::ReadOptions &options) {
   return NewIterator(options, db_->DefaultColumnFamily());
 }
 
+void Storage::recordKeyspaceStat(const rocksdb::ColumnFamilyHandle 
*column_family, const rocksdb::Status &s) {
+  if (column_family->GetName() != kMetadataColumnFamilyName) return;
+
+  // Don't record keyspace hits here because we cannot tell
+  // if the key was expired or not. So we record it when parsing the metadata.
+  if (s.IsNotFound() || s.IsInvalidArgument()) {
+    RecordStat(StatType::KeyspaceMisses, 1);
+  }
+}
+
 rocksdb::Iterator *Storage::NewIterator(const rocksdb::ReadOptions &options,
                                         rocksdb::ColumnFamilyHandle 
*column_family) {
   auto iter = db_->NewIterator(options, column_family);
@@ -566,6 +580,10 @@ void Storage::MultiGet(const rocksdb::ReadOptions 
&options, rocksdb::ColumnFamil
   } else {
     db_->MultiGet(options, column_family, num_keys, keys, values, statuses, 
false);
   }
+
+  for (size_t i = 0; i < num_keys; i++) {
+    recordKeyspaceStat(column_family, statuses[i]);
+  }
 }
 
 rocksdb::Status Storage::Write(const rocksdb::WriteOptions &options, 
rocksdb::WriteBatch *updates) {
@@ -641,6 +659,23 @@ Status Storage::ReplicaApplyWriteBatch(std::string 
&&raw_batch) {
   return Status::OK();
 }
 
+void Storage::RecordStat(StatType type, uint64_t v) {
+  switch (type) {
+    case StatType::FlushCount:
+      db_stats_.flush_count += v;
+      break;
+    case StatType::CompactionCount:
+      db_stats_.compaction_count += v;
+      break;
+    case StatType::KeyspaceHits:
+      db_stats_.keyspace_hits += v;
+      break;
+    case StatType::KeyspaceMisses:
+      db_stats_.keyspace_misses += v;
+      break;
+  }
+}
+
 rocksdb::ColumnFamilyHandle *Storage::GetCFHandle(const std::string &name) {
   if (name == kMetadataColumnFamilyName) {
     return cf_handles_[1];
diff --git a/src/storage/storage.h b/src/storage/storage.h
index 43c3cf1c..b0de2dd0 100644
--- a/src/storage/storage.h
+++ b/src/storage/storage.h
@@ -80,6 +80,20 @@ inline const std::vector<CompressionOption> 
CompressionOptions = {
     {rocksdb::kZSTD, "zstd", "kZSTD"},
 };
 
+enum class StatType {
+  CompactionCount,
+  FlushCount,
+  KeyspaceHits,
+  KeyspaceMisses,
+};
+
+struct DBStats {
+  std::atomic<uint64_t> compaction_count = 0;
+  std::atomic<uint64_t> flush_count = 0;
+  std::atomic<uint64_t> keyspace_hits = 0;
+  std::atomic<uint64_t> keyspace_misses = 0;
+};
+
 class Storage {
  public:
   explicit Storage(Config *config);
@@ -93,7 +107,6 @@ class Storage {
   void SetBlobDB(rocksdb::ColumnFamilyOptions *cf_options);
   rocksdb::Options InitRocksDBOptions();
   Status SetOptionForAllColumnFamilies(const std::string &key, const 
std::string &value);
-  Status SetOption(const std::string &key, const std::string &value);
   Status SetDBOption(const std::string &key, const std::string &value);
   Status CreateColumnFamilies(const rocksdb::Options &options);
   Status CreateBackup();
@@ -145,13 +158,12 @@ class Storage {
   std::shared_lock<std::shared_mutex> ReadLockGuard();
   std::unique_lock<std::shared_mutex> WriteLockGuard();
 
-  uint64_t GetFlushCount() const { return flush_count_; }
-  void IncrFlushCount(uint64_t n) { flush_count_.fetch_add(n); }
-  uint64_t GetCompactionCount() const { return compaction_count_; }
-  void IncrCompactionCount(uint64_t n) { compaction_count_.fetch_add(n); }
   bool IsSlotIdEncoded() const { return config_->slot_id_encoded; }
   Config *GetConfig() const { return config_; }
 
+  const DBStats *GetDBStats() const { return &db_stats_; }
+  void RecordStat(StatType type, uint64_t v);
+
   Status BeginTxn();
   Status CommitTxn();
   ObserverOrUniquePtr<rocksdb::WriteBatchBase> GetWriteBatchBase();
@@ -214,8 +226,8 @@ class Storage {
   std::vector<rocksdb::ColumnFamilyHandle *> cf_handles_;
   LockManager lock_mgr_;
   bool db_size_limit_reached_ = false;
-  std::atomic<uint64_t> flush_count_{0};
-  std::atomic<uint64_t> compaction_count_{0};
+
+  DBStats db_stats_;
 
   std::shared_mutex db_rw_lock_;
   bool db_closing_ = true;
@@ -234,6 +246,7 @@ class Storage {
   rocksdb::WriteOptions write_opts_ = rocksdb::WriteOptions();
 
   rocksdb::Status writeToDB(const rocksdb::WriteOptions &options, 
rocksdb::WriteBatch *updates);
+  void recordKeyspaceStat(const rocksdb::ColumnFamilyHandle *column_family, 
const rocksdb::Status &s);
 };
 
 }  // namespace engine
diff --git a/src/types/redis_string.cc b/src/types/redis_string.cc
index cc851b0b..7178311a 100644
--- a/src/types/redis_string.cc
+++ b/src/types/redis_string.cc
@@ -47,20 +47,11 @@ std::vector<rocksdb::Status> String::getRawValues(const 
std::vector<Slice> &keys
     if (!statuses[i].ok()) continue;
     (*raw_values)[i].assign(pin_values[i].data(), pin_values[i].size());
     Metadata metadata(kRedisNone, false);
-    auto s = metadata.Decode((*raw_values)[i]);
+    Slice slice = (*raw_values)[i];
+    auto s = ParseMetadata({kRedisString}, &slice, &metadata);
     if (!s.ok()) {
-      (*raw_values)[i].clear();
       statuses[i] = s;
-      continue;
-    }
-    if (metadata.Expired()) {
-      (*raw_values)[i].clear();
-      statuses[i] = rocksdb::Status::NotFound(kErrMsgKeyExpired);
-      continue;
-    }
-    if (metadata.Type() != kRedisString && metadata.size > 0) {
       (*raw_values)[i].clear();
-      statuses[i] = rocksdb::Status::InvalidArgument(kErrMsgWrongType);
       continue;
     }
   }
@@ -70,23 +61,12 @@ std::vector<rocksdb::Status> String::getRawValues(const 
std::vector<Slice> &keys
 rocksdb::Status String::getRawValue(const std::string &ns_key, std::string 
*raw_value) {
   raw_value->clear();
 
-  rocksdb::ReadOptions read_options;
-  LatestSnapShot ss(storage_);
-  read_options.snapshot = ss.GetSnapShot();
-  rocksdb::Status s = storage_->Get(read_options, metadata_cf_handle_, ns_key, 
raw_value);
+  auto s = GetRawMetadata(ns_key, raw_value);
   if (!s.ok()) return s;
 
   Metadata metadata(kRedisNone, false);
-  s = metadata.Decode(*raw_value);
-  if (!s.ok()) return s;
-  if (metadata.Expired()) {
-    raw_value->clear();
-    return rocksdb::Status::NotFound(kErrMsgKeyExpired);
-  }
-  if (metadata.Type() != kRedisString && metadata.size > 0) {
-    return rocksdb::Status::InvalidArgument(kErrMsgWrongType);
-  }
-  return rocksdb::Status::OK();
+  Slice slice = *raw_value;
+  return ParseMetadata({kRedisString}, &slice, &metadata);
 }
 
 rocksdb::Status String::getValueAndExpire(const std::string &ns_key, 
std::string *value, uint64_t *expire) {
diff --git a/tests/gocase/unit/info/info_test.go 
b/tests/gocase/unit/info/info_test.go
index 136be197..dd4b0cdc 100644
--- a/tests/gocase/unit/info/info_test.go
+++ b/tests/gocase/unit/info/info_test.go
@@ -26,6 +26,8 @@ import (
        "testing"
        "time"
 
+       "github.com/redis/go-redis/v9"
+
        "github.com/apache/kvrocks/tests/gocase/util"
        "github.com/stretchr/testify/require"
 )
@@ -101,3 +103,42 @@ func TestInfo(t *testing.T) {
                require.Equal(t, "1", util.FindInfoEntry(rdb0, 
"cluster_enabled", "cluster"))
        })
 }
+
+func TestKeyspaceHitMiss(t *testing.T) {
+       srv0 := util.StartServer(t, map[string]string{})
+       defer func() { srv0.Close() }()
+       rdb0 := srv0.NewClient()
+       defer func() { require.NoError(t, rdb0.Close()) }()
+
+       srv := util.StartServer(t, map[string]string{})
+       defer srv.Close()
+
+       ctx := context.Background()
+       rdb := srv.NewClient()
+       defer func() { require.NoError(t, rdb.Close()) }()
+
+       require.Equal(t, "0", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       require.Equal(t, "0", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+       require.NoError(t, rdb0.Set(ctx, "foo", "bar", 0).Err())
+
+       require.NoError(t, rdb0.Get(ctx, "foo").Err())
+       require.Equal(t, "1", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       require.Equal(t, "0", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+
+       require.EqualError(t, rdb0.Get(ctx, "no_exists_key").Err(), 
redis.Nil.Error())
+       require.Equal(t, "1", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       require.Equal(t, "1", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+
+       require.NoError(t, rdb0.HSet(ctx, "hash", "f1", "v1").Err())
+       require.Equal(t, "1", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       // increase by 2 because of the previous HSet will try to get the key 
first
+       require.Equal(t, "2", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+
+       require.NoError(t, rdb0.HGet(ctx, "hash", "f1").Err())
+       require.Equal(t, "2", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       require.Equal(t, "2", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+
+       require.EqualError(t, rdb0.HGet(ctx, "no_exists_hash", "f1").Err(), 
redis.Nil.Error())
+       require.Equal(t, "2", util.FindInfoEntry(rdb0, "keyspace_hits", 
"stats"))
+       require.Equal(t, "3", util.FindInfoEntry(rdb0, "keyspace_misses", 
"stats"))
+}

Reply via email to