rovenue/cache/
remote_config.rs

1use rusqlite::params;
2
3use crate::error::RovenueResult;
4
5use super::CacheStore;
6
7/// Persistent store for the last-known Remote Config response, mirroring
8/// [`OfferingsCacheRepo`](super::offerings::OfferingsCacheRepo). The cached
9/// `body` is the raw `ConfigResponse` JSON (the `data` payload) keyed by a
10/// stable resource name so flags/experiments keep resolving offline.
11pub struct RemoteConfigCacheRepo<'a> {
12    store: &'a CacheStore,
13}
14
15impl<'a> RemoteConfigCacheRepo<'a> {
16    pub fn new(store: &'a CacheStore) -> Self {
17        Self { store }
18    }
19
20    pub fn get(&self, resource: &str) -> RovenueResult<Option<String>> {
21        self.store.with_conn(|c| {
22            let mut stmt = c.prepare("SELECT body FROM remote_config_cache WHERE resource = ?1")?;
23            let mut rows = stmt.query(params![resource])?;
24            if let Some(r) = rows.next()? {
25                Ok(Some(r.get::<_, String>(0)?))
26            } else {
27                Ok(None)
28            }
29        })
30    }
31
32    pub fn put(&self, resource: &str, body: &str, updated_at_ms: u64) -> RovenueResult<()> {
33        self.store.with_conn(|c| {
34            c.execute(
35                "INSERT INTO remote_config_cache (resource, body, updated_at_ms)
36                 VALUES (?1, ?2, ?3)
37                 ON CONFLICT(resource) DO UPDATE SET
38                   body = excluded.body,
39                   updated_at_ms = excluded.updated_at_ms",
40                params![resource, body, updated_at_ms as i64],
41            )?;
42            Ok(())
43        })
44    }
45}