systemprompt_security/authz/parent_chain/
cache.rs1use std::sync::Arc;
17use std::time::{Duration, Instant};
18
19use tokio::sync::RwLock;
20
21use super::{ChainSources, ParentChainIndex};
22use crate::authz::error::AuthzResult;
23use crate::authz::repository::{AccessControlRepository, ChainFingerprint};
24
25const DEFAULT_TTL: Duration = Duration::from_secs(60);
26const DEFAULT_RECHECK: Duration = Duration::from_secs(5);
27
28#[derive(Debug)]
29struct CachedIndex {
30 index: Arc<ParentChainIndex>,
31 fingerprint: ChainFingerprint,
32 loaded_at: Instant,
33 checked_at: Instant,
34}
35
36#[derive(Debug)]
37pub struct ChainIndexCache {
38 slot: RwLock<Option<CachedIndex>>,
39 ttl: Duration,
40 recheck: Duration,
41}
42
43impl Default for ChainIndexCache {
44 fn default() -> Self {
45 Self::new(DEFAULT_TTL, DEFAULT_RECHECK)
46 }
47}
48
49impl ChainIndexCache {
50 #[must_use]
51 pub fn new(ttl: Duration, recheck: Duration) -> Self {
52 Self {
53 slot: RwLock::new(None),
54 ttl,
55 recheck,
56 }
57 }
58
59 pub async fn get(
60 &self,
61 repo: &AccessControlRepository,
62 sources: Arc<ChainSources>,
63 ) -> AuthzResult<Arc<ParentChainIndex>> {
64 let now = Instant::now();
65 let fresh = {
66 let slot = self.slot.read().await;
67 slot.as_ref()
68 .filter(|cached| now.duration_since(cached.checked_at) < self.recheck)
69 .map(|cached| Arc::clone(&cached.index))
70 };
71 if let Some(index) = fresh {
72 return Ok(index);
73 }
74
75 {
76 let mut slot = self.slot.write().await;
77 if let Some(cached) = slot.as_mut() {
78 if now.duration_since(cached.checked_at) < self.recheck {
79 return Ok(Arc::clone(&cached.index));
80 }
81 if let Ok(fingerprint) = repo.chain_fingerprint().await
82 && fingerprint == cached.fingerprint
83 && now.duration_since(cached.loaded_at) < self.ttl
84 {
85 cached.checked_at = now;
86 return Ok(Arc::clone(&cached.index));
87 }
88 }
89 }
90
91 let fingerprint = repo.chain_fingerprint().await?;
92 let index = Arc::new(ParentChainIndex::load(repo, sources).await?);
93 *self.slot.write().await = Some(CachedIndex {
94 index: Arc::clone(&index),
95 fingerprint,
96 loaded_at: now,
97 checked_at: now,
98 });
99 Ok(index)
100 }
101}