perf: more aggressive cache fairness to prevent thrashing
v3.40.1 fixed the eviction formula but fairness parameters were too gentle, allowing metadata to regenerate faster than eviction could keep up. This caused cache thrashing: evict 20% → regenerate → evict 20% → repeat. Changes to prevent thrashing: 1. Fairness interval: 60s → 30s (faster response to imbalances) 2. Size threshold: 90% → 70% (earlier intervention) 3. Access threshold: <10% → <15% (catch more imbalances) 4. Eviction amount: 20% → 50% (more aggressive cleanup) 5. Proactive checking: Added immediate fairness check during set() operations to prevent imbalance formation rather than just reacting to it This should eliminate the "still creating relationships" slowdown reported by Soulcraft Studio while maintaining the OOM crash prevention from v3.40.1.
This commit is contained in:
parent
76466f7f24
commit
829a8a61a2
1 changed files with 37 additions and 6 deletions
|
|
@ -113,7 +113,7 @@ export class UnifiedCache {
|
|||
this.config = {
|
||||
enableRequestCoalescing: true,
|
||||
enableFairnessCheck: true,
|
||||
fairnessCheckInterval: 60000, // Check fairness every minute
|
||||
fairnessCheckInterval: 30000, // Check fairness every 30 seconds (v3.40.2: was 60s)
|
||||
persistPatterns: true,
|
||||
enableMemoryMonitoring: true,
|
||||
memoryCheckInterval: 30000, // Check memory every 30s
|
||||
|
|
@ -231,6 +231,12 @@ export class UnifiedCache {
|
|||
this.currentSize += size
|
||||
this.typeAccessCounts[type]++
|
||||
this.totalAccessCount++
|
||||
|
||||
// Proactive fairness check (v3.40.2): Check immediately if adding to a dominant type
|
||||
// This prevents imbalance formation instead of reacting to it
|
||||
if (this.config.enableFairnessCheck && this.cache.size > 10) {
|
||||
this.checkProactiveFairness(type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -335,15 +341,40 @@ export class UnifiedCache {
|
|||
other: typeSizes.other / totalSize
|
||||
}
|
||||
|
||||
// Check for starvation (90% cache but <10% accesses)
|
||||
// Check for starvation (v3.40.2: more aggressive - 70% cache with <15% accesses)
|
||||
// Previous: 90% cache, <10% access (too lenient, caused thrashing)
|
||||
for (const type of ['hnsw', 'metadata', 'embedding', 'other'] as const) {
|
||||
if (sizeRatios[type] > 0.9 && accessRatios[type] < 0.1) {
|
||||
if (sizeRatios[type] > 0.7 && accessRatios[type] < 0.15) {
|
||||
prodLog.warn(`Type ${type} is hogging cache (${(sizeRatios[type] * 100).toFixed(1)}% size, ${(accessRatios[type] * 100).toFixed(1)}% access)`)
|
||||
this.evictType(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proactive fairness check (v3.40.2)
|
||||
* Called immediately when adding items to prevent imbalance formation
|
||||
* Uses same thresholds as periodic check but runs on-demand
|
||||
*/
|
||||
private checkProactiveFairness(addedType: 'hnsw' | 'metadata' | 'embedding' | 'other'): void {
|
||||
// Quick check: only evaluate the type being added
|
||||
let typeSize = 0
|
||||
for (const item of this.cache.values()) {
|
||||
if (item.type === addedType) {
|
||||
typeSize += item.size
|
||||
}
|
||||
}
|
||||
|
||||
const sizeRatio = typeSize / (this.currentSize || 1)
|
||||
const accessRatio = this.typeAccessCounts[addedType] / (this.totalAccessCount || 1)
|
||||
|
||||
// Same threshold as periodic check: 70% size, <15% access
|
||||
if (sizeRatio > 0.7 && accessRatio < 0.15) {
|
||||
prodLog.debug(`Proactive fairness: ${addedType} reaching dominance (${(sizeRatio * 100).toFixed(1)}% size, ${(accessRatio * 100).toFixed(1)}% access)`)
|
||||
this.evictType(addedType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force evict items of a specific type
|
||||
*/
|
||||
|
|
@ -360,9 +391,9 @@ export class UnifiedCache {
|
|||
// Sort by score (lower is worse)
|
||||
candidates.sort((a, b) => a[1] - b[1])
|
||||
|
||||
// Evict bottom 20% of this type
|
||||
const evictCount = Math.max(1, Math.floor(candidates.length * 0.2))
|
||||
|
||||
// Evict bottom 50% of this type (v3.40.2: was 20%, too slow to prevent thrashing)
|
||||
const evictCount = Math.max(1, Math.floor(candidates.length * 0.5))
|
||||
|
||||
for (let i = 0; i < evictCount && i < candidates.length; i++) {
|
||||
const [key, , item] = candidates[i]
|
||||
this.currentSize -= item.size
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue