Skip to content

Commit ff33422

Browse files
authored
Fix/empty hostname cluster slots (valkey-io#5373)
Fix: treat empty hostname in CLUSTER SLOTS metadata as absent AWS ElastiCache (plaintext, cluster mode, Redis 7.2.4) returns `hostname: ""` (empty string) in the CLUSTER SLOTS metadata for every node. The parser was wrapping this in `Some("")`, which caused `unwrap_or_else` at line 252 to use the empty string instead of falling back to the IP address from the primary identifier. This resulted in connection addresses like `:6379` (no host), which fail immediately. Because `refresh_slots_inner()` silently drops connection errors, `createClient` resolves successfully but the client has zero usable connections. Every subsequent command fails with `AllConnectionsUnavailable`. The fix filters out empty hostname strings at parse time, so they are treated the same as absent hostnames, and the IP from the primary identifier is used instead. Signed-off-by: Daniel Rinehart <danielr@philo.com>
1 parent 96a6c96 commit ff33422

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* CORE: Track HELLO and AUTH state for reconnection ([#5145](https://github.com/valkey-io/valkey-glide/issues/5145))
2424

2525
#### Fixes
26+
* CORE: Fix empty hostname in CLUSTER SLOTS metadata causing AllConnectionsUnavailable ([#5367](https://github.com/valkey-io/valkey-glide/issues/5367)). AWS ElastiCache (plaintext, cluster mode) returns `hostname: ""` in node metadata, which was used as the connection address instead of falling back to the IP.
2627
* Node: Fix to handle non-string types in toBuffersArray ([#4842](https://github.com/valkey-io/valkey-glide/issues/4842))
2728
* CORE: Enforce connection_timeout for initial standalone connection failures ([#4991](https://github.com/valkey-io/valkey-glide/issues/4991))
2829
* Node: Fixed `Failed to convert napi value Undefined into rust type u32` error ([#5128](https://github.com/valkey-io/valkey-glide/pull/5128))

glide-core/redis-rs/redis/src/cluster_topology.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,10 @@ pub(crate) fn parse_and_count_slots(
200200
metadata_ip =
201201
String::from_utf8_lossy(value_bytes).parse::<IpAddr>().ok();
202202
} else if key_str == "hostname" {
203-
metadata_hostname =
204-
Some(String::from_utf8_lossy(value_bytes).into_owned());
203+
let h = String::from_utf8_lossy(value_bytes);
204+
if !h.is_empty() {
205+
metadata_hostname = Some(h.into_owned());
206+
}
205207
}
206208
// Other keys are ignored - we only need ip and hostname
207209
};
@@ -946,6 +948,46 @@ mod tests {
946948
});
947949
}
948950

951+
#[test]
952+
fn parse_slots_empty_hostname_in_metadata_falls_back_to_ip() {
953+
// ElastiCache (plaintext, cluster mode) returns hostname: "" (empty string)
954+
// in CLUSTER SLOTS metadata. The parser should treat this as absent and
955+
// fall back to the IP address from the primary identifier.
956+
run_with_both_formats(|format| {
957+
let view = Value::Array(vec![slot_value_with_metadata(
958+
0,
959+
16383,
960+
vec![
961+
("172.20.43.71", 6379, Some(vec![("hostname", "")])),
962+
("172.20.78.117", 6379, Some(vec![("hostname", "")])),
963+
],
964+
format,
965+
)]);
966+
967+
let ParsedSlotsResult {
968+
slots_count,
969+
slots,
970+
address_to_ip_map,
971+
} = parse_and_count_slots(&view, None, "fallback").unwrap();
972+
973+
assert_eq!(slots_count, 16384);
974+
assert_eq!(slots.len(), 1);
975+
// Should use the IP as the address, not the empty hostname
976+
assert_eq!(slots[0].master(), "172.20.43.71:6379");
977+
assert_eq!(slots[0].replicas(), vec!["172.20.78.117:6379".to_string()]);
978+
979+
assert_eq!(address_to_ip_map.len(), 2);
980+
assert_eq!(
981+
address_to_ip_map.get("172.20.43.71:6379"),
982+
Some(&"172.20.43.71".parse().unwrap())
983+
);
984+
assert_eq!(
985+
address_to_ip_map.get("172.20.78.117:6379"),
986+
Some(&"172.20.78.117".parse().unwrap())
987+
);
988+
});
989+
}
990+
949991
enum ViewType {
950992
SingleNodeViewFullCoverage,
951993
SingleNodeViewMissingSlots,

0 commit comments

Comments
 (0)