--- a/server/media-cache.js +++ b/server/media-cache.js @@ -226,7 +226,10 @@ let { bytes } = await db.mediaStats(); if (bytes + est > maxBytes) { const cutoff = now() - EVICT_PROTECT_MS; - for (const r of await db.listMediaLru()) { + // Retention order (plan 010) when the server provides it: copies that + // are neither top nor recent go first. Otherwise plain LRU. + const order = db.listMediaEvictionOrder ? await db.listMediaEvictionOrder() : await db.listMediaLru(); + for (const r of order) { if (bytes + est <= maxBytes) break; if (r.video_id === id || r.last_access > cutoff || jobs.has(r.video_id) || optActive === r.video_id) continue; await evict(r.video_id, 'budget'); --- a/server/media-cache.test.js +++ b/server/media-cache.test.js @@ -240,6 +240,27 @@ expect(await cache.getReady('lruAAAAAAA3')).not.toBeNull(); }); + test('eviction follows listMediaEvictionOrder when the db provides it', async () => { + await clearDb(); + const size = readFileSync(fx('good.mp4')).length; + const db = { + getMedia: dbmod.getMedia, upsertMedia: dbmod.upsertMedia, deleteMedia: dbmod.deleteMedia, + listMedia: dbmod.listMedia, listMediaLru: dbmod.listMediaLru, touchMedia: dbmod.touchMedia, + mediaStats: dbmod.mediaStats, + // Retention says the MORE recently played #2 is the one to drop (e.g. no views). + listMediaEvictionOrder: async () => (await dbmod.listMediaLru()).reverse(), + }; + const { cache } = makeCache({ db, maxBytes: size * 2 + 60 * 250 * 1024 + 50_000, duration: 10 }); + await cache.init(); + await cache.ensureCached('retAAAAAAA1', { priority: HIGH }); + await cache.ensureCached('retAAAAAAA2', { priority: HIGH }); + await dbmod.upsertMedia('retAAAAAAA1', { last_access: 1000 }); + await dbmod.upsertMedia('retAAAAAAA2', { last_access: 2000 }); + await cache.ensureCached('retAAAAAAA3', { priority: HIGH }); + expect(await dbmod.getMedia('retAAAAAAA2')).toBeNull(); + expect(await dbmod.getMedia('retAAAAAAA1')).not.toBeNull(); + }); + test('free-disk guard skips caching', async () => { await clearDb(); const { cache, calls } = makeCache({ freeBytes: () => 1024 ** 3, minFreeBytes: 5 * 1024 ** 3 });