Skip to main content
Glama

Codebase MCP Server

by Ravenight13
cache-service.md6.57 kB
# SQLite Cache Service ## Overview The SQLite cache service provides persistent, file-based caching with automatic TTL (Time-To-Live) management for offline fallback when PostgreSQL is unavailable. **Location**: `src/services/cache.py` ## Key Features - **30-minute TTL**: All cached entries automatically expire after 30 minutes - **Async Context Manager**: Automatic connection lifecycle management - **Schema Mirroring**: Matches PostgreSQL table structure for transparent fallback - **Type Safety**: Full mypy --strict compliance with comprehensive type annotations - **Performance**: <1ms cache lookups via indexed primary keys - **File-based Persistence**: Survives process restarts (default: `cache/project_status.db`) ## Constitutional Compliance - **Principle II**: Local-first (no external dependencies) - **Principle III**: Protocol compliance (async operations matching AsyncPG) - **Principle IV**: Performance (<1ms lookups, <100ms bulk operations) - **Principle V**: Production quality (comprehensive error handling) - **Principle VIII**: Type safety (mypy --strict compliance) ## Usage ### Basic Operations ```python from src.services.cache import SQLiteCache from uuid import uuid4 # Initialize cache with async context manager async with SQLiteCache() as cache: # Cache vendor extractor vendor_data = { "id": str(uuid4()), "name": "vendor_abc", "status": "operational", "version": 1, "extractor_version": "2.3.1", "metadata_": {...}, "created_at": datetime.now(timezone.utc).isoformat(), "updated_at": datetime.now(timezone.utc).isoformat(), "created_by": "mcp-client" } await cache.set_vendor("vendor_abc", vendor_data) # Retrieve cached vendor cached = await cache.get_vendor("vendor_abc") if cached: print(f"Found vendor: {cached['name']}") # Cache work item item_id = uuid4() work_item_data = {...} await cache.set_work_item(item_id, work_item_data) # Check staleness is_stale = await cache.is_stale(f"vendor:{vendor_name}") if is_stale: print("Entry expired, needs refresh") # Clear stale entries removed_count = await cache.clear_stale() print(f"Removed {removed_count} stale entries") ``` ### Advanced Operations ```python # Get cache statistics stats = await cache.get_stats() print(f"Total entries: {stats.total_entries}") print(f"Stale entries: {stats.stale_entries}") # Clear all cache entries cleared = await cache.clear_all() print(f"Cleared {cleared} entries") # Custom cache path cache = SQLiteCache(db_path=Path("custom/path/cache.db")) async with cache: # Operations... ``` ## API Reference ### SQLiteCache Class **Constructor**: - `db_path: Path` - Database file path (default: `cache/project_status.db`) **Vendor Operations**: - `async get_vendor(name: str) -> dict | None` - Retrieve cached vendor by name - `async set_vendor(name: str, data: dict) -> None` - Cache vendor data **Work Item Operations**: - `async get_work_item(item_id: UUID) -> dict | None` - Retrieve cached work item - `async set_work_item(item_id: UUID, data: dict) -> None` - Cache work item data **TTL Operations**: - `async is_stale(cache_key: str) -> bool` - Check if entry exceeds TTL - `async clear_stale() -> int` - Remove all expired entries **Statistics**: - `async get_stats() -> CacheStats` - Calculate cache metrics **Maintenance**: - `async clear_all() -> int` - Clear all cache entries ### CacheStats Model ```python class CacheStats(BaseModel): total_entries: int # Total cached entries stale_entries: int # Entries exceeding TTL vendors_cached: int # Vendor extractor count work_items_cached: int # Work item count oldest_entry_age_seconds: float # Age of oldest entry ``` ## Schema ### cached_vendors Table ```sql CREATE TABLE cached_vendors ( name TEXT PRIMARY KEY, data TEXT NOT NULL, -- JSON serialized vendor cached_at REAL NOT NULL -- Unix timestamp ); CREATE INDEX idx_vendors_cached_at ON cached_vendors (cached_at); ``` ### cached_work_items Table ```sql CREATE TABLE cached_work_items ( id TEXT PRIMARY KEY, data TEXT NOT NULL, -- JSON serialized work item cached_at REAL NOT NULL -- Unix timestamp ); CREATE INDEX idx_work_items_cached_at ON cached_work_items (cached_at); ``` ### cache_metadata Table ```sql CREATE TABLE cache_metadata ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at REAL NOT NULL ); ``` ## Performance Targets - **Cache Lookup**: <1ms (indexed primary keys) - **Bulk Cleanup**: <100ms (50 entries) - **TTL**: 30 minutes (1800 seconds) ## Error Handling All cache operations are designed for graceful degradation: ```python # RuntimeError raised if cache not initialized try: await cache.get_vendor("test") except RuntimeError as e: print(f"Cache error: {e}") # ValueError raised for invalid cache keys try: await cache.is_stale("invalid_key") except ValueError as e: print(f"Invalid key format: {e}") # None returned for cache misses vendor = await cache.get_vendor("nonexistent") if vendor is None: print("Cache miss - fetch from PostgreSQL") ``` ## Testing Run the comprehensive test suite: ```bash pytest tests/test_cache.py -v --cov=src/services/cache ``` **Coverage**: 83% (25 tests, all passing) Run the usage example: ```bash python examples/cache_usage.py ``` ## Integration with PostgreSQL Fallback The cache is designed to work alongside PostgreSQL: ```python # Try PostgreSQL first try: vendor = await postgres_session.query(VendorExtractor).filter_by(name="vendor_abc").first() except DatabaseError: # PostgreSQL unavailable - use cache vendor_data = await cache.get_vendor("vendor_abc") if vendor_data is None: raise RuntimeError("Vendor not found in cache") ``` ## Dependencies - **aiosqlite** >=0.19.0 - Async SQLite interface - **pydantic** >=2.5.0 - Data validation Added to `requirements.txt`: ``` aiosqlite>=0.19.0 ``` ## Future Enhancements - [ ] Automatic background cleanup task for stale entries - [ ] Configurable TTL per entry type - [ ] Cache warming strategy on PostgreSQL reconnect - [ ] Compression for large cached entries - [ ] Cache hit/miss metrics tracking ## See Also - [SQLite Cache Implementation](../src/services/cache.py) - [Cache Tests](../tests/test_cache.py) - [Usage Example](../examples/cache_usage.py) - [Research: SQLite Cache Decision](../specs/003-database-backed-project/research.md)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ravenight13/codebase-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server