The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
67 lines
3.3 KiB
Markdown
67 lines
3.3 KiB
Markdown
---
|
|
kind: PROJECT_DECISION
|
|
slug: three-axes-of-evidence
|
|
title: 전송·업무·스트림 증거는 세 축이고 서로를 함의하지 않는다
|
|
topic: commit-ambiguity-as-a-result
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: decision:three-axes-of-evidence
|
|
decisionStatus: ADOPTED
|
|
decidedOn: 2026-08-30
|
|
source:
|
|
- docs/adr/ADR-GRPC-003-three-axis-execution-evidence.md — Status accepted, Date 2026-08-30, Scope grpc-core-api, grpc-policy, grpc-testkit
|
|
- analysis/20-grpc-platform.md
|
|
- analysis/grpc/grpc-core-api.md
|
|
---
|
|
|
|
# 전송·업무·스트림 증거는 세 축이고 서로를 함의하지 않는다
|
|
|
|
## 결정문
|
|
|
|
RPC 에서 일어난 일을 전송 증거와 업무 증거와 스트림 증거 세 축으로 나누어 기록하고, 축 사이의 추론을 거부한다.
|
|
|
|
## 판단 이유
|
|
|
|
ADR 의 출발점은 상태 코드가 호출자의 실제 질문에 대한 답이 아니라는 관찰이다.
|
|
|
|
변경 연산에 대한 DEADLINE_EXCEEDED 는 그 변경이 일어났는지 말해 주지 않는다. 요청이 전송된 뒤의 UNAVAILABLE 은 서버가 그것을 보지 않았다는 뜻이 아니다. 응답 헤더가 도착했다는 것은 트랜잭션이 커밋됐다는 뜻이 아니다.
|
|
|
|
셋 다 그럴듯한 추론이 중복 쓰기나 손실을 만드는 지점이고, 정상 경로만 검사하는 테스트에서는 어느 것도 보이지 않는다.
|
|
|
|
그래서 세 축을 독립적으로 두고 각 축이 자기 질문에만 답하게 한다.
|
|
|
|
전송 증거는 클라이언트가 회선에서 관측한 것을 기록하고, 클라이언트가 자기 전송 실패를 본 NOT_SENT 와 그 외 아무것도 알 수 없는 UNOBSERVED 를 구별한다.
|
|
|
|
업무 증거는 애플리케이션이 확인한 것을 기록하고, COMMIT_UNKNOWN 을 자리 표시자가 아니라 실제 상태로 둔다.
|
|
|
|
스트림 증거는 sealed 계층이며, 비어 있지 않은 경우는 전부 위치를 함께 들고 다닌다. 마지막 시퀀스 없는 부분 상태는 재개할 수도 조정할 수도 없기 때문이다.
|
|
|
|
그리고 관측 불가능한 조합은 실행 증거 타입이 생성 시점에 거부한다. 응답 헤더를 확정 커밋으로 승격하려면 한 메서드를 고쳐야만 가능하다.
|
|
|
|
## 영향
|
|
|
|
감수하는 것
|
|
|
|
값이 세 개로 늘어난다. 호출자가 세 축을 각각 읽어야 하고, 하나만 보고 판단하면 이 결정이 막으려던 추론을 다시 하게 된다.
|
|
|
|
세 축을 모두 채우는 책임이 전송 계층에 붙는다. 새 전송 구현마다 같은 품질로 채워야 한다.
|
|
|
|
조합 검증 때문에 표현할 수 없는 값이 생긴다. 테스트 픽스처가 편의상 만들던 조합 중 일부는 더 이상 만들 수 없다.
|
|
|
|
얻는 것
|
|
|
|
상태 코드에서 결과를 추론하는 경로가 타입 수준에서 닫힌다.
|
|
|
|
같은 원칙이 HTTP 쪽 전송 증거와 JPA 쪽 커밋 증거와 한 어휘를 이룬다.
|
|
|
|
## 근거
|
|
|
|
- **모르는 것은 성공도 실패도 아닌 세 번째 결과여야 한다**
|
|
세 축 각각이 이 규칙을 따른다.
|
|
- **위험한 조합은 정책이 아니라 생성자가 거부하게 만든다**
|
|
축 사이의 추론을 막는 구현 방법이다.
|
|
- **completion-unknown은 자동으로도 수동으로도 재시도하지 않는다**
|
|
같은 원칙이 JPA 쪽에서 채택된 결정이다.
|
|
|