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:
co-authored by
Claude Opus 4.6
parent
fbc61ef154
commit
46d560af24
@@ -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 }
|
||||||
}
|
}
|
||||||
audioCache.set(key, buf)
|
if (lruKey) audioCache.delete(lruKey)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user