Add Layer 4 caching to reduce AI load
Implemented 5-minute short-term caching for relationship inference: **store.rs**: - Added relationship_cache SQLite table - save_relationship_cache(), get_cached_relationship() - save_all_relationships_cache(), get_cached_all_relationships() - clear_relationship_cache() - called on memory create/update/delete - Cache duration: 5 minutes (configurable constant) **relationship.rs**: - Modified infer_all_relationships() to use cache - Added get_relationship() function with caching support - Cache hit: return immediately - Cache miss: compute, save to cache, return **base.rs**: - Updated tool_get_relationship() to use cached version - Reduced load from O(n) scan to O(1) cache lookup **Benefits**: - Reduces AI load when frequently querying relationships - Automatic cache invalidation on data changes - Scales better with growing memory count - No user-facing changes **Documentation**: - Updated ARCHITECTURE.md with caching strategy details This addresses scalability concerns for Layer 4 as memory data grows.
This commit is contained in:
@@ -102,6 +102,17 @@ impl MemoryStore {
|
||||
[],
|
||||
)?;
|
||||
|
||||
// Create relationship_cache table (Layer 4 - relationship inference cache)
|
||||
// entity_id = "" for all_relationships cache
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS relationship_cache (
|
||||
entity_id TEXT PRIMARY KEY,
|
||||
data TEXT NOT NULL,
|
||||
cached_at TEXT NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
@@ -137,6 +148,10 @@ impl MemoryStore {
|
||||
memory.updated_at.to_rfc3339(),
|
||||
],
|
||||
)?;
|
||||
|
||||
// Clear relationship cache since memory data changed
|
||||
self.clear_relationship_cache()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -204,6 +219,9 @@ impl MemoryStore {
|
||||
return Err(MemoryError::NotFound(memory.id.clone()));
|
||||
}
|
||||
|
||||
// Clear relationship cache since memory data changed
|
||||
self.clear_relationship_cache()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -217,6 +235,9 @@ impl MemoryStore {
|
||||
return Err(MemoryError::NotFound(id.to_string()));
|
||||
}
|
||||
|
||||
// Clear relationship cache since memory data changed
|
||||
self.clear_relationship_cache()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -464,6 +485,123 @@ impl MemoryStore {
|
||||
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
// ========== Layer 4: Relationship Cache Methods ==========
|
||||
|
||||
/// Cache duration in minutes
|
||||
const RELATIONSHIP_CACHE_DURATION_MINUTES: i64 = 5;
|
||||
|
||||
/// Save relationship inference to cache
|
||||
pub fn save_relationship_cache(
|
||||
&self,
|
||||
entity_id: &str,
|
||||
relationship: &super::relationship::RelationshipInference,
|
||||
) -> Result<()> {
|
||||
let data = serde_json::to_string(relationship)?;
|
||||
let cached_at = Utc::now().to_rfc3339();
|
||||
|
||||
self.conn.execute(
|
||||
"INSERT OR REPLACE INTO relationship_cache (entity_id, data, cached_at) VALUES (?1, ?2, ?3)",
|
||||
params![entity_id, data, cached_at],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get cached relationship inference
|
||||
pub fn get_cached_relationship(
|
||||
&self,
|
||||
entity_id: &str,
|
||||
) -> Result<Option<super::relationship::RelationshipInference>> {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT data, cached_at FROM relationship_cache WHERE entity_id = ?1")?;
|
||||
|
||||
let result = stmt.query_row([entity_id], |row| {
|
||||
let data: String = row.get(0)?;
|
||||
let cached_at: String = row.get(1)?;
|
||||
Ok((data, cached_at))
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok((data, cached_at_str)) => {
|
||||
// Check if cache is still valid (within 5 minutes)
|
||||
let cached_at = DateTime::parse_from_rfc3339(&cached_at_str)
|
||||
.map_err(|e| MemoryError::Parse(e.to_string()))?
|
||||
.with_timezone(&Utc);
|
||||
|
||||
let age_minutes = (Utc::now() - cached_at).num_minutes();
|
||||
|
||||
if age_minutes < Self::RELATIONSHIP_CACHE_DURATION_MINUTES {
|
||||
let relationship: super::relationship::RelationshipInference =
|
||||
serde_json::from_str(&data)?;
|
||||
Ok(Some(relationship))
|
||||
} else {
|
||||
// Cache expired
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Save all relationships list to cache (use empty string as entity_id)
|
||||
pub fn save_all_relationships_cache(
|
||||
&self,
|
||||
relationships: &[super::relationship::RelationshipInference],
|
||||
) -> Result<()> {
|
||||
let data = serde_json::to_string(relationships)?;
|
||||
let cached_at = Utc::now().to_rfc3339();
|
||||
|
||||
self.conn.execute(
|
||||
"INSERT OR REPLACE INTO relationship_cache (entity_id, data, cached_at) VALUES ('', ?1, ?2)",
|
||||
params![data, cached_at],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get cached all relationships list
|
||||
pub fn get_cached_all_relationships(
|
||||
&self,
|
||||
) -> Result<Option<Vec<super::relationship::RelationshipInference>>> {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT data, cached_at FROM relationship_cache WHERE entity_id = ''")?;
|
||||
|
||||
let result = stmt.query_row([], |row| {
|
||||
let data: String = row.get(0)?;
|
||||
let cached_at: String = row.get(1)?;
|
||||
Ok((data, cached_at))
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok((data, cached_at_str)) => {
|
||||
let cached_at = DateTime::parse_from_rfc3339(&cached_at_str)
|
||||
.map_err(|e| MemoryError::Parse(e.to_string()))?
|
||||
.with_timezone(&Utc);
|
||||
|
||||
let age_minutes = (Utc::now() - cached_at).num_minutes();
|
||||
|
||||
if age_minutes < Self::RELATIONSHIP_CACHE_DURATION_MINUTES {
|
||||
let relationships: Vec<super::relationship::RelationshipInference> =
|
||||
serde_json::from_str(&data)?;
|
||||
Ok(Some(relationships))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all relationship caches (call when memories are modified)
|
||||
pub fn clear_relationship_cache(&self) -> Result<()> {
|
||||
self.conn.execute("DELETE FROM relationship_cache", [])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user