Skip to main content

rustvello_redis/
client_data_store.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use redis::AsyncCommands;
5
6use rustvello_core::client_data_store::ClientDataStore;
7use rustvello_core::error::RustvelloResult;
8
9use crate::connection::{redis_err, scan_keys, RedisPool};
10
11/// Redis-backed client data store.
12///
13/// Uses simple GET/SET with content-hash keys for deduplication.
14#[non_exhaustive]
15pub struct RedisClientDataStore {
16    pool: Arc<RedisPool>,
17    key_prefix: String,
18}
19
20impl RedisClientDataStore {
21    pub fn new(pool: Arc<RedisPool>) -> Self {
22        let p = pool.prefix();
23        Self {
24            key_prefix: format!("{p}cds:"),
25            pool,
26        }
27    }
28}
29
30#[async_trait]
31impl ClientDataStore for RedisClientDataStore {
32    async fn store(&self, key: &str, value: &str) -> RustvelloResult<()> {
33        let mut conn = self.pool.conn().await?;
34        let rkey = format!("{}{}", self.key_prefix, key);
35        conn.set::<_, _, ()>(&rkey, value).await.map_err(redis_err)
36    }
37
38    async fn retrieve(&self, key: &str) -> RustvelloResult<String> {
39        let mut conn = self.pool.conn().await?;
40        let rkey = format!("{}{}", self.key_prefix, key);
41        let val: Option<String> = conn.get(&rkey).await.map_err(redis_err)?;
42        val.ok_or_else(|| {
43            rustvello_core::error::RustvelloError::state_backend(format!(
44                "CDS key not found: {}",
45                key
46            ))
47        })
48    }
49
50    async fn purge(&self) -> RustvelloResult<()> {
51        let mut conn = self.pool.conn().await?;
52        let keys = scan_keys(&mut conn, &format!("{}*", self.key_prefix)).await?;
53        if !keys.is_empty() {
54            conn.del::<_, ()>(keys).await.map_err(redis_err)?;
55        }
56        Ok(())
57    }
58}