docs(keycloak-session-store): import the session-storage lab as a new project
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
43bccd08a8
commit
b2963105a8
+123
@@ -0,0 +1,123 @@
|
||||
---
|
||||
kind: CONCEPT
|
||||
slug: file-state-machine-and-ready
|
||||
title: 파일 상태 기계와 READY가 뜻하는 것
|
||||
topic: fileserver-state-and-fencing
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: concept:file-state-machine-and-ready
|
||||
evidenceCapturedOn: 2026-09-01
|
||||
assets:
|
||||
- key: file-state-machine-and-ready
|
||||
file: ../../../final/evidence/rendered/file-state-machine-and-ready.svg
|
||||
- key: file-state-machine-and-ready-diagram
|
||||
file: ../../../final/assets/diagrams/file-state-machine-and-ready.svg
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/file-state-machine-and-ready.txt
|
||||
source:
|
||||
- 원본 분석 절은 final/document.md#4-4 · analysis/08 §V1 이다.
|
||||
---
|
||||
|
||||
# 파일 상태 기계와 READY가 뜻하는 것
|
||||
|
||||
파일이 공개적으로 읽을 수 있는지는 파일시스템이 아니라 관계형 레코드가 정한다. 그래서 모든 상태 전이가 상태와 버전 양쪽으로 가드된다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **공개 읽기 가능한 상태는 완전한 identity를 DB 제약으로 요구한다**
|
||||
이 개념에서 끌어낸 규칙이다.
|
||||
- **물리 경로와 원본 파일명을 저장하지 않는다**
|
||||
이 메타데이터가 무엇을 담지 않는지 정한 결정이다.
|
||||
- **claim이 소유자와 토큰과 만료를 기록하지 않아 죽은 worker의 항목이 영영 남았다**
|
||||
같은 리프의 상태 기계에서 나온 사례다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
파일이 11개 상태를 지나고 **공개 읽기 가능한 상태는 `READY` 하나뿐**이라는 설계의 설명이다. V1 헤더가 원칙을 적는다 — "파일시스템이 아니라 관계형 레코드가 파일이 공개적으로 읽을 수 있는지 결정한다."
|
||||
|
||||
## 공개 읽기가 가능한 상태
|
||||
|
||||
:::evidence key="file-state-machine-and-ready-diagram" alt="READY 만 공개 읽기 가능 경계 안에 놓이고 업로드 중과 검증 중, 실패와 삭제 대기가 바깥에 빗금으로 놓인다" caption="공개 읽기가 가능한 상태" zoom="false"
|
||||
:::
|
||||
|
||||
## 전이와 완전성을 무엇이 가드하나
|
||||
|
||||
모든 전이가 `state`와 `version` 둘 다로 가드되고, `READY`의 완전성이 DB CHECK 제약으로 강제된다 — `content_key`·`actual_size`·`sha256`·`strong_etag`·`published_at`이 전부 NOT NULL이다.
|
||||
|
||||
## V1 헤더가 적은 원칙
|
||||
|
||||
:::evidence key="file-state-machine-and-ready" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
|
||||
:::
|
||||
|
||||
## 저장하지 않는 것
|
||||
|
||||
물리 경로·마운트·원본 물리 파일명을 저장하지 않는다 — `content_key`는 서버 생성 opaque key이고 `original_name`은 신뢰할 수 없는 표시용 텍스트다.
|
||||
|
||||
:::note
|
||||
|
||||
컨테이너 레인 미실행
|
||||
|
||||
:::
|
||||
|
||||
## 진실의 출처
|
||||
|
||||
```sql
|
||||
-- Fileserver platform metadata. The relational record — not the filesystem — decides whether a
|
||||
-- file is publicly readable, so every state transition is guarded by both `state` and `version`.
|
||||
```
|
||||
|
||||
파일시스템에 바이트가 있다는 것이 공개 가능하다는 뜻이 아니다. 레코드가 그것을 정한다.
|
||||
|
||||
이 선택의 결과가 전이 가드다. 상태만 보면 동시에 두 전이가 일어날 수 있으므로 버전도 함께 본다.
|
||||
|
||||
## 무엇을 담지 않는가
|
||||
|
||||
```sql
|
||||
-- No physical path, mount, or original physical filename is stored here: `content_key` is a
|
||||
-- server-generated opaque key and `original_name` is untrusted display text only.
|
||||
```
|
||||
|
||||
세 가지를 담지 않는다. 물리 경로와 마운트와 원본 물리 파일명이다.
|
||||
|
||||
콘텐츠 키는 서버가 만든 불투명 키다. 원본 이름은 신뢰할 수 없는 표시용 텍스트일 뿐이라고 명시된다. 즉 그 값으로 무언가를 찾거나 열지 않는다.
|
||||
|
||||
## 스키마가 자기 전제를 검사한다
|
||||
|
||||
```sql
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM capability_schema_registry
|
||||
WHERE capability_id = 'jpa-flyway-migration'
|
||||
AND core_epoch >= 1
|
||||
AND lifecycle_state = 'ACTIVE'
|
||||
) THEN
|
||||
RAISE EXCEPTION 'fileserver metadata requires active core epoch 1';
|
||||
END IF;
|
||||
```
|
||||
|
||||
이 스트림은 코어 에포크가 활성일 때만 적용된다. 능력 스키마 레지스트리가 그 사실을 답한다.
|
||||
|
||||
:::note
|
||||
|
||||
스키마 설치와 사용 승인을 나눈 레지스트리가 여기서 실제로 쓰인다. 이 마이그레이션은 레지스트리에 질의해 전제를 확인하고, 아니면 예외로 멈춘다.
|
||||
|
||||
:::
|
||||
|
||||
## 상태와 버전이 함께 가드하는 이유
|
||||
|
||||
상태만으로 전이하면 같은 상태에서 출발한 두 전이가 모두 성공할 수 있다. 버전을 함께 조건에 넣으면 하나만 성공하고 다른 하나는 갱신 건수 0 이 된다.
|
||||
|
||||
이 형태는 이 저장소의 다른 상태 기계와 같다. 소유자 튜플을 조건에 반복하고 갱신 건수를 답으로 쓰는 구조다.
|
||||
|
||||
## 공개 가능 상태의 무게
|
||||
|
||||
공개적으로 읽을 수 있는 상태는 다른 상태보다 요구가 많다. 그 상태에 도달한 레코드는 신원이 완전해야 하고, 그 완전성은 애플리케이션 검사가 아니라 데이터베이스 제약으로 표현된다.
|
||||
|
||||
애플리케이션 검사로 두면 그 검사를 지나지 않는 경로가 언젠가 생긴다.
|
||||
|
||||
<!-- body:end -->
|
||||
Reference in New Issue
Block a user