Skip to content

Commit b7fa750

Browse files
committed
fix(helpers): add TTL cache to dir_size_gb to prevent blocking rglob walks
1 parent 10716cd commit b7fa750

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

dream-server/extensions/services/dashboard-api/helpers.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@
1717
from config import SERVICES, INSTALL_DIR, DATA_DIR, LLM_BACKEND
1818
from models import ServiceStatus, DiskUsage, ModelInfo, BootstrapStatus
1919

20+
21+
class _DirSizeCache:
22+
"""Per-path TTL cache for dir_size_gb to avoid repeated rglob walks."""
23+
24+
def __init__(self, ttl: float = 60.0):
25+
self._ttl = ttl
26+
self._store: dict[str, tuple[float, float]] = {}
27+
28+
def get(self, path: Path) -> float | None:
29+
key = str(path.resolve())
30+
entry = self._store.get(key)
31+
if entry is None:
32+
return None
33+
expires_at, value = entry
34+
if time.monotonic() > expires_at:
35+
del self._store[key]
36+
return None
37+
return value
38+
39+
def set(self, path: Path, value: float):
40+
self._store[str(path.resolve())] = (time.monotonic() + self._ttl, value)
41+
42+
43+
_dir_size_cache = _DirSizeCache()
44+
2045
# Lemonade serves at /api/v1 instead of llama.cpp's /v1
2146
_LLM_API_PREFIX = "/api/v1" if LLM_BACKEND == "lemonade" else "/v1"
2247

@@ -307,8 +332,13 @@ def dir_size_gb(path: Path) -> float:
307332
"""Calculate total size of a directory in GB. Returns 0.0 if path doesn't exist.
308333
309334
Skips symlinks to avoid following links outside DATA_DIR and double-counting.
335+
Results are cached for 60 seconds to avoid repeated expensive rglob walks.
310336
"""
337+
cached = _dir_size_cache.get(path)
338+
if cached is not None:
339+
return cached
311340
if not path.exists():
341+
_dir_size_cache.set(path, 0.0)
312342
return 0.0
313343
total = 0
314344
try:
@@ -320,7 +350,19 @@ def dir_size_gb(path: Path) -> float:
320350
pass
321351
except (PermissionError, OSError):
322352
pass
323-
return round(total / (1024**3), 2)
353+
result = round(total / (1024**3), 2)
354+
_dir_size_cache.set(path, result)
355+
return result
356+
357+
358+
def invalidate_dir_size_cache(path: Path):
359+
"""Remove cached size for a specific path after it has been modified."""
360+
_dir_size_cache._store.pop(str(path.resolve()), None)
361+
362+
363+
def clear_dir_size_cache():
364+
"""Clear the entire dir_size_gb cache (e.g. after bulk operations)."""
365+
_dir_size_cache._store.clear()
324366

325367

326368
def get_disk_usage() -> DiskUsage:

dream-server/extensions/services/dashboard-api/routers/extensions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,10 +1297,11 @@ def purge_extension_data(
12971297
if not body.confirm:
12981298
raise HTTPException(status_code=400, detail="Confirmation required: set confirm=true")
12991299

1300-
from helpers import dir_size_gb # noqa: PLC0415
1300+
from helpers import dir_size_gb, invalidate_dir_size_cache # noqa: PLC0415
13011301
size_gb = dir_size_gb(data_path)
13021302

13031303
shutil.rmtree(data_path, ignore_errors=True)
1304+
invalidate_dir_size_cache(data_path)
13041305

13051306
if data_path.exists():
13061307
raise HTTPException(status_code=500, detail=f"Could not fully remove data/{service_id}. Some files may be owned by root.")

0 commit comments

Comments
 (0)