Repository navigation
perf: Redis incremental stats tracking - O(1) vs O(545K) SCAN (Issue #65 P1 #1) #71
Copy link
Copy link
Closed
Labels
Description
Summary
Replace expensive SCAN operations with incremental counter tracking for Redis stats collection. Current implementation scans 545K+ keys which is O(n) complexity, severely impacting performance.
Problem
Current Implementation (src/knowledge_base.py):
# O(545K) - Scans ALL keys to count
def get_stats(self):
fact_count = len(self._scan_redis_keys("fact:*"))
doc_count = len(self._scan_redis_keys("doc:*"))
# ... scanning 545,255+ keysImpact:
- Time Complexity: O(n) where n = 545K+ keys
- Blocking: Blocks event loop during scan
- Memory: Loads all keys into memory
- Frequency: Called on every stats request
Proposed Solution
Maintain Running Counters:
# O(1) - Increment on add, decrement on delete
class KnowledgeBase:
def __init__(self):
self._stats_counters = {
"total_facts": 0,
"total_documents": 0,
"total_chunks": 0
}
async def add_fact(self, ...):
# ... add fact logic
self._stats_counters["total_facts"] += 1
# Persist to Redis for durability
await self.redis_client.hincrby("kb:stats", "total_facts", 1)
async def delete_fact(self, ...):
# ... delete logic
self._stats_counters["total_facts"] -= 1
await self.redis_client.hincrby("kb:stats", "total_facts", -1)
async def get_stats(self):
# O(1) - Just return counters
return self._stats_counters.copy()Implementation Plan
-
Add Counter Storage (Redis hash for durability)
kb:statshash with fields for each counter- Atomic increment/decrement operations
-
Migrate Existing Counts
- One-time scan to initialize counters
- Run during startup or migration script
-
Update CRUD Operations
add_fact()→ increment fact counterdelete_fact()→ decrement fact counteradd_document()→ increment doc counter- Same for chunks, vectors, etc.
-
Replace Stats Methods
get_stats()→ return counters (O(1))- Remove SCAN-based counting
- Add cache for counter values (in-memory)
-
Add Consistency Checks
- Periodic verification (compare counters to actual counts)
- Auto-correction if drift detected
- Log discrepancies for monitoring
Files to Modify
src/knowledge_base.py- Main implementationbackend/api/knowledge.py- Stats endpoint optimization- Add migration script for initial counter setup
Expected Impact
- Performance: O(1) vs O(545K) = ~545,000x improvement
- Response Time: Stats endpoint from seconds to milliseconds
- Resource Usage: No memory spike from loading all keys
- Scalability: Constant time regardless of data size
Acceptance Criteria
- Redis hash stores all counter types
- All CRUD operations update counters atomically
-
get_stats()returns O(1) complexity - Migration script initializes counters from existing data
- Periodic consistency check implemented
- No regressions in stats accuracy
- Performance benchmarks show improvement
Priority
P1 - High Priority (from Issue #65)
Estimated Effort
6-8 hours development + 2-4 hours testing
Related
- Parent: Issue perf: Performance optimization opportunities (21 identified improvements) #65 (P1 Docs improvements #1)
- Similar pattern: Issue refactor: Enforce HTTPClient singleton across 34+ files (Issue #65 P1 #3) #66 (resource optimization)
Reactions are currently unavailable
Activity
mrveiss commentedon Nov 29, 2025
Starting implementation of Redis incremental stats tracking.
mrveiss commentedon Nov 29, 2025
Implementation Complete: O(1) Redis Stats Tracking
Summary
Replaced expensive O(545K) SCAN operations with O(1) atomic counter lookups using Redis hash.
Changes Made to
src/knowledge_base.py1. Added Stats Counter Infrastructure
self._stats_key = 'kb:stats'- Redis hash key for all counters_increment_stat(field, amount=1)- Atomic increment_decrement_stat(field, amount=1)- Atomic decrement_get_stat(field)- Get single counter value_get_all_stats()- Get all counters as dict2. One-Time Migration
_initialize_stats_counters()- Runs on first startup ifkb:statshash doesn't existinitialize()(Step 4)3. Updated CRUD Operations
store_fact()- Incrementstotal_factsandtotal_vectorson successdelete_fact()- Decrementstotal_factsandtotal_vectorson success4. Updated
get_stats()5. Consistency Check
_verify_stats_consistency(auto_correct=True)- Periodic verificationPerformance Impact
get_stats()store_fact()delete_fact()Redis Data Structure
Testing
Files Modified
src/knowledge_base.pymrveiss commentedon Dec 5, 2025
✅ COMPLETE
This P1 optimization was fully implemented in the StatsMixin refactor. The implementation is production-ready and functional.
Implementation Details:
src/knowledge/stats.py- StatsMixin classkb:statsstores all counters_increment_stat(),_decrement_stat(),_get_stat(),_get_all_stats()_initialize_stats_counters()called during KB initPerformance Impact:
Verification:
Integration:
store_fact()increments countersdelete_fact()decrements countersget_stats()uses O(1) lookupClosing as complete. Part of Issue #65 P1 tasks.