fix: correct LRU cache eviction and expiration logic in cache-manager

This commit is contained in:
2025-11-11 19:16:43 +00:00
parent 18be9bbd55
commit a688945df2

View File

@@ -32,6 +32,7 @@ export async function initDatabase(): Promise<IDBDatabase> {
export async function cacheChapter(chapter: BibleChapter): Promise<void> { export async function cacheChapter(chapter: BibleChapter): Promise<void> {
if (!db) await initDatabase() if (!db) await initDatabase()
return new Promise((resolve, reject) => {
const entry: CacheEntry = { const entry: CacheEntry = {
chapterId: chapter.id, chapterId: chapter.id,
data: chapter, data: chapter,
@@ -39,28 +40,39 @@ export async function cacheChapter(chapter: BibleChapter): Promise<void> {
expiresAt: Date.now() + CACHE_DURATION_MS expiresAt: Date.now() + CACHE_DURATION_MS
} }
return new Promise((resolve, reject) => {
const transaction = db!.transaction([STORE_NAME], 'readwrite') const transaction = db!.transaction([STORE_NAME], 'readwrite')
const store = transaction.objectStore(STORE_NAME) const store = transaction.objectStore(STORE_NAME)
// Delete oldest entries if over limit // First, check if we need to delete oldest entry
const countRequest = store.count() const countRequest = store.count()
countRequest.onsuccess = () => { countRequest.onsuccess = () => {
if (countRequest.result >= MAX_CACHE_SIZE) { if (countRequest.result >= MAX_CACHE_SIZE) {
// Delete oldest entry
const index = store.index('timestamp') const index = store.index('timestamp')
const oldestRequest = index.openCursor() const deleteRequest = index.openCursor()
oldestRequest.onsuccess = (event) => { let deleted = false
deleteRequest.onsuccess = (event) => {
const cursor = (event.target as IDBRequest).result const cursor = (event.target as IDBRequest).result
if (cursor) { if (cursor && !deleted) {
cursor.delete() cursor.delete()
deleted = true
// Continue with adding new entry after delete
const putRequest = store.put(entry)
putRequest.onerror = () => reject(putRequest.error)
putRequest.onsuccess = () => resolve()
} }
} }
deleteRequest.onerror = () => reject(deleteRequest.error)
} else {
// Just add the entry
const putRequest = store.put(entry)
putRequest.onerror = () => reject(putRequest.error)
putRequest.onsuccess = () => resolve()
} }
} }
const request = store.put(entry) countRequest.onerror = () => reject(countRequest.error)
request.onerror = () => reject(request.error)
request.onsuccess = () => resolve()
}) })
} }
@@ -90,16 +102,19 @@ export async function clearExpiredCache(): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const transaction = db!.transaction([STORE_NAME], 'readwrite') const transaction = db!.transaction([STORE_NAME], 'readwrite')
const store = transaction.objectStore(STORE_NAME) const store = transaction.objectStore(STORE_NAME)
const index = store.index('timestamp') const request = store.openCursor()
const range = IDBKeyRange.upperBound(Date.now() - CACHE_DURATION_MS) const now = Date.now()
const request = index.openCursor(range)
request.onsuccess = (event) => { request.onsuccess = (event) => {
const cursor = (event.target as IDBRequest).result const cursor = (event.target as IDBRequest).result
if (cursor) { if (cursor) {
const entry = cursor.value as CacheEntry
if (entry.expiresAt < now) {
cursor.delete() cursor.delete()
}
cursor.continue() cursor.continue()
} else { } else {
// Cursor is done, resolve
resolve() resolve()
} }
} }