Skip to content

Commit d59cdbe

Browse files
committed
fix(server): handle INFO command with multiple sections correctly
Fix INFO command to support multiple sections, resolving syntax errors when more than one section is requested. Valid sections are returned, while invalid ones are ignored, matching Redis server behavior. Adds tests for positive and negative multi-section cases. Improves code clarity and ensures metrics are fetched efficiently for relevant sections. Signed-off-by: Gil Levkovich <[email protected]>
1 parent 1e14f80 commit d59cdbe

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

src/server/server_family.cc

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,7 +3270,7 @@ string ServerFamily::FormatInfoMetrics(const Metrics& m, std::string_view sectio
32703270
if (show) {
32713271
size_t total = stats.events.hits + stats.events.misses;
32723272
double hit_ratio =
3273-
(total > 0) ? static_cast<double>(stats.events.hits) / (total)*100.0 : 0.0;
3273+
(total > 0) ? static_cast<double>(stats.events.hits) / (total) * 100.0 : 0.0;
32743274
string val = StrCat("keys=", stats.key_count, ",expires=", stats.expire_count,
32753275
",hits=", stats.events.hits, ",misses=", stats.events.misses,
32763276
",hit_ratio=", absl::StrFormat("%.2f", hit_ratio),
@@ -3330,26 +3330,44 @@ string ServerFamily::FormatInfoMetrics(const Metrics& m, std::string_view sectio
33303330
}
33313331

33323332
void ServerFamily::Info(CmdArgList args, const CommandContext& cmd_cntx) {
3333-
if (args.size() > 1) {
3334-
return cmd_cntx.rb->SendError(kSyntaxErr);
3335-
}
3336-
3337-
string section;
3333+
std::vector<std::string> sections;
3334+
auto need_metrics{false}; // Save time - do not fetch metrics if we don't need them.
3335+
Metrics metrics;
33383336

3339-
if (args.size() == 1) {
3340-
section = absl::AsciiStrToUpper(ArgS(args, 0));
3337+
sections.reserve(args.size());
3338+
for (const auto& arg : args) {
3339+
sections.emplace_back(absl::AsciiStrToUpper(arg));
3340+
const auto& section = sections.back();
3341+
if (!need_metrics && (section != "SERVER") && (section != "REPLICATION")) {
3342+
need_metrics = true;
3343+
metrics = GetMetrics(cmd_cntx.conn_cntx->ns);
3344+
}
33413345
}
33423346

3343-
Metrics metrics;
3344-
3345-
// Save time by not calculating metrics if we don't need them.
3346-
if (!(section == "SERVER" || section == "REPLICATION")) {
3347-
metrics = GetMetrics(cmd_cntx.conn_cntx->ns);
3348-
} else if (!IsMaster()) {
3347+
if (!need_metrics && !IsMaster()) {
33493348
metrics.replica_side_info = GetReplicaSummary();
33503349
}
33513350

3352-
string info = FormatInfoMetrics(metrics, section, cmd_cntx.conn_cntx->conn()->IsPrivileged());
3351+
std::string info;
3352+
// For multiple requested sections, invalid section names are ignored (not included in the output).
3353+
// The command does not abort or return an error if some sections are invalid. This matches Redis behavior.
3354+
if (sections.empty()) { // No sections: default to all sections.
3355+
info = FormatInfoMetrics(metrics, "", cmd_cntx.conn_cntx->conn()->IsPrivileged());
3356+
} else if (sections.size() == 1) { // Single section
3357+
info = FormatInfoMetrics(metrics, sections[0], cmd_cntx.conn_cntx->conn()->IsPrivileged());
3358+
} else { // Multiple sections: concatenate results for each requested section.
3359+
for (const auto& section : sections) {
3360+
const std::string section_str =
3361+
FormatInfoMetrics(metrics, section, cmd_cntx.conn_cntx->conn()->IsPrivileged());
3362+
if (!section_str.empty()) {
3363+
if (!info.empty()) {
3364+
absl::StrAppend(&info, "\r\n", section_str);
3365+
} else {
3366+
info = section_str;
3367+
}
3368+
}
3369+
}
3370+
}
33533371

33543372
auto* rb = static_cast<RedisReplyBuilder*>(cmd_cntx.rb);
33553373
rb->SendVerbatimString(info);

src/server/server_family_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,4 +646,22 @@ TEST_F(ServerFamilyTest, PubSubCommandErr) {
646646
"PUBSUB HELP."));
647647
}
648648

649+
TEST_F(ServerFamilyTest, InfoMultipleSections) {
650+
// Check that when querying multiple valid sections, both are returned non empty.
651+
Run({"set", "foo", "bar"}); // set some data
652+
auto resp = Run({"info", "replication", "persistence"});
653+
auto info = resp.GetString();
654+
EXPECT_NE(info.find("# Replication"), std::string::npos);
655+
EXPECT_NE(info.find("# Persistence"), std::string::npos);
656+
}
657+
658+
TEST_F(ServerFamilyTest, InfoMultipleSectionsInvalid) {
659+
// Check that when querying a valid and an invalid section, only the valid section is returned.
660+
Run({"set", "foo", "bar"}); // set some data
661+
auto resp = Run({"info", "replication", "invalidsection"});
662+
auto info = resp.GetString();
663+
EXPECT_NE(info.find("# Replication"), std::string::npos);
664+
EXPECT_EQ(info.find("# invalidsection"), std::string::npos);
665+
}
666+
649667
} // namespace dfly

0 commit comments

Comments
 (0)