Honor configurable cache size with LRU eviction
This commit is contained in:
+31
-6
@@ -31,6 +31,7 @@ JELLYFIN_LIBRARY_ROOT = os.environ.get("JELLYFIN_LIBRARY_ROOT", "/data/library")
|
||||
JELLYFIN_ORIGINAL_ROOT = os.environ.get("JELLYFIN_ORIGINAL_ROOT", "/data/original").rstrip("/")
|
||||
JELLYFIN_CACHE_ROOT = os.environ.get("JELLYFIN_CACHE_ROOT", "/data/cache").rstrip("/")
|
||||
JELLYFIN_API_KEY = os.environ.get("JELLYFIN_API_KEY", "")
|
||||
CACHE_MAX_BYTES = env_int("CACHE_MAX_BYTES", 1_000_000_000_000)
|
||||
SERIES_COOLDOWN_SECONDS = env_int("SERIES_COOLDOWN_SECONDS", 3600)
|
||||
POLL_SECONDS = env_int("POLL_SECONDS", 5)
|
||||
RESCAN_SECONDS = env_int("RESCAN_SECONDS", 300)
|
||||
@@ -501,9 +502,33 @@ class Manager:
|
||||
manifest.unlink(missing_ok=True)
|
||||
return True
|
||||
|
||||
def cache_usage(self):
|
||||
return sum(
|
||||
path.stat().st_size
|
||||
for path in (CACHE_ROOT / "files").iterdir()
|
||||
if path.is_file()
|
||||
)
|
||||
|
||||
def cache_available(self):
|
||||
stat = os.statvfs(CACHE_ROOT)
|
||||
filesystem_free = stat.f_bavail * stat.f_frsize
|
||||
configured_free = max(0, CACHE_MAX_BYTES - self.cache_usage())
|
||||
return min(filesystem_free, configured_free)
|
||||
|
||||
def enforce_cache_limit(self):
|
||||
usage = self.cache_usage()
|
||||
for _, relative, output, manifest in self.cache_entries():
|
||||
if usage <= CACHE_MAX_BYTES:
|
||||
break
|
||||
if self.is_locked(relative):
|
||||
continue
|
||||
size = output.stat().st_size + manifest.stat().st_size
|
||||
log(f"Evicting {relative} after cache resize")
|
||||
if self.remove_cache(relative):
|
||||
usage -= size
|
||||
|
||||
def ensure_space(self, required, may_evict):
|
||||
free = os.statvfs(CACHE_ROOT).f_bavail * os.statvfs(CACHE_ROOT).f_frsize
|
||||
if free >= required:
|
||||
if self.cache_available() >= required:
|
||||
return True
|
||||
if not may_evict:
|
||||
return False
|
||||
@@ -512,8 +537,7 @@ class Manager:
|
||||
continue
|
||||
log(f"Evicting {relative}")
|
||||
self.remove_cache(relative)
|
||||
free = os.statvfs(CACHE_ROOT).f_bavail * os.statvfs(CACHE_ROOT).f_frsize
|
||||
if free >= required:
|
||||
if self.cache_available() >= required:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -625,6 +649,7 @@ class Manager:
|
||||
if not JELLYFIN_API_KEY:
|
||||
raise RuntimeError("JELLYFIN_API_KEY is required")
|
||||
self.read_sessions()
|
||||
self.enforce_cache_limit()
|
||||
self.scan_inventory()
|
||||
self.sync_view()
|
||||
self.save()
|
||||
@@ -632,6 +657,7 @@ class Manager:
|
||||
if not self.read_sessions():
|
||||
time.sleep(POLL_SECONDS)
|
||||
continue
|
||||
self.enforce_cache_limit()
|
||||
now = time.time()
|
||||
if now - self.last_scan >= RESCAN_SECONDS:
|
||||
changed = self.scan_inventory()
|
||||
@@ -651,8 +677,7 @@ class Manager:
|
||||
for rank, _, relative in candidates:
|
||||
source = SOURCE_ROOT / relative
|
||||
estimate = min(max(int(source.stat().st_size * 2.2), 1_000_000_000), 100_000_000_000)
|
||||
free = os.statvfs(CACHE_ROOT).f_bavail * os.statvfs(CACHE_ROOT).f_frsize
|
||||
if rank == 0 or free >= estimate:
|
||||
if rank == 0 or self.cache_available() >= estimate:
|
||||
selected = (rank == 0, relative)
|
||||
break
|
||||
if not selected:
|
||||
|
||||
Reference in New Issue
Block a user