Files
DongHyeonkaandClaude Opus 5 ab59130196 chore: 이전 세션이 남긴 변경을 커밋한다
이번 파이프라인 작업과 무관하게 작업 트리에 남아 있던 것을 그대로 올린다.
사용자가 「전부 커밋」으로 정했고, 이번 작업과 섞이지 않게 커밋만 나눴다.

대부분은 clean-architecture-backend-template 의 그림 정본 재배치다 —
final/assets/diagrams/<이름>/ 에 있던 것이 CLAUDE.md 가 적은 배치인
final/assets/<이름>/ 로 옮겨졌고 .techviz/<이름>/ 이 함께 들어왔다.
삽입 줄의 대부분(3.15M)이 그 .techviz context.json 이다.

그 밖에 ca-tmpl·document-haness 의 정리, .claude/agents/ 열한 개,
writing-practitioner-guides 스킬, .playwright-mcp 세션 산출물,
scripts/check-ssot-facts.py 와 그 시험이 들어 있다.

이 커밋의 내용은 내가 만든 것이 아니라 이전 세션이 남긴 것이고 검증하지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:02:02 +09:00

58 lines
2.5 KiB
Markdown

---
kind: REFERENCE
slug: read-the-clock-after-the-lock
title: 시간은 DB에서, 그리고 행을 잠근 다음에 읽는다
topic: owner-safe-state-machines
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: reference:read-the-clock-after-the-lock
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
---
# 시간은 DB에서, 그리고 행을 잠근 다음에 읽는다
애플리케이션 시계로 리스 만료를 판단하거나 잠그기 전의 시각으로 판단해, 서로 다른 노드가 같은 행에 대해 다른 답을 내는 것을 막는다. 시각은 데이터베이스에서 읽고, 행을 잠근 다음에 읽으며, 판정과 갱신을 한 문장 안에 둔다.
## 목적
애플리케이션 시계로 리스 만료를 판단하거나 잠그기 전의 시각으로 판단해, 서로 다른 노드가 같은 행에 대해 다른 답을 내는 것을 막는다.
## 규칙
1. 시각은 데이터베이스에서 읽는다
여러 노드의 시계는 서로 다르다. 리스 만료 판정의 기준 시각이 노드마다 다르면 두 노드가 동시에 소유자가 될 수 있다.
2. 행을 잠근 다음에 읽는다
잠그기 전의 시각으로 판단하면 잠금을 기다리는 동안 리스가 만료될 수 있다.
3. 판정과 갱신을 한 문장 안에 둔다
시각 비교를 where 절에 넣으면 판정과 갱신 사이에 시간이 흐르지 않는다.
4. 만료 시각을 계산할 때도 같은 시계를 쓴다
읽은 시각과 쓰는 시각의 출처가 다르면 리스 길이가 의도와 달라진다.
## 적용 조건
리스와 청구와 예약처럼 시각이 소유권을 정하는 모든 상태 기계
여러 인스턴스가 같은 테이블을 폴링하는 구조
## 예외
단일 인스턴스만 접근하고 그 보장이 구조적인 경우는 애플리케이션 시계로 충분하다. 그 보장을 적어 둔다.
## 예시
청구 결정 트리가 데이터베이스에서 읽은 현재 시각으로 리스 만료를 판정한다.
리스를 발행 타임아웃보다 길게 두는 것은 확률을 낮출 뿐이고, GC 정지나 스케줄러 지연을 데이터 제약으로 바꾸지 않는다.
## 관계
- **CAS 튜플을 where 절에 전부 반복하고 update count를 답으로 쓴다**
같은 문장 안에서 함께 쓰이는 규칙이다.
- **fenced lease — 만료 시각만으로는 부족한 이유**
시각만으로 부족한 이유를 다룬 개념이다.