fix(B04 · B06): 유입 캐시 상한 넷 · 흙막이 보낸 값 열쇠에 프로젝트 · 노선 (PLAN 572-2)

- B04 유입 배열 캐시 = 최근 프로젝트 넷만(_DETAIL_CACHE 꼴) · 서버 켠 뒤 본 프로젝트마다 쌓이던 것
- B06 soilSent 열쇠 = {프로젝트}:{노선}:측점:역할 · 다른 프로젝트 · 노선 같은 측점에 같은 값 안 먹던 흠
- 시험 test_572_2_inflow_cache 새로 · test_54_10 다른 프로젝트 같은 값 · helper_387_4b scope 칸

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 17:24:07 +09:00
co-authored by Claude Opus 5.5
parent 45b8188121
commit 92d952d8ac
7 changed files with 72 additions and 4 deletions
+11 -1
View File
@@ -34,7 +34,9 @@ router = APIRouter(prefix="/api/projects", tags=["B04 Surface Inflow"])
# 읽어 둔 배열을 프로세스 메모리에 들고 있는다. 마커를 누를 때마다 수십 MB npz를 다시 푸는
# 것을 막는다. 파일이 새로 쓰이면(재분석) mtime이 달라져 자동으로 버려진다.
# 프로젝트마다 한 벌(≈ 11 MB) — 최근 쓴 것 넷만 남김.
_CACHE: dict[str, tuple[float, dict[str, Any]]] = {}
_CACHE_SIZE = 4
# 유입 셀 외곽선은 조각이 많을 수 있다. 화면에서 읽을 수 있는 수준까지만 보낸다.
_MAX_RINGS = 40
@@ -43,6 +45,13 @@ def _routing_path(stored_path: str) -> Path:
return drainage_dir(stored_path) / f"{STAGES['road_routing']}_road_routing.npz"
def _remember(key: str, value: tuple[float, dict[str, Any]]) -> None:
_CACHE.pop(key, None)
_CACHE[key] = value
while len(_CACHE) > _CACHE_SIZE:
_CACHE.pop(next(iter(_CACHE)))
def _load_routing(stored_path: str) -> dict[str, Any] | None:
"""`03_road_routing.npz`를 읽어 필요한 배열만 남긴다(mtime 기준 캐시)."""
path = _routing_path(stored_path)
@@ -51,6 +60,7 @@ def _load_routing(stored_path: str) -> dict[str, Any] | None:
stamp = path.stat().st_mtime
cached = _CACHE.get(stored_path)
if cached is not None and cached[0] == stamp:
_remember(stored_path, cached)
return cached[1]
with np.load(path, allow_pickle=False) as data:
loaded = {
@@ -65,7 +75,7 @@ def _load_routing(stored_path: str) -> dict[str, Any] | None:
"road_chainage": data["road_chainage"],
"path_length": data["path_length"],
}
_CACHE[stored_path] = (stamp, loaded)
_remember(stored_path, (stamp, loaded))
logger.info("배수유역: 도로 귀속 배열을 읽었습니다 (%s).", path.name)
return loaded