use crate::{
cache::CacheKey,
cache_redis::{RedisCacheError, RedisCacheResult},
};
#[derive(Debug, Clone)]
pub struct RedisClusterRouter {
max_redirects: u32,
slots: crate::cache_redis::RedisClusterSlotMap,
}
impl RedisClusterRouter {
pub fn new(max_redirects: u32) -> Self {
Self {
max_redirects: max_redirects.max(1),
slots: crate::cache_redis::RedisClusterSlotMap::new(),
}
}
pub fn slot_for_key(key: &CacheKey) -> u16 {
crate::cache_redis::redis_cluster_slot(&key.render())
}
pub fn endpoint_for_key(&self, key: &CacheKey) -> Option<&str> {
self.slots.endpoint_for_slot(Self::slot_for_key(key))
}
pub fn apply_redirect(
&mut self,
redirect: &crate::cache_redis::RedisClusterRedirect,
) -> crate::cache_redis::RedisClusterRouteAction {
self.slots.apply_redirect(redirect)
}
pub fn ensure_redirect_allowed(&self, attempts: u32, key: &CacheKey) -> RedisCacheResult<()> {
if attempts > self.max_redirects {
return Err(RedisCacheError::too_many_cluster_redirects(
attempts,
&key.render(),
));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
cache::CacheKey,
cache_redis::{RedisClusterRedirect, RedisClusterRouter},
};
#[test]
fn cluster_router_updates_moved_slot_mapping() {
let key = CacheKey::new("rs-zero", ["users", "42"]);
let mut router = RedisClusterRouter::new(2);
let redirect = RedisClusterRedirect::parse(&format!(
"MOVED {} 10.0.0.2:6379",
RedisClusterRouter::slot_for_key(&key)
))
.expect("redirect");
router.apply_redirect(&redirect);
assert_eq!(router.endpoint_for_key(&key), Some("10.0.0.2:6379"));
assert!(router.ensure_redirect_allowed(3, &key).is_err());
}
}