--- kind: CONCEPT slug: application-core-c09 title: 메타데이터와 물리 내용이 원자적이지 않다는 사실을 숨기지 않는다 topic: state-machines-and-ownership project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: concept:application-core-c09 evidenceCapturedOn: 2026-09-01 assets: - key: application-core-c09 file: ../../../final/evidence/rendered/application-core-c09.svg evidence: - ../../../final/evidence/raw/application-core-c09.txt source: - 원본 분석 절은 final/document.md#a03#L200 이다. module: application-core --- # 메타데이터와 물리 내용이 원자적이지 않다는 사실을 숨기지 않는다 fileserver의 핵심 contract는 metadata transaction과 filesystem/object I/O가 원자적이지 않다는 사실을 숨기지 않고 recovery model을 두는 것이다. ## 관계 - **legacy storage/notification compatibility surface의 제거 조건 추적** 같은 분석 리프에서 끌어낸 규칙이다. ## 본문 fileserver는 application-core 안의 가장 큰 독립 orchestration 중 하나다. 핵심 contract는 "metadata transaction과 filesystem/object I/O가 원자적이지 않다"는 사실을 숨기지 않고 recovery model을 두는 것이다. ## 거절이 부작용을 남기지 않게 하는 순서 upload admission은 authorization을 quota/storage admission보다 먼저 수행해 denial이 side-effect-free이도록 한다. reservation metadata/session은 DB transaction에서 만들지만 staging physical object는 외부 작업이므로 실패 시 compensation/reconciliation 대상이 된다. ## AmbiguousCompletionException 참조 위치 :::evidence key="application-core-c09" alt="코드베이스에서 AmbiguousCompletionException 를 검색한 출력 26줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="AmbiguousCompletionException 코드베이스 검색 — 26줄 · exit 0" zoom="true" ::: ## writer가 fencing token을 쓰는 이유 writer는 one-writer lease + fencing token을 사용한다. stale token은 append/finalize를 진행할 수 없고 takeover는 새 token을 만든다. append 시 metadata offset과 physical length가 다르면 자동 repair하지 않고 conflict로 중단한다. ## finalize의 검사 순서와 READY의 지위 finalize는 declared length, server-computed digest, optional client digest를 순서대로 검사한다. client digest는 server digest를 대체하지 않는다. 이후 VERIFYING으로 이동하고 verifier가 publish 승인해야 READY가 된다. READY가 유일한 public/downloadable state다. ## publish 뒤 메타데이터가 실패하면 재시도가 아니다 publish physical success 뒤 READY metadata transaction이 실패하면 결과는 단순 retryable failure가 아니라 `AmbiguousCompletionException`과 recovery queue로 간다. physical publish가 이미 발생했을 수 있기 때문이다. cancel/cleanup race를 막기 위해 cleanup claim은 writer/cleaner barrier를 형성하며 stale cleanup claim을 reclaim하는 경로가 실제로 호출된다.