This is an automated email from the ASF dual-hosted git repository.
twice 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 5e9d61da feat(info): support to specify multiple sections for INFO
(#2772)
5e9d61da is described below
commit 5e9d61da41e1c73960eb5054db48b2c9a9d61568
Author: Twice <[email protected]>
AuthorDate: Thu Feb 6 22:36:04 2025 +0800
feat(info): support to specify multiple sections for INFO (#2772)
---
src/commands/cmd_server.cc | 10 +-
src/server/server.cc | 221 +++++++++++++++++-------------------
src/server/server.h | 5 +-
tests/gocase/unit/info/info_test.go | 7 ++
4 files changed, 117 insertions(+), 126 deletions(-)
diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc
index a8512936..17815cbf 100644
--- a/src/commands/cmd_server.cc
+++ b/src/commands/cmd_server.cc
@@ -235,14 +235,12 @@ class CommandConfig : public Commander {
class CommandInfo : public Commander {
public:
Status Execute([[maybe_unused]] engine::Context &ctx, Server *srv,
Connection *conn, std::string *output) override {
- std::string section = "all";
- if (args_.size() == 2) {
- section = util::ToLower(args_[1]);
- } else if (args_.size() > 2) {
- return {Status::RedisParseErr, errInvalidSyntax};
+ std::vector<std::string> sections;
+ for (size_t i = 1; i < args_.size(); ++i) {
+ sections.push_back(util::ToLower(args_[i]));
}
std::string info;
- srv->GetInfo(conn->GetNamespace(), section, &info);
+ srv->GetInfo(conn->GetNamespace(), sections, &info);
*output = conn->VerbatimString("txt", info);
return Status::OK();
}
diff --git a/src/server/server.cc b/src/server/server.cc
index 0c15abef..07a66cf9 100644
--- a/src/server/server.cc
+++ b/src/server/server.cc
@@ -27,6 +27,7 @@
#include <sys/statvfs.h>
#include <sys/utsname.h>
+#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
@@ -853,6 +854,8 @@ void Server::cron() {
}
void Server::GetRocksDBInfo(std::string *info) {
+ if (is_loading_) return;
+
std::ostringstream string_stream;
rocksdb::DB *db = storage->GetDB();
@@ -1028,6 +1031,8 @@ void Server::GetMemoryInfo(std::string *info) {
}
void Server::GetReplicationInfo(std::string *info) {
+ if (is_loading_) return;
+
std::ostringstream string_stream;
string_stream << "# Replication\r\n";
string_stream << "role:" << (IsSlave() ? "slave" : "master") << "\r\n";
@@ -1207,141 +1212,119 @@ void Server::GetClusterInfo(std::string *info) {
*info = string_stream.str();
}
-// WARNING: we must not access DB(i.e. RocksDB) when server is loading since
-// DB is closed and the pointer is invalid. Server may crash if we access DB
during loading.
-// If you add new fields which access DB into INFO command output, make sure
-// this section can't be shown when loading(i.e. !is_loading_).
-void Server::GetInfo(const std::string &ns, const std::string §ion,
std::string *info) {
- info->clear();
+void Server::GetPersistenceInfo(std::string *info) {
+ std::ostringstream string_stream;
+
+ string_stream << "# Persistence\r\n";
+ string_stream << "loading:" << is_loading_ << "\r\n";
+
+ std::lock_guard<std::mutex> lg(db_job_mu_);
+ string_stream << "bgsave_in_progress:" << (is_bgsave_in_progress_ ? 1 : 0)
<< "\r\n";
+ string_stream << "last_bgsave_time:"
+ << (last_bgsave_timestamp_secs_ == -1 ? start_time_secs_ :
last_bgsave_timestamp_secs_) << "\r\n";
+ string_stream << "last_bgsave_status:" << last_bgsave_status_ << "\r\n";
+ string_stream << "last_bgsave_time_sec:" << last_bgsave_duration_secs_ <<
"\r\n";
+ *info = string_stream.str();
+}
+
+void Server::GetCpuInfo(std::string *info) { //
NOLINT(readability-convert-member-functions-to-static)
std::ostringstream string_stream;
- bool all = section == "all";
- int section_cnt = 0;
- if (all || section == "server") {
- std::string server_info;
- GetServerInfo(&server_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << server_info;
- }
+ rusage self_ru;
+ getrusage(RUSAGE_SELF, &self_ru);
+ string_stream << "# CPU\r\n";
+ string_stream << "used_cpu_sys:"
+ << static_cast<float>(self_ru.ru_stime.tv_sec) +
static_cast<float>(self_ru.ru_stime.tv_usec / 1000000)
+ << "\r\n";
+ string_stream << "used_cpu_user:"
+ << static_cast<float>(self_ru.ru_utime.tv_sec) +
static_cast<float>(self_ru.ru_utime.tv_usec / 1000000)
+ << "\r\n";
- if (all || section == "clients") {
- std::string clients_info;
- GetClientsInfo(&clients_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << clients_info;
- }
+ *info = string_stream.str();
+}
- if (all || section == "memory") {
- std::string memory_info;
- GetMemoryInfo(&memory_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << memory_info;
- }
+void Server::GetKeyspaceInfo(const std::string &ns, std::string *info) {
+ if (is_loading_) return;
- if (all || section == "persistence") {
- if (section_cnt++) string_stream << "\r\n";
- string_stream << "# Persistence\r\n";
- string_stream << "loading:" << is_loading_ << "\r\n";
+ std::ostringstream string_stream;
- std::lock_guard<std::mutex> lg(db_job_mu_);
- string_stream << "bgsave_in_progress:" << (is_bgsave_in_progress_ ? 1 : 0)
<< "\r\n";
- string_stream << "last_bgsave_time:"
- << (last_bgsave_timestamp_secs_ == -1 ? start_time_secs_ :
last_bgsave_timestamp_secs_) << "\r\n";
- string_stream << "last_bgsave_status:" << last_bgsave_status_ << "\r\n";
- string_stream << "last_bgsave_time_sec:" << last_bgsave_duration_secs_ <<
"\r\n";
- }
-
- if (all || section == "stats") {
- std::string stats_info;
- GetStatsInfo(&stats_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << stats_info;
- }
-
- // In replication section, we access DB, so we can't do that when loading
- if (!is_loading_ && (all || section == "replication")) {
- std::string replication_info;
- GetReplicationInfo(&replication_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << replication_info;
- }
-
- if (all || section == "cpu") {
- rusage self_ru;
- getrusage(RUSAGE_SELF, &self_ru);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << "# CPU\r\n";
- string_stream << "used_cpu_sys:"
- << static_cast<float>(self_ru.ru_stime.tv_sec) +
- static_cast<float>(self_ru.ru_stime.tv_usec / 1000000)
- << "\r\n";
- string_stream << "used_cpu_user:"
- << static_cast<float>(self_ru.ru_utime.tv_sec) +
- static_cast<float>(self_ru.ru_utime.tv_usec / 1000000)
- << "\r\n";
- }
+ KeyNumStats stats;
+ GetLatestKeyNumStats(ns, &stats);
- if (all || section == "commandstats") {
- std::string commands_stats_info;
- GetCommandsStatsInfo(&commands_stats_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << commands_stats_info;
- }
+ // FIXME(mwish): output still requires std::tm.
+ auto last_scan_time = static_cast<time_t>(GetLastScanTime(ns));
+ std::tm last_scan_tm{};
+ localtime_r(&last_scan_time, &last_scan_tm);
- if (all || section == "cluster") {
- std::string cluster_info;
- GetClusterInfo(&cluster_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << cluster_info;
+ string_stream << "# Keyspace\r\n";
+ if (last_scan_time == 0) {
+ string_stream << "# WARN: DBSIZE SCAN never performed yet\r\n";
+ } else {
+ string_stream << "# Last DBSIZE SCAN time: " <<
std::put_time(&last_scan_tm, "%a %b %e %H:%M:%S %Y") << "\r\n";
+ }
+ string_stream << "db0:keys=" << stats.n_key << ",expires=" <<
stats.n_expires << ",avg_ttl=" << stats.avg_ttl
+ << ",expired=" << stats.n_expired << "\r\n";
+ string_stream << "sequence:" << storage->GetDB()->GetLatestSequenceNumber()
<< "\r\n";
+ string_stream << "used_db_size:" << storage->GetTotalSize(ns) << "\r\n";
+ string_stream << "max_db_size:" << config_->max_db_size * GiB << "\r\n";
+ double used_percent = config_->max_db_size ?
static_cast<double>(storage->GetTotalSize() * 100) /
+
static_cast<double>(config_->max_db_size * GiB)
+ : 0;
+ string_stream << "used_percent: " << used_percent << "%\r\n";
+
+ struct statvfs stat;
+ if (statvfs(config_->db_dir.c_str(), &stat) == 0) {
+ auto disk_capacity = stat.f_blocks * stat.f_frsize;
+ auto used_disk_size = (stat.f_blocks - stat.f_bavail) * stat.f_frsize;
+ string_stream << "disk_capacity:" << disk_capacity << "\r\n";
+ string_stream << "used_disk_size:" << used_disk_size << "\r\n";
+ double used_disk_percent = static_cast<double>(used_disk_size * 100) /
static_cast<double>(disk_capacity);
+ string_stream << "used_disk_percent: " << used_disk_percent << "%\r\n";
}
- // In keyspace section, we access DB, so we can't do that when loading
- if (!is_loading_ && (all || section == "keyspace")) {
- KeyNumStats stats;
- GetLatestKeyNumStats(ns, &stats);
+ *info = string_stream.str();
+}
- // FIXME(mwish): output still requires std::tm.
- auto last_scan_time = static_cast<time_t>(GetLastScanTime(ns));
- std::tm last_scan_tm{};
- localtime_r(&last_scan_time, &last_scan_tm);
+// WARNING: we must not access DB(i.e. RocksDB) when server is loading since
+// DB is closed and the pointer is invalid. Server may crash if we access DB
during loading.
+// If you add new fields which access DB into INFO command output, make sure
+// this section can't be shown when loading(i.e. !is_loading_).
+void Server::GetInfo(const std::string &ns, const std::vector<std::string>
§ions, std::string *info) {
+ info->clear();
- if (section_cnt++) string_stream << "\r\n";
- string_stream << "# Keyspace\r\n";
- if (last_scan_time == 0) {
- string_stream << "# WARN: DBSIZE SCAN never performed yet\r\n";
- } else {
- string_stream << "# Last DBSIZE SCAN time: " <<
std::put_time(&last_scan_tm, "%a %b %e %H:%M:%S %Y") << "\r\n";
- }
- string_stream << "db0:keys=" << stats.n_key << ",expires=" <<
stats.n_expires << ",avg_ttl=" << stats.avg_ttl
- << ",expired=" << stats.n_expired << "\r\n";
- string_stream << "sequence:" <<
storage->GetDB()->GetLatestSequenceNumber() << "\r\n";
- string_stream << "used_db_size:" << storage->GetTotalSize(ns) << "\r\n";
- string_stream << "max_db_size:" << config_->max_db_size * GiB << "\r\n";
- double used_percent = config_->max_db_size ?
static_cast<double>(storage->GetTotalSize() * 100) /
-
static_cast<double>(config_->max_db_size * GiB)
- : 0;
- string_stream << "used_percent: " << used_percent << "%\r\n";
-
- struct statvfs stat;
- if (statvfs(config_->db_dir.c_str(), &stat) == 0) {
- auto disk_capacity = stat.f_blocks * stat.f_frsize;
- auto used_disk_size = (stat.f_blocks - stat.f_bavail) * stat.f_frsize;
- string_stream << "disk_capacity:" << disk_capacity << "\r\n";
- string_stream << "used_disk_size:" << used_disk_size << "\r\n";
- double used_disk_percent = static_cast<double>(used_disk_size * 100) /
static_cast<double>(disk_capacity);
- string_stream << "used_disk_percent: " << used_disk_percent << "%\r\n";
+ std::vector<std::pair<std::string, std::function<void(Server *, std::string
*)>>> info_funcs = {
+ {"server", &Server::GetServerInfo},
+ {"clients", &Server::GetClientsInfo},
+ {"memory", &Server::GetMemoryInfo},
+ {"persistence", &Server::GetPersistenceInfo},
+ {"stats", &Server::GetStatsInfo},
+ {"replication", &Server::GetReplicationInfo},
+ {"cpu", &Server::GetCpuInfo},
+ {"commandstats", &Server::GetCommandsStatsInfo},
+ {"cluster", &Server::GetClusterInfo},
+ {"keyspace", [&ns](Server *srv, std::string *info) {
srv->GetKeyspaceInfo(ns, info); }},
+ {"rocksdb", &Server::GetRocksDBInfo},
+ };
+
+ std::stringstream string_stream;
+
+ bool all = sections.empty() || std::find(sections.begin(), sections.end(),
"all") != sections.end();
+
+ bool first = true;
+ for (const auto &[sec, fn] : info_funcs) {
+ if (all || std::find(sections.begin(), sections.end(), sec) !=
sections.end()) {
+ if (first)
+ first = false;
+ else
+ string_stream << "\r\n";
+
+ std::string out;
+ fn(this, &out);
+ string_stream << out;
}
}
- // In rocksdb section, we access DB, so we can't do that when loading
- if (!is_loading_ && (all || section == "rocksdb")) {
- std::string rocksdb_info;
- GetRocksDBInfo(&rocksdb_info);
- if (section_cnt++) string_stream << "\r\n";
- string_stream << rocksdb_info;
- }
-
*info = string_stream.str();
}
diff --git a/src/server/server.h b/src/server/server.h
index 55059d97..c82c1019 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -241,7 +241,10 @@ class Server {
void GetRoleInfo(std::string *info);
void GetCommandsStatsInfo(std::string *info);
void GetClusterInfo(std::string *info);
- void GetInfo(const std::string &ns, const std::string §ion, std::string
*info);
+ void GetPersistenceInfo(std::string *info);
+ void GetCpuInfo(std::string *info);
+ void GetKeyspaceInfo(const std::string &ns, std::string *info);
+ void GetInfo(const std::string &ns, const std::vector<std::string>
§ions, std::string *info);
std::string GetRocksDBStatsJson() const;
ReplState GetReplicationState();
diff --git a/tests/gocase/unit/info/info_test.go
b/tests/gocase/unit/info/info_test.go
index 128e5774..aace8279 100644
--- a/tests/gocase/unit/info/info_test.go
+++ b/tests/gocase/unit/info/info_test.go
@@ -119,6 +119,13 @@ func TestInfo(t *testing.T) {
require.Contains(t, splitValues, "count")
require.Contains(t, splitValues, "inf")
})
+
+ t.Run("multiple sections", func(t *testing.T) {
+ info := rdb.Info(ctx, "server", "cpu")
+ require.NoError(t, info.Err())
+ require.Contains(t, info.Val(), "# Server")
+ require.Contains(t, info.Val(), "# CPU")
+ })
}
func TestKeyspaceHitMiss(t *testing.T) {