brainy/examples/monitor-cache-performance.ts
David Snelling 46c6af3f21 feat: implement always-adaptive caching with getCacheStats monitoring
Replaces lazy mode concept with always-adaptive caching strategy:

- Rename getLazyModeStats() → getCacheStats() with enhanced metrics
- Change lazyModeEnabled boolean → cachingStrategy enum ('preloaded' | 'on-demand')
- Update preloading threshold from 30% to 80% for better cache utilization
- Add comprehensive production monitoring and diagnostics
- Add memory detection for containers (Docker/K8s cgroups v1/v2)
- Add adaptive memory sizing from 2GB to 128GB+ systems

Breaking changes: None (backward compatible, deprecated lazy option ignored)

New APIs:
- getCacheStats(): Comprehensive cache performance statistics
- cachingStrategy field: Transparent strategy reporting
- Enhanced fairness metrics and memory pressure monitoring

Documentation:
- Add migration guide for v3.36.0
- Add operations/capacity-planning.md for enterprise deployments
- Update all examples and troubleshooting guides
- Rename monitor-lazy-mode.ts → monitor-cache-performance.ts
2025-10-10 14:09:30 -07:00

307 lines
12 KiB
TypeScript

/**
* Cache Performance Monitoring Example
*
* Demonstrates comprehensive monitoring of Brainy's adaptive memory system
* and cache performance in production environments.
*
* Features:
* - Real-time cache performance monitoring
* - Memory pressure detection
* - Fairness violation alerts
* - Actionable recommendations
*
* Usage:
* ts-node examples/monitor-cache-performance.ts
*/
import { Brainy, NounType } from '@soulcraft/brainy'
// ANSI color codes for pretty output
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
gray: '\x1b[90m'
}
function formatBytes(bytes: number): string {
const mb = bytes / 1024 / 1024
return mb >= 1024 ? `${(mb / 1024).toFixed(2)} GB` : `${mb.toFixed(2)} MB`
}
function colorStatus(value: number, thresholds: { good: number, warning: number }): string {
if (value >= thresholds.good) return colors.green
if (value >= thresholds.warning) return colors.yellow
return colors.red
}
/**
* Initialize Brainy with production configuration
*/
async function initializeBrain() {
console.log(`${colors.cyan}Initializing Brainy...${colors.reset}`)
const brain = new Brainy({
storage: {
type: 'filesystem',
path: './brainy-data'
},
model: { precision: 'q8' }
})
await brain.init()
console.log(`${colors.green}✓ Brainy initialized${colors.reset}\n`)
return brain
}
/**
* Display comprehensive cache performance statistics
*/
function displayCacheStats(brain: Brainy) {
const stats = brain.hnsw.getCacheStats()
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}`)
console.log(`${colors.blue} CACHE PERFORMANCE & STATUS${colors.reset}`)
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}`)
// Caching Strategy Status
const modeColor = stats.cachingStrategy === 'on-demand' ? colors.cyan : colors.green
const modeStatus = stats.cachingStrategy === 'on-demand' ? 'ON-DEMAND (adaptive)' : 'PRELOADED (all in memory)'
console.log(`\n${modeColor}Caching Strategy:${colors.reset} ${modeStatus}`)
// Entity Count
console.log(`${colors.gray}Entities:${colors.reset} ${stats.autoDetection.entityCount.toLocaleString()}`)
// Cache Hit Rate
const hitRate = stats.unifiedCache.hitRatePercent
const hitRateColor = colorStatus(hitRate, { good: 80, warning: 60 })
console.log(`\n${colors.blue}Cache Performance:${colors.reset}`)
console.log(` Hit Rate: ${hitRateColor}${hitRate.toFixed(1)}%${colors.reset}`)
console.log(` ${colors.gray}Hits: ${stats.unifiedCache.hits.toLocaleString()}${colors.reset}`)
console.log(` ${colors.gray}Misses: ${stats.unifiedCache.misses.toLocaleString()}${colors.reset}`)
// HNSW Cache Details
console.log(`\n${colors.blue}HNSW Cache:${colors.reset}`)
console.log(` Memory: ${colors.cyan}${stats.hnswCache.estimatedMemoryMB.toFixed(2)} MB${colors.reset}`)
console.log(` ${colors.gray}Vectors Cached: ${stats.hnswCache.vectorsCached.toLocaleString()}${colors.reset}`)
console.log(` ${colors.gray}Cache Utilization: ${stats.hnswCache.sizePercent.toFixed(1)}%${colors.reset}`)
if (stats.lazyModeEnabled) {
const hnswHitRate = stats.hnswCache.hitRatePercent
const hnswHitColor = colorStatus(hnswHitRate, { good: 75, warning: 50 })
console.log(` HNSW Hit Rate: ${hnswHitColor}${hnswHitRate.toFixed(1)}%${colors.reset}`)
}
// Fairness Metrics
console.log(`\n${colors.blue}Fairness Metrics:${colors.reset}`)
if (stats.fairness.fairnessViolation) {
console.log(` ${colors.red}⚠ VIOLATION DETECTED${colors.reset}`)
console.log(` ${colors.red}HNSW Access: ${stats.fairness.hnswAccessPercent.toFixed(1)}%${colors.reset}`)
console.log(` ${colors.red}HNSW Cache: ${stats.hnswCache.sizePercent.toFixed(1)}%${colors.reset}`)
} else {
console.log(` ${colors.green}✓ No violations${colors.reset}`)
console.log(` ${colors.gray}HNSW Access: ${stats.fairness.hnswAccessPercent.toFixed(1)}%${colors.reset}`)
}
// Memory Pressure
const memoryInfo = brain.hnsw.unifiedCache.getMemoryInfo()
console.log(`\n${colors.blue}Memory Status:${colors.reset}`)
const pressureColor = {
low: colors.green,
moderate: colors.yellow,
high: colors.red,
critical: colors.red
}[memoryInfo.currentPressure.pressure]
console.log(` Pressure: ${pressureColor}${memoryInfo.currentPressure.pressure.toUpperCase()}${colors.reset}`)
if (memoryInfo.currentPressure.warnings.length > 0) {
console.log(` ${colors.red}Warnings:${colors.reset}`)
memoryInfo.currentPressure.warnings.forEach(warning => {
console.log(` ${colors.red}${warning}${colors.reset}`)
})
}
// Recommendations
console.log(`\n${colors.blue}Recommendations:${colors.reset}`)
if (stats.recommendations.length === 0) {
console.log(` ${colors.green}✓ All metrics healthy - no action needed${colors.reset}`)
} else {
stats.recommendations.forEach(rec => {
console.log(` ${colors.yellow}${rec}${colors.reset}`)
})
}
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}\n`)
}
/**
* Display memory allocation breakdown
*/
function displayMemoryAllocation(brain: Brainy) {
const memoryInfo = brain.hnsw.unifiedCache.getMemoryInfo()
const cacheStats = brain.hnsw.unifiedCache.getStats()
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}`)
console.log(`${colors.blue} MEMORY ALLOCATION${colors.reset}`)
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}`)
console.log(`\n${colors.cyan}System Configuration:${colors.reset}`)
console.log(` Environment: ${colors.gray}${cacheStats.memory.environment}${colors.reset}`)
console.log(` Container: ${colors.gray}${memoryInfo.memoryInfo.isContainer ? 'Yes' : 'No'}${colors.reset}`)
if (memoryInfo.memoryInfo.isContainer) {
console.log(` Detection: ${colors.gray}${memoryInfo.memoryInfo.source}${colors.reset}`)
}
console.log(`\n${colors.cyan}Memory Breakdown:${colors.reset}`)
console.log(` System Total: ${colors.gray}${formatBytes(memoryInfo.memoryInfo.systemTotal)}${colors.reset}`)
console.log(` Available: ${colors.gray}${formatBytes(memoryInfo.memoryInfo.available)}${colors.reset}`)
console.log(`\n${colors.cyan}Model Memory (Reserved):${colors.reset}`)
console.log(` Total: ${colors.gray}${formatBytes(cacheStats.memory.modelMemory)}${colors.reset}`)
console.log(` Precision: ${colors.gray}${cacheStats.memory.modelPrecision.toUpperCase()}${colors.reset}`)
console.log(`\n${colors.cyan}UnifiedCache Allocation:${colors.reset}`)
console.log(` Size: ${colors.green}${formatBytes(cacheStats.maxSize)}${colors.reset}`)
console.log(` Ratio: ${colors.gray}${(cacheStats.memory.allocationRatio * 100).toFixed(0)}%${colors.reset}`)
console.log(` Current Usage: ${colors.gray}${formatBytes(cacheStats.currentSize)}${colors.reset}`)
console.log(`${colors.blue}═══════════════════════════════════════════════════════════${colors.reset}\n`)
}
/**
* Add sample data to demonstrate lazy mode
*/
async function addSampleData(brain: Brainy, count: number) {
console.log(`${colors.cyan}Adding ${count.toLocaleString()} sample entities...${colors.reset}`)
const sampleTexts = [
'Machine learning is transforming artificial intelligence',
'Cloud computing enables scalable infrastructure',
'Kubernetes orchestrates containerized applications',
'TypeScript adds type safety to JavaScript',
'React builds interactive user interfaces',
'Node.js runs JavaScript on the server',
'PostgreSQL is a powerful relational database',
'Redis provides in-memory data caching',
'GraphQL offers flexible API queries',
'Docker containerizes application environments'
]
for (let i = 0; i < count; i++) {
const text = sampleTexts[i % sampleTexts.length]
await brain.add({
data: `${text} - Sample ${i}`,
type: NounType.Document,
metadata: {
index: i,
category: 'tech',
timestamp: Date.now()
}
})
// Progress indicator
if ((i + 1) % 100 === 0 || i === count - 1) {
process.stdout.write(`\r ${colors.gray}Progress: ${i + 1}/${count}${colors.reset}`)
}
}
console.log(`\n${colors.green}✓ Sample data added${colors.reset}\n`)
}
/**
* Perform sample searches to generate cache activity
*/
async function performSampleSearches(brain: Brainy) {
console.log(`${colors.cyan}Performing sample searches...${colors.reset}`)
const queries = [
'machine learning artificial intelligence',
'cloud computing infrastructure',
'kubernetes docker containers',
'typescript javascript development',
'react user interface'
]
for (const query of queries) {
const startTime = Date.now()
const results = await brain.search(query, { limit: 10 })
const latency = Date.now() - startTime
const latencyColor = latency < 10 ? colors.green : latency < 20 ? colors.yellow : colors.red
console.log(` ${colors.gray}Query: "${query.substring(0, 30)}..."${colors.reset}`)
console.log(` ${colors.gray}Results: ${results.length}, Latency: ${latencyColor}${latency}ms${colors.reset}`)
}
console.log(`${colors.green}✓ Searches completed${colors.reset}\n`)
}
/**
* Continuous monitoring loop (optional)
*/
function startContinuousMonitoring(brain: Brainy, intervalMs: number = 60000) {
console.log(`${colors.cyan}Starting continuous monitoring (every ${intervalMs / 1000}s)...${colors.reset}`)
console.log(`${colors.gray}Press Ctrl+C to stop${colors.reset}\n`)
setInterval(() => {
const timestamp = new Date().toISOString()
console.log(`${colors.gray}[${timestamp}]${colors.reset}`)
displayCacheStats(brain)
}, intervalMs)
}
/**
* Main example
*/
async function main() {
console.clear()
console.log(`${colors.blue}╔═══════════════════════════════════════════════════════════╗${colors.reset}`)
console.log(`${colors.blue}║ Brainy v3.36.0+ Cache Performance Monitoring Example ║${colors.reset}`)
console.log(`${colors.blue}╚═══════════════════════════════════════════════════════════╝${colors.reset}\n`)
// Initialize Brainy
const brain = await initializeBrain()
// Display initial memory allocation
displayMemoryAllocation(brain)
// Add sample data (adjust count based on available memory)
await addSampleData(brain, 500)
// Display initial stats
console.log(`${colors.cyan}Initial Statistics:${colors.reset}\n`)
displayCacheStats(brain)
// Perform searches to generate cache activity
await performSampleSearches(brain)
// Display stats after searches
console.log(`${colors.cyan}After Search Activity:${colors.reset}\n`)
displayCacheStats(brain)
// Optional: Start continuous monitoring
const continuousMonitoring = process.argv.includes('--continuous')
if (continuousMonitoring) {
startContinuousMonitoring(brain, 60000)
} else {
console.log(`${colors.gray}Tip: Run with --continuous flag for live monitoring${colors.reset}`)
await brain.close()
console.log(`\n${colors.green}✓ Example completed${colors.reset}`)
}
}
// Run example
if (require.main === module) {
main().catch(error => {
console.error(`${colors.red}Error:${colors.reset}`, error)
process.exit(1)
})
}
export { displayCacheStats, displayMemoryAllocation }