128 lines
4.2 KiB
Python
128 lines
4.2 KiB
Python
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
|
# Copyright (C) 2026 Artificial Sweetener and contributors
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
"""Apply global LRU policy to inactive SimpleSyrup quant cache artifacts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from ..runtime.quant_cache_leases import (
|
|
GLOBAL_QUANT_CACHE_LEASES,
|
|
QuantCacheLeaseRegistry,
|
|
)
|
|
from ..runtime.quant_cache_repository import QuantCacheArtifact, QuantCacheRepository
|
|
from ..shared.logging import get_logger
|
|
|
|
LOGGER = get_logger(__name__)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class QuantCacheStatus:
|
|
"""Summarize the global cache for settings and diagnostics."""
|
|
|
|
path: str
|
|
usage_bytes: int
|
|
artifact_count: int
|
|
active_artifact_count: int
|
|
|
|
def to_payload(self, limit_bytes: int) -> dict[str, object]:
|
|
"""Return the settings-route JSON representation."""
|
|
|
|
return {
|
|
"path": self.path,
|
|
"usage_bytes": self.usage_bytes,
|
|
"limit_bytes": limit_bytes,
|
|
"artifact_count": self.artifact_count,
|
|
"active_artifact_count": self.active_artifact_count,
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class QuantCacheEvictionResult:
|
|
"""Summarize one bounded eviction attempt."""
|
|
|
|
removed_artifacts: int
|
|
removed_bytes: int
|
|
remaining_bytes: int
|
|
|
|
|
|
class QuantCacheService:
|
|
"""Own global cache accounting, LRU eviction, and safe clearing."""
|
|
|
|
def __init__(
|
|
self,
|
|
repository: QuantCacheRepository | None = None,
|
|
leases: QuantCacheLeaseRegistry | None = None,
|
|
) -> None:
|
|
"""Create a cache service with injectable persistence and leases."""
|
|
|
|
self._repository = repository or QuantCacheRepository()
|
|
self._leases = leases or GLOBAL_QUANT_CACHE_LEASES
|
|
|
|
def status(self) -> QuantCacheStatus:
|
|
"""Return current managed artifact usage."""
|
|
|
|
artifacts = self._repository.list_artifacts()
|
|
return QuantCacheStatus(
|
|
path=self._repository.relative_display_path(),
|
|
usage_bytes=sum(item.manifest.artifact_size_bytes for item in artifacts),
|
|
artifact_count=len(artifacts),
|
|
active_artifact_count=self._leases.active_count(),
|
|
)
|
|
|
|
def enforce_limit(
|
|
self,
|
|
limit_bytes: int,
|
|
protected_paths: frozenset[Path] = frozenset(),
|
|
) -> QuantCacheEvictionResult:
|
|
"""Evict least-recently-used inactive artifacts until within the limit."""
|
|
|
|
if limit_bytes < 0:
|
|
raise ValueError("Quant cache limit must not be negative.")
|
|
artifacts = list(self._repository.list_artifacts())
|
|
remaining = sum(item.manifest.artifact_size_bytes for item in artifacts)
|
|
removed_count = 0
|
|
removed_bytes = 0
|
|
protected = {path.resolve() for path in protected_paths}
|
|
for artifact in sorted(artifacts, key=_last_used):
|
|
if remaining <= limit_bytes:
|
|
break
|
|
if artifact.path.resolve() in protected or self._leases.is_leased(
|
|
artifact.path
|
|
):
|
|
continue
|
|
if not self._repository.remove(artifact):
|
|
continue
|
|
removed_count += 1
|
|
removed_bytes += artifact.manifest.artifact_size_bytes
|
|
remaining -= artifact.manifest.artifact_size_bytes
|
|
if removed_count:
|
|
LOGGER.info(
|
|
"quant cache eviction completed",
|
|
extra={
|
|
"removed_artifacts": removed_count,
|
|
"removed_bytes": removed_bytes,
|
|
"remaining_bytes": remaining,
|
|
"limit_bytes": limit_bytes,
|
|
},
|
|
)
|
|
return QuantCacheEvictionResult(removed_count, removed_bytes, remaining)
|
|
|
|
def clear_inactive(self) -> QuantCacheEvictionResult:
|
|
"""Remove every inactive managed artifact and preserve loaded models."""
|
|
|
|
return self.enforce_limit(0)
|
|
|
|
|
|
def _last_used(artifact: QuantCacheArtifact) -> float:
|
|
"""Return the manifest timestamp used for deterministic LRU ordering."""
|
|
|
|
try:
|
|
return datetime.fromisoformat(artifact.manifest.last_used_at).timestamp()
|
|
except ValueError:
|
|
return float("-inf")
|