From 0830e03f89a8d43a9750912b3e00822a09d2685e Mon Sep 17 00:00:00 2001 From: dalew <91363952+dal1wg@users.noreply.github.com> Date: Wed, 17 Jun 2026 00:21:00 +0800 Subject: [PATCH] fix(redis): format INFO output as plain text in cluster mode --- crates/dbx-core/src/db/redis_driver.rs | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/dbx-core/src/db/redis_driver.rs b/crates/dbx-core/src/db/redis_driver.rs index d42abba2a..cd887fe10 100644 --- a/crates/dbx-core/src/db/redis_driver.rs +++ b/crates/dbx-core/src/db/redis_driver.rs @@ -981,6 +981,15 @@ where redis::cmd("FLUSHDB").query_async::<()>(con).await.map_err(|e| e.to_string()) } +/// Extract a string reference from a `RedisRawValue` if it is a BulkString or SimpleString. +fn redis_raw_value_as_str(v: &RedisRawValue) -> Option<&str> { + match v { + RedisRawValue::BulkString(bytes) => std::str::from_utf8(bytes).ok(), + RedisRawValue::SimpleString(s) => Some(s.as_str()), + _ => None, + } +} + pub async fn execute_command( con: &mut C, command_text: &str, @@ -1002,6 +1011,34 @@ where } let raw: RedisRawValue = cmd.query_async(con).await.map_err(|e| e.to_string())?; + // Special handling for INFO command in cluster mode. + // redis-rs ClusterConnection routes INFO to all primaries and + // aggregates the results as a Map(node_addr → full_info_text). + // We detect this pattern and format it as human-readable plain text + // instead of converting to a JSON array of {key, value} objects. + if command == "INFO" { + if let RedisRawValue::Map(entries) = &raw { + // Cluster-aggregated INFO has multi-line values starting with "# Server". + // This distinguishes it from a RESP3 standalone INFO map where values + // are single field values (e.g. "redis_version", "os"). + let is_cluster_aggregation = + entries.iter().any(|(_, v)| redis_raw_value_as_str(v).is_some_and(|s| s.starts_with("# Server"))); + + if is_cluster_aggregation { + let mut parts: Vec = Vec::with_capacity(entries.len()); + for (key, value) in entries { + let addr = redis_raw_value_as_str(key); + let info = redis_raw_value_as_str(value); + if let (Some(addr), Some(info)) = (addr, info) { + parts.push(format!("{addr}\n{info}")); + } + } + let text = parts.join("\n\n"); + return Ok(RedisCommandResult { command, safety, value: serde_json::Value::String(text) }); + } + } + } + Ok(RedisCommandResult { command, safety, value: redis_command_raw_to_json(raw) }) }