[model-gateway] Optimize consistent hashing hot path to eliminate allocations (#17467)

This commit is contained in:
Praneth Paruchuri
2026-01-27 22:46:11 -08:00
committed by GitHub
parent c0b4dd68a2
commit 897c35b457
4 changed files with 58 additions and 15 deletions
+10 -13
View File
@@ -57,27 +57,24 @@ impl ConsistentHashRing {
}
}
/// Get K owners for a key
pub fn get_owners(&self, key: &str) -> Vec<String> {
pub fn get_owners(&self, key: &str) -> Vec<&str> {
if self.ring.is_empty() {
return Vec::new();
}
let key_hash = Self::hash(key);
let mut owners = Vec::new();
let mut seen_nodes = HashSet::new();
let mut owners = Vec::with_capacity(NUM_OWNERS);
let total_unique_nodes = self.node_hashes.len();
// Find the first node >= key_hash (clockwise)
let mut iter = self.ring.range(key_hash..);
while owners.len() < NUM_OWNERS && seen_nodes.len() < total_unique_nodes {
while owners.len() < NUM_OWNERS && owners.len() < total_unique_nodes {
if let Some((_, node)) = iter.next() {
if !seen_nodes.contains(node) {
owners.push(node.clone());
seen_nodes.insert(node.clone());
if !owners.contains(&node.as_str()) {
owners.push(node.as_str());
}
} else {
// Wrap around to the beginning
iter = self.ring.range(..);
}
}
@@ -87,7 +84,7 @@ impl ConsistentHashRing {
/// Check if a node is an owner of a key
pub fn is_owner(&self, key: &str, node_name: &str) -> bool {
self.get_owners(key).contains(&node_name.to_string())
self.get_owners(key).contains(&node_name)
}
/// Get all nodes in the ring
@@ -197,8 +194,8 @@ mod tests {
let owners = ring.get_owners("test_key");
// Should return all available nodes (2) without infinite loop
assert_eq!(owners.len(), 2);
assert!(owners.contains(&"node1".to_string()));
assert!(owners.contains(&"node2".to_string()));
assert!(owners.contains(&"node1"));
assert!(owners.contains(&"node2"));
}
#[test]
+8 -2
View File
@@ -379,6 +379,9 @@ impl RateLimitStore {
pub fn get_owners(&self, key: &str) -> Vec<String> {
let ring = self.hash_ring.read();
ring.get_owners(key)
.into_iter()
.map(|s| s.to_string())
.collect()
}
/// Get or create counter (only if this node is an owner)
@@ -441,8 +444,11 @@ impl RateLimitStore {
for key in counters.keys() {
let owners = ring.get_owners(key);
// Check if any owner has failed
if owners.iter().any(|owner| failed_nodes.contains(owner)) {
if owners
.iter()
.any(|&owner| failed_nodes.iter().any(|f| f == owner))
{
// Check if we are now an owner
if ring.is_owner(key, &self.self_name) {
affected_keys.push(key.clone());