Skip to main content

ubiquity_database/
memory_pools.rs

1//! Memory pool implementations for 7-wide parallel processing
2
3use async_trait::async_trait;
4use std::collections::HashMap;
5use std::path::PathBuf;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8use crate::{DatabaseConfig, DatabaseError, MemoryPool, MemoryPoolManager, PoolStats};
9
10/// SQLite-based memory pools using file storage
11pub struct SqliteMemoryPools {
12    pools: Vec<Arc<SqliteMemoryPool>>,
13    config: DatabaseConfig,
14}
15
16impl SqliteMemoryPools {
17    pub async fn new(config: DatabaseConfig) -> Result<Self, DatabaseError> {
18        // Create pool directory
19        tokio::fs::create_dir_all(&config.sqlite.pool_directory).await?;
20        
21        // Create 7 pools
22        let mut pools = Vec::with_capacity(config.memory_pools.pool_count);
23        for i in 0..config.memory_pools.pool_count {
24            let pool = SqliteMemoryPool::new(
25                config.sqlite.pool_directory.join(format!("pool_{}.db", i)),
26                config.memory_pools.max_pool_size_mb,
27            ).await?;
28            pools.push(Arc::new(pool));
29        }
30        
31        Ok(Self { pools, config })
32    }
33}
34
35#[async_trait]
36impl MemoryPoolManager for SqliteMemoryPools {
37    async fn get_pool(&self, index: usize) -> Result<Box<dyn MemoryPool>, DatabaseError> {
38        if index >= self.pools.len() {
39            return Err(DatabaseError::InvalidPoolIndex(index));
40        }
41        Ok(Box::new(self.pools[index].clone()))
42    }
43    
44    async fn get_pool_for_key(&self, key: &str) -> Result<Box<dyn MemoryPool>, DatabaseError> {
45        // Simple consistent hashing
46        let hash = seahash::hash(key.as_bytes());
47        let index = (hash as usize) % self.pools.len();
48        self.get_pool(index).await
49    }
50    
51    async fn all_stats(&self) -> Result<Vec<PoolStats>, DatabaseError> {
52        let mut stats = Vec::new();
53        for pool in &self.pools {
54            stats.push(pool.stats().await?);
55        }
56        Ok(stats)
57    }
58    
59    async fn rebalance(&self) -> Result<(), DatabaseError> {
60        // SQLite pools don't need rebalancing
61        Ok(())
62    }
63}
64
65/// Individual SQLite memory pool
66#[derive(Clone)]
67pub struct SqliteMemoryPool {
68    pool: Arc<sqlx::SqlitePool>,
69    stats: Arc<RwLock<PoolStats>>,
70    max_size_bytes: usize,
71}
72
73impl SqliteMemoryPool {
74    async fn new(path: PathBuf, max_size_mb: usize) -> Result<Self, DatabaseError> {
75        let pool = sqlx::SqlitePool::connect(&format!("sqlite:{}", path.display())).await?;
76        
77        // Create table
78        sqlx::query(
79            "CREATE TABLE IF NOT EXISTS memory_pool (
80                key TEXT PRIMARY KEY,
81                value BLOB NOT NULL,
82                created_at INTEGER NOT NULL,
83                accessed_at INTEGER NOT NULL,
84                access_count INTEGER DEFAULT 1
85            )"
86        )
87        .execute(&pool)
88        .await?;
89        
90        // Create indices
91        sqlx::query("CREATE INDEX IF NOT EXISTS idx_accessed_at ON memory_pool(accessed_at)")
92            .execute(&pool)
93            .await?;
94        
95        let stats = PoolStats {
96            size: 0,
97            items: 0,
98            hits: 0,
99            misses: 0,
100            evictions: 0,
101        };
102        
103        Ok(Self {
104            pool: Arc::new(pool),
105            stats: Arc::new(RwLock::new(stats)),
106            max_size_bytes: max_size_mb * 1024 * 1024,
107        })
108    }
109}
110
111#[async_trait]
112impl MemoryPool for SqliteMemoryPool {
113    async fn store(&self, key: &str, value: &[u8]) -> Result<(), DatabaseError> {
114        let now = chrono::Utc::now().timestamp();
115        
116        // Check if we need to evict
117        let current_size = self.get_total_size().await?;
118        if current_size + value.len() > self.max_size_bytes {
119            self.evict_lru().await?;
120        }
121        
122        sqlx::query(
123            "INSERT OR REPLACE INTO memory_pool (key, value, created_at, accessed_at) 
124             VALUES (?, ?, ?, ?)"
125        )
126        .bind(key)
127        .bind(value)
128        .bind(now)
129        .bind(now)
130        .execute(&*self.pool)
131        .await?;
132        
133        let mut stats = self.stats.write().await;
134        stats.items += 1;
135        stats.size += value.len();
136        
137        Ok(())
138    }
139    
140    async fn retrieve(&self, key: &str) -> Result<Option<Vec<u8>>, DatabaseError> {
141        let now = chrono::Utc::now().timestamp();
142        
143        let result: Option<(Vec<u8>,)> = sqlx::query_as(
144            "UPDATE memory_pool 
145             SET accessed_at = ?, access_count = access_count + 1 
146             WHERE key = ? 
147             RETURNING value"
148        )
149        .bind(now)
150        .bind(key)
151        .fetch_optional(&*self.pool)
152        .await?;
153        
154        let mut stats = self.stats.write().await;
155        if result.is_some() {
156            stats.hits += 1;
157        } else {
158            stats.misses += 1;
159        }
160        
161        Ok(result.map(|r| r.0))
162    }
163    
164    async fn delete(&self, key: &str) -> Result<(), DatabaseError> {
165        let result = sqlx::query("DELETE FROM memory_pool WHERE key = ?")
166            .bind(key)
167            .execute(&*self.pool)
168            .await?;
169        
170        if result.rows_affected() > 0 {
171            let mut stats = self.stats.write().await;
172            stats.items -= 1;
173        }
174        
175        Ok(())
176    }
177    
178    async fn list_keys(&self, prefix: &str) -> Result<Vec<String>, DatabaseError> {
179        let pattern = format!("{}%", prefix);
180        let keys: Vec<(String,)> = sqlx::query_as(
181            "SELECT key FROM memory_pool WHERE key LIKE ? ORDER BY key"
182        )
183        .bind(pattern)
184        .fetch_all(&*self.pool)
185        .await?;
186        
187        Ok(keys.into_iter().map(|k| k.0).collect())
188    }
189    
190    async fn stats(&self) -> Result<PoolStats, DatabaseError> {
191        Ok(self.stats.read().await.clone())
192    }
193}
194
195impl SqliteMemoryPool {
196    async fn get_total_size(&self) -> Result<usize, DatabaseError> {
197        let size: (i64,) = sqlx::query_as(
198            "SELECT COALESCE(SUM(LENGTH(value)), 0) FROM memory_pool"
199        )
200        .fetch_one(&*self.pool)
201        .await?;
202        
203        Ok(size.0 as usize)
204    }
205    
206    async fn evict_lru(&self) -> Result<(), DatabaseError> {
207        // Evict 10% of least recently used items
208        let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM memory_pool")
209            .fetch_one(&*self.pool)
210            .await?;
211        
212        let to_evict = (count.0 as f64 * 0.1).ceil() as i64;
213        
214        sqlx::query(
215            "DELETE FROM memory_pool 
216             WHERE key IN (
217                 SELECT key FROM memory_pool 
218                 ORDER BY accessed_at ASC 
219                 LIMIT ?
220             )"
221        )
222        .bind(to_evict)
223        .execute(&*self.pool)
224        .await?;
225        
226        let mut stats = self.stats.write().await;
227        stats.evictions += to_evict as u64;
228        stats.items -= to_evict as usize;
229        
230        Ok(())
231    }
232}
233
234/// Astra DB-based memory pools using collections
235#[cfg(feature = "astra")]
236pub struct AstraMemoryPools {
237    pools: Vec<Arc<AstraMemoryPool>>,
238    config: DatabaseConfig,
239}
240
241#[cfg(feature = "astra")]
242impl AstraMemoryPools {
243    pub async fn new(config: DatabaseConfig) -> Result<Self, DatabaseError> {
244        // Would initialize Astra DB collections here
245        // For now, return not implemented
246        Err(DatabaseError::NotImplemented)
247    }
248}
249
250#[cfg(feature = "astra")]
251#[async_trait]
252impl MemoryPoolManager for AstraMemoryPools {
253    async fn get_pool(&self, index: usize) -> Result<Box<dyn MemoryPool>, DatabaseError> {
254        if index >= self.pools.len() {
255            return Err(DatabaseError::InvalidPoolIndex(index));
256        }
257        Ok(Box::new(self.pools[index].clone()))
258    }
259    
260    async fn get_pool_for_key(&self, key: &str) -> Result<Box<dyn MemoryPool>, DatabaseError> {
261        let hash = seahash::hash(key.as_bytes());
262        let index = (hash as usize) % self.pools.len();
263        self.get_pool(index).await
264    }
265    
266    async fn all_stats(&self) -> Result<Vec<PoolStats>, DatabaseError> {
267        let mut stats = Vec::new();
268        for pool in &self.pools {
269            stats.push(pool.stats().await?);
270        }
271        Ok(stats)
272    }
273    
274    async fn rebalance(&self) -> Result<(), DatabaseError> {
275        // Astra DB handles rebalancing automatically
276        Ok(())
277    }
278}
279
280/// Individual Astra memory pool
281#[cfg(feature = "astra")]
282#[derive(Clone)]
283pub struct AstraMemoryPool {
284    collection_name: String,
285    stats: Arc<RwLock<PoolStats>>,
286}
287
288#[cfg(feature = "astra")]
289#[async_trait]
290impl MemoryPool for AstraMemoryPool {
291    async fn store(&self, _key: &str, _value: &[u8]) -> Result<(), DatabaseError> {
292        Err(DatabaseError::NotImplemented)
293    }
294    
295    async fn retrieve(&self, _key: &str) -> Result<Option<Vec<u8>>, DatabaseError> {
296        Err(DatabaseError::NotImplemented)
297    }
298    
299    async fn delete(&self, _key: &str) -> Result<(), DatabaseError> {
300        Err(DatabaseError::NotImplemented)
301    }
302    
303    async fn list_keys(&self, _prefix: &str) -> Result<Vec<String>, DatabaseError> {
304        Err(DatabaseError::NotImplemented)
305    }
306    
307    async fn stats(&self) -> Result<PoolStats, DatabaseError> {
308        Ok(self.stats.read().await.clone())
309    }
310}
311
312// Implement MemoryPool for Arc<SqliteMemoryPool>
313#[async_trait]
314impl MemoryPool for Arc<SqliteMemoryPool> {
315    async fn store(&self, key: &str, value: &[u8]) -> Result<(), DatabaseError> {
316        self.as_ref().store(key, value).await
317    }
318    
319    async fn retrieve(&self, key: &str) -> Result<Option<Vec<u8>>, DatabaseError> {
320        self.as_ref().retrieve(key).await
321    }
322    
323    async fn delete(&self, key: &str) -> Result<(), DatabaseError> {
324        self.as_ref().delete(key).await
325    }
326    
327    async fn list_keys(&self, prefix: &str) -> Result<Vec<String>, DatabaseError> {
328        self.as_ref().list_keys(prefix).await
329    }
330    
331    async fn stats(&self) -> Result<PoolStats, DatabaseError> {
332        self.as_ref().stats().await
333    }
334}
335
336mod seahash {
337    pub fn hash(data: &[u8]) -> u64 {
338        let mut hasher = std::collections::hash_map::DefaultHasher::new();
339        hasher.write(data);
340        hasher.finish()
341    }
342    
343    use std::hash::Hasher;
344}