比較提交
1
次程式碼提交
| 作者 | SHA1 | 日期 | |
|---|---|---|---|
|
|
4a83b25bf6 |
+7
-17
@@ -451,12 +451,9 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
}
|
||||
known.set(fileID, dest);
|
||||
// A fresh original may have crossed the limit; make room by
|
||||
// evicting least-recently-used originals. The file just written
|
||||
// is excluded from the candidates so an on-demand fetch that is
|
||||
// itself over budget proceeds over-limit and returns a path that
|
||||
// exists on disk. Thumbnails are never bounded or evicted.
|
||||
if (kind === "original")
|
||||
await this.enforceOriginalsLimit(fileID);
|
||||
// evicting least-recently-used originals (never this one, just
|
||||
// touched). Thumbnails are never bounded or evicted.
|
||||
if (kind === "original") await this.enforceOriginalsLimit();
|
||||
return { path: dest, bytes: size, cached: false };
|
||||
},
|
||||
{ priority, key: fileID },
|
||||
@@ -543,23 +540,16 @@ export class ContentCache implements PhotoContent, ThumbnailsAPI {
|
||||
}
|
||||
|
||||
// Evict least-recently-used originals until usage fits the limit. Pinned
|
||||
// originals are skipped, as is `justWrittenID` (the original this write just
|
||||
// stored, so an over-budget on-demand fetch never evicts the file it is
|
||||
// about to return). When only such originals remain the cache stays
|
||||
// over-limit until the protected set shrinks; the skipped file becomes
|
||||
// eligible on a later write.
|
||||
private enforceOriginalsLimit(justWrittenID: number): Promise<void> {
|
||||
// originals are skipped; when only pinned originals remain the cache stays
|
||||
// over-limit until the pinned set shrinks.
|
||||
private enforceOriginalsLimit(): Promise<void> {
|
||||
return this.serializeEnforce(async () => {
|
||||
const { entries, used } = await this.measureOriginals();
|
||||
const limit = await this.originalsLimit(used);
|
||||
let remaining = used;
|
||||
if (remaining > limit) {
|
||||
const evictable = entries
|
||||
.filter(
|
||||
(e) =>
|
||||
e.fileID !== justWrittenID &&
|
||||
!this.isPinned(e.fileID),
|
||||
)
|
||||
.filter((e) => !this.isPinned(e.fileID))
|
||||
.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
||||
for (const e of evictable) {
|
||||
if (remaining <= limit) break;
|
||||
|
||||
@@ -187,33 +187,6 @@ describe("originals eviction", () => {
|
||||
expect(status.usedBytes).toBeGreaterThan(status.limitBytes ?? 0);
|
||||
});
|
||||
|
||||
it("keeps a just-written original larger than the limit, evicting it only on a later write", async () => {
|
||||
// A 5-byte cap cannot hold even one 10-byte original, but a fetch must
|
||||
// never evict the file it just wrote and is about to return.
|
||||
const { cache } = buildCache({
|
||||
statfs: abundantFree,
|
||||
cacheOriginalsMaxBytes: 5,
|
||||
freeBelowBytes: 0,
|
||||
});
|
||||
await cache.open();
|
||||
|
||||
const result = await cache.original(1);
|
||||
|
||||
// The just-written original survives its own over-budget write and the
|
||||
// returned path exists on disk.
|
||||
expect(existsSync(result.path)).toBe(true);
|
||||
expect(existsSync(originalPath(1))).toBe(true);
|
||||
expect(cache.originalsStatus().usedBytes).toBe(10);
|
||||
setMtime(1, 1000);
|
||||
|
||||
// A later write finds file 1 eligible and evicts it to make room, while
|
||||
// the newly written file 2 is itself kept over-limit.
|
||||
await cache.original(2);
|
||||
expect(existsSync(originalPath(1))).toBe(false);
|
||||
expect(existsSync(originalPath(2))).toBe(true);
|
||||
expect(cache.originalsStatus().usedBytes).toBe(10);
|
||||
});
|
||||
|
||||
it("adapts the limit down as the disk fills and up as space returns", async () => {
|
||||
// The configured max is generous; free space drives the limit. Eviction
|
||||
// only kicks in once free space falls below the protected reserve.
|
||||
|
||||
新增問題並參考
封鎖使用者