feat: 가상화 문서들 추가

This commit is contained in:
DongHyeonka
2026-09-10 08:54:05 +09:00
parent e9f6a93327
commit 43e1aadef0
695 changed files with 153404 additions and 12754 deletions
@@ -7,9 +7,9 @@
"revision": "cdac9b8178391311d8eca1ebc6cac15bb62d79af",
"verified": "이 커밋이 A-1 부터 D-4a 까지 실험 전량을 병합한 tip 이고 저장소 HEAD 다. git branch --contains 가 experiment 브랜치 26 개를 모두 낸다 (2026-09-07 확인)"
},
"ssotSha256": "ae0aee375daebc00d72590db8745ff9ef1018911785a80e9fd7b62f893dea19d",
"ssotSha256": "0ae5674723f25dc85d5069529890f07be1a11b768b56901103fa3ea6ac3dcf55",
"sourceRevision": "keycloak-session-lab@2026-09",
"generatedAt": "2026-09-07",
"generatedAt": "2026-09-08",
"candidateScope": {
"document": "final/document.md",
"sections": [
@@ -72,10 +72,6 @@
"OFFLINE_USER_SESSION",
"sessions 캐시"
],
"evidence": [
"evidence/raw/a1-jgroups-transport-block__09-cross-node-under-partition.txt",
"evidence/raw/a1-jgroups-transport-block__10-logout-not-propagated.txt"
],
"classification": "로그가 가리키는 원인과 실제 경로가 다르다는 것을 노드 B 의 SQL 문장 로깅으로 확정했고, 그 확정이 A-1 의 빗나간 예측까지 설명하며 닫힌다",
"missing-verification": "Infinispan 복제를 명시적으로 켠 구성에서는 재지 않았다. 이 결론은 persistent-user-sessions 가 켜진 26.7.0 기본값에 한정된다",
"relations": [
@@ -84,6 +80,14 @@
"reference:state-the-version-and-the-setting-with-the-result"
],
"kind": "case",
"ssot-assets": [
"session-sharing-path",
"a1-transport-vs-discovery"
],
"ssot-evidence": [
"raw/a1-jgroups-transport-block__09-cross-node-under-partition.txt",
"raw/a1-jgroups-transport-block__10-logout-not-propagated.txt"
],
"publication": "미작성"
},
{
@@ -98,10 +102,6 @@
"REVOKED_TOKEN",
"DEFAULT_SCOPE"
],
"evidence": [
"evidence/raw/a7a-volatile-cause__01-cause-determined.txt",
"evidence/raw/a7-volatile-comparison__05-a2-rerun-db-loss.txt"
],
"classification": "가설(REVOKED_TOKEN)을 문장 로깅으로 반증하고 실제 조회를 확정한 뒤, 캐시 온도 셋을 모두 재현해 표 하나로 닫히지 않는다는 결론까지 갔다",
"missing-verification": "캐시가 식는 시간을 재지 않았다. 냉·중간·온 세 상태를 만들어 확인했을 뿐 그 사이의 전이는 관측하지 않았다",
"relations": [
@@ -109,6 +109,13 @@
"reference:state-the-version-and-the-setting-with-the-result"
],
"kind": "case",
"ssot-assets": [
"cache-temperature-outcomes"
],
"ssot-evidence": [
"raw/a7a-volatile-cause__01-cause-determined.txt",
"raw/a7-volatile-comparison__05-a2-rerun-db-loss.txt"
],
"publication": "미작성"
},
{
@@ -121,10 +128,6 @@
"code": [
"persistent-user-sessions"
],
"evidence": [
"evidence/raw/a8-rolling-restart__01-restart-availability.txt",
"evidence/raw/a8-rolling-restart__02-session-survival.txt"
],
"classification": "재시작 전후의 DB 세션 수와 캐시 수를 따로 세어 무엇이 남고 무엇이 사라지는지 갈랐고, 그 설정을 켜는 이유로 닫힌다",
"missing-verification": "표본이 적어 무중단 주장을 처음에 과장했다가 고쳤다. 재시작 중 진행 중이던 요청은 재지 않았다",
"relations": [
@@ -132,6 +135,13 @@
"case:session-sharing-is-the-database-not-replication"
],
"kind": "case",
"ssot-assets": [
"a8-cache-vs-session"
],
"ssot-evidence": [
"raw/a8-rolling-restart__01-restart-availability.txt",
"raw/a8-rolling-restart__02-session-survival.txt"
],
"publication": "미작성"
}
],
@@ -151,6 +161,9 @@
"reference:state-the-version-and-the-setting-with-the-result"
],
"kind": "concept",
"ssot-assets": [
"version-conditional-results"
],
"publication": "미작성"
}
],
@@ -195,11 +208,6 @@
"SET LOCAL synchronous_commit TO OFF",
"wal_writer_delay"
],
"evidence": [
"evidence/raw/a3-database-crash__03-loss-measurement.txt",
"evidence/raw/a3-database-crash__07-loss-result.txt",
"evidence/raw/a3-database-crash__08-wal-settings.txt"
],
"classification": "153 건 중 149 건만 남은 것을 세어 확인하고 원인을 커밋 설정에서 확정했으며, 의도된 설계의 대가를 숫자로 닫았다",
"missing-verification": "synchronous_commit 을 켠 대조군을 돌리지 않았다. 유실이 0 이 되는지와 그때의 지연 비용은 재지 않았다",
"relations": [
@@ -208,6 +216,14 @@
"reference:most-of-an-outage-is-noticing"
],
"kind": "case",
"ssot-assets": [
"a3-commit-to-disk-gap"
],
"ssot-evidence": [
"raw/a3-database-crash__03-loss-measurement.txt",
"raw/a3-database-crash__07-loss-result.txt",
"raw/a3-database-crash__08-wal-settings.txt"
],
"publication": "미작성"
},
{
@@ -222,11 +238,6 @@
"tolerationSeconds",
"StatefulSet"
],
"evidence": [
"evidence/raw/a4-node-loss__02-worker-node-killed.txt",
"evidence/raw/a4-node-loss__03-state-during-loss.txt",
"evidence/raw/a4-node-loss__04-eviction-timing.txt"
],
"classification": "같은 복구 시간을 내는 두 장애의 원인이 다르다는 것을 갈랐고, 축출까지 5분 40초라는 내역으로 닫힌다",
"missing-verification": "저장소를 두 노드에 나눠 배치한 구성에서는 재지 않았다. 4a 의 결과는 이 실험대의 배치에 걸려 있다",
"relations": [
@@ -235,6 +246,15 @@
"reference:most-of-an-outage-is-noticing"
],
"kind": "case",
"ssot-assets": [
"a4-two-node-losses",
"lab-topology"
],
"ssot-evidence": [
"raw/a4-node-loss__02-worker-node-killed.txt",
"raw/a4-node-loss__03-state-during-loss.txt",
"raw/a4-node-loss__04-eviction-timing.txt"
],
"publication": "미작성"
},
{
@@ -248,10 +268,6 @@
"agroal 커넥션 풀",
"readiness probe"
],
"evidence": [
"evidence/raw/a6-latency-injection__02-delay-injected.txt",
"evidence/raw/a6-latency-injection__04-pool-under-load.txt"
],
"classification": "28 배가 어디서 왔는지를 왕복 누적과 풀 경합 두 단계로 분해했고, 느림이 장애로 승격되는 지점까지 관측해 닫았다",
"missing-verification": "지연 값을 200ms 한 점에서만 쟀다. 어느 지연부터 readiness 가 실패하는지는 재지 않았다",
"relations": [
@@ -259,6 +275,13 @@
"case:two-ways-to-lose-a-node"
],
"kind": "case",
"ssot-assets": [
"a6-latency-multiplication"
],
"ssot-evidence": [
"raw/a6-latency-injection__02-delay-injected.txt",
"raw/a6-latency-injection__04-pool-under-load.txt"
],
"publication": "미작성"
}
],
@@ -278,6 +301,9 @@
"concept:the-up-metric-cannot-see-alive-but-useless"
],
"kind": "concept",
"ssot-assets": [
"observation-points"
],
"publication": "미작성"
}
],
@@ -323,10 +349,6 @@
"AuthenticatedPrincipalOAuth2AuthorizedClientRepository",
"SPRING_SESSION_STORE_TYPE"
],
"evidence": [
"evidence/raw/b0-bff-redis-deploy__03-beans-analysis.txt",
"evidence/raw/b1-redis-session-store__03-redis-contents.txt"
],
"classification": "저장소를 붙이기 전에 자동구성이 무엇을 골랐는지 먼저 읽고, 그 조회 키 차이가 다음 실험의 결과를 만든다는 것까지 이어 닫았다",
"missing-verification": "Redis 를 붙인 상태에서 토큰 저장소만 따로 두는 조합은 B-2 에서 다시 쟀다. 여기서는 세션 쪽만 확인했다",
"relations": [
@@ -335,6 +357,10 @@
"decision:split-the-two-stores-and-design-each"
],
"kind": "case",
"ssot-evidence": [
"raw/b0-bff-redis-deploy__03-beans-analysis.txt",
"raw/b1-redis-session-store__03-redis-contents.txt"
],
"publication": "미작성"
},
{
@@ -348,9 +374,6 @@
"JdbcOAuth2AuthorizedClientService",
"PRIMARY KEY (client_registration_id, principal_name)"
],
"evidence": [
"evidence/raw/b0-bff-redis-deploy__02-autoconfiguration.txt"
],
"classification": "저장소를 나눠 옮긴 뒤 네 가지를 확인해 둘은 풀리고 둘은 남는 것을 갈랐고, 남은 둘의 원인이 저장소가 아니라 DDL 한 줄임을 확정했다",
"missing-verification": "세션 id 를 키에 넣은 스키마로 고쳐서 다시 재지 않았다. 덮어쓰기가 사라지는지는 확인하지 않았다",
"relations": [
@@ -360,6 +383,12 @@
"decision:split-the-two-stores-and-design-each"
],
"kind": "case",
"ssot-assets": [
"b2-primary-key-overwrite"
],
"ssot-evidence": [
"raw/b0-bff-redis-deploy__02-autoconfiguration.txt"
],
"publication": "미작성"
},
{
@@ -373,9 +402,6 @@
"revokeRefreshToken=true",
"refreshTokenMaxReuse=0"
],
"evidence": [
"evidence/raw/b0-bff-redis-deploy__01-deploy.txt"
],
"classification": "동시 5 건을 실제로 경합시켜 「하나는 성공한다」가 아니라 전부 못 쓰게 되는 것을 관측했고, 원인을 client session 삭제로 확정했다",
"missing-verification": "재시도 설계를 붙여서 다시 재지 않았다. 어떤 재시도가 이 상황을 복구하는지는 확인하지 않았다",
"relations": [
@@ -383,6 +409,12 @@
"decision:split-the-two-stores-and-design-each"
],
"kind": "case",
"ssot-assets": [
"b3-rotation-contention"
],
"ssot-evidence": [
"raw/b0-bff-redis-deploy__01-deploy.txt"
],
"publication": "미작성"
},
{
@@ -397,10 +429,6 @@
"NimbusJwtDecoder",
"JWKS kid"
],
"evidence": [
"evidence/raw/b5-redis-loss__04-persistence.txt",
"evidence/raw/b6-key-rotation__03-old-key-removed.txt"
],
"classification": "설정만 보면 되어 있는 것으로 읽히는 두 자리를 각각 실제로 죽여 보고 회전시켜 확인했으며, 설정과 매체를 나눠 봐야 한다는 결론으로 닫힌다",
"missing-verification": "볼륨을 붙인 Redis 로 다시 재지 않았다. 영속화가 실제로 동작하는지는 확인하지 않았다",
"relations": [
@@ -408,6 +436,13 @@
"reference:look-at-the-lookup-key-before-moving-the-store"
],
"kind": "case",
"ssot-assets": [
"b5-b6-storage-and-keys"
],
"ssot-evidence": [
"raw/b5-redis-loss__04-persistence.txt",
"raw/b6-key-rotation__03-old-key-removed.txt"
],
"publication": "미작성"
}
],
@@ -427,6 +462,9 @@
"decision:split-the-two-stores-and-design-each"
],
"kind": "concept",
"ssot-assets": [
"bff-store-lookup-keys"
],
"publication": "미작성"
}
],
@@ -494,9 +532,6 @@
"proxy_set_header X-Auth-Request-Roles",
"--cookie-refresh"
],
"evidence": [
"evidence/raw/b4-edge-authorization__01-header-handling.txt"
],
"classification": "위조 헤더를 실제로 보내 통과하는 것을 관측하고 원인을 지우지 않은 설정에서 확정했으며, 클레임 변경이 재인증까지 반영되지 않는 것도 같은 실험에서 닫았다",
"missing-verification": "--cookie-refresh 를 켠 구성에서는 재지 않았다. 클레임 반영 지연이 얼마나 줄어드는지는 확인하지 않았다",
"relations": [
@@ -504,6 +539,12 @@
"case:nobody-implemented-backchannel-logout"
],
"kind": "case",
"ssot-assets": [
"b4-header-trust-boundary"
],
"ssot-evidence": [
"raw/b4-edge-authorization__01-header-handling.txt"
],
"publication": "미작성"
},
{
@@ -517,9 +558,6 @@
"backchannelLogoutUrl",
"oidcLogout"
],
"evidence": [
"evidence/raw/b4-edge-authorization__01-header-handling.txt"
],
"classification": "네 가지를 순서대로 확인해 설정 결손과 기능 부재가 한 경로에 겹쳐 있는 것을 갈랐고, 한쪽만 고쳐서는 되지 않는다는 결론으로 닫힌다",
"missing-verification": "양쪽을 다 구현해서 전파가 되는지 확인하지 않았다. 원인 확정까지가 이 실험의 범위다",
"relations": [
@@ -527,6 +565,12 @@
"reference:clear-the-header-before-you-trust-it"
],
"kind": "case",
"ssot-assets": [
"c2-backchannel-both-sides"
],
"ssot-evidence": [
"raw/b4-edge-authorization__01-header-handling.txt"
],
"publication": "미작성"
},
{
@@ -541,10 +585,6 @@
"--cookie-refresh",
"refresh:disabled"
],
"evidence": [
"evidence/raw/b7a-orphan-session__01-orphan-lifecycle.txt",
"evidence/raw/b7-cookie-secret__01-deploy.txt"
],
"classification": "secret 교체가 티켓을 못 풀게 만든 상태에서 무엇을 지울지 고르는 방법을 TTL 역산으로 세우고 로그와 1 초 차로 교차검증해 닫았다",
"missing-verification": "--cookie-refresh 를 켠 구성에서는 이 역산이 성립하지 않는다. 그 경우의 정리 방법은 재지 않았다",
"relations": [
@@ -552,6 +592,13 @@
"reference:clear-the-header-before-you-trust-it"
],
"kind": "case",
"ssot-assets": [
"b7-cookie-session-tradeoff"
],
"ssot-evidence": [
"raw/b7a-orphan-session__01-orphan-lifecycle.txt",
"raw/b7-cookie-secret__01-deploy.txt"
],
"publication": "미작성"
}
],
@@ -598,12 +645,6 @@
"/etc/letsencrypt/renewal-hooks/",
"nginx 마스터·워커 PID"
],
"evidence": [
"evidence/raw/d4-certificate-renewal__07-renewal-hook-missing.txt",
"evidence/raw/d4-certificate-renewal__09-serial-timeline.txt",
"evidence/raw/d4-certificate-renewal__12-certbot-state.txt",
"evidence/raw/d4-certificate-renewal__13-verdict.txt"
],
"classification": "디스크 기록 시각과 실제 서빙 시각을 따로 재어 공백을 확정하고 원인 셋이 모두 비어 있는 것을 확인했으며, 88 일 잠복한다는 성질까지 닫았다",
"missing-verification": "실제 만료가 임박한 상태를 만들지 않았다. 이 결함이 만료로 드러나는 경로는 재지 않았다",
"relations": [
@@ -612,6 +653,15 @@
"question:does-the-renewal-timer-actually-renew"
],
"kind": "case",
"ssot-assets": [
"renewal-to-serving-gap"
],
"ssot-evidence": [
"raw/d4-certificate-renewal__07-renewal-hook-missing.txt",
"raw/d4-certificate-renewal__09-serial-timeline.txt",
"raw/d4-certificate-renewal__12-certbot-state.txt",
"raw/d4-certificate-renewal__13-verdict.txt"
],
"publication": "미작성"
},
{
@@ -625,11 +675,6 @@
"renewal-hooks/deploy/",
"types_hash 경고"
],
"evidence": [
"evidence/raw/d4a-deploy-hook__01-hook-verified.txt",
"evidence/raw/d4a-deploy-hook__02-certbot-with-hook.txt",
"evidence/raw/d4a-deploy-hook__03-after-state.txt"
],
"classification": "처방을 실제로 넣고 전후를 같은 방법으로 재어 확인했고, 훅 로그의 error 문구가 실패가 아니라는 것까지 갈라 닫았다",
"missing-verification": "실제 갱신 주기에서 훅이 도는 것은 확인하지 않았다. 강제 갱신으로만 검증했다",
"relations": [
@@ -638,6 +683,14 @@
"reference:judge-a-reload-by-the-worker-pid-not-the-log"
],
"kind": "case",
"ssot-assets": [
"d4a-hook-effect"
],
"ssot-evidence": [
"raw/d4a-deploy-hook__01-hook-verified.txt",
"raw/d4a-deploy-hook__02-certbot-with-hook.txt",
"raw/d4a-deploy-hook__03-after-state.txt"
],
"publication": "미작성"
},
{
@@ -652,9 +705,6 @@
"databasechangelog",
"StatefulSet 롤링 업데이트"
],
"evidence": [
"evidence/raw/d4-certificate-renewal__01-certificate-state.txt"
],
"classification": "롤백 실패를 관측하고 원인을 체크섬으로 확정한 뒤, 스키마가 움직이지 않은 방향은 실제로 성공하는 것까지 확인해 조건을 붙여 닫았다",
"missing-verification": "스키마가 크게 바뀌는 메이저 업그레이드에서는 재지 않았다. 두 패치 버전 사이만 확인했다",
"relations": [
@@ -662,6 +712,12 @@
"decision:put-the-reload-in-a-deploy-hook"
],
"kind": "case",
"ssot-assets": [
"d2-upgrade-direction"
],
"ssot-evidence": [
"raw/d4-certificate-renewal__01-certificate-state.txt"
],
"publication": "미작성"
}
],
@@ -752,10 +808,6 @@
"PID 1 SIGKILL",
"flannel VXLAN"
],
"evidence": [
"evidence/raw/a1-jgroups-transport-block__05-conntrack-problem.txt",
"evidence/raw/a6-latency-injection__03-flannel-injection.txt"
],
"classification": "아홉 건이 각각 다른 이유로 실패한 것을 하나씩 확정하고, 실패한 주입과 영향 없는 주입이 구별되지 않는다는 공통 원인으로 닫았다",
"missing-verification": "아홉 건 모두를 고친 뒤 다시 돌린 것은 아니다. 일부는 다른 주입 방법으로 우회했다",
"relations": [
@@ -764,6 +816,14 @@
"concept:the-up-metric-cannot-see-alive-but-useless"
],
"kind": "case",
"ssot-assets": [
"injection-verification",
"a5-partition-asymmetry"
],
"ssot-evidence": [
"raw/a1-jgroups-transport-block__05-conntrack-problem.txt",
"raw/a6-latency-injection__03-flannel-injection.txt"
],
"publication": "미작성"
},
{
@@ -777,10 +837,6 @@
"TCP 연결수 0",
"50µs"
],
"evidence": [
"evidence/raw/d4-certificate-renewal__05-control-no-injection.txt",
"evidence/raw/d4-certificate-renewal__11-inflight-full.txt"
],
"classification": "같은 순간의 대조 폴링과 연결수·소요 시간·재현율 넷으로 귀속을 뒤집었고, 대조군이 없었으면 무엇이 됐을지까지 닫았다",
"missing-verification": "클라이언트 쪽 원인을 특정하지 않았다. 서버 탓이 아니라는 것까지가 이 확인의 범위다",
"relations": [
@@ -789,6 +845,13 @@
"reference:never-subtract-values-from-two-clocks"
],
"kind": "case",
"ssot-assets": [
"measurement-control"
],
"ssot-evidence": [
"raw/d4-certificate-renewal__05-control-no-injection.txt",
"raw/d4-certificate-renewal__11-inflight-full.txt"
],
"publication": "미작성"
},
{
@@ -803,9 +866,6 @@
"kubectl run --rm -i",
"상주 탐침"
],
"evidence": [
"evidence/raw/a6-latency-injection__04-pool-under-load.txt"
],
"classification": "헤드라인 수치를 만든 명령 넷이 산문이던 것을 셸 표현식으로 바꾸고 실제로 돌려 확인했으며, 그 확인에서 한 건이 깨진 것까지 닫았다",
"missing-verification": "고친 명령을 다른 환경에서 돌려 보지 않았다. 이 실험대에서만 확인했다",
"relations": [
@@ -813,6 +873,12 @@
"reference:verify-the-injection-landed-separately-from-the-result"
],
"kind": "case",
"ssot-assets": [
"reproducibility-gap"
],
"ssot-evidence": [
"raw/a6-latency-injection__04-pool-under-load.txt"
],
"publication": "미작성"
}
],
@@ -1408,5 +1474,11 @@
"unwritten": 33,
"unlisted": 0,
"candidates": 42
},
"unassignedSsotAssets": {
"d3-secret-exposure": "D-3 의 base64 관측은 KEEP_IN_SSOT 로 두어 글감이 없다. 그림도 함께 남긴다",
"wrong-predictions": "틀린 예측 다섯을 한 자리에 모은 그림이라 특정 Case 에 붙지 않는다. 주제 전체의 그림이다",
"open-questions-answered": "앞 프로젝트가 남긴 열린 질문 넷에 답한 것을 모은 그림이라 이 프로젝트의 어느 한 글감에 붙지 않는다",
"not-applicable-conditions": "적용되지 않는 조건을 모은 그림이고 그것을 담는 것은 Reference 다. Reference 에는 본문이 없어 그림을 렌더링할 자리가 없다"
}
}