perf(M01): 저장 검사를 고친 줄 둘레로 좁히고 서버가 뜰 때 마스터를 미리 읽음 (일감 33)
- 저장(`POST /save`) 6.8초 → 0.8초 — 고친 줄과 그 줄을 부르던 줄만 검사 (안 보는 줄이 부르는 키는 목록이 미리 세어 둔 것을 씀 · 돌고 도는 길은 전체 그림으로) - 로직 줄만 고쳤으면 품셈재료 연결 · 인력 준용 검사(`check_links`)도 건너뜀 - 로직 밖 파일(요소 · 표)이 섞인 저장은 예전처럼 전부 검사 — 전체 검사는 `check_master` 몫 - 켠 뒤 첫 목록 3.4초 → 0.03초 — M01 라우터가 뜰 때 뒤에서 한 번 읽어 둠(`cache.warm`) - 저장 전 검사(`check_saved`)를 M01 쪽(`_Store_Cache`)으로 옮김 — 좁히는 값이 있는 자리 · `check_master` 가 700줄에 닿음 - 시험 `test_m01_cache.py` 에 넷 더함 — 없는 로직 키 · 없는 요소 키 · 돌고 도는 참조 · 다른 파일에서 그 줄을 부르던 줄이 깨짐(모두 422) · 미리 읽은 뒤 첫 목록 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
자국(파일 시각·크기)이 바뀐 파일만 다시 읽고, 로직 막힘도 미리 세어 둠 —
|
||||
목록은 검사를 돌리지 않고 세어 둔 값을 씀. 고치는 길(`calc` · `text` · `save`)은
|
||||
사본을 만지므로 그대로 `cm.load` 로 새로 읽음.
|
||||
저장 전 검사(`check_saved`)도 여기 — 세어 둔 부르기 그림으로 고친 줄 둘레만 봄(`narrow`).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -112,3 +113,55 @@ def blocks(folder: Path) -> dict[str, list[str]]:
|
||||
for key in [k for k, (_, calls) in got.items() if calls & moved]:
|
||||
got[key] = cm.mf.check_logic(whole, rows[key])
|
||||
return {key: why for _, got in _BLOCK.values() for key, (why, _) in got.items()}
|
||||
|
||||
|
||||
def graph(folder: Path) -> dict[str, set]:
|
||||
"""로직 키마다 부르는 로직 키 — 미리 세어 둔 것(저장 때 검사를 좁히는 데 씀)."""
|
||||
with _FRESH:
|
||||
blocks(folder)
|
||||
return {key: calls for _, got in _BLOCK.values() for key, (_, calls) in got.items()}
|
||||
|
||||
|
||||
def narrow(folder: Path, batch: list[dict], before: dict, after: dict):
|
||||
"""(검사할 로직 키, 안 보는 줄이 부르는 키) — 저장이 건드린 줄과 그 줄을 부르던 줄만.
|
||||
|
||||
로직 밖 파일(요소 · 표)이 섞이면 어느 로직이 흔들릴지 모르므로 None — 전부 검사.
|
||||
"""
|
||||
if any(after[part["file"]].get("그룹") != "로직" for part in batch):
|
||||
return None
|
||||
calls, touched, gone = graph(folder), set(), set()
|
||||
for part in batch:
|
||||
was, now = [
|
||||
{str(r.get("키")) for r in side[part["file"]].get("줄") or []}
|
||||
for side in (before, after)
|
||||
]
|
||||
touched |= now - was # 더한 줄(키는 저장 때 대장이 냄)
|
||||
gone |= was - now
|
||||
touched |= {str(c.get("key")) for c in part.get("changes") or [] if c.get("op") != "add"}
|
||||
only = touched | {key for key, called in calls.items() if called & touched}
|
||||
return only, {key: called for key, called in calls.items() if key not in gone}
|
||||
|
||||
|
||||
def check_saved(files: dict, changed: list[str], book: dict, narrowed=None) -> list[str]:
|
||||
"""저장 전 검사(M01) — 고친 파일의 틀 + 로직의 변수 · 키 + 조합.
|
||||
|
||||
`files` = 폴더 전부(고친 뒤) · `book` = 키 대장 · `narrowed` = `narrow` 가 준 좁힘
|
||||
(None 이면 전부 검사 — 마스터 전체 검사는 `check_master` 몫).
|
||||
"""
|
||||
whole = cm.mf.Master(files)
|
||||
out = [x for name in changed for x in cm.check_form(name, files[name])]
|
||||
return out + cm.check_logics(files, whole, book, narrowed) + cm.mcb.check_all(files, whole)
|
||||
|
||||
|
||||
def warm(folder: Path) -> threading.Thread:
|
||||
"""서버가 뜰 때 뒤에서 한 번 읽어 둠 — 켠 뒤 첫 목록이 3초 걸리던 것을 없앰."""
|
||||
|
||||
def run() -> None:
|
||||
try:
|
||||
blocks(folder)
|
||||
except Exception: # 마스터가 깨져 있어도 서버 시동을 막지 않음 — 목록에서 다시 드러남
|
||||
pass
|
||||
|
||||
thread = threading.Thread(target=run, name="m01-warm", daemon=True)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
Reference in New Issue
Block a user