use crate::redis::connection::RedisPool;
use crate::storage::LogStore;
use crate::topic::retention::RetentionPolicy;
use crate::topic::store::LogEntry;
#[derive(Clone)]
pub struct RedisLogStore {
pool: RedisPool,
}
impl RedisLogStore {
pub fn new(pool: RedisPool) -> Self {
Self { pool }
}
fn log_key(&self, topic: &str) -> String {
self.pool.topic_key("log", topic)
}
fn encode_entry(entry: &LogEntry) -> Vec<u8> {
let mut buf = Vec::new();
ciborium::into_writer(entry, &mut buf).expect("LogEntry CBOR serialization failed");
buf
}
fn decode_entry(data: &[u8]) -> Option<LogEntry> {
ciborium::from_reader(data).ok()
}
}
impl LogStore for RedisLogStore {
fn append(&self, topic: &str, entry: LogEntry, retention: RetentionPolicy) {
let key = self.log_key(topic);
let member = Self::encode_entry(&entry);
let offset = entry.offset;
let (key2, member2) = (key.clone(), member.clone());
let _: () = self.pool.sync_cmd(move |c| {
redis::cmd("ZADD")
.arg(&key2)
.arg(offset)
.arg(&member2)
.query::<()>(c)
});
match retention {
RetentionPolicy::None => {
let key3 = key.clone();
let _: () = self
.pool
.sync_cmd(move |c| redis::cmd("DEL").arg(&key3).query::<()>(c));
}
RetentionPolicy::Count(n) if n > 0 => {
let key3 = key.clone();
let _: () = self.pool.sync_cmd(move |c| {
redis::cmd("ZREMRANGEBYRANK")
.arg(&key3)
.arg(0)
.arg(-((n as i64) + 1))
.query::<()>(c)
});
}
RetentionPolicy::Latest => {
let key3 = key.clone();
let _: () = self.pool.sync_cmd(move |c| {
redis::cmd("ZREMRANGEBYRANK")
.arg(&key3)
.arg(0)
.arg(-2)
.query::<()>(c)
});
}
_ => {}
}
}
fn range(&self, topic: &str, from: i64, to: i64) -> Vec<LogEntry> {
let key = self.log_key(topic);
let data: Vec<Vec<u8>> = self.pool.sync_cmd(|c| {
redis::cmd("ZRANGEBYSCORE")
.arg(&key)
.arg(from)
.arg(to)
.query::<Vec<Vec<u8>>>(c)
});
data.iter().filter_map(|d| Self::decode_entry(d)).collect()
}
fn latest(&self, topic: &str) -> Option<LogEntry> {
let key = self.log_key(topic);
let data: Vec<Vec<u8>> = self.pool.sync_cmd(|c| {
redis::cmd("ZREVRANGE")
.arg(&key)
.arg(0)
.arg(0)
.query::<Vec<Vec<u8>>>(c)
});
data.first().and_then(|d| Self::decode_entry(d))
}
fn remove(&self, topic: &str) {
let key = self.log_key(topic);
let _: () = self
.pool
.sync_cmd(|c| redis::cmd("DEL").arg(&key).query::<()>(c));
}
}