docs(clean-architecture-backend-template): 제1부가 채택한 것만 글감으로 남기고 다시 고른다
글감 1,001개 중 제1부(§3~§11) 앵커를 하나라도 가진 것은 112개뿐이었다. 나머지 889개는
제2부 모듈 분석 65편의 절 제목에서 나온 것이고, 그것이 재판정이 필요했던 이유다.
주제 44 → 16 (43개가 독자 질문 없이 있었다. 지금은 전부 있다)
글감 1,001 → 123 (제1부 앵커 112 + 제1부가 채택했는데 비어 있던 자리 11)
후보 965 → 1,088 · PENDING 905 → 0
error 3,042 → 0
내려온 889개는 후보 대장에 KEEP_IN_SSOT 로 남는다 — 버린 것이 아니라 분석에 남기고 독립
기록으로 만들지 않기로 한 것이다. 그 글감을 받치던 기록 파일 828개는 지웠다. 계약이 정본이고,
파일이 남아 있다는 이유로 계약에서 뺀 주제가 되살아나면 안 된다. 이력에는 그대로 있다 —
git checkout a0ca2bb -- <경로>.
제1부가 채택했는데 글감이 없던 자리 열하나를 채웠다: mongo high-water mark 가 재전달 이벤트를
삼킨 P1, admin plane 이 가드만 켜고 서비스는 켜지 않은 것과 그 짝인 결정, 실패 어휘 세 층과
SQLState 매트릭스 병합 규칙, 부하 아래에서만 새는 admission 경계, 발행 증거와 완료 판정의
분리, keyset·JSONB 결정 둘.
Concept 17개에 basis-version 을 채우고, 계약 제목과 기록 제목이 갈라져 있던 23건을 기록 쪽에
맞췄다. candidateScope 에 excludedAnchorPattern 을 적어 제2부 앵커만 가진 글감이 다시 올라올
수 없게 한다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a0ca2bb72a
commit
1f04117bbf
-116
@@ -1,116 +0,0 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: a-digest-that-covered-who-but-not-what
|
||||
title: transition digest가 "누가·언제"만 덮고 "무엇"을 덮지 않아 다른 전이를 같다고 보고했다
|
||||
topic: owner-safe-state-machines
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: case:a-digest-that-covered-who-but-not-what
|
||||
evidenceCapturedOn: 2026-09-02
|
||||
assets:
|
||||
- key: a-digest-that-covered-who-but-not-what
|
||||
file: ../../../final/evidence/rendered/a-digest-that-covered-who-but-not-what.svg
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/a-digest-that-covered-who-but-not-what.txt
|
||||
source:
|
||||
- 이 사례의 사실은 분석 문서가 아니라 다이제스트 정책 클래스의 javadoc — 고친 쪽이 남긴 사후 기록 — 과 현재 구현에서 왔다. 후보 원장이 지정한 `final/document.md#a05` 의 절 번호는 그 파일에 존재하지 않는다. 완료 쪽이 아직 열려 있다는 것은 같은 문서 §59.2 이고, 위에 인용한 탐침 값이 그 절에 있다.
|
||||
---
|
||||
|
||||
# transition digest가 "누가·언제"만 덮고 "무엇"을 덮지 않아 다른 전이를 같다고 보고했다
|
||||
|
||||
전이 다이제스트가 전이 종류와 연산 식별자와 소유자와 시도와 상태 리비전을 덮었다. 누가 언제 했는지는 덮고 무엇을 했는지는 덮지 않아서, 결말이 다른 전이가 같은 값을 냈다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **digest는 길이 프레이밍하고 버전을 붙인다**
|
||||
이 사례가 만든 규칙이다.
|
||||
- **CAS 튜플을 where 절에 전부 반복하고 update count를 답으로 쓴다**
|
||||
같은 저장소의 소유자 안전 규칙이다.
|
||||
- **complete()의 replay 판정이 replayTtl 변경을 무시한다**
|
||||
이 다이제스트가 실제 판정에 쓰이지 않는 경로다.
|
||||
|
||||
## 문제
|
||||
|
||||
다이제스트가 덮던 다섯 성분은 전부 누가 몇 번째로 어느 상태에서 했는지를 말한다. 무엇을 했는지를 말하는 성분이 없었다.
|
||||
|
||||
그래서 재시도 가능한 실패와 포기한 실패가 같은 값을 냈고, 응답이 다른 두 완료와 보존 기간이 다른 두 완료도 그랬다.
|
||||
|
||||
## 결론
|
||||
|
||||
다이제스트를 비교한 재생 판정이 세 쌍을 같은 전이로 결론지었다. 세 쌍에서 갈린 것은 호출자의 행동이 달라지는 자리뿐이었다.
|
||||
|
||||
수정은 두 층으로 왔다. 호출부마다 의미 인자를 넘기게 했고, 실패 쪽은 전이 종류 자체를 성향별로 갈랐다.
|
||||
|
||||
이어 붙이는 방식도 바뀌었다. 성분마다 앞에 길이를 적고, 버전 상수를 다이제스트 입력에 넣는다.
|
||||
|
||||
다만 완료 쪽 재생 분기는 이 다이제스트를 비교하지 않는다. 그래서 응답이 같고 재생 창만 다른 두 완료는 지금도 같은 결과로 판정된다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
OpenJDK : 21.0.12
|
||||
확인 방식 : 다이제스트 정책과 다섯 호출부, 완료 재생 분기 확인
|
||||
소스 수정 : x
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 다이제스트 정책의 javadoc 에서 덮던 성분과 충돌한 쌍을 읽는다.
|
||||
2. 전이 다이제스트를 부르는 다섯 지점에서 각각 무엇을 넘기는지 확인한다.
|
||||
3. 실패 쪽 전이 종류가 어떻게 갈라지는지 확인한다.
|
||||
4. 이어 붙이기가 각 성분 앞에 적는 길이와, 해시가 실제로 먹는 바이트를 확인한다.
|
||||
5. 완료 재생 분기가 무엇을 비교하는지 확인한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
전이 다이제스트가 덮던 것은 전이 종류와 연산 식별자와 소유자 토큰과 시도와 상태 리비전이었다.
|
||||
|
||||
## 결말을 가르는 성분이 하나도 없었다
|
||||
|
||||
:::evidence key="a-digest-that-covered-who-but-not-what" alt="코드베이스에서 전이 다이제스트가 덮던 다섯 성분과 그때 같은 값을 내던 쌍을 적은 javadoc, 지금 다섯 호출부가 실제로 넘기는 의미 인자, 각 성분 앞에 적는 길이와 해시가 먹는 바이트, 버전 상수, 그리고 완료 재생 분기가 비교하는 값을 뽑은 출력 49줄. 완료 재생 분기가 전이 다이제스트가 아니라 연산 식별자와 응답 다이제스트만 본다는 것이 그 출력에 그대로 보인다." caption="덮던 다섯과 충돌한 쌍 · 호출부별 의미 인자 · 길이 프레이밍과 해시 입력 · 완료 재생 분기가 비교하는 값 — 49줄 · exit 0" zoom="true"
|
||||
:::
|
||||
|
||||
다섯은 전부 누가 몇 번째로 어느 상태에서 했는지를 말한다. 결말에 가장 가까운 것이 전이 종류인데, 그때는 실패가 성향과 무관하게 하나의 `FAIL` 이었다.
|
||||
|
||||
javadoc 이 같은 값을 내던 쌍 셋을 든다. 재시도 가능한 실패와 포기한 실패, 응답이 다른 두 완료, 보존 기간이 다른 두 완료다.
|
||||
|
||||
세 쌍이 달랐던 부분은 호출자가 실제로 행동을 바꾸는 부분뿐이었다. 다이제스트를 비교한 재생 판정은 그 셋을 전부 같은 전이라고 답했다.
|
||||
|
||||
## 무엇을 넘길지는 호출부가 정한다
|
||||
|
||||
지금은 다섯 호출부가 각자 인자를 넘긴다.
|
||||
|
||||
갱신은 처리 임차 유효 기간을 넘긴다. 완료는 응답 다이제스트와 재생 유효 기간을 넘기고, 그 자리 주석이 이유를 적는다 — 응답 다이제스트를 전이 다이제스트 대신 쓰면 같은 응답을 낸 서로 다른 연산의 완료가 구별되지 않고, 완료가 함께 결정하는 재생 창도 잃는다.
|
||||
|
||||
실패는 성향과 보존 기간을 넘긴다. 그리고 전이 종류 자체가 `FAIL_RETRYABLE` 과 `FAIL_ABANDONED` 로 갈린다. 첫 번째 쌍은 두 겹으로 갈라진 셈이다.
|
||||
|
||||
시작과 해제는 아무것도 넘기지 않는다. javadoc 이 예시로 든 코덱 신원은 지금 어느 호출부도 넘기지 않는다.
|
||||
|
||||
## 이어 붙이는 방식이 구별을 잃게 할 수 있다
|
||||
|
||||
성분은 전부 가변 폭 텍스트다. 그중 소유자 토큰은 문자 집합을 이 플랫폼이 정하지 않는다 — 소유자 타입이 강제하는 것은 1자 이상 128자 이하와 공백 불가뿐이다.
|
||||
|
||||
구분자로 이으면 서로 다른 성분 목록이 하나의 문자열로 렌더링될 수 있다. 그래서 각 성분 앞에 길이를 적고 콜론을 찍은 뒤 값을 적는다.
|
||||
|
||||
적는 길이는 자바 문자 수다. 해시가 먹는 것은 UTF-8 바이트다. 같은 저장소의 메시징 파티션 키는 같은 목적에 UTF-8 바이트 수를 쓰고, 비 ASCII 골든 벡터로 그 선택을 고정한다.
|
||||
|
||||
## 버전 상수는 다이제스트 입력 안에 있다
|
||||
|
||||
상수 값은 2 이고, 이어 붙이기가 그 값을 `v2` 로 맨 앞에 쓴다. 형식이 바뀌면 값이 바뀌므로 옛 형식으로 계산된 다이제스트와 섞이지 않는다.
|
||||
|
||||
저장소 이력에는 이 파일이 지금 형태 그대로 커밋 하나에 들어와 있다. 값이 언제 몇 번 올랐는지는 남아 있지 않다.
|
||||
|
||||
## 완료 쪽은 아직 이 다이제스트를 보지 않는다
|
||||
|
||||
정책이 고쳐진 것과 그 정책이 판정에 쓰이는 것은 다르다.
|
||||
|
||||
이미 완료된 동일 연산의 재생 분기는 전이 다이제스트를 비교하지 않는다. 상태가 COMPLETED 인지, 마지막 전이 종류가 COMPLETE 인지, 연산 식별자가 같은지를 보고, 그다음 응답 다이제스트만 비교한다.
|
||||
|
||||
그래서 응답은 같고 재생 유효 기간만 다른 두 완료가 같은 결과로 판정된다. 분석 문서가 실제 PostgreSQL 탐침으로 그것을 확인했다 — 첫 호출에 한 시간, 두 번째 호출에 아홉 시간을 넘겼는데 두 번째가 같은 결과로 판정됐고, 저장된 창은 3600초 그대로였다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
수정 이전의 충돌을 실행으로 재현하지 않았다. 지금 정책이 지켜지는지는 다이제스트 정책 전용 테스트가 고정한다 — 성향 차이, 응답 차이, 재생 창 차이, 길이 프레이밍, 그리고 널 인자와 인자 없음의 구분이다. 완료 쪽이 아직 열려 있다는 것은 분석 문서의 실측 탐침이 확인했다.
|
||||
|
||||
<!-- body:end -->
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: expired-claim-versus-expired-execution
|
||||
title: 만료된 CLAIMED는 takeover하고 만료된 EXECUTING은 조정을 요구하도록 갈랐다
|
||||
topic: owner-safe-state-machines
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: case:expired-claim-versus-expired-execution
|
||||
evidenceCapturedOn: 2026-09-01
|
||||
assets:
|
||||
- key: expired-claim-versus-expired-execution
|
||||
file: ../../../final/evidence/rendered/expired-claim-versus-expired-execution.svg
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/expired-claim-versus-expired-execution.txt
|
||||
source:
|
||||
- 원본 분석 절은 final/document.md#a05 §10.1, §10.4 이다.
|
||||
---
|
||||
|
||||
# 만료된 CLAIMED는 takeover하고 만료된 EXECUTING은 조정을 요구하도록 갈랐다
|
||||
|
||||
리스가 만료된 두 상태를 같이 다루면 안 된다. 청구만 하고 실행하지 않은 소유자는 밀어내도 되지만, 실행을 시작한 소유자는 무엇을 했는지 알 수 없으므로 조정으로 넘긴다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **만료된 claim과 만료된 실행은 다르게 다뤄야 한다**
|
||||
이 사례에서 끌어낸 규칙이다.
|
||||
- **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다**
|
||||
조정으로 넘기는 판정이 그 규칙의 적용이다.
|
||||
- **fenced lease — 만료 시각만으로는 부족한 이유**
|
||||
리스 만료를 다루는 맥락이다.
|
||||
|
||||
## 문제
|
||||
|
||||
리스가 만료되면 다른 작업자가 그 레코드를 가져갈 수 있어야 한다. 그러지 않으면 죽은 작업자의 레코드가 영원히 막힌다.
|
||||
|
||||
문제는 만료된 소유자가 무엇을 하다가 만료됐는지에 따라 안전한 처리가 다르다는 점이다.
|
||||
|
||||
## 결론
|
||||
|
||||
청구 결정 트리가 두 상태를 갈라 다르게 답한다.
|
||||
|
||||
같은 소유자 토큰이면 연산 충돌로 답한다
|
||||
상태가 CLAIMED 이고 리스가 지났으면 청구를 재설정한다. 즉 가져간다
|
||||
상태가 재시도 가능 실패면 마찬가지로 재설정한다
|
||||
상태가 EXECUTING 이고 리스가 지났으면 복구 필요로 답한다
|
||||
상태가 포기됨이면 복구 필요로 답한다
|
||||
그 외에는 진행 중으로 답하고 재시도 시각을 준다
|
||||
|
||||
CLAIMED 는 자리를 잡았지만 아직 아무것도 실행하지 않은 상태다. 그 소유자를 밀어내도 외부 효과가 없다.
|
||||
|
||||
EXECUTING 은 실행을 시작한 상태다. 그 소유자가 무엇을 어디까지 했는지 이 저장소는 모른다. 밀어내고 다시 실행하면 그 작업이 두 번 일어날 수 있다.
|
||||
|
||||
그래서 EXECUTING 만료는 자동 처리 대상이 아니라 조정 대상이다. 결과 타입이 복구 필요라는 별도 값을 갖고, 그 값이 시도 번호를 함께 들고 간다.
|
||||
|
||||
같은 구별이 해제 경로에도 있다. 소유자 튜플이 다르면 소유자 아님으로 답하고, 상태가 이미 EXECUTING 이면 실행이 시작되었음으로 답하며, CLAIMED 가 아니면 연산 충돌로 답한다. 해제는 아직 실행하지 않은 청구에 대해서만 허용된다.
|
||||
|
||||
같은 형태가 인박스 어댑터에도 있다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
OpenJDK : 21.0.12
|
||||
데이터베이스 : PostgreSQL
|
||||
확인 방식 : 결정 트리와 그 javadoc 확인
|
||||
소스 수정 : x
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 소유자 안전 멱등성 저장소의 청구 결정 트리를 읽는다.
|
||||
2. CLAIMED 만료와 EXECUTING 만료가 각각 어떤 결과를 내는지 확인한다.
|
||||
3. 해제 경로의 상태 검사를 확인한다.
|
||||
4. 인박스 어댑터의 같은 형태를 비교한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
만료된 lease를 일률적으로 takeover하면 이미 실행이 시작된 작업을 blind retry하게 된다.
|
||||
|
||||
## 이 저장소는 상태로 나눈다
|
||||
|
||||
만료된 `CLAIMED`는 `resetClaim`으로 takeover하고, 만료된 `EXECUTING`은 `abandonExpiredExecution`으로 `ABANDONED`에 넣고 `RecoveryRequired`를 반환한다.
|
||||
|
||||
## RecoveryRequired 참조 위치
|
||||
|
||||
:::evidence key="expired-claim-versus-expired-execution" alt="코드베이스에서 RecoveryRequired 를 검색한 출력 10줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="RecoveryRequired 코드베이스 검색 — 10줄 · exit 0" zoom="true"
|
||||
:::
|
||||
|
||||
## inbox도 같은 축을 쓴다
|
||||
|
||||
`RECEIVED`(takeover 가능)와 `PROCESSING`(→ DEAD, recovery-required)로 나눈다. 즉 "claim만 했다"와 "실행에 들어갔다"가 만료 시 다른 결론을 낳는다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
만료된 EXECUTING 이 실제로 조정 큐로 흘러가 사람이 처리하는 경로를 따라가지 않았다. 확인한 것은 저장소가 그 상태를 별도 결과로 답한다는 것이다.
|
||||
|
||||
컨테이너 레인 미실행
|
||||
|
||||
<!-- body:end -->
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
---
|
||||
kind: PROJECT_DECISION
|
||||
slug: state-machines-carry-no-stereotype
|
||||
title: 상태 기계 구현은 Spring stereotype을 갖지 않는다
|
||||
topic: owner-safe-state-machines
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: decision:state-machines-carry-no-stereotype
|
||||
decisionStatus: ADOPTED
|
||||
decidedOn: 2026-08-30
|
||||
source:
|
||||
- src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/idempotency
|
||||
- src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/config/JpaAdapterComponentsConfig.java
|
||||
- final/document.md#a05
|
||||
---
|
||||
|
||||
# 상태 기계 구현은 Spring stereotype을 갖지 않는다
|
||||
|
||||
## 결정문
|
||||
|
||||
소유자 안전 상태 기계 구현 클래스에는 컴포넌트 스캔 대상이 되는 애너테이션을 붙이지 않고, 조립은 명시적으로 한다.
|
||||
|
||||
## 판단 이유
|
||||
|
||||
이 클래스들은 어떤 데이터소스와 어떤 트랜잭션 경계에 묶이는지가 정확해야 한다. 스캔으로 들어오면 그 결정이 스캔 범위와 조건에 흩어진다.
|
||||
|
||||
같은 리프에서 그 흩어짐이 실제로 문제가 된 사례가 있다. 활성 트랜잭션 검사가 어느 데이터소스인지를 묻지 않아, 다른 데이터소스의 트랜잭션 안에서 발행된 변경이 이 저장소의 커넥션에 대해 트랜잭션 밖에서 커밋됐다.
|
||||
|
||||
명시적 조립은 그 결정을 한 자리에 모은다. 어떤 데이터소스가 어떤 저장소에 들어가는지가 코드로 보인다.
|
||||
|
||||
그리고 스캔되지 않으면 조건을 반복할 필요도 없다. 능력이 꺼진 배포에서 이 클래스들이 후보가 되는 일 자체가 없다.
|
||||
|
||||
## 영향
|
||||
|
||||
감수하는 것
|
||||
|
||||
조립 코드를 사람이 써야 한다. 새 상태 기계를 추가하면 조립 지점도 함께 고쳐야 한다.
|
||||
|
||||
조립을 잊으면 그 상태 기계가 없는 채로 배포된다. 스캔은 그 실수를 자동으로 막아 주지만 명시 조립은 그렇지 않다.
|
||||
|
||||
얻는 것
|
||||
|
||||
데이터소스와 트랜잭션 경계가 조립 지점에서 명시된다.
|
||||
|
||||
능력이 꺼진 배포에서 이 클래스들이 조건 평가 대상이 되지 않는다.
|
||||
|
||||
## 근거
|
||||
|
||||
- **활성 트랜잭션 검사가 data source를 묻지 않아 다른 커넥션에서 커밋됐다**
|
||||
조립 결정이 흩어졌을 때의 결과다.
|
||||
- **꺼짐은 조건의 반복이 아니라 구조여야 한다**
|
||||
같은 계열의 조립 원칙이다.
|
||||
- **Bean 애너테이션이 있다는 것은 조립 증거가 아니다**
|
||||
명시 조립을 확인할 때 쓰는 규칙이다.
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
---
|
||||
kind: REFERENCE
|
||||
slug: digest-must-be-length-framed-and-versioned
|
||||
title: digest는 길이 프레이밍하고 버전을 붙인다
|
||||
topic: owner-safe-state-machines
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: reference:digest-must-be-length-framed-and-versioned
|
||||
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
|
||||
---
|
||||
|
||||
# digest는 길이 프레이밍하고 버전을 붙인다
|
||||
|
||||
## 목적
|
||||
|
||||
다이제스트가 서로 다른 입력에 대해 같은 값을 내거나, 구성이 바뀐 뒤 옛 값과 비교되는 것을 막는다.
|
||||
|
||||
## 규칙
|
||||
|
||||
1. 무엇을 덮는지가 정책이다
|
||||
다이제스트가 빠뜨린 입력은 두 개의 다른 대상이 같은 값을 낼 수 있는 입력이다. 그 목록은 구현 세부가 아니라 정책이므로 자기 타입을 갖는다.
|
||||
|
||||
2. 결과를 덮는다
|
||||
누가 언제 했는지만 덮으면 무엇을 했는지가 다른 두 전이가 같아진다.
|
||||
|
||||
3. 길이 프레이밍한다
|
||||
구성 요소가 가변 길이 텍스트이고 그중 하나라도 이 플랫폼이 제약할 수 없는 값이면, 구분자로 이었을 때 서로 다른 목록이 한 문자열로 렌더링될 수 있다.
|
||||
|
||||
4. 버전을 붙이고 구성이 바뀌면 올린다
|
||||
저장된 다이제스트가 구성 경계를 넘어 비교되지 않게 한다.
|
||||
|
||||
5. 비교 실패를 조용히 처리하지 않는다
|
||||
버전이 다르면 같다고도 다르다고도 결론 내리지 않는다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
재생 판정이나 중복 판정에 쓰이는 모든 다이제스트
|
||||
|
||||
멱등성 키와 요청 지문
|
||||
|
||||
## 예외
|
||||
|
||||
캐시 키처럼 충돌이 성능 문제일 뿐 정확성 문제가 아닌 경우는 이 규칙이 과하다.
|
||||
|
||||
## 예시
|
||||
|
||||
전이 다이제스트가 전이 종류와 연산과 소유자와 시도와 리비전만 덮어, 재시도 가능한 실패와 포기한 실패가 같은 값을 냈다. 서로 다른 응답을 담은 두 완료도 마찬가지였다.
|
||||
|
||||
소유자 토큰은 이 플랫폼이 형식을 제약하는 값이 아니므로 길이 프레이밍이 필요하다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **transition digest가 누가와 언제만 덮고 무엇을 덮지 않아 다른 전이를 같다고 보고했다**
|
||||
이 규칙을 만든 사례다.
|
||||
- **서명된 커서의 구조와 검증 순서**
|
||||
같은 계열의 형식 결정을 다룬다.
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
---
|
||||
kind: REFERENCE
|
||||
slug: expired-claim-and-expired-execution-differ
|
||||
title: 만료된 claim과 만료된 실행은 다르게 다뤄야 한다
|
||||
topic: owner-safe-state-machines
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: reference:expired-claim-and-expired-execution-differ
|
||||
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
|
||||
---
|
||||
|
||||
# 만료된 claim과 만료된 실행은 다르게 다뤄야 한다
|
||||
|
||||
## 목적
|
||||
|
||||
리스 만료를 한 가지로 처리해, 실행을 시작했던 소유자의 작업을 두 번 수행하는 것을 막는다.
|
||||
|
||||
## 규칙
|
||||
|
||||
1. 만료된 청구는 인계한다
|
||||
자리를 잡았지만 아직 아무것도 실행하지 않은 소유자를 밀어내도 외부 효과가 없다.
|
||||
|
||||
2. 만료된 실행은 조정으로 넘긴다
|
||||
그 소유자가 무엇을 어디까지 했는지 알 수 없다. 다시 실행하면 그 작업이 두 번 일어날 수 있다.
|
||||
|
||||
3. 조정 결과는 별도 값이어야 한다
|
||||
성공이나 실패로 접으면 그 구별이 사라진다. 결과 타입에 세 번째 변형이 필요하다.
|
||||
|
||||
4. 해제도 같은 구별을 따른다
|
||||
실행이 시작된 청구는 해제할 수 없다. 해제는 아직 실행하지 않은 청구에만 허용한다.
|
||||
|
||||
5. 재시도 가능 실패는 인계 대상이다
|
||||
그 상태는 이미 결과가 확정된 것이므로 새 소유자가 처음부터 시작해도 된다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
청구와 실행을 별도 상태로 갖는 모든 상태 기계
|
||||
|
||||
멱등성 저장소와 인박스와 아웃박스
|
||||
|
||||
## 예외
|
||||
|
||||
실행이 외부 효과를 남기지 않는 것이 구조적으로 보장되면 두 상태를 같이 다뤄도 된다. 그 보장을 적어 둔다.
|
||||
|
||||
## 예시
|
||||
|
||||
청구 결정 트리가 만료된 청구는 재설정하고 만료된 실행은 복구 필요로 답한다. 복구 필요 결과는 시도 번호를 함께 들고 간다.
|
||||
|
||||
해제 경로는 이미 실행이 시작된 경우 실행 시작됨으로 답하고 해제하지 않는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **만료된 CLAIMED는 takeover하고 만료된 EXECUTING은 조정을 요구하도록 갈랐다**
|
||||
이 규칙을 만든 사례다.
|
||||
- **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다**
|
||||
세 번째 규칙이 기대는 상위 규칙이다.
|
||||
|
||||
Reference in New Issue
Block a user