본문 바로가기
C.W.K.
Stream
Lesson 03 of 04 · published

세 시간 묵은 이미지를 내준 캐시

~12 min · caching, invalidation, digests, staleness

Level 0불 꺼진 작업장
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

렌더 파이프라인에 캐시가 왜 필요한가

단계가 비싸. 합성은 글자마다 돈이고, 이미지 생성은 장마다 돈이고, 인코딩은 분 단위로 시간을 먹어. 결과를 검토하는 사람은 하나 고치고 다시 돌리기를 되풀이하고. 캐시가 없으면 작은 수정마다 파이프라인 전체 값을 물어야 하고, 검토가 충분히 비싸지면 사람들이 검토를 그만둬. 그러니까 여기서 캐시는 성능 최적화가 아냐. 품질 절차를 감당 가능하게 만드는 장치야.

그 말은 잘못된 캐시가 그냥 느려지게 만드는 게 아니라는 뜻이야. 자기가 가능하게 하려던 그 검토를 소리 없이 무력화해.

판정하지 말고 이름을 붙여

순진한 캐시는 산출물을 고정된 경로에 넣어두고, 거기 있는 게 아직 쓸 만한지 물어. 그 질문엔 믿을 만한 답이 없어. 시각 기록은 체크아웃 앞에서 거짓말하고, "입력 중에 바뀐 게 있나?"는 네가 피하고 싶었던 바로 그 장부질이거든.

제대로 되는 버전은 출력에 영향을 줄 수 있는 전부의 다이제스트에서 경로를 뽑아. 장면을 고치면 주소가 바뀌니까, 이전 산출물은 여전히 존재하는데 아무도 안 묻는 자리에 있어. 낡음이 조심해서 피하는 게 아니라 구조적으로 불가능해져. 맞는 산출물은 있거나 없거나 둘 중 하나고, 그럴싸한데 틀린 게 맞는 주소에 앉아 있는 세 번째 상태가 없어.

키는 출력을 바꾸는 전부를 담아야 한다는 걸 기억해. 판은 자기 장면 정의에 달려 있어. 조각은 자기 장면이랑 타임라인에서 얼마나 오래 유지되는지에 달려 있고. 같은 그림을 4 초 물고 있는 거랑 9 초 물고 있는 건 서로 다른 산출물이야. 길이가 빠진 키는 하나를 다른 하나로 아주 기꺼이 내줘.

실제로 벌어진 실패

시험 운행이 세 시간 묵은 이미지를 완성된 렌더에 실어 보냈어. 원인은 내용 키잡이가 없어서가 아니었어. 구현돼 있었거든. 원인은 키에 난 구멍이었어. 픽셀을 바꿀 수 있는 뭔가가 다이제스트에 안 들어가 있었고, 그래서 수정이 같은 주소를 만들어냈고, 캐시는 거기 저장된 걸 정확하게 돌려줬지.

이게 이 접근법 특유의 실패고, 정확하게 이름 붙일 값어치가 있어. 캐시를 내용으로 키잡고 나면 낡음에 대해 생각하기를 그만둬. 낡음을 풀었으니까. 남은 버그는 캐시 버그처럼 안 생겼어. 수정이 안 먹은 것처럼 생겼지. 그럼 키가 아니라 네 수정을 들여다보게 되고.

내용 키를 쓰는 캐시는 자기 키를 말할 수 있어야 해. 다이제스트에 뭐가 들어가는지를 그걸 계산하는 함수 옆에 목록으로 적어둬. 그러면 출력에 영향을 주는 입력을 새로 붙일 때, 안 그랬으면 절대 안 던졌을 질문이 한 줄 거리에 있게 돼. 이거 키에 들어가야 하나? 구멍 대부분은 키를 설계한 지 한참 뒤에 붙은 입력이야.

Code

키, 문서화된 그 내용물, 그리고 구멍이 생기는 자리·python
def scene_digest(sc: dict) -> str:
    """Digest of everything that can change this scene's pixels.

    IN THE KEY (keep this list current - it is the contract):
      - scene kind and all its content fields
      - text, labels, values, node states (struck / active)
      - palette + font selection
      - canvas geometry

    DELIBERATELY NOT IN THE KEY:
      - the scene's position in the timeline (does not change pixels)
      - the episode slug (already in the path)
    """
    payload = json.dumps(sc, sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(payload.encode()).hexdigest()[:12]


def scene_clip_path(ep: Path, sc: dict, dur: float) -> Path:
    # duration MUST be in the clip key even though it is not in the
    # plate key: the same still held 4s and 9s are two artifacts.
    return ep / "clips" / f"{sc['key']}-{scene_digest(sc)}-{dur:.2f}.mp4"


# A hole is any output-affecting input missing from that list.
# Its symptom is never "stale cache" - it is "my edit did nothing",
# which sends you to debug the edit.

External links

Exercise

네 시스템에서 입력 전체의 다이제스트가 아닌 걸로 키를 잡은 캐시를 찾아봐. 사용자 id 든 파일명이든 질의 문자열이든. 캐시된 값을 바꿀 수 있는 입력을 전부 적고, 그중 뭐가 키에 있는지 확인해. 없는 것마다, 그게 바뀌었을 때 사용자가 뭘 보게 될지 따져봐. 보통은 '고쳤는데 안 먹은 것처럼 보이는' 변화고, 그건 아무도 캐시 탓으로 안 돌리는 증상이야.
Hint
설정이랑 템플릿이 흔한 구멍이야. 키를 설계할 때 그걸 입력으로 안 쳤거든. 템플릿을 고치려면 캐시를 손으로 비워야 한다면, 그 템플릿은 키에서 빠진 입력이고 다들 의식처럼 그걸 보정해온 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.