From 12f0e4bd0bc34f2174c5e754a1099be91a50075a Mon Sep 17 00:00:00 2001 From: James Lucas Date: Thu, 25 Apr 2024 15:41:38 -0500 Subject: [PATCH] Add redis eviction-policy check/warning (#80) We want to warn users if queues are at potential risk of deletion in low-memory situations. Add a startup check that will log a warning if the eviction policy can't be determined or if an unsafe eviction policy is found. Note: The `warn` is likely to fire all the time on cloud providers, which tend to disable the `CONFIG` command entirely. --- omniqueue/src/backends/redis/mod.rs | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/omniqueue/src/backends/redis/mod.rs b/omniqueue/src/backends/redis/mod.rs index 0281965..b688625 100644 --- a/omniqueue/src/backends/redis/mod.rs +++ b/omniqueue/src/backends/redis/mod.rs @@ -44,6 +44,7 @@ use redis::{ }; use serde::Serialize; use svix_ksuid::KsuidLike; +use thiserror::Error; use tokio::task::JoinSet; use tracing::{debug, error, trace, warn}; @@ -90,6 +91,49 @@ impl RedisConnection for RedisClusterConnectionManager { } } +#[derive(Debug, Error)] +enum EvictionCheckError { + #[error("Unable to verify eviction policy. Ensure `maxmemory-policy` set to `noeviction` or `volatile-*`")] + CheckEvictionPolicyFailed, + #[error("Unsafe eviction policy found. Your queue is at risk of data loss. Please ensure `maxmemory-policy` set to `noeviction` or `volatile-*`")] + UnsafeEvictionPolicy, +} + +async fn check_eviction_policy( + pool: bb8::Pool, +) -> std::result::Result<(), EvictionCheckError> { + let mut conn = pool + .get() + .await + .map_err(|_| EvictionCheckError::CheckEvictionPolicyFailed)?; + + let results: Vec = redis::cmd("CONFIG") + .arg("GET") + .arg("maxmemory-policy") + .query_async::<::Connection, Vec>(&mut *conn) + .await + .map_err(|_| EvictionCheckError::CheckEvictionPolicyFailed)?; + + let eviction_policy = results + .get(1) + .ok_or(EvictionCheckError::CheckEvictionPolicyFailed)?; + + if [ + "noeviction", + "volatile-lru", + "volatile-lfu", + "volatile-random", + "volatile-ttl", + ] + .contains(&eviction_policy.as_str()) + { + tracing::debug!("Eviction policy `{eviction_policy}` found"); + Ok(()) + } else { + Err(EvictionCheckError::UnsafeEvictionPolicy) + } +} + pub struct RedisConfig { pub dsn: String, pub max_connections: u16, @@ -326,6 +370,15 @@ impl RedisBackendBuilder { } }); + join_set.spawn({ + async move { + if let Err(e) = check_eviction_policy(redis.clone()).await { + tracing::warn!("{e}"); + } + Ok(()) + } + }); + Arc::new(join_set) } }