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)]
39pub struct ChainIndexCache {
40 slot: RwLock<Option<CachedIndex>>,
41 ttl: Duration,
42 recheck: Duration,
43}
44
45impl Default for ChainIndexCache {
46 fn default() -> Self {
47 Self::new(DEFAULT_TTL, DEFAULT_RECHECK)
48 }
49}
50
51impl ChainIndexCache {
52 #[must_use]
53 pub fn new(ttl: Duration, recheck: Duration) -> Self {
54 Self {
55 slot: RwLock::new(None),
56 ttl,
57 recheck,
58 }
59 }
60
61 pub async fn get(
62 &self,
63 repo: &AccessControlRepository,
64 sources: Arc<ChainSources>,
65 ) -> AuthzResult<Arc<ParentChainIndex>> {
66 let now = Instant::now();
67 let fresh = {
68 let slot = self.slot.read().await;
69 slot.as_ref()
70 .filter(|cached| now.duration_since(cached.checked_at) < self.recheck)
71 .map(|cached| Arc::clone(&cached.index))
72 };
73 if let Some(index) = fresh {
74 return Ok(index);
75 }
76
77 {
78 let mut slot = self.slot.write().await;
79 if let Some(cached) = slot.as_mut() {
80 if now.duration_since(cached.checked_at) < self.recheck {
81 return Ok(Arc::clone(&cached.index));
82 }
83 if let Ok(fingerprint) = repo.chain_fingerprint().await
87 && fingerprint == cached.fingerprint
88 && now.duration_since(cached.loaded_at) < self.ttl
89 {
90 cached.checked_at = now;
91 return Ok(Arc::clone(&cached.index));
92 }
93 }
94 }
95
96 let fingerprint = repo.chain_fingerprint().await?;
97 let index = Arc::new(ParentChainIndex::load(repo, sources).await?);
98 *self.slot.write().await = Some(CachedIndex {
99 index: Arc::clone(&index),
100 fingerprint,
101 loaded_at: now,
102 checked_at: now,
103 });
104 Ok(index)
105 }
106}