docs(keycloak-session-store): take the sentences out of the diagram canvases
The skill says drawings carry names and sentences go in <desc> and the paragraph beside the figure. I put sentences in node details and edge labels instead, and 27 of the 28 diagrams shipped with prose inside the canvas — "예측 다섯 개가 틀렸다", "아홉 번 조용히 실패했다", "막혀서 닿지 않는다". Only label and details render on the canvas; description does not. So every sentence moved to a noun phrase and the meaning stays in description, which was already carrying it. 막혀서 닿지 않는다 -> 차단 아홉 번 조용히 실패했다 -> 조용한 실패 9건 예측 다섯 개가 틀렸다 -> 틀린 예측 5건 로그아웃이 정리하지 않는다 -> 로그아웃 미정리 볼륨이 없으면 여기까지다 -> 볼륨 없음 Three node labels were sentences too and became names: 세션 스냅샷, 예측 선기록, 대조군 확보. What stays is what the rules protect — identifiers, commands and measured values: PRIMARY KEY (client_registration_id, principal_name), ValidationFailedException: 1 changesets check sum, nginx -t && nginx -s reload, SET LOCAL synchronous_commit TO OFF. Those are names of things, not prose about them. 294 canvas strings across 28 diagrams, none matching a sentence ending, average 11 characters. All 28 still lint clean and re-rendered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@@ -139,8 +139,13 @@ function positiveChecks(text, lines, docMode, rulesMode) {
|
||||
// 사건을 서술하는 대신 그것이 무엇인지 이름 붙이고 끝낸다. 「확인한 자리다」·「~하는 실례다」·
|
||||
// 「~라는 점이다」. 참고 여섯 편은 이 어미를 통틀어 세 번 쓴다(17386 1 · 22396 2). 한 문서에
|
||||
// 이것이 몰려 있으면 사건을 적지 않고 분류만 한 것이다.
|
||||
const namingEnd = [...text.matchAll(/[가-힣)\]`]\s*(자리|실례|경우|출발점|사실|이유|점|것|뿐|셈)(이다|입니다)[.!]?(?=\s|$)/g)];
|
||||
const alwaysBad = namingEnd.filter((m) => /(자리|실례|출발점)/.test(m[1]));
|
||||
// 「자리다」·「실례다」·「경우다」는 받침이 없어 `이`가 붙지 않는다. `이다` 만 찾으면 정작
|
||||
// 가장 나쁜 세 개를 놓친다 — 처음 이 규칙을 넣었을 때 그렇게 됐다.
|
||||
const namingEnd = [
|
||||
...text.matchAll(/[가-힣)\]`]\s*(사실|이유|점|것|뿐|셈)(이다|입니다)[.!]?(?=\s|$)/g),
|
||||
...text.matchAll(/[가-힣)\]`]\s*(자리|실례|경우|출발점|사례|대목)(다|이다|입니다)[.!]?(?=\s|$)/g),
|
||||
];
|
||||
const alwaysBad = namingEnd.filter((m) => /(자리|실례|출발점|대목)/.test(m[1]));
|
||||
for (const m of alwaysBad) {
|
||||
out.push({ id: 'naming-instead-of-telling', sev: ERR, index: m.index,
|
||||
excerpt: text.slice(Math.max(0, m.index - 30), m.index + m[0].length),
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "무효화 통지가 지나는 길. NetworkPolicy 로 막았다.",
|
||||
"details": [
|
||||
"8080·9000 만 열고 누락시킨다"
|
||||
"허용 포트 8080 · 9000"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -72,7 +72,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "통지를 못 받아 세션이 죽은 것을 모른다.",
|
||||
"details": [
|
||||
"400 이어야 할 refresh 가 200"
|
||||
"refresh 200 (기대 400)"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "t-k1",
|
||||
"from": "transport",
|
||||
"to": "k1",
|
||||
"label": "막혀서 닿지 않는다",
|
||||
"label": "차단",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "k1-db",
|
||||
"from": "k1",
|
||||
"to": "db",
|
||||
"label": "세션은 여기서 읽는다 — 7800 과 무관",
|
||||
"label": "세션 SELECT",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "200 과 토큰을 받는다.",
|
||||
"details": [
|
||||
"로그인 153건 성공"
|
||||
"성공 응답 153건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -92,7 +92,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "여기 닿아야 살아남는다.",
|
||||
"details": [
|
||||
"DB 에 실제로 존재 149건"
|
||||
"DB 존재 149건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "traefik 과 API 서버. 잃으면 000 이다.",
|
||||
"details": [
|
||||
"kubectl 도 불통"
|
||||
"kubectl 불통"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -72,7 +72,7 @@
|
||||
"emphasis": "normal",
|
||||
"description": "4b 에서는 계속 돌고 있었다.",
|
||||
"details": [
|
||||
"도달할 수 없을 뿐이다"
|
||||
"도달 불가"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "4a 에서 함께 죽는다. 잃으면 503 이다.",
|
||||
"details": [
|
||||
"local-path PVC 라 재배치 불가"
|
||||
"local-path PVC · 재배치 불가"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"emphasis": "normal",
|
||||
"description": "비대칭 차단에서는 여기로 재연결한다.",
|
||||
"details": [
|
||||
"가르지 못한다"
|
||||
"재연결 경로"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "f-k1",
|
||||
"from": "fwd",
|
||||
"to": "k1",
|
||||
"label": "막혀서 닿지 않는다",
|
||||
"label": "차단",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
"id": "i-r",
|
||||
"from": "inject",
|
||||
"to": "roundtrips",
|
||||
"label": "왕복마다 더해진다",
|
||||
"label": "왕복당 누적",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "r-p",
|
||||
"from": "roundtrips",
|
||||
"to": "pool",
|
||||
"label": "커넥션 점유가 길어진다",
|
||||
"label": "커넥션 점유",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
"id": "r-c",
|
||||
"from": "restart",
|
||||
"to": "cache",
|
||||
"label": "비운다",
|
||||
"label": "캐시 초기화",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "r-db",
|
||||
"from": "restart",
|
||||
"to": "db",
|
||||
"label": "건드리지 않는다",
|
||||
"label": "변경 없음",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -136,7 +136,7 @@
|
||||
"id": "db-rt",
|
||||
"from": "db",
|
||||
"to": "refresh",
|
||||
"label": "세션이 남아 있어 통한다",
|
||||
"label": "세션 조회",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"id": "s1-t",
|
||||
"from": "s1",
|
||||
"to": "token-row",
|
||||
"label": "principal 이름으로 쓴다",
|
||||
"label": "principal 이름",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -118,7 +118,7 @@
|
||||
"id": "s2-t",
|
||||
"from": "s2",
|
||||
"to": "token-row",
|
||||
"label": "같은 키로 덮어쓴다",
|
||||
"label": "같은 키 · 덮어쓰기",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -132,7 +132,7 @@
|
||||
"id": "t-l",
|
||||
"from": "token-row",
|
||||
"to": "leftover",
|
||||
"label": "로그아웃이 정리하지 않는다",
|
||||
"label": "로그아웃 미정리",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "같은 refresh token 을 쓴다.",
|
||||
"details": [
|
||||
"& 와 wait 이 없으면 재현되지 않는다"
|
||||
"& + wait 필요"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -132,7 +132,7 @@
|
||||
"id": "cs-nt",
|
||||
"from": "client-session",
|
||||
"to": "new-token",
|
||||
"label": "세션이 없으니 못 쓴다",
|
||||
"label": "사용 불가",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "설정하지 않은 이름은 덮어쓰지 않는다.",
|
||||
"details": [
|
||||
"proxy_set_header ... \"\" 로 먼저 지워야 한다"
|
||||
"proxy_set_header ... \"\""
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -92,7 +92,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "헤더를 믿고 인가한다.",
|
||||
"details": [
|
||||
"세션은 로그인 시점의 스냅샷"
|
||||
"로그인 시점 스냅샷"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "n-p",
|
||||
"from": "nginx",
|
||||
"to": "proxy",
|
||||
"label": "지우지 않으면 그대로 지난다",
|
||||
"label": "미삭제 시 통과",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "컨테이너 파일시스템이다.",
|
||||
"details": [
|
||||
"appendonlydir 이 만들어졌다 버려진다"
|
||||
"appendonlydir"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "죽으면 /data 도 같이 사라진다.",
|
||||
"details": [
|
||||
"볼륨이 없으면 여기까지다"
|
||||
"볼륨 없음"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "c-d",
|
||||
"from": "config",
|
||||
"to": "datadir",
|
||||
"label": "AOF 파일을 쓴다",
|
||||
"label": "AOF 기록",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "d-c",
|
||||
"from": "datadir",
|
||||
"to": "container",
|
||||
"label": "컨테이너와 함께 죽는다",
|
||||
"label": "컨테이너와 함께 소멸",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "d-v",
|
||||
"from": "datadir",
|
||||
"to": "volume",
|
||||
"label": "볼륨을 붙여야 남는다",
|
||||
"label": "볼륨 마운트",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "세션 id 와 복호화 키가 함께 암호화되어 있다.",
|
||||
"details": [
|
||||
"secret 을 바꾸면 못 푼다"
|
||||
"secret 교체 시 복호화 불가"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "티켓을 못 풀면 어느 키인지도 모른다.",
|
||||
"details": [
|
||||
"고아로 남는다"
|
||||
"고아 세션"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -92,7 +92,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "요청으로 갱신되지 않아 생성 시각의 함수다.",
|
||||
"details": [
|
||||
"역산으로 고아를 고른다"
|
||||
"생성 시각 역산"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -108,7 +108,7 @@
|
||||
"id": "s-t",
|
||||
"from": "secret",
|
||||
"to": "ticket",
|
||||
"label": "티켓을 암호화한다",
|
||||
"label": "티켓 암호화",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "t-r",
|
||||
"from": "ticket",
|
||||
"to": "redis-session",
|
||||
"label": "키 이름을 만든다",
|
||||
"label": "키 이름 생성",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -136,7 +136,7 @@
|
||||
"id": "r-ttl",
|
||||
"from": "redis-session",
|
||||
"to": "ttl",
|
||||
"label": "생성 시각이 여기 남는다",
|
||||
"label": "생성 시각",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "클라이언트 설정. 비어 있었다.",
|
||||
"details": [
|
||||
"Keycloak 은 부를 주소를 모른다"
|
||||
"주소 없음"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "그대로 남는다.",
|
||||
"details": [
|
||||
"네트워크는 200 으로 닿는다"
|
||||
"네트워크 도달 200"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "l-c",
|
||||
"from": "logout",
|
||||
"to": "kc-config",
|
||||
"label": "통지할 주소를 찾는다",
|
||||
"label": "통지 주소 조회",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "c-e",
|
||||
"from": "kc-config",
|
||||
"to": "app-endpoint",
|
||||
"label": "주소가 있어야 부른다",
|
||||
"label": "호출 (주소 필요)",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "e-s",
|
||||
"from": "app-endpoint",
|
||||
"to": "app-session",
|
||||
"label": "받을 곳이 있어야 지운다",
|
||||
"label": "세션 삭제 (엔드포인트 필요)",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "refresh 만 쏘고 첫 번째만 쏜다.",
|
||||
"details": [
|
||||
"DEFAULT_SCOPE='f' — 선택적 스코프"
|
||||
"DEFAULT_SCOPE='f'"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "세운 상태다. 여기 닿는 조회만 실패한다.",
|
||||
"details": [
|
||||
"캐시에 있으면 닿지 않는다"
|
||||
"캐시 미스 시에만 도달"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "c-s",
|
||||
"from": "client-lookup",
|
||||
"to": "scope-lookup",
|
||||
"label": "refresh 는 스코프도 다시 계산한다",
|
||||
"label": "스코프 재계산",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "s-db",
|
||||
"from": "scope-lookup",
|
||||
"to": "db",
|
||||
"label": "캐시에 없으면 여기까지 간다",
|
||||
"label": "캐시 미스",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "첫 파드가 실패하면 거기서 멈춘다.",
|
||||
"details": [
|
||||
"나머지 파드는 건드리지 않는다"
|
||||
"나머지 파드 유지"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "전후로 같으면 롤백된다.",
|
||||
"details": [
|
||||
"늘었으면 스키마가 움직였다"
|
||||
"전후 행 수 비교"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -136,7 +136,7 @@
|
||||
"id": "l-ro",
|
||||
"from": "liquibase",
|
||||
"to": "rolling",
|
||||
"label": "실패가 여기서 멈춘다",
|
||||
"label": "중단 지점",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -150,7 +150,7 @@
|
||||
"id": "ro-s",
|
||||
"from": "rolling",
|
||||
"to": "service",
|
||||
"label": "남은 파드가 응답한다",
|
||||
"label": "잔여 파드 응답",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -164,7 +164,7 @@
|
||||
"id": "l-cl",
|
||||
"from": "liquibase",
|
||||
"to": "changelog",
|
||||
"label": "무엇을 보고 판단하나",
|
||||
"label": "판단 기준",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "base64 로 담긴다.",
|
||||
"details": [
|
||||
"인코딩이지 암호화가 아니다"
|
||||
"base64 인코딩"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -88,7 +88,7 @@
|
||||
"id": "s-e",
|
||||
"from": "secret",
|
||||
"to": "etcd",
|
||||
"label": "여기 저장된다",
|
||||
"label": "저장",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -102,7 +102,7 @@
|
||||
"id": "s-p",
|
||||
"from": "secret",
|
||||
"to": "pod",
|
||||
"label": "주입된다",
|
||||
"label": "주입",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "w-s",
|
||||
"from": "worker",
|
||||
"to": "serving",
|
||||
"label": "새 워커가 새 인증서를 읽는다",
|
||||
"label": "새 인증서 적재",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "NetworkPolicy · tc · kill · iptables.",
|
||||
"details": [
|
||||
"아홉 번 조용히 실패했다"
|
||||
"조용한 실패 9건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "여기만 보면 두 상황이 같아 보인다.",
|
||||
"details": [
|
||||
"주입 실패 = 영향 없음 으로 읽힌다"
|
||||
"실패 · 무영향 구별 불가"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"id": "i-v",
|
||||
"from": "inject",
|
||||
"to": "verify",
|
||||
"label": "걸렸는가를 먼저 본다",
|
||||
"label": "상태 확인",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -104,7 +104,7 @@
|
||||
"id": "v-o",
|
||||
"from": "verify",
|
||||
"to": "observe",
|
||||
"label": "걸린 뒤에만 결과를 읽는다",
|
||||
"label": "결과 판독",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "대조군과 대비될 때만 성립한다.",
|
||||
"details": [
|
||||
"대조군이 0 이므로 한 번도 갱신 탓"
|
||||
"대조군 0 → 귀속 성립"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "대조군이 없으면 여기로 간다.",
|
||||
"details": [
|
||||
"76건 실패는 로컬 아티팩트였다"
|
||||
"로컬 아티팩트 76건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "b-o",
|
||||
"from": "baseline",
|
||||
"to": "observation",
|
||||
"label": "비교 기준을 준다",
|
||||
"label": "비교 기준",
|
||||
"kind": "read",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "o-a",
|
||||
"from": "observation",
|
||||
"to": "attribution",
|
||||
"label": "대조가 있으면",
|
||||
"label": "대조군 있음",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "o-m",
|
||||
"from": "observation",
|
||||
"to": "misreport",
|
||||
"label": "대조가 없으면",
|
||||
"label": "대조군 없음",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "26 미만이면 세션의 거처가 다르다.",
|
||||
"details": [
|
||||
"A층 결론 중 셋이 뒤집힌다"
|
||||
"A층 결론 3건 반전"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "v-c",
|
||||
"from": "version",
|
||||
"to": "cache",
|
||||
"label": "버전이 먼저다",
|
||||
"label": "선행 조건",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "c-i",
|
||||
"from": "cache",
|
||||
"to": "instances",
|
||||
"label": "그 다음",
|
||||
"label": "다음 조건",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "i-cl",
|
||||
"from": "instances",
|
||||
"to": "clock",
|
||||
"label": "수치를 읽을 때",
|
||||
"label": "수치 해석",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "normal",
|
||||
"description": "사용자가 겪는 것.",
|
||||
"details": [
|
||||
"분단돼도 200 이 나올 수 있다"
|
||||
"분단 시에도 200"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "덮어쓰기와 정리 누락은 여기가 원인이다.",
|
||||
"details": [
|
||||
"세션 id 가 키에 없다"
|
||||
"키에 세션 id 없음"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -86,7 +86,7 @@
|
||||
},
|
||||
{
|
||||
"id": "snapshot",
|
||||
"label": "세션은 스냅샷이다",
|
||||
"label": "세션 스냅샷",
|
||||
"kind": "process",
|
||||
"role": "target",
|
||||
"emphasis": "warning",
|
||||
@@ -108,7 +108,7 @@
|
||||
"id": "q-s",
|
||||
"from": "questions",
|
||||
"to": "store-choice",
|
||||
"label": "먼저 여기를 본다",
|
||||
"label": "1차 검토",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "s-sc",
|
||||
"from": "store-choice",
|
||||
"to": "schema",
|
||||
"label": "여기서 풀리지 않는 것",
|
||||
"label": "미해결 잔여",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -136,7 +136,7 @@
|
||||
"id": "sc-sn",
|
||||
"from": "schema",
|
||||
"to": "snapshot",
|
||||
"label": "남는 것",
|
||||
"label": "최종 잔여",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "심볼릭 링크. 경로는 그대로이고 가리키는 대상만 바뀐다.",
|
||||
"details": [
|
||||
"archive/cert2.pem 을 가리킨다"
|
||||
"→ archive/cert2.pem"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "needs-reload",
|
||||
"from": "live-link",
|
||||
"to": "nginx",
|
||||
"label": "reload 없이는 닿지 않는다",
|
||||
"label": "reload 필요",
|
||||
"kind": "blocked",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
"nodes": [
|
||||
{
|
||||
"id": "prose",
|
||||
"label": "산문으로 적힌 측정 장치",
|
||||
"label": "산문 상태의 측정 장치",
|
||||
"kind": "component",
|
||||
"role": "source",
|
||||
"emphasis": "warning",
|
||||
"description": "괄호와 설명으로 남아 있었다.",
|
||||
"details": [
|
||||
"( curl ... ) & 를 20개 띄우고 wait"
|
||||
"부하 생성기 20건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -90,7 +90,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "실행까지 확인된 것만.",
|
||||
"details": [
|
||||
"상주 탐침으로 20/20"
|
||||
"상주 탐침 20/20"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "p-c",
|
||||
"from": "prose",
|
||||
"to": "command",
|
||||
"label": "바꿔 적는다",
|
||||
"label": "명령화",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -120,7 +120,7 @@
|
||||
"id": "c-e",
|
||||
"from": "command",
|
||||
"to": "execute",
|
||||
"label": "돌려본다",
|
||||
"label": "실행",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -134,7 +134,7 @@
|
||||
"id": "e-v",
|
||||
"from": "execute",
|
||||
"to": "verified",
|
||||
"label": "통과한 것만 남긴다",
|
||||
"label": "통과분 채택",
|
||||
"kind": "request",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "로그인을 처리하고 자기 sessions 캐시에만 엔트리를 남긴다.",
|
||||
"details": [
|
||||
"자기가 처리한 로그인만 캐시"
|
||||
"자기 처리분만 캐시"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "OFFLINE_USER_SESSION 에 세션 행을 보관한다. 두 노드가 같은 행을 본다.",
|
||||
"details": [
|
||||
"offline_flag='0' 이 온라인 세션"
|
||||
"offline_flag='0'"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "다른 노드가 만든 세션을 캐시로 받지 않고 데이터베이스에서 읽는다.",
|
||||
"details": [
|
||||
"refresh 요청을 받으면 DB 를 조회"
|
||||
"refresh 시 DB 조회"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -97,7 +97,8 @@
|
||||
"end_line": 46
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
"assumption": false,
|
||||
"details": []
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"emphasis": "primary",
|
||||
"description": "Keycloak 26 기본값.",
|
||||
"details": [
|
||||
"세션을 DB 에 쓴다"
|
||||
"세션 → DB"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -56,7 +56,7 @@
|
||||
"emphasis": "warning",
|
||||
"description": "24 이전 방식.",
|
||||
"details": [
|
||||
"세션이 메모리에만 있다"
|
||||
"세션 → 메모리"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -108,7 +108,7 @@
|
||||
"id": "p-to-db",
|
||||
"from": "persistent",
|
||||
"to": "db-session",
|
||||
"label": "세션을 여기 쓴다",
|
||||
"label": "세션 기록",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
@@ -122,7 +122,7 @@
|
||||
"id": "v-to-mem",
|
||||
"from": "volatile",
|
||||
"to": "memory-session",
|
||||
"label": "세션이 여기 있다",
|
||||
"label": "세션 보관",
|
||||
"kind": "write",
|
||||
"evidence": [
|
||||
{
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
"nodes": [
|
||||
{
|
||||
"id": "predict",
|
||||
"label": "예측을 먼저 적는다",
|
||||
"label": "예측 선기록",
|
||||
"kind": "process",
|
||||
"role": "source",
|
||||
"emphasis": "primary",
|
||||
"description": "결과를 보고 나면 정직하게 쓸 수 없다.",
|
||||
"details": [
|
||||
"예측 다섯 개가 틀렸다"
|
||||
"틀린 예측 5건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -50,13 +50,13 @@
|
||||
},
|
||||
{
|
||||
"id": "verify",
|
||||
"label": "주입이 걸렸는지 확인",
|
||||
"label": "주입 검증",
|
||||
"kind": "process",
|
||||
"role": "control",
|
||||
"emphasis": "primary",
|
||||
"description": "아무 일도 없었다와 영향이 없다를 가른다.",
|
||||
"details": [
|
||||
"아홉 번 조용히 실패했다"
|
||||
"조용한 실패 9건"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
{
|
||||
"id": "control",
|
||||
"label": "대조군을 잡는다",
|
||||
"label": "대조군 확보",
|
||||
"kind": "process",
|
||||
"role": "control",
|
||||
"emphasis": "warning",
|
||||
|
||||
@@ -18,5 +18,5 @@
|
||||
## Relationships
|
||||
|
||||
- **keycloak-0 → TCP 7800:** 무효화 통지. Evidence: L186–L190.
|
||||
- **keycloak-1 → PostgreSQL:** 세션은 여기서 읽는다 — 7800 과 무관. Evidence: L186–L189.
|
||||
- **TCP 7800 → keycloak-1:** 막혀서 닿지 않는다. Evidence: L186–L190.
|
||||
- **keycloak-1 → PostgreSQL:** 세션 SELECT. Evidence: L186–L189.
|
||||
- **TCP 7800 → keycloak-1:** 차단. Evidence: L186–L190.
|
||||
|
||||
@@ -14,5 +14,5 @@ n3: "PostgreSQL" {
|
||||
shape: cylinder
|
||||
}
|
||||
n0 -> n1: "무효화 통지"
|
||||
n1 -> n2: "막혀서 닿지 않는다"
|
||||
n2 -> n3: "세션은 여기서 읽는다 — 7800 과 무관"
|
||||
n1 -> n2: "차단"
|
||||
n2 -> n3: "세션 SELECT"
|
||||
|
||||
@@ -7,6 +7,6 @@ digraph techviz {
|
||||
n2 [label="keycloak-1", shape=box, style="rounded,filled"];
|
||||
n3 [label="PostgreSQL", shape=cylinder, style="rounded,filled"];
|
||||
n0 -> n1 [label="무효화 통지", style=solid];
|
||||
n1 -> n2 [label="막혀서 닿지 않는다", style=solid];
|
||||
n2 -> n3 [label="세션은 여기서 읽는다 — 7800 과 무관", style=solid];
|
||||
n1 -> n2 [label="차단", style=solid];
|
||||
n2 -> n3 [label="세션 SELECT", style=solid];
|
||||
}
|
||||
|
||||
@@ -6,30 +6,30 @@
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_k0" value="keycloak-0" tooltip="로그아웃을 처리한 노드. | Evidence: L183-L189" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="92.5" y="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxGeometry x="82.0" y="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_transport" value="TCP 7800<br/>8080·9000 만 열고 누락시킨다" tooltip="무효화 통지가 지나는 길. NetworkPolicy 로 막았다. | Evidence: L191-L196" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="80.5" y="220.0" width="174.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_transport" value="TCP 7800<br/>허용 포트 8080 · 9000" tooltip="무효화 통지가 지나는 길. NetworkPolicy 로 막았다. | Evidence: L191-L196" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="80.5" y="220.0" width="153.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_k1" value="keycloak-1<br/>400 이어야 할 refresh 가 200" tooltip="통지를 못 받아 세션이 죽은 것을 모른다. | Evidence: L183-L189" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="387.0" width="195.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_k1" value="keycloak-1<br/>refresh 200 (기대 400)" tooltip="통지를 못 받아 세션이 죽은 것을 모른다. | Evidence: L183-L189" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="387.0" width="174.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_db" value="PostgreSQL<br/>교차 노드 refresh 200" tooltip="세션이 여기 있어 7800 과 무관하다. | Evidence: L186-L190" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="91.0" y="554.0" width="153.0" height="97.0" as="geometry"/>
|
||||
<mxGeometry x="80.5" y="554.0" width="153.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_k0-t" value="무효화 통지" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_k0" target="n_transport">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="172.0" as="offset"/>
|
||||
<mxPoint x="181.0" y="172.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_k1-db" value="세션은 여기서 읽는다 — 7800 과 무관" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_k1" target="n_db">
|
||||
<mxCell id="e_k1-db" value="세션 SELECT" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_k1" target="n_db">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="506.0" as="offset"/>
|
||||
<mxPoint x="181.0" y="506.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_t-k1" value="막혀서 닿지 않는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_transport" target="n_k1">
|
||||
<mxCell id="e_t-k1" value="차단" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_transport" target="n_k1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="339.0" as="offset"/>
|
||||
<mxPoint x="181.0" y="339.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{
|
||||
"id": "edge-k0-t",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"x": 157.0,
|
||||
"y": 124.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -66,7 +66,7 @@
|
||||
{
|
||||
"id": "edge-label-k0-t",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"x": 136.0,
|
||||
"y": 160.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
@@ -105,7 +105,7 @@
|
||||
{
|
||||
"id": "edge-k1-db",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"x": 157.0,
|
||||
"y": 458.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -165,9 +165,9 @@
|
||||
{
|
||||
"id": "edge-label-k1-db",
|
||||
"type": "text",
|
||||
"x": 99.5,
|
||||
"x": 136.0,
|
||||
"y": 494.0,
|
||||
"width": 184,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -193,18 +193,18 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "세션은 여기서 읽는다 — 7800 과 무관",
|
||||
"text": "세션 SELECT",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "세션은 여기서 읽는다 — 7800 과 무관",
|
||||
"originalText": "세션 SELECT",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-t-k1",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"x": 157.0,
|
||||
"y": 291.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -264,7 +264,7 @@
|
||||
{
|
||||
"id": "edge-label-t-k1",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"x": 136.0,
|
||||
"y": 327.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
@@ -292,18 +292,18 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "막혀서 닿지 않는다",
|
||||
"text": "차단",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "막혀서 닿지 않는다",
|
||||
"originalText": "차단",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-k0",
|
||||
"type": "rectangle",
|
||||
"x": 92.5,
|
||||
"x": 82.0,
|
||||
"y": 60.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
@@ -333,7 +333,7 @@
|
||||
{
|
||||
"id": "node-label-k0",
|
||||
"type": "text",
|
||||
"x": 102.5,
|
||||
"x": 92.0,
|
||||
"y": 70.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
@@ -374,7 +374,7 @@
|
||||
"type": "rectangle",
|
||||
"x": 80.5,
|
||||
"y": 220.0,
|
||||
"width": 174.0,
|
||||
"width": 153.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -404,7 +404,7 @@
|
||||
"type": "text",
|
||||
"x": 90.5,
|
||||
"y": 230.0,
|
||||
"width": 154.0,
|
||||
"width": 133.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -430,11 +430,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "TCP 7800\n8080·9000 만 열고 누락시킨다",
|
||||
"text": "TCP 7800\n허용 포트 8080 · 9000",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "TCP 7800\n8080·9000 만 열고 누락시킨다",
|
||||
"originalText": "TCP 7800\n허용 포트 8080 · 9000",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -443,7 +443,7 @@
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 387.0,
|
||||
"width": 195.0,
|
||||
"width": 174.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -473,7 +473,7 @@
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 397.0,
|
||||
"width": 175.0,
|
||||
"width": 154.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -499,18 +499,18 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "keycloak-1\n400 이어야 할 refresh 가 200",
|
||||
"text": "keycloak-1\nrefresh 200 (기대 400)",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "keycloak-1\n400 이어야 할 refresh 가 200",
|
||||
"originalText": "keycloak-1\nrefresh 200 (기대 400)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-db",
|
||||
"type": "rectangle",
|
||||
"x": 91.0,
|
||||
"x": 80.5,
|
||||
"y": 554.0,
|
||||
"width": 153.0,
|
||||
"height": 97.0,
|
||||
@@ -540,7 +540,7 @@
|
||||
{
|
||||
"id": "node-label-db",
|
||||
"type": "text",
|
||||
"x": 101.0,
|
||||
"x": 90.5,
|
||||
"y": 564.0,
|
||||
"width": 133.0,
|
||||
"height": 77.0,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a1-transport-vs-discovery",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "af42cd905290687548b620849f1e143752f9ab346de4de352701256cc6657e32",
|
||||
"spec_sha256": "f54cae81276a31a8b73961148cadf3ae5cd256bdc38a9c002997fce63c243ebf",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -6,5 +6,5 @@ flowchart TB
|
||||
n2["keycloak-1"]
|
||||
n3[("PostgreSQL")]
|
||||
n0 -->|"무효화 통지"| n1
|
||||
n1 -->|"막혀서 닿지 않는다"| n2
|
||||
n2 -->|"세션은 여기서 읽는다 — 7800 과 무관"| n3
|
||||
n1 -->|"차단"| n2
|
||||
n2 -->|"세션 SELECT"| n3
|
||||
|
||||
@@ -50,35 +50,35 @@
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="696" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="167.5,124.0 167.5,172.0 167.5,172.0 167.5,220.0" data-evidence="186-190" />
|
||||
<rect class="edge-label-bg" x="162.4" y="158.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="173.0">무효화 통지</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="167.5,458.0 167.5,506.0 167.5,506.0 167.5,554.0" data-evidence="186-189" />
|
||||
<rect class="edge-label-bg" x="105.5" y="492.0" width="172.1" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="507.0">세션은 여기서 읽는다 — 7800 과 무관</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="167.5,291.0 167.5,339.0 167.5,339.0 167.5,387.0" data-evidence="186-190" />
|
||||
<rect class="edge-label-bg" x="149.0" y="325.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="340.0">막혀서 닿지 않는다</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="157.0,124.0 157.0,172.0 157.0,172.0 157.0,220.0" data-evidence="186-190" />
|
||||
<rect class="edge-label-bg" x="151.9" y="158.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="173.0">무효화 통지</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="157.0,458.0 157.0,506.0 157.0,506.0 157.0,554.0" data-evidence="186-189" />
|
||||
<rect class="edge-label-bg" x="141.8" y="492.0" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="507.0">세션 SELECT</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="157.0,291.0 157.0,339.0 157.0,339.0 157.0,387.0" data-evidence="186-190" />
|
||||
<rect class="edge-label-bg" x="159.0" y="325.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="340.0">차단</text>
|
||||
<g id="node-k0">
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="183-189" x="92.5" y="60.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="90.0">keycloak-0</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="183-189" x="82.0" y="60.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="90.0">keycloak-0</text>
|
||||
</g>
|
||||
<g id="node-transport">
|
||||
<rect class="node-shape kind-channel emphasis-warning role-control" data-evidence="191-196" x="80.5" y="220.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="247.0">TCP 7800</text>
|
||||
<line class="node-detail-divider" x1="94.5" y1="268.0" x2="240.5" y2="268.0" />
|
||||
<text class="node-detail" x="96.5" y="285.0">8080·9000 만 열고 누락시킨다</text>
|
||||
<rect class="node-shape kind-channel emphasis-warning role-control" data-evidence="191-196" x="80.5" y="220.0" width="153.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="247.0">TCP 7800</text>
|
||||
<line class="node-detail-divider" x1="94.5" y1="268.0" x2="219.5" y2="268.0" />
|
||||
<text class="node-detail" x="96.5" y="285.0">허용 포트 8080 · 9000</text>
|
||||
</g>
|
||||
<g id="node-k1">
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="183-189" x="70.0" y="387.0" width="195.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="414.0">keycloak-1</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="435.0" x2="251.0" y2="435.0" />
|
||||
<text class="node-detail" x="86.0" y="452.0">400 이어야 할 refresh 가 200</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="183-189" x="70.0" y="387.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="414.0">keycloak-1</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="435.0" x2="230.0" y2="435.0" />
|
||||
<text class="node-detail" x="86.0" y="452.0">refresh 200 (기대 400)</text>
|
||||
</g>
|
||||
<g id="node-db">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-store" data-evidence="186-190" x="91.0" y="567.0" width="153.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-store" cx="167.5" cy="567.0" rx="76.5" ry="13.0" /><path class="storage-bottom" d="M 91.0 638.0 A 76.5 13.0 0 0 0 244.0 638.0" />
|
||||
<text class="node-label" x="167.5" y="594.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="105.0" y1="615.0" x2="230.0" y2="615.0" />
|
||||
<text class="node-detail" x="107.0" y="632.0">교차 노드 refresh 200</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-store" data-evidence="186-190" x="80.5" y="567.0" width="153.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-store" cx="157.0" cy="567.0" rx="76.5" ry="13.0" /><path class="storage-bottom" d="M 80.5 638.0 A 76.5 13.0 0 0 0 233.5 638.0" />
|
||||
<text class="node-label" x="157.0" y="594.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="94.5" y1="615.0" x2="219.5" y2="615.0" />
|
||||
<text class="node-detail" x="96.5" y="632.0">교차 노드 refresh 200</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -5,7 +5,7 @@
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_client" value="클라이언트<br/>로그인 153건 성공" tooltip="200 과 토큰을 받는다. | Evidence: L207-L213" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxCell id="n_client" value="클라이언트<br/>성공 응답 153건" tooltip="200 과 토큰을 받는다. | Evidence: L207-L213" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="134.5" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_keycloak" value="Keycloak<br/>SET LOCAL synchronous_commit TO OFF" tooltip="트랜잭션마다 synchronous_commit 을 끈다. | Evidence: L211-L215" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
@@ -14,7 +14,7 @@
|
||||
<mxCell id="n_pg" value="PostgreSQL<br/>wal_writer_delay 200ms" tooltip="COMMIT 을 즉시 반환한다. WAL 은 아직 메모리에 있다. | Evidence: L211-L215" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="115.5" y="407.0" width="188.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_disk" value="디스크의 WAL<br/>DB 에 실제로 존재 149건" tooltip="여기 닿아야 살아남는다. | Evidence: L207-L215" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxCell id="n_disk" value="디스크의 WAL<br/>DB 존재 149건" tooltip="여기 닿아야 살아남는다. | Evidence: L207-L215" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="134.5" y="600.0" width="150.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_c-k" value="로그인 요청" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_client" target="n_keycloak">
|
||||
|
||||
@@ -361,11 +361,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "클라이언트\n로그인 153건 성공",
|
||||
"text": "클라이언트\n성공 응답 153건",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "클라이언트\n로그인 153건 성공",
|
||||
"originalText": "클라이언트\n성공 응답 153건",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -568,11 +568,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "디스크의 WAL\nDB 에 실제로 존재 149건",
|
||||
"text": "디스크의 WAL\nDB 존재 149건",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "디스크의 WAL\nDB 에 실제로 존재 149건",
|
||||
"originalText": "디스크의 WAL\nDB 존재 149건",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a3-commit-to-disk-gap",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "29e279f39e07ba519e4e0685e5719d5d63b3ecf8b61a18816cbd5a1529baf854",
|
||||
"spec_sha256": "1dfb52c8df3953df103ea2d515cf5a4f3cfed507d286e093fad31b583a851ce2",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -79,6 +79,6 @@
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="207-215" x="134.5" y="613.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="209.5" cy="613.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 134.5 684.0 A 75.0 13.0 0 0 0 284.5 684.0" />
|
||||
<text class="node-label" x="209.5" y="640.0">디스크의 WAL</text>
|
||||
<line class="node-detail-divider" x1="148.5" y1="661.0" x2="270.5" y2="661.0" />
|
||||
<text class="node-detail" x="150.5" y="678.0">DB 에 실제로 존재 149건</text>
|
||||
<text class="node-detail" x="150.5" y="678.0">DB 존재 149건</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -8,13 +8,13 @@
|
||||
<mxCell id="n_client" value="외부 요청" tooltip="사용자. | Evidence: L221-L228" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="92.5" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_entry" value="진입 경로 (kc-lab-1)<br/>kubectl 도 불통" tooltip="traefik 과 API 서버. 잃으면 000 이다. | Evidence: L221-L231" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;rhombus;perimeter=rhombusPerimeter;fillColor=#fff7e8;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxCell id="n_entry" value="진입 경로 (kc-lab-1)<br/>kubectl 불통" tooltip="traefik 과 API 서버. 잃으면 000 이다. | Evidence: L221-L231" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;rhombus;perimeter=rhombusPerimeter;fillColor=#fff7e8;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="92.5" y="240.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_workload" value="keycloak-0 (kc-lab-2)<br/>도달할 수 없을 뿐이다" tooltip="4b 에서는 계속 돌고 있었다. | Evidence: L221-L231" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxCell id="n_workload" value="keycloak-0 (kc-lab-2)<br/>도달 불가" tooltip="4b 에서는 계속 돌고 있었다. | Evidence: L221-L231" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="77.0" y="407.0" width="181.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_store" value="PostgreSQL (kc-lab-2)<br/>local-path PVC 라 재배치 불가" tooltip="4a 에서 함께 죽는다. 잃으면 503 이다. | Evidence: L221-L233" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxCell id="n_store" value="PostgreSQL (kc-lab-2)<br/>local-path PVC · 재배치 불가" tooltip="4a 에서 함께 죽는다. 잃으면 503 이다. | Evidence: L221-L233" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="574.0" width="195.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_c-e" value="요청" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_client" target="n_entry">
|
||||
|
||||
@@ -430,11 +430,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "진입 경로 (kc-lab-1)\nkubectl 도 불통",
|
||||
"text": "진입 경로 (kc-lab-1)\nkubectl 불통",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "진입 경로 (kc-lab-1)\nkubectl 도 불통",
|
||||
"originalText": "진입 경로 (kc-lab-1)\nkubectl 불통",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -499,11 +499,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "keycloak-0 (kc-lab-2)\n도달할 수 없을 뿐이다",
|
||||
"text": "keycloak-0 (kc-lab-2)\n도달 불가",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "keycloak-0 (kc-lab-2)\n도달할 수 없을 뿐이다",
|
||||
"originalText": "keycloak-0 (kc-lab-2)\n도달 불가",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -568,11 +568,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "PostgreSQL (kc-lab-2)\nlocal-path PVC 라 재배치 불가",
|
||||
"text": "PostgreSQL (kc-lab-2)\nlocal-path PVC · 재배치 불가",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "PostgreSQL (kc-lab-2)\nlocal-path PVC 라 재배치 불가",
|
||||
"originalText": "PostgreSQL (kc-lab-2)\nlocal-path PVC · 재배치 불가",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a4-two-node-losses",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "788cb5ef3210d1bb16d41feb27bd35f4041e93fea3e9826e4463391b82ccee5e",
|
||||
"spec_sha256": "3c9bc2fcbc853f176e8a38fc838062a36779083d1affe8e46a560484e230233b",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -67,18 +67,18 @@
|
||||
<polygon class="node-shape kind-gateway emphasis-warning role-control" data-evidence="221-231" points="167.5,240.0 242.5,275.5 167.5,311.0 92.5,275.5" />
|
||||
<text class="node-label" x="167.5" y="267.0">진입 경로 (kc-lab-1)</text>
|
||||
<line class="node-detail-divider" x1="106.5" y1="288.0" x2="228.5" y2="288.0" />
|
||||
<text class="node-detail" x="108.5" y="305.0">kubectl 도 불통</text>
|
||||
<text class="node-detail" x="108.5" y="305.0">kubectl 불통</text>
|
||||
</g>
|
||||
<g id="node-workload">
|
||||
<rect class="node-shape kind-service emphasis-normal role-control" data-evidence="221-231" x="77.0" y="407.0" width="181.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="434.0">keycloak-0 (kc-lab-2)</text>
|
||||
<line class="node-detail-divider" x1="91.0" y1="455.0" x2="244.0" y2="455.0" />
|
||||
<text class="node-detail" x="93.0" y="472.0">도달할 수 없을 뿐이다</text>
|
||||
<text class="node-detail" x="93.0" y="472.0">도달 불가</text>
|
||||
</g>
|
||||
<g id="node-store">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="221-233" x="70.0" y="587.0" width="195.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="167.5" cy="587.0" rx="97.5" ry="13.0" /><path class="storage-bottom" d="M 70.0 658.0 A 97.5 13.0 0 0 0 265.0 658.0" />
|
||||
<text class="node-label" x="167.5" y="614.0">PostgreSQL (kc-lab-2)</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="635.0" x2="251.0" y2="635.0" />
|
||||
<text class="node-detail" x="86.0" y="652.0">local-path PVC 라 재배치 불가</text>
|
||||
<text class="node-detail" x="86.0" y="652.0">local-path PVC · 재배치 불가</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -17,6 +17,6 @@ JGroups 는 한 방향이 막혀도 열린 방향으로 재연결한다. 그래
|
||||
|
||||
## Relationships
|
||||
|
||||
- **막은 방향 → keycloak-1:** 막혀서 닿지 않는다. Evidence: L226–L232.
|
||||
- **막은 방향 → keycloak-1:** 차단. Evidence: L226–L232.
|
||||
- **keycloak-0 → 막은 방향:** JGroups 메시지. Evidence: L226–L232.
|
||||
- **keycloak-1 → 열린 반대 방향:** 반대 방향으로 재연결. Evidence: L226–L232.
|
||||
|
||||
@@ -14,5 +14,5 @@ n3: "열린 반대 방향" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "JGroups 메시지"
|
||||
n1 -> n2: "막혀서 닿지 않는다"
|
||||
n1 -> n2: "차단"
|
||||
n2 -> n3: "반대 방향으로 재연결"
|
||||
|
||||
@@ -7,6 +7,6 @@ digraph techviz {
|
||||
n2 [label="keycloak-1", shape=box, style="rounded,filled"];
|
||||
n3 [label="열린 반대 방향", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="JGroups 메시지", style=solid];
|
||||
n1 -> n2 [label="막혀서 닿지 않는다", style=solid];
|
||||
n1 -> n2 [label="차단", style=solid];
|
||||
n2 -> n3 [label="반대 방향으로 재연결", style=solid];
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<mxCell id="n_k1" value="keycloak-1<br/>readiness 실패" tooltip="분단되면 스스로 로드밸런서에서 빠진다. | Evidence: L226-L236" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="394.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_rev" value="열린 반대 방향<br/>가르지 못한다" tooltip="비대칭 차단에서는 여기로 재연결한다. | Evidence: L226-L232" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxCell id="n_rev" value="열린 반대 방향<br/>재연결 경로" tooltip="비대칭 차단에서는 여기로 재연결한다. | Evidence: L226-L232" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="561.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_f-k1" value="막혀서 닿지 않는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_fwd" target="n_k1">
|
||||
<mxCell id="e_f-k1" value="차단" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_fwd" target="n_k1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="169.0" y="346.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -94,11 +94,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "막혀서 닿지 않는다",
|
||||
"text": "차단",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "막혀서 닿지 않는다",
|
||||
"originalText": "차단",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -568,11 +568,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "열린 반대 방향\n가르지 못한다",
|
||||
"text": "열린 반대 방향\n재연결 경로",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "열린 반대 방향\n가르지 못한다",
|
||||
"originalText": "열린 반대 방향\n재연결 경로",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a5-partition-asymmetry",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "26b2dc620b94c979befdef3808475899361f52b7f622fc06a78b5c6c59cd72a7",
|
||||
"spec_sha256": "9b10dfa9cfb1f24e5b105da13bc8fde85130cc1cca2ddad54cd57c32bc2dfcf5",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -6,5 +6,5 @@ flowchart TB
|
||||
n2["keycloak-1"]
|
||||
n3["열린 반대 방향"]
|
||||
n0 -->|"JGroups 메시지"| n1
|
||||
n1 -->|"막혀서 닿지 않는다"| n2
|
||||
n1 -->|"차단"| n2
|
||||
n2 -->|"반대 방향으로 재연결"| n3
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="677" />
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="145.0,298.0 145.0,346.0 145.0,346.0 145.0,394.0" data-evidence="226-232" />
|
||||
<rect class="edge-label-bg" x="126.5" y="332.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="169.0" y="347.0">막혀서 닿지 않는다</text>
|
||||
<rect class="edge-label-bg" x="147.0" y="332.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="169.0" y="347.0">차단</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="145.0,131.0 145.0,179.0 145.0,179.0 145.0,227.0" data-evidence="226-232" />
|
||||
<rect class="edge-label-bg" x="123.2" y="165.0" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="169.0" y="180.0">JGroups 메시지</text>
|
||||
@@ -81,6 +81,6 @@
|
||||
<rect class="node-shape kind-channel emphasis-normal role-support" data-evidence="226-232" x="70.0" y="561.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="588.0">열린 반대 방향</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="609.0" x2="206.0" y2="609.0" />
|
||||
<text class="node-detail" x="86.0" y="626.0">가르지 못한다</text>
|
||||
<text class="node-detail" x="86.0" y="626.0">재연결 경로</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -17,6 +17,6 @@
|
||||
|
||||
## Relationships
|
||||
|
||||
- **주입한 지연 → 왕복 횟수만큼 누적:** 왕복마다 더해진다. Evidence: L253–L263.
|
||||
- **주입한 지연 → 왕복 횟수만큼 누적:** 왕복당 누적. Evidence: L253–L263.
|
||||
- **커넥션 풀 대기 → readiness 실패:** 프로브도 타임아웃. Evidence: L264–L273.
|
||||
- **왕복 횟수만큼 누적 → 커넥션 풀 대기:** 커넥션 점유가 길어진다. Evidence: L259–L268.
|
||||
- **왕복 횟수만큼 누적 → 커넥션 풀 대기:** 커넥션 점유. Evidence: L259–L268.
|
||||
|
||||
@@ -13,6 +13,6 @@ n2: "커넥션 풀 대기" {
|
||||
n3: "readiness 실패" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "왕복마다 더해진다"
|
||||
n1 -> n2: "커넥션 점유가 길어진다"
|
||||
n0 -> n1: "왕복당 누적"
|
||||
n1 -> n2: "커넥션 점유"
|
||||
n2 -> n3: "프로브도 타임아웃"
|
||||
|
||||
@@ -6,7 +6,7 @@ digraph techviz {
|
||||
n1 [label="왕복 횟수만큼 누적", shape=box, style="rounded,filled"];
|
||||
n2 [label="커넥션 풀 대기", shape=box, style="rounded,filled"];
|
||||
n3 [label="readiness 실패", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="왕복마다 더해진다", style=solid];
|
||||
n1 -> n2 [label="커넥션 점유가 길어진다", style=solid];
|
||||
n0 -> n1 [label="왕복당 누적", style=solid];
|
||||
n1 -> n2 [label="커넥션 점유", style=solid];
|
||||
n2 -> n3 [label="프로브도 타임아웃", style=solid];
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<mxCell id="n_probe" value="readiness 실패<br/>동시 20건 최대 22.2초" tooltip="느린 노드가 로드밸런서에서 빠진다. | Evidence: L269-L273" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="71.5" y="561.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_i-r" value="왕복마다 더해진다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_inject" target="n_roundtrips">
|
||||
<mxCell id="e_i-r" value="왕복당 누적" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_inject" target="n_roundtrips">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="170.5" y="179.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
@@ -27,7 +27,7 @@
|
||||
<mxPoint x="170.5" y="513.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-p" value="커넥션 점유가 길어진다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_roundtrips" target="n_pool">
|
||||
<mxCell id="e_r-p" value="커넥션 점유" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_roundtrips" target="n_pool">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="170.5" y="346.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -94,11 +94,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "왕복마다 더해진다",
|
||||
"text": "왕복당 누적",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "왕복마다 더해진다",
|
||||
"originalText": "왕복당 누적",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -264,9 +264,9 @@
|
||||
{
|
||||
"id": "edge-label-r-p",
|
||||
"type": "text",
|
||||
"x": 122.5,
|
||||
"x": 125.5,
|
||||
"y": 334.0,
|
||||
"width": 96,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -292,11 +292,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "커넥션 점유가 길어진다",
|
||||
"text": "커넥션 점유",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "커넥션 점유가 길어진다",
|
||||
"originalText": "커넥션 점유",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a6-latency-multiplication",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "c9d669f597dc9b26d68636bb52e7239c266a8c5985c73d51c3fe0b7ebd7ff231",
|
||||
"spec_sha256": "0b2994cb0a306f6fc1bd7fb2dae907ed79b6fe0bb6e908dba8509b0db1ca9f2b",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -5,6 +5,6 @@ flowchart TB
|
||||
n1["왕복 횟수만큼 누적"]
|
||||
n2["커넥션 풀 대기"]
|
||||
n3["readiness 실패"]
|
||||
n0 -->|"왕복마다 더해진다"| n1
|
||||
n1 -->|"커넥션 점유가 길어진다"| n2
|
||||
n0 -->|"왕복당 누적"| n1
|
||||
n1 -->|"커넥션 점유"| n2
|
||||
n2 -->|"프로브도 타임아웃"| n3
|
||||
|
||||
@@ -51,14 +51,14 @@
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="677" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="146.5,131.0 146.5,179.0 146.5,179.0 146.5,227.0" data-evidence="253-263" />
|
||||
<rect class="edge-label-bg" x="131.3" y="165.0" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="180.0">왕복마다 더해진다</text>
|
||||
<rect class="edge-label-bg" x="141.4" y="165.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="180.0">왕복당 누적</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="146.5,465.0 146.5,513.0 146.5,513.0 146.5,561.0" data-evidence="264-273" />
|
||||
<rect class="edge-label-bg" x="131.3" y="499.0" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="514.0">프로브도 타임아웃</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="146.5,298.0 146.5,346.0 146.5,346.0 146.5,394.0" data-evidence="259-268" />
|
||||
<rect class="edge-label-bg" x="121.3" y="332.0" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="347.0">커넥션 점유가 길어진다</text>
|
||||
<rect class="edge-label-bg" x="141.4" y="332.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="347.0">커넥션 점유</text>
|
||||
<g id="node-inject">
|
||||
<rect class="node-shape kind-process emphasis-warning role-source" data-evidence="253-258" x="71.5" y="60.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="146.5" y="87.0">주입한 지연</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -17,6 +17,6 @@
|
||||
|
||||
## Relationships
|
||||
|
||||
- **PostgreSQL 세션 행 → 재시작 전 발급한 refresh token:** 세션이 남아 있어 통한다. Evidence: L270–L278.
|
||||
- **롤링 재시작 → Infinispan sessions 캐시:** 비운다. Evidence: L270–L278.
|
||||
- **롤링 재시작 → PostgreSQL 세션 행:** 건드리지 않는다. Evidence: L270–L278.
|
||||
- **PostgreSQL 세션 행 → 재시작 전 발급한 refresh token:** 세션 조회. Evidence: L270–L278.
|
||||
- **롤링 재시작 → Infinispan sessions 캐시:** 캐시 초기화. Evidence: L270–L278.
|
||||
- **롤링 재시작 → PostgreSQL 세션 행:** 변경 없음. Evidence: L270–L278.
|
||||
|
||||
@@ -13,6 +13,6 @@ n2: "PostgreSQL 세션 행" {
|
||||
n3: "재시작 전 발급한 refresh token" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "비운다"
|
||||
n0 -> n2: "건드리지 않는다"
|
||||
n2 -> n3: "세션이 남아 있어 통한다"
|
||||
n0 -> n1: "캐시 초기화"
|
||||
n0 -> n2: "변경 없음"
|
||||
n2 -> n3: "세션 조회"
|
||||
|
||||
@@ -6,7 +6,7 @@ digraph techviz {
|
||||
n1 [label="Infinispan sessions 캐시", shape=box, style="rounded,filled"];
|
||||
n2 [label="PostgreSQL 세션 행", shape=cylinder, style="rounded,filled"];
|
||||
n3 [label="재시작 전 발급한 refresh token", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="비운다", style=solid];
|
||||
n0 -> n2 [label="건드리지 않는다", style=solid];
|
||||
n2 -> n3 [label="세션이 남아 있어 통한다", style=solid];
|
||||
n0 -> n1 [label="캐시 초기화", style=solid];
|
||||
n0 -> n2 [label="변경 없음", style=solid];
|
||||
n2 -> n3 [label="세션 조회", style=solid];
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
<mxCell id="n_refresh" value="재시작 전 발급한 refresh token<br/>200" tooltip="여전히 통한다. | Evidence: L270-L278" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="192.5" y="420.0" width="153.0" height="91.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_db-rt" value="세션이 남아 있어 통한다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_db" target="n_refresh">
|
||||
<mxCell id="e_db-rt" value="세션 조회" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_db" target="n_refresh">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="331.0" y="344.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-c" value="비운다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_restart" target="n_cache">
|
||||
<mxCell id="e_r-c" value="캐시 초기화" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_restart" target="n_cache">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="212.0" y="157.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-db" value="건드리지 않는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_restart" target="n_db">
|
||||
<mxCell id="e_r-db" value="변경 없음" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_restart" target="n_db">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="335.5" y="151.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -66,9 +66,9 @@
|
||||
{
|
||||
"id": "edge-label-db-rt",
|
||||
"type": "text",
|
||||
"x": 279.0,
|
||||
"x": 286.0,
|
||||
"y": 332.0,
|
||||
"width": 104,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -94,11 +94,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "세션이 남아 있어 통한다",
|
||||
"text": "세션 조회",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "세션이 남아 있어 통한다",
|
||||
"originalText": "세션 조회",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -193,11 +193,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "비운다",
|
||||
"text": "캐시 초기화",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "비운다",
|
||||
"originalText": "캐시 초기화",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -292,11 +292,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "건드리지 않는다",
|
||||
"text": "변경 없음",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "건드리지 않는다",
|
||||
"originalText": "변경 없음",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a8-cache-vs-session",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "60ae0f0551684a3d013de2a0eca5a41d5edf858f99511c29441388c02f7310c2",
|
||||
"spec_sha256": "02e971c0aedba252dce01773d7896eb2a751cd8752eb14d643b43e8f96646102",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -5,6 +5,6 @@ flowchart TB
|
||||
n1["Infinispan sessions 캐시"]
|
||||
n2[("PostgreSQL 세션 행")]
|
||||
n3["재시작 전 발급한 refresh token"]
|
||||
n0 -->|"비운다"| n1
|
||||
n0 -->|"건드리지 않는다"| n2
|
||||
n2 -->|"세션이 남아 있어 통한다"| n3
|
||||
n0 -->|"캐시 초기화"| n1
|
||||
n0 -->|"변경 없음"| n2
|
||||
n2 -->|"세션 조회"| n3
|
||||
|
||||
@@ -51,14 +51,14 @@
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="556" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="393.0,324.0 393.0,372.0 269.0,372.0 269.0,420.0" data-evidence="270-278" />
|
||||
<rect class="edge-label-bg" x="278.4" y="330.0" width="105.1" height="22" rx="3" />
|
||||
<text class="edge-label" x="331.0" y="345.0">세션이 남아 있어 통한다</text>
|
||||
<rect class="edge-label-bg" x="305.2" y="330.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="331.0" y="345.0">세션 조회</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="260.0,131.0 260.0,185.5 164.0,185.5 164.0,240.0" data-evidence="270-278" />
|
||||
<rect class="edge-label-bg" x="190.0" y="143.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="212.0" y="158.5">비운다</text>
|
||||
<rect class="edge-label-bg" x="182.9" y="143.5" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="212.0" y="158.5">캐시 초기화</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="278.0,131.0 278.0,179.0 393.0,179.0 393.0,227.0" data-evidence="270-278" />
|
||||
<rect class="edge-label-bg" x="299.7" y="137.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="335.5" y="152.0">건드리지 않는다</text>
|
||||
<rect class="edge-label-bg" x="309.8" y="137.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="335.5" y="152.0">변경 없음</text>
|
||||
<g id="node-restart">
|
||||
<rect class="node-shape kind-process emphasis-primary role-source" data-evidence="270-274" x="171.5" y="60.0" width="195.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="269.0" y="87.0">롤링 재시작</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -17,6 +17,6 @@
|
||||
|
||||
## Relationships
|
||||
|
||||
- **브라우저 A 세션 → 토큰 행:** principal 이름으로 쓴다. Evidence: L370–L377.
|
||||
- **브라우저 B 세션 → 토큰 행:** 같은 키로 덮어쓴다. Evidence: L370–L377.
|
||||
- **토큰 행 → 로그아웃 후 남는 것:** 로그아웃이 정리하지 않는다. Evidence: L378–L384.
|
||||
- **브라우저 A 세션 → 토큰 행:** principal 이름. Evidence: L370–L377.
|
||||
- **브라우저 B 세션 → 토큰 행:** 같은 키 · 덮어쓰기. Evidence: L370–L377.
|
||||
- **토큰 행 → 로그아웃 후 남는 것:** 로그아웃 미정리. Evidence: L378–L384.
|
||||
|
||||
@@ -13,6 +13,6 @@ n2: "토큰 행" {
|
||||
n3: "로그아웃 후 남는 것" {
|
||||
shape: cylinder
|
||||
}
|
||||
n0 -> n2: "principal 이름으로 쓴다"
|
||||
n1 -> n2: "같은 키로 덮어쓴다"
|
||||
n2 -> n3: "로그아웃이 정리하지 않는다"
|
||||
n0 -> n2: "principal 이름"
|
||||
n1 -> n2: "같은 키 · 덮어쓰기"
|
||||
n2 -> n3: "로그아웃 미정리"
|
||||
|
||||
@@ -6,7 +6,7 @@ digraph techviz {
|
||||
n1 [label="브라우저 B 세션", shape=box, style="rounded,filled"];
|
||||
n2 [label="토큰 행", shape=cylinder, style="rounded,filled"];
|
||||
n3 [label="로그아웃 후 남는 것", shape=cylinder, style="rounded,filled"];
|
||||
n0 -> n2 [label="principal 이름으로 쓴다", style=solid];
|
||||
n1 -> n2 [label="같은 키로 덮어쓴다", style=solid];
|
||||
n2 -> n3 [label="로그아웃이 정리하지 않는다", style=solid];
|
||||
n0 -> n2 [label="principal 이름", style=solid];
|
||||
n1 -> n2 [label="같은 키 · 덮어쓰기", style=solid];
|
||||
n2 -> n3 [label="로그아웃 미정리", style=solid];
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
<mxCell id="n_leftover" value="로그아웃 후 남는 것<br/>평문 refresh token" tooltip="Redis 세션은 0 키인데 여기는 1 행이 남는다. | Evidence: L378-L384" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="175.0" y="413.0" width="150.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_s1-t" value="principal 이름으로 쓴다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_s1" target="n_token-row">
|
||||
<mxCell id="e_s1-t" value="principal 이름" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_s1" target="n_token-row">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="193.0" y="144.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_s2-t" value="같은 키로 덮어쓴다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_s2" target="n_token-row">
|
||||
<mxCell id="e_s2-t" value="같은 키 · 덮어쓰기" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_s2" target="n_token-row">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="307.0" y="144.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_t-l" value="로그아웃이 정리하지 않는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_token-row" target="n_leftover">
|
||||
<mxCell id="e_t-l" value="로그아웃 미정리" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_token-row" target="n_leftover">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="274.0" y="365.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -66,9 +66,9 @@
|
||||
{
|
||||
"id": "edge-label-s1-t",
|
||||
"type": "text",
|
||||
"x": 125.0,
|
||||
"x": 145.0,
|
||||
"y": 132.0,
|
||||
"width": 136,
|
||||
"width": 96,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -94,11 +94,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "principal 이름으로 쓴다",
|
||||
"text": "principal 이름",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "principal 이름으로 쓴다",
|
||||
"originalText": "principal 이름",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -193,11 +193,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "같은 키로 덮어쓴다",
|
||||
"text": "같은 키 · 덮어쓰기",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "같은 키로 덮어쓴다",
|
||||
"originalText": "같은 키 · 덮어쓰기",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -264,9 +264,9 @@
|
||||
{
|
||||
"id": "edge-label-t-l",
|
||||
"type": "text",
|
||||
"x": 218.0,
|
||||
"x": 229.0,
|
||||
"y": 353.0,
|
||||
"width": 112,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -292,11 +292,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "로그아웃이 정리하지 않는다",
|
||||
"text": "로그아웃 미정리",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "로그아웃이 정리하지 않는다",
|
||||
"originalText": "로그아웃 미정리",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b2-primary-key-overwrite",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "653f795b5dd9a627e79a865316e22d8d7ca35f86cbdf6e72dea25c631687cadf",
|
||||
"spec_sha256": "88795858ea78e4c8a0f6fca3a6131c36883a2378246384b83b9d31b636a7b5a4",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -5,6 +5,6 @@ flowchart TB
|
||||
n1["브라우저 B 세션"]
|
||||
n2[("토큰 행")]
|
||||
n3[("로그아웃 후 남는 것")]
|
||||
n0 -->|"principal 이름으로 쓴다"| n2
|
||||
n1 -->|"같은 키로 덮어쓴다"| n2
|
||||
n2 -->|"로그아웃이 정리하지 않는다"| n3
|
||||
n0 -->|"principal 이름"| n2
|
||||
n1 -->|"같은 키 · 덮어쓰기"| n2
|
||||
n2 -->|"로그아웃 미정리"| n3
|
||||
|
||||
@@ -51,14 +51,14 @@
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="555" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="145.0,124.0 145.0,172.0 241.0,172.0 241.0,220.0" data-evidence="370-377" />
|
||||
<rect class="edge-label-bg" x="127.0" y="130.0" width="131.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="193.0" y="145.0">principal 이름으로 쓴다</text>
|
||||
<rect class="edge-label-bg" x="143.8" y="130.0" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="193.0" y="145.0">principal 이름</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="355.0,124.0 355.0,172.0 259.0,172.0 259.0,220.0" data-evidence="370-377" />
|
||||
<rect class="edge-label-bg" x="264.5" y="130.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="307.0" y="145.0">같은 키로 덮어쓴다</text>
|
||||
<rect class="edge-label-bg" x="261.1" y="130.0" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="307.0" y="145.0">같은 키 · 덮어쓰기</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="250.0,317.0 250.0,365.0 250.0,365.0 250.0,413.0" data-evidence="378-384" />
|
||||
<rect class="edge-label-bg" x="218.1" y="351.0" width="111.8" height="22" rx="3" />
|
||||
<text class="edge-label" x="274.0" y="366.0">로그아웃이 정리하지 않는다</text>
|
||||
<rect class="edge-label-bg" x="238.2" y="351.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="274.0" y="366.0">로그아웃 미정리</text>
|
||||
<g id="node-s1">
|
||||
<rect class="node-shape kind-component emphasis-primary role-source" data-evidence="360-366" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="90.0">브라우저 A 세션</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -18,5 +18,5 @@ revokeRefreshToken 을 켜고 refreshTokenMaxReuse 를 0 으로 둔 상태에서
|
||||
## Relationships
|
||||
|
||||
- **동시 refresh 5건 → 회전 검사:** 동시 도착. Evidence: L369–L377.
|
||||
- **client session → 이긴 요청의 새 토큰:** 세션이 없으니 못 쓴다. Evidence: L378–L384.
|
||||
- **client session → 이긴 요청의 새 토큰:** 사용 불가. Evidence: L378–L384.
|
||||
- **회전 검사 → client session:** 경쟁 감지 시 삭제. Evidence: L378–L384.
|
||||
|
||||
@@ -15,4 +15,4 @@ n3: "이긴 요청의 새 토큰" {
|
||||
}
|
||||
n0 -> n1: "동시 도착"
|
||||
n1 -> n2: "경쟁 감지 시 삭제"
|
||||
n2 -> n3: "세션이 없으니 못 쓴다"
|
||||
n2 -> n3: "사용 불가"
|
||||
|
||||
@@ -8,5 +8,5 @@ digraph techviz {
|
||||
n3 [label="이긴 요청의 새 토큰", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="동시 도착", style=solid];
|
||||
n1 -> n2 [label="경쟁 감지 시 삭제", style=solid];
|
||||
n2 -> n3 [label="세션이 없으니 못 쓴다", style=solid];
|
||||
n2 -> n3 [label="사용 불가", style=solid];
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_concurrent" value="동시 refresh 5건<br/>& 와 wait 이 없으면 재현되지 않는다" tooltip="같은 refresh token 을 쓴다. | Evidence: L369-L375" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="135.0" y="60.0" width="170.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_concurrent" value="동시 refresh 5건<br/>& + wait 필요" tooltip="같은 refresh token 을 쓴다. | Evidence: L369-L375" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="145.0" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_rotation" value="회전 검사<br/>revokeRefreshToken=true · refreshTokenMaxReuse=0" tooltip="이미 쓴 토큰인지 본다. | Evidence: L369-L377" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="240.0" width="300.0" height="71.0" as="geometry"/>
|
||||
@@ -22,7 +22,7 @@
|
||||
<mxPoint x="244.0" y="192.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_cs-nt" value="세션이 없으니 못 쓴다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_client-session" target="n_new-token">
|
||||
<mxCell id="e_cs-nt" value="사용 불가" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_client-session" target="n_new-token">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="244.0" y="519.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -165,9 +165,9 @@
|
||||
{
|
||||
"id": "edge-label-cs-nt",
|
||||
"type": "text",
|
||||
"x": 196.0,
|
||||
"x": 199.0,
|
||||
"y": 507.0,
|
||||
"width": 96,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -193,11 +193,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "세션이 없으니 못 쓴다",
|
||||
"text": "사용 불가",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "세션이 없으니 못 쓴다",
|
||||
"originalText": "사용 불가",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -303,9 +303,9 @@
|
||||
{
|
||||
"id": "node-concurrent",
|
||||
"type": "rectangle",
|
||||
"x": 135.0,
|
||||
"x": 145.0,
|
||||
"y": 60.0,
|
||||
"width": 170.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -333,9 +333,9 @@
|
||||
{
|
||||
"id": "node-label-concurrent",
|
||||
"type": "text",
|
||||
"x": 145.0,
|
||||
"x": 155.0,
|
||||
"y": 70.0,
|
||||
"width": 150.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -361,11 +361,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "동시 refresh 5건\n& 와 wait 이 없으면 재현되지 않는다",
|
||||
"text": "동시 refresh 5건\n& + wait 필요",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "동시 refresh 5건\n& 와 wait 이 없으면 재현되지 않는다",
|
||||
"originalText": "동시 refresh 5건\n& + wait 필요",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b3-rotation-contention",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "3fbbe8b3528bbcc2ad23e48a820e9ddc6fe6072857bfbe2fa3df0e566fe99162",
|
||||
"spec_sha256": "31779474e5400d5e719faf81a62da35a657994d952e4e0324a1c76c5d18183e4",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -7,6 +7,6 @@ flowchart TB
|
||||
n3["이긴 요청의 새 토큰"]
|
||||
n0 -->|"동시 도착"| n1
|
||||
n1 -->|"경쟁 감지 시 삭제"| n2
|
||||
n2 -->|"세션이 없으니 못 쓴다"| n3
|
||||
n2 -->|"사용 불가"| n3
|
||||
classDef external stroke-dasharray: 6 4
|
||||
class n0 external
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
<rect class="edge-label-bg" x="218.2" y="178.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="244.0" y="193.0">동시 도착</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="220.0,471.0 220.0,519.0 220.0,519.0 220.0,567.0" data-evidence="378-384" />
|
||||
<rect class="edge-label-bg" x="194.8" y="505.0" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="244.0" y="520.0">세션이 없으니 못 쓴다</text>
|
||||
<rect class="edge-label-bg" x="218.2" y="505.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="244.0" y="520.0">사용 불가</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="220.0,311.0 220.0,359.0 220.0,359.0 220.0,407.0" data-evidence="378-384" />
|
||||
<rect class="edge-label-bg" x="201.5" y="345.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="244.0" y="360.0">경쟁 감지 시 삭제</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -18,5 +18,5 @@ nginx 는 자기가 proxy_set_header 로 설정한 헤더만 덮어쓴다. 설
|
||||
## Relationships
|
||||
|
||||
- **밖에서 보낸 위조 헤더 → nginx:** 위조 헤더. Evidence: L393–L400.
|
||||
- **nginx → oauth2-proxy:** 지우지 않으면 그대로 지난다. Evidence: L393–L400.
|
||||
- **nginx → oauth2-proxy:** 미삭제 시 통과. Evidence: L393–L400.
|
||||
- **oauth2-proxy → 앱:** 인가 헤더. Evidence: L393–L408.
|
||||
|
||||
@@ -14,5 +14,5 @@ n3: "앱" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "위조 헤더"
|
||||
n1 -> n2: "지우지 않으면 그대로 지난다"
|
||||
n1 -> n2: "미삭제 시 통과"
|
||||
n2 -> n3: "인가 헤더"
|
||||
|
||||
@@ -7,6 +7,6 @@ digraph techviz {
|
||||
n2 [label="oauth2-proxy", shape=diamond, style="rounded,filled"];
|
||||
n3 [label="앱", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="위조 헤더", style=solid];
|
||||
n1 -> n2 [label="지우지 않으면 그대로 지난다", style=solid];
|
||||
n1 -> n2 [label="미삭제 시 통과", style=solid];
|
||||
n2 -> n3 [label="인가 헤더", style=solid];
|
||||
}
|
||||
|
||||
@@ -6,30 +6,30 @@
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_attacker" value="밖에서 보낸 위조 헤더<br/>X-Auth-Request-Roles" tooltip="앱이 믿는 이름을 그대로 쓴다. | Evidence: L393-L400" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;dashed=1;fillColor=#f5f7fa;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="124.5" y="60.0" width="170.0" height="84.0" as="geometry"/>
|
||||
<mxGeometry x="82.5" y="60.0" width="170.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_nginx" value="nginx<br/>proxy_set_header ... "" 로 먼저 지워야 한다" tooltip="설정하지 않은 이름은 덮어쓰지 않는다. | Evidence: L393-L400" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;rhombus;perimeter=rhombusPerimeter;fillColor=#fff7e8;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="240.0" width="279.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_nginx" value="nginx<br/>proxy_set_header ... """ tooltip="설정하지 않은 이름은 덮어쓰지 않는다. | Evidence: L393-L400" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;rhombus;perimeter=rhombusPerimeter;fillColor=#fff7e8;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="240.0" width="195.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_proxy" value="oauth2-proxy<br/>--set-xauthrequest" tooltip="인증 결과를 헤더로 넣는다. | Evidence: L393-L400" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;rhombus;perimeter=rhombusPerimeter;fillColor=#fff7e8;" vertex="1" parent="1">
|
||||
<mxGeometry x="129.5" y="407.0" width="160.0" height="71.0" as="geometry"/>
|
||||
<mxGeometry x="87.5" y="407.0" width="160.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_app" value="앱<br/>세션은 로그인 시점의 스냅샷" tooltip="헤더를 믿고 인가한다. | Evidence: L401-L408" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="134.5" y="574.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_app" value="앱<br/>로그인 시점 스냅샷" tooltip="헤더를 믿고 인가한다. | Evidence: L401-L408" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="92.5" y="574.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_a-n" value="위조 헤더" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_attacker" target="n_nginx">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="192.0" as="offset"/>
|
||||
<mxPoint x="191.5" y="192.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_n-p" value="지우지 않으면 그대로 지난다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_nginx" target="n_proxy">
|
||||
<mxCell id="e_n-p" value="미삭제 시 통과" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_nginx" target="n_proxy">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="359.0" as="offset"/>
|
||||
<mxPoint x="191.5" y="359.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_p-a" value="인가 헤더" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_proxy" target="n_app">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="526.0" as="offset"/>
|
||||
<mxPoint x="191.5" y="526.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{
|
||||
"id": "edge-a-n",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"x": 167.5,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -66,7 +66,7 @@
|
||||
{
|
||||
"id": "edge-label-a-n",
|
||||
"type": "text",
|
||||
"x": 188.5,
|
||||
"x": 146.5,
|
||||
"y": 180.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
@@ -105,7 +105,7 @@
|
||||
{
|
||||
"id": "edge-n-p",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"x": 167.5,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -165,9 +165,9 @@
|
||||
{
|
||||
"id": "edge-label-n-p",
|
||||
"type": "text",
|
||||
"x": 173.5,
|
||||
"x": 146.5,
|
||||
"y": 347.0,
|
||||
"width": 120,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -193,18 +193,18 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "지우지 않으면 그대로 지난다",
|
||||
"text": "미삭제 시 통과",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "지우지 않으면 그대로 지난다",
|
||||
"originalText": "미삭제 시 통과",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-p-a",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"x": 167.5,
|
||||
"y": 478.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
@@ -264,7 +264,7 @@
|
||||
{
|
||||
"id": "edge-label-p-a",
|
||||
"type": "text",
|
||||
"x": 188.5,
|
||||
"x": 146.5,
|
||||
"y": 514.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
@@ -303,7 +303,7 @@
|
||||
{
|
||||
"id": "node-attacker",
|
||||
"type": "rectangle",
|
||||
"x": 124.5,
|
||||
"x": 82.5,
|
||||
"y": 60.0,
|
||||
"width": 170.0,
|
||||
"height": 84.0,
|
||||
@@ -333,7 +333,7 @@
|
||||
{
|
||||
"id": "node-label-attacker",
|
||||
"type": "text",
|
||||
"x": 134.5,
|
||||
"x": 92.5,
|
||||
"y": 70.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
@@ -374,7 +374,7 @@
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"width": 279.0,
|
||||
"width": 195.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -404,7 +404,7 @@
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"width": 259.0,
|
||||
"width": 175.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -430,18 +430,18 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "nginx\nproxy_set_header ... \"\" 로 먼저 지워야 한다",
|
||||
"text": "nginx\nproxy_set_header ... \"\"",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "nginx\nproxy_set_header ... \"\" 로 먼저 지워야 한다",
|
||||
"originalText": "nginx\nproxy_set_header ... \"\"",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-proxy",
|
||||
"type": "rectangle",
|
||||
"x": 129.5,
|
||||
"x": 87.5,
|
||||
"y": 407.0,
|
||||
"width": 160.0,
|
||||
"height": 71.0,
|
||||
@@ -471,7 +471,7 @@
|
||||
{
|
||||
"id": "node-label-proxy",
|
||||
"type": "text",
|
||||
"x": 139.5,
|
||||
"x": 97.5,
|
||||
"y": 417.0,
|
||||
"width": 140.0,
|
||||
"height": 51.0,
|
||||
@@ -510,7 +510,7 @@
|
||||
{
|
||||
"id": "node-app",
|
||||
"type": "rectangle",
|
||||
"x": 134.5,
|
||||
"x": 92.5,
|
||||
"y": 574.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
@@ -540,7 +540,7 @@
|
||||
{
|
||||
"id": "node-label-app",
|
||||
"type": "text",
|
||||
"x": 144.5,
|
||||
"x": 102.5,
|
||||
"y": 584.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
@@ -568,11 +568,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "앱\n세션은 로그인 시점의 스냅샷",
|
||||
"text": "앱\n로그인 시점 스냅샷",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "앱\n세션은 로그인 시점의 스냅샷",
|
||||
"originalText": "앱\n로그인 시점 스냅샷",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b4-header-trust-boundary",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "9ae5cf39dfc20e31d81b892ae7483d2b9ccf94fa2c91a03837200b463bfc1103",
|
||||
"spec_sha256": "56747d5f7826ad63f202d8dc4d6a4f1323f12eeaedb4bf989b67d0bfab5f09cd",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -6,7 +6,7 @@ flowchart TB
|
||||
n2{"oauth2-proxy"}
|
||||
n3["앱"]
|
||||
n0 -->|"위조 헤더"| n1
|
||||
n1 -->|"지우지 않으면 그대로 지난다"| n2
|
||||
n1 -->|"미삭제 시 통과"| n2
|
||||
n2 -->|"인가 헤더"| n3
|
||||
classDef external stroke-dasharray: 6 4
|
||||
class n0 external
|
||||
|
||||
@@ -50,35 +50,35 @@
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="690" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="209.5,144.0 209.5,192.0 209.5,192.0 209.5,240.0" data-evidence="393-400" />
|
||||
<rect class="edge-label-bg" x="207.8" y="178.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="193.0">위조 헤더</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="209.5,311.0 209.5,359.0 209.5,359.0 209.5,407.0" data-evidence="393-400" />
|
||||
<rect class="edge-label-bg" x="174.2" y="345.0" width="118.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="360.0">지우지 않으면 그대로 지난다</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="209.5,478.0 209.5,526.0 209.5,526.0 209.5,574.0" data-evidence="393-408" />
|
||||
<rect class="edge-label-bg" x="207.8" y="512.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="527.0">인가 헤더</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="167.5,144.0 167.5,192.0 167.5,192.0 167.5,240.0" data-evidence="393-400" />
|
||||
<rect class="edge-label-bg" x="165.8" y="178.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="193.0">위조 헤더</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="167.5,311.0 167.5,359.0 167.5,359.0 167.5,407.0" data-evidence="393-400" />
|
||||
<rect class="edge-label-bg" x="155.7" y="345.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="360.0">미삭제 시 통과</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="167.5,478.0 167.5,526.0 167.5,526.0 167.5,574.0" data-evidence="393-408" />
|
||||
<rect class="edge-label-bg" x="165.8" y="512.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="527.0">인가 헤더</text>
|
||||
<g id="node-attacker">
|
||||
<g class="actor-symbol emphasis-warning" data-evidence="393-400"><circle cx="209.5" cy="80.0" r="11.0" /><line x1="209.5" y1="96.0" x2="209.5" y2="105.0" /><line x1="191.5" y1="106.0" x2="227.5" y2="106.0" /><line x1="209.5" y1="105.0" x2="194.5" y2="122.0" /><line x1="209.5" y1="105.0" x2="224.5" y2="122.0" /></g>
|
||||
<text class="node-label" x="209.5" y="137.0">밖에서 보낸 위조 헤더</text>
|
||||
<g class="actor-symbol emphasis-warning" data-evidence="393-400"><circle cx="167.5" cy="80.0" r="11.0" /><line x1="167.5" y1="96.0" x2="167.5" y2="105.0" /><line x1="149.5" y1="106.0" x2="185.5" y2="106.0" /><line x1="167.5" y1="105.0" x2="152.5" y2="122.0" /><line x1="167.5" y1="105.0" x2="182.5" y2="122.0" /></g>
|
||||
<text class="node-label" x="167.5" y="137.0">밖에서 보낸 위조 헤더</text>
|
||||
</g>
|
||||
<g id="node-nginx">
|
||||
<polygon class="node-shape kind-gateway emphasis-warning role-control" data-evidence="393-400" points="209.5,240.0 349.0,275.5 209.5,311.0 70.0,275.5" />
|
||||
<text class="node-label" x="209.5" y="267.0">nginx</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="335.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">proxy_set_header ... "" 로 먼저 지워야 한다</text>
|
||||
<polygon class="node-shape kind-gateway emphasis-warning role-control" data-evidence="393-400" points="167.5,240.0 265.0,275.5 167.5,311.0 70.0,275.5" />
|
||||
<text class="node-label" x="167.5" y="267.0">nginx</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="251.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">proxy_set_header ... ""</text>
|
||||
</g>
|
||||
<g id="node-proxy">
|
||||
<polygon class="node-shape kind-gateway emphasis-normal role-control" data-evidence="393-400" points="209.5,407.0 289.5,442.5 209.5,478.0 129.5,442.5" />
|
||||
<text class="node-label" x="209.5" y="434.0">oauth2-proxy</text>
|
||||
<line class="node-detail-divider" x1="143.5" y1="455.0" x2="275.5" y2="455.0" />
|
||||
<text class="node-detail" x="145.5" y="472.0">--set-xauthrequest</text>
|
||||
<polygon class="node-shape kind-gateway emphasis-normal role-control" data-evidence="393-400" points="167.5,407.0 247.5,442.5 167.5,478.0 87.5,442.5" />
|
||||
<text class="node-label" x="167.5" y="434.0">oauth2-proxy</text>
|
||||
<line class="node-detail-divider" x1="101.5" y1="455.0" x2="233.5" y2="455.0" />
|
||||
<text class="node-detail" x="103.5" y="472.0">--set-xauthrequest</text>
|
||||
</g>
|
||||
<g id="node-app">
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="401-408" x="134.5" y="574.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="209.5" y="601.0">앱</text>
|
||||
<line class="node-detail-divider" x1="148.5" y1="622.0" x2="270.5" y2="622.0" />
|
||||
<text class="node-detail" x="150.5" y="639.0">세션은 로그인 시점의 스냅샷</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="401-408" x="92.5" y="574.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="601.0">앱</text>
|
||||
<line class="node-detail-divider" x1="106.5" y1="622.0" x2="228.5" y2="622.0" />
|
||||
<text class="node-detail" x="108.5" y="639.0">로그인 시점 스냅샷</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -17,6 +17,6 @@ redis-cli config set appendonly yes 를 켜도 아무것도 달라지지 않았
|
||||
|
||||
## Relationships
|
||||
|
||||
- **appendonly yes → /data:** AOF 파일을 쓴다. Evidence: L406–L412.
|
||||
- **/data → 컨테이너:** 컨테이너와 함께 죽는다. Evidence: L406–L412.
|
||||
- **/data → PersistentVolume:** 볼륨을 붙여야 남는다. Evidence: L406–L412.
|
||||
- **appendonly yes → /data:** AOF 기록. Evidence: L406–L412.
|
||||
- **/data → 컨테이너:** 컨테이너와 함께 소멸. Evidence: L406–L412.
|
||||
- **/data → PersistentVolume:** 볼륨 마운트. Evidence: L406–L412.
|
||||
|
||||
@@ -13,6 +13,6 @@ n2: "컨테이너" {
|
||||
n3: "PersistentVolume" {
|
||||
shape: cylinder
|
||||
}
|
||||
n0 -> n1: "AOF 파일을 쓴다"
|
||||
n1 -> n2: "컨테이너와 함께 죽는다"
|
||||
n1 -> n3: "볼륨을 붙여야 남는다"
|
||||
n0 -> n1: "AOF 기록"
|
||||
n1 -> n2: "컨테이너와 함께 소멸"
|
||||
n1 -> n3: "볼륨 마운트"
|
||||
|
||||
@@ -6,7 +6,7 @@ digraph techviz {
|
||||
n1 [label="/data", shape=cylinder, style="rounded,filled"];
|
||||
n2 [label="컨테이너", shape=box, style="rounded,filled"];
|
||||
n3 [label="PersistentVolume", shape=cylinder, style="rounded,filled"];
|
||||
n0 -> n1 [label="AOF 파일을 쓴다", style=solid];
|
||||
n1 -> n2 [label="컨테이너와 함께 죽는다", style=solid];
|
||||
n1 -> n3 [label="볼륨을 붙여야 남는다", style=solid];
|
||||
n0 -> n1 [label="AOF 기록", style=solid];
|
||||
n1 -> n2 [label="컨테이너와 함께 소멸", style=solid];
|
||||
n1 -> n3 [label="볼륨 마운트", style=solid];
|
||||
}
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
<mxCell id="n_config" value="appendonly yes<br/>redis-cli config set" tooltip="설정은 적용된다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="163.0" y="60.0" width="174.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_datadir" value="/data<br/>appendonlydir 이 만들어졌다 버려진다" tooltip="컨테이너 파일시스템이다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="142.0" y="227.0" width="216.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_datadir" value="/data<br/>appendonlydir" tooltip="컨테이너 파일시스템이다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="175.0" y="227.0" width="150.0" height="97.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_container" value="컨테이너<br/>볼륨이 없으면 여기까지다" tooltip="죽으면 /data 도 같이 사라진다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxCell id="n_container" value="컨테이너<br/>볼륨 없음" tooltip="죽으면 /data 도 같이 사라진다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="70.0" y="429.5" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_volume" value="PersistentVolume" tooltip="여기 있어야 설정이 뜻을 갖는다. | Evidence: L406-L412" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;shape=cylinder3;boundedLbl=1;backgroundOutline=1;fillColor=#eef6fb;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="280.0" y="420.0" width="150.0" height="90.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_c-d" value="AOF 파일을 쓴다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_config" target="n_datadir">
|
||||
<mxCell id="e_c-d" value="AOF 기록" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_config" target="n_datadir">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="274.0" y="179.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_d-c" value="컨테이너와 함께 죽는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_datadir" target="n_container">
|
||||
<mxCell id="e_d-c" value="컨테이너와 함께 소멸" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_datadir" target="n_container">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="193.0" y="348.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_d-v" value="볼륨을 붙여야 남는다" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_datadir" target="n_volume">
|
||||
<mxCell id="e_d-v" value="볼륨 마운트" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_datadir" target="n_volume">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="307.0" y="344.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
|
||||
@@ -94,11 +94,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "AOF 파일을 쓴다",
|
||||
"text": "AOF 기록",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "AOF 파일을 쓴다",
|
||||
"originalText": "AOF 기록",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -165,9 +165,9 @@
|
||||
{
|
||||
"id": "edge-label-d-c",
|
||||
"type": "text",
|
||||
"x": 145.0,
|
||||
"x": 148.0,
|
||||
"y": 336.75,
|
||||
"width": 96,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -193,11 +193,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "컨테이너와 함께 죽는다",
|
||||
"text": "컨테이너와 함께 소멸",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "컨테이너와 함께 죽는다",
|
||||
"originalText": "컨테이너와 함께 소멸",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -292,11 +292,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "볼륨을 붙여야 남는다",
|
||||
"text": "볼륨 마운트",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "볼륨을 붙여야 남는다",
|
||||
"originalText": "볼륨 마운트",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -372,9 +372,9 @@
|
||||
{
|
||||
"id": "node-datadir",
|
||||
"type": "rectangle",
|
||||
"x": 142.0,
|
||||
"x": 175.0,
|
||||
"y": 227.0,
|
||||
"width": 216.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -402,9 +402,9 @@
|
||||
{
|
||||
"id": "node-label-datadir",
|
||||
"type": "text",
|
||||
"x": 152.0,
|
||||
"x": 185.0,
|
||||
"y": 237.0,
|
||||
"width": 196.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -430,11 +430,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "/data\nappendonlydir 이 만들어졌다 버려진다",
|
||||
"text": "/data\nappendonlydir",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "/data\nappendonlydir 이 만들어졌다 버려진다",
|
||||
"originalText": "/data\nappendonlydir",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
@@ -499,11 +499,11 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "컨테이너\n볼륨이 없으면 여기까지다",
|
||||
"text": "컨테이너\n볼륨 없음",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "컨테이너\n볼륨이 없으면 여기까지다",
|
||||
"originalText": "컨테이너\n볼륨 없음",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b5-b6-storage-and-keys",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "51b618881bf3ef23a734b2beb319e756c12a9a7ff1ae578b7b5ef78a1461b0e9",
|
||||
"spec_sha256": "6be204030cc95c5d9aec2ec4f07eae62bd88e7f344d00c2e014a20e18d6f8dba",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
|
||||
@@ -5,6 +5,6 @@ flowchart TB
|
||||
n1[("/data")]
|
||||
n2["컨테이너"]
|
||||
n3[("PersistentVolume")]
|
||||
n0 -->|"AOF 파일을 쓴다"| n1
|
||||
n1 -->|"컨테이너와 함께 죽는다"| n2
|
||||
n1 -->|"볼륨을 붙여야 남는다"| n3
|
||||
n0 -->|"AOF 기록"| n1
|
||||
n1 -->|"컨테이너와 함께 소멸"| n2
|
||||
n1 -->|"볼륨 마운트"| n3
|
||||
|
||||
@@ -51,14 +51,14 @@
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="555" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="250.0,131.0 250.0,179.0 250.0,179.0 250.0,227.0" data-evidence="406-412" />
|
||||
<rect class="edge-label-bg" x="231.5" y="165.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="274.0" y="180.0">AOF 파일을 쓴다</text>
|
||||
<rect class="edge-label-bg" x="244.9" y="165.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="274.0" y="180.0">AOF 기록</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="241.0,324.0 241.0,376.8 145.0,376.8 145.0,429.5" data-evidence="406-412" />
|
||||
<rect class="edge-label-bg" x="143.8" y="334.8" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="193.0" y="349.8">컨테이너와 함께 죽는다</text>
|
||||
<rect class="edge-label-bg" x="147.2" y="334.8" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="193.0" y="349.8">컨테이너와 함께 소멸</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="259.0,324.0 259.0,372.0 355.0,372.0 355.0,420.0" data-evidence="406-412" />
|
||||
<rect class="edge-label-bg" x="261.1" y="330.0" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="307.0" y="345.0">볼륨을 붙여야 남는다</text>
|
||||
<rect class="edge-label-bg" x="277.9" y="330.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="307.0" y="345.0">볼륨 마운트</text>
|
||||
<g id="node-config">
|
||||
<rect class="node-shape kind-process emphasis-warning role-source" data-evidence="406-412" x="163.0" y="60.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="250.0" y="87.0">appendonly yes</text>
|
||||
@@ -66,16 +66,16 @@
|
||||
<text class="node-detail" x="179.0" y="125.0">redis-cli config set</text>
|
||||
</g>
|
||||
<g id="node-datadir">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-control" data-evidence="406-412" x="142.0" y="240.0" width="216.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-control" cx="250.0" cy="240.0" rx="108.0" ry="13.0" /><path class="storage-bottom" d="M 142.0 311.0 A 108.0 13.0 0 0 0 358.0 311.0" />
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-control" data-evidence="406-412" x="175.0" y="240.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-control" cx="250.0" cy="240.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 175.0 311.0 A 75.0 13.0 0 0 0 325.0 311.0" />
|
||||
<text class="node-label" x="250.0" y="267.0">/data</text>
|
||||
<line class="node-detail-divider" x1="156.0" y1="288.0" x2="344.0" y2="288.0" />
|
||||
<text class="node-detail" x="158.0" y="305.0">appendonlydir 이 만들어졌다 버려진다</text>
|
||||
<line class="node-detail-divider" x1="189.0" y1="288.0" x2="311.0" y2="288.0" />
|
||||
<text class="node-detail" x="191.0" y="305.0">appendonlydir</text>
|
||||
</g>
|
||||
<g id="node-container">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="406-412" x="70.0" y="429.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="456.5">컨테이너</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="477.5" x2="206.0" y2="477.5" />
|
||||
<text class="node-detail" x="86.0" y="494.5">볼륨이 없으면 여기까지다</text>
|
||||
<text class="node-detail" x="86.0" y="494.5">볼륨 없음</text>
|
||||
</g>
|
||||
<g id="node-volume">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="406-412" x="280.0" y="433.0" width="150.0" height="64.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="355.0" cy="433.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 280.0 497.0 A 75.0 13.0 0 0 0 430.0 497.0" />
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -17,6 +17,6 @@ oauth2-proxy 는 세션 전체를 쿠키에 담고 replica 는 같은 k8s Secret
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Redis 의 세션 → TTL:** 생성 시각이 여기 남는다. Evidence: L441–L452.
|
||||
- **k8s Secret → 쿠키의 티켓:** 티켓을 암호화한다. Evidence: L419–L432.
|
||||
- **쿠키의 티켓 → Redis 의 세션:** 키 이름을 만든다. Evidence: L429–L438.
|
||||
- **Redis 의 세션 → TTL:** 생성 시각. Evidence: L441–L452.
|
||||
- **k8s Secret → 쿠키의 티켓:** 티켓 암호화. Evidence: L419–L432.
|
||||
- **쿠키의 티켓 → Redis 의 세션:** 키 이름 생성. Evidence: L429–L438.
|
||||
|
||||