fix: replace TTS FIFO cache with LRU eviction, add database indexes

TTS audioCache now tracks lastAccess timestamp per entry and evicts
the least-recently-used entry when full (was FIFO, deleting commonly
used phrases). Adds 6 new database indexes for bets, bot type/active
filtering, payment status lookups, and tournament entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 07:38:04 +00:00
co-authored by Claude Opus 4.6
parent fbc61ef154
commit 46d560af24
2 changed files with 18 additions and 7 deletions
+12 -7
View File
@@ -120,7 +120,7 @@ const _pending = new Map<number, {
// --- Audio state (main thread only) --- // --- Audio state (main thread only) ---
let _audioCtx: AudioContext | null = null let _audioCtx: AudioContext | null = null
const audioCache = new Map<string, AudioBuffer>() const audioCache = new Map<string, { buf: AudioBuffer; lastAccess: number }>()
const MAX_CACHE = 50 const MAX_CACHE = 50
const activeSources: Set<AudioBufferSourceNode> = new Set() const activeSources: Set<AudioBufferSourceNode> = new Set()
@@ -265,7 +265,7 @@ async function _generateAndCache(text: string, profile: string): Promise<AudioBu
if (!_workerReady) return null if (!_workerReady) return null
const key = _cacheKey(text, profile) const key = _cacheKey(text, profile)
const cached = audioCache.get(key) const cached = audioCache.get(key)
if (cached) return cached if (cached) { cached.lastAccess = Date.now(); return cached.buf }
// Dedup: if already generating this exact audio, reuse the in-flight promise // Dedup: if already generating this exact audio, reuse the in-flight promise
const existing = _inflight.get(key) const existing = _inflight.get(key)
@@ -288,12 +288,16 @@ async function _doGenerate(text: string, profile: string, key: string): Promise<
const buf = ctx.createBuffer(1, raw.audio.length, raw.sampleRate) const buf = ctx.createBuffer(1, raw.audio.length, raw.sampleRate)
buf.getChannelData(0).set(raw.audio) buf.getChannelData(0).set(raw.audio)
// Evict oldest if cache is full // Evict least-recently-used if cache is full
if (audioCache.size >= MAX_CACHE) { if (audioCache.size >= MAX_CACHE) {
const oldest = audioCache.keys().next().value let lruKey: string | undefined
if (oldest) audioCache.delete(oldest) let lruTime = Infinity
for (const [k, v] of audioCache) {
if (v.lastAccess < lruTime) { lruTime = v.lastAccess; lruKey = k }
}
if (lruKey) audioCache.delete(lruKey)
} }
audioCache.set(key, buf) audioCache.set(key, { buf, lastAccess: Date.now() })
return buf return buf
} }
@@ -355,7 +359,8 @@ export function kokoroSpeak(
const key = _cacheKey(text, profileName) const key = _cacheKey(text, profileName)
const cached = audioCache.get(key) const cached = audioCache.get(key)
if (cached) { if (cached) {
_playBuffer(cached, dest, volume) cached.lastAccess = Date.now()
_playBuffer(cached.buf, dest, volume)
return true return true
} }
// Generate in worker — will play when ready // Generate in worker — will play when ready
+6
View File
@@ -182,6 +182,12 @@ export function runMigrations() {
CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status); CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status);
CREATE INDEX IF NOT EXISTS idx_payments_bot ON payments(bot_id); CREATE INDEX IF NOT EXISTS idx_payments_bot ON payments(bot_id);
CREATE INDEX IF NOT EXISTS idx_tournament_matches_tournament ON tournament_matches(tournament_id); CREATE INDEX IF NOT EXISTS idx_tournament_matches_tournament ON tournament_matches(tournament_id);
CREATE INDEX IF NOT EXISTS idx_bets_fight ON bets(fight_id);
CREATE INDEX IF NOT EXISTS idx_bets_bettor ON bets(bettor_pubkey, created_at);
CREATE INDEX IF NOT EXISTS idx_bots_type_active ON bots(bot_type, is_active);
CREATE INDEX IF NOT EXISTS idx_payments_status_created ON payments(status, created_at);
CREATE INDEX IF NOT EXISTS idx_tournament_entries_tournament ON tournament_entries(tournament_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_analytics_date_metric ON analytics(date, metric);
`) `)
// Run PRAGMA optimize on startup for query planner stats // Run PRAGMA optimize on startup for query planner stats