본문으로 건너뛰기

빌드 캐시 — 바뀐 것이 없으면 아무것도 하지 않기

문서 목록으로

지금은 실행마다 전부 합니다. 워크북을 다시 디코딩하고, 테이블을 다시 쿠킹하고, 모든 타깃을 다시 내고, 파일을 전부 다시 씁니다. 아무것도 바뀌지 않은 재실행도 같은 시간이 걸립니다.

이 문서는 그 재실행을 0에 가깝게 만드는 설계입니다. 그리고 그것보다 긴 분량을 무효화가 틀렸을 때 무슨 일이 일어나는지에 씁니다 — 캐시의 실패는 「느리다」가 아니라 「조용히 낡은 것이 나간다」이고, 후자는 몇 달 뒤 다른 자리에서 증상을 냅니다.


1. 실측 — 시간이 어디로 가는가

코드를 고치지 않고 종료 지점 3개로 갈랐습니다. --dump-schema는 쿠킹 직후 멈추고, --validate-only는 검증 직후 멈추고, 전체는 끝까지 갑니다. 단계별 세부는 파일 로그의 카테고리와 타임스탬프에서 읽었습니다.

대상은 전량 변환 recipe입니다 — 워크북 36개(1,149 MB, .xlsb 21개 포함), 테이블 548개, 타깃 7개.

측정시간
--dump-schema — 임포트 + 쿠킹87.2초
--validate-only — 임포트 + 쿠킹 + 검증96.1초
전체161.5초 · 136.4초

전체 실행 하나(161.5초)의 단계별 소요입니다. 로그의 카테고리별 첫·마지막 타임스탬프에서 계산하였고, 합이 160.6초로 총 소요와 맞습니다.

단계시간비율
임포트 + 쿠킹 + 검증81.8초51%
binary 익스포트45.5초28%
json 익스포트23.2초14%
코드 생성 5개 언어 — csharp·typescript·cpp·rust·go7.5초5%
커밋 — 파일 9,470개 이동2.6초2%

읽을 것이 3가지입니다.

  1. 아무것도 바뀌지 않아도 이 시간을 전부 지불합니다. 이 문서가 제거하려는 것이 그것입니다.
  2. 절반은 입력, 절반은 산출입니다. 어느 한쪽만 캐시하면 절반밖에 줄지 않습니다. 무변경 재실행을 0으로 만드는 것은 양쪽을 함께 건너뛰는 것뿐입니다.
  3. 코드 생성은 비용이 아닙니다. 5개 언어를 합쳐 7.5초이고, 비용은 binaryjson 익스포터의 인코딩입니다. 언어를 더 붙이는 것이 느려지는 원인이 아니라는 뜻이고, 이것이 §8의 「하지 않기로 한 것」 중 하나의 근거입니다.

흔들림을 함께 적습니다. 측정 중 같은 기계에서 다른 테스트 스위트가 실행되고 있었습니다. 같은 작업의 두 측정이 81.2초와 87.2초, 전체가 161.5초와 136.4초로 벌어집니다. 절대값이 아니라 비율로 읽어야 하고, 확정 수치는 계측(§8의 0단계) 뒤에 다시 측정합니다.

2. 규격 — 인장 하나와 키 3개

인장은 지난 실행이 무엇을 입력으로 받았고 무엇을 출력했는지 기록한 파일입니다.

인장이 맞고 출력이 그대로 있으면, 이 실행이 만들 산출은 지난 실행의 산출과 같습니다. 그러면 아무것도 하지 않고 종료합니다.

이 전제는 변환이 결정적이라는 것 하나만 요구합니다. 그것은 이미 성립합니다 — .tcb의 nonce가 평문의 SHA-256이고, JSON의 줄바꿈과 마지막 개행이 기계가 아니라 코드에서 정해지고, 워크북을 읽는 순서가 파일시스템이 아니라 정렬로 정해집니다. 결정적이지 않은 자리 하나는 §5에 있습니다.

키는 3개입니다.

무엇이 들어가는가바뀌면
모델 키도구 버전 · 레시피의 Sources와 전역 설정 · 입력의 내용 · 산출에 영향을 주는 CLI 옵션전부 다시
검증 키모델 키 + Validation 섹션 + 규칙 파일 + --skip-runtime-validation검증만 다시
타깃 키모델 키 + 그 Targets[i] 항목의 JSON + 확정된 target side그 타깃만 다시

왜 하나가 아니라 3개인가. 타깃 하나의 Indented를 고친 것이 워크북 36개를 다시 읽을 이유가 되지 않습니다. 반대로 DefaultDelimiter를 고친 것은 모든 배열 셀의 값이 달라지는 것이라 전부 다시 해야 합니다. 이 구분을 하려면 레시피를 한 덩어리로 해시하면 안 되고 섹션별로 갈라야 합니다. 그리고 그 분할은 이미 존재합니다 — Targets는 항목마다 독립된 JObject이고 (출력 항목을 Targets 하나로), 레지스트리가 그것을 하나씩 타깃에 전달합니다.

레시피 해시는 ${} 치환이 끝난 파싱된 문서에 대해 계산합니다. 결과가 2가지입니다.

  • 주석과 공백을 고쳐도 캐시가 유지됩니다. 파서가 주석을 이미 버립니다 (RecipeModel.LoadFromFileCommentHandling.Ignore). recipe-all.jsonc의 절반이 주석이므로 이것은 실제로 의미가 있습니다.
  • 환경변수가 바뀌면 무효화됩니다. 치환된 값이 해시에 들어가기 때문이고, ${TABBIT_ENV}로 출력 경로가 갈리는 레시피에서 이것이 없으면 다른 환경의 산출을 재사용합니다.

이 문서의 나머지

무엇어디
무엇을 입력으로 보는가입력 판정 · 전체 실행을 강제하는 옵션 · 건너뛰면 안 되는 타깃
6. 함정캐시가 틀린 답을 낼 수 있는 자리들
CLI · 단계 · 게이트CLI 표면과 보고 · 구현 단계 · 게이트 · 설계가 바뀐 자리