cache: single-pass directory walk in eviction functions

evict_oldest and evict_oldest_thumbnails now collect paths, stats,
and sizes in one iterdir() pass instead of separate passes for
sorting, sizing, and deleting. evict_oldest also accepts a
current_bytes arg to skip a redundant cache_size_bytes() call.
This commit is contained in:
pax 2026-04-11 23:01:44 -05:00
parent 10f1b3fd10
commit b964a77688

View File

@ -599,23 +599,36 @@ def cache_file_count(include_thumbnails: bool = True) -> tuple[int, int]:
return images, thumbs return images, thumbs
def evict_oldest(max_bytes: int, protected_paths: set[str] | None = None) -> int: def evict_oldest(max_bytes: int, protected_paths: set[str] | None = None,
"""Delete oldest non-protected cached images until under max_bytes. Returns count deleted.""" current_bytes: int | None = None) -> int:
protected = protected_paths or set() """Delete oldest non-protected cached images until under max_bytes. Returns count deleted.
files = sorted(cache_dir().iterdir(), key=lambda f: f.stat().st_mtime)
deleted = 0
current = cache_size_bytes(include_thumbnails=False)
for f in files: *current_bytes* avoids a redundant directory scan when the caller
already measured the cache size.
"""
protected = protected_paths or set()
# Single directory walk: collect (path, stat) pairs, sort by mtime,
# and sum sizes — avoids the previous pattern of iterdir() for the
# sort + a second full iterdir()+stat() inside cache_size_bytes().
entries = []
total = 0
for f in cache_dir().iterdir():
if not f.is_file():
continue
st = f.stat()
entries.append((f, st))
total += st.st_size
current = current_bytes if current_bytes is not None else total
entries.sort(key=lambda e: e[1].st_mtime)
deleted = 0
for f, st in entries:
if current <= max_bytes: if current <= max_bytes:
break break
if not f.is_file() or str(f) in protected or f.suffix == ".part": if str(f) in protected or f.suffix == ".part":
continue continue
size = f.stat().st_size
f.unlink() f.unlink()
current -= size current -= st.st_size
deleted += 1 deleted += 1
return deleted return deleted
@ -624,17 +637,23 @@ def evict_oldest_thumbnails(max_bytes: int) -> int:
td = thumbnails_dir() td = thumbnails_dir()
if not td.exists(): if not td.exists():
return 0 return 0
files = sorted(td.iterdir(), key=lambda f: f.stat().st_mtime) entries = []
deleted = 0 current = 0
current = sum(f.stat().st_size for f in td.iterdir() if f.is_file()) for f in td.iterdir():
for f in files:
if current <= max_bytes:
break
if not f.is_file(): if not f.is_file():
continue continue
size = f.stat().st_size st = f.stat()
entries.append((f, st))
current += st.st_size
if current <= max_bytes:
return 0
entries.sort(key=lambda e: e[1].st_mtime)
deleted = 0
for f, st in entries:
if current <= max_bytes:
break
f.unlink() f.unlink()
current -= size current -= st.st_size
deleted += 1 deleted += 1
return deleted return deleted