refactor: 문서 개선 중
@@ -10,13 +10,13 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **keycloak-0** (service): 로그아웃을 처리한 노드. Evidence: L183–L189.
|
||||
- **TCP 7800** (channel): 무효화 통지가 지나는 길. NetworkPolicy 로 막았다. Evidence: L191–L196.
|
||||
- **keycloak-1** (service): 통지를 못 받아 세션이 죽은 것을 모른다. Evidence: L183–L189.
|
||||
- **PostgreSQL** (datastore): 세션이 여기 있어 7800 과 무관하다. Evidence: L186–L190.
|
||||
- **keycloak-0** (service): 로그아웃을 처리한 노드. Evidence: L485–L489.
|
||||
- **TCP 7800** (channel): 무효화 통지가 지나는 길. NetworkPolicy 로 막았다. Evidence: L491–L495.
|
||||
- **keycloak-1** (service): 통지를 못 받아 세션이 죽은 것을 모른다. Evidence: L485–L489.
|
||||
- **PostgreSQL** (datastore): 세션이 여기 있어 7800 과 무관하다. Evidence: L487–L490.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **keycloak-0 → TCP 7800:** 무효화 통지. Evidence: L186–L190.
|
||||
- **keycloak-1 → PostgreSQL:** 세션 SELECT. Evidence: L186–L189.
|
||||
- **TCP 7800 → keycloak-1:** 차단. Evidence: L186–L190.
|
||||
- **keycloak-0 → TCP 7800:** 무효화 통지. Evidence: L487–L490.
|
||||
- **keycloak-1 → PostgreSQL:** 세션 SELECT. Evidence: L487–L489.
|
||||
- **TCP 7800 → keycloak-1:** 차단. Evidence: L487–L490.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a1-transport-vs-discovery" name="발견과 전송은 다른 경로다">
|
||||
<mxGraphModel dx="680" dy="696" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1218" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1218" math="0" shadow="0">
|
||||
<root>
|
||||
<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="82.0" y="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_k0" value="keycloak-0" tooltip="로그아웃을 처리한 노드. | Evidence: L485-L489" 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="38.5" y="145.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="153.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_transport" value="TCP 7800<br/>허용 포트 8080 · 9000" tooltip="무효화 통지가 지나는 길. NetworkPolicy 로 막았다. | Evidence: L491-L495" 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="35.0" y="455.0" width="153.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_k1" value="keycloak-1<br/>refresh 200 (기대 400)" tooltip="통지를 못 받아 세션이 죽은 것을 모른다. | Evidence: L485-L489" 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="35.0" y="768.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="80.5" y="554.0" width="153.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_db" value="PostgreSQL<br/>교차 노드 refresh 200" tooltip="세션이 여기 있어 7800 과 무관하다. | Evidence: L487-L490" 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="35.0" y="1102.0" width="153.0" height="71.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="181.0" y="172.0" as="offset"/>
|
||||
<mxPoint x="112.5" y="304.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<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="181.0" y="506.0" as="offset"/>
|
||||
<mxPoint x="116.8" y="942.5" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="339.0" as="offset"/>
|
||||
<mxPoint x="116.8" y="619.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-k0-t",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 124.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 111.5,
|
||||
"y": 209.0,
|
||||
"width": 2.0,
|
||||
"height": 246.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -32,20 +32,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
2.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
2.0,
|
||||
123.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
123.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
246.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-k0-t",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 160.0,
|
||||
"x": 67.5,
|
||||
"y": 292.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-k1-db",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 458.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 111.5,
|
||||
"y": 839.0,
|
||||
"width": 10.5,
|
||||
"height": 263.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
10.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
10.5,
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
263.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-k1-db",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 494.0,
|
||||
"x": 71.75,
|
||||
"y": 930.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-t-k1",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 291.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 111.5,
|
||||
"y": 526.0,
|
||||
"width": 10.5,
|
||||
"height": 242.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -235,15 +235,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
121.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
10.5,
|
||||
121.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
10.5,
|
||||
242.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-t-k1",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 327.0,
|
||||
"x": 71.75,
|
||||
"y": 607.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-k0",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 60.0,
|
||||
"x": 38.5,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-k0",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 70.0,
|
||||
"x": 48.5,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-transport",
|
||||
"type": "rectangle",
|
||||
"x": 80.5,
|
||||
"y": 220.0,
|
||||
"x": 35.0,
|
||||
"y": 455.0,
|
||||
"width": 153.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-transport",
|
||||
"type": "text",
|
||||
"x": 90.5,
|
||||
"y": 230.0,
|
||||
"x": 45.0,
|
||||
"y": 465.0,
|
||||
"width": 133.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-k1",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 387.0,
|
||||
"x": 35.0,
|
||||
"y": 768.0,
|
||||
"width": 174.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-k1",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 397.0,
|
||||
"x": 45.0,
|
||||
"y": 778.0,
|
||||
"width": 154.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,10 +510,10 @@
|
||||
{
|
||||
"id": "node-db",
|
||||
"type": "rectangle",
|
||||
"x": 80.5,
|
||||
"y": 554.0,
|
||||
"x": 35.0,
|
||||
"y": 1102.0,
|
||||
"width": 153.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-label-db",
|
||||
"type": "text",
|
||||
"x": 90.5,
|
||||
"y": 564.0,
|
||||
"x": 45.0,
|
||||
"y": 1112.0,
|
||||
"width": 133.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a1-transport-vs-discovery",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "f54cae81276a31a8b73961148cadf3ae5cd256bdc38a9c002997fce63c243ebf",
|
||||
"spec_sha256": "c62dd7f9d01772578e1cbfa1eef70d63eae4a68555e0fc4c71bcab862eff2c2a",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-1 · JGroups 전송(TCP 7800) 차단",
|
||||
"line": 185
|
||||
"line": 485
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"a1-transport-vs-discovery.excalidraw",
|
||||
"a1-transport-vs-discovery.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="696" viewBox="0 0 680 696" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1218" viewBox="0 0 680 1218" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">발견과 전송은 다른 경로다</title>
|
||||
<desc id="diagram-description">노드가 서로를 찾는 경로와 실제로 이야기하는 경로가 다르다. 발견은 PostgreSQL 의 JGROUPS_PING 테이블을 쓰고 전송은 TCP 7800 을 쓴다. 7800 만 막으면 둘 다 데이터베이스에 등록된 채로 남아 서로 존재한다고 믿지만 메시지는 오가지 않는다. 세션은 데이터베이스에 있으므로 교차 노드 refresh 는 200 을 유지하고, 로그아웃 무효화 통지는 7800 을 타므로 400 이어야 할 것이 200 이 된다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a1-transport-vs-discovery","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-1 · JGroups 전송(TCP 7800) 차단","line":185}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a1-transport-vs-discovery","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-1 · JGroups 전송(TCP 7800) 차단","line":485}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="696" />
|
||||
<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>
|
||||
<rect class="canvas" width="680" height="1218" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="113.5,209.0 113.5,332.0 111.5,332.0 111.5,455.0" data-evidence="487-490" />
|
||||
<rect class="edge-label-bg" x="83.4" y="290.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="112.5" y="305.0">무효화 통지</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="122.0,839.0 122.0,970.5 111.5,970.5 111.5,1102.0" data-evidence="487-489" />
|
||||
<rect class="edge-label-bg" x="77.6" y="928.5" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="116.8" y="943.5">세션 SELECT</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="111.5,526.0 111.5,647.0 122.0,647.0 122.0,768.0" data-evidence="487-490" />
|
||||
<rect class="edge-label-bg" x="94.8" y="605.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="116.8" y="620.0">차단</text>
|
||||
<g id="node-k0">
|
||||
<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>
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="485-489" x="38.5" y="145.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="113.5" y="175.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="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>
|
||||
<rect class="node-shape kind-channel emphasis-warning role-control" data-evidence="491-495" x="35.0" y="455.0" width="153.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="111.5" y="482.0">TCP 7800</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="503.0" x2="174.0" y2="503.0" />
|
||||
<text class="node-detail" x="51.0" y="520.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="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>
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="485-489" x="35.0" y="768.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="122.0" y="795.0">keycloak-1</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="816.0" x2="195.0" y2="816.0" />
|
||||
<text class="node-detail" x="51.0" y="833.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="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>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-store" data-evidence="487-490" x="35.0" y="1113.8" width="153.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-store" cx="111.5" cy="1113.8" rx="76.5" ry="11.8" /><path class="storage-bottom" d="M 35.0 1161.2 A 76.5 11.8 0 0 0 188.0 1161.2" />
|
||||
<text class="node-label" x="111.5" y="1129.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="1150.0" x2="174.0" y2="1150.0" />
|
||||
<text class="node-detail" x="51.0" y="1167.0">교차 노드 refresh 200</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -10,13 +10,13 @@ Keycloak 은 트랜잭션마다 SET LOCAL synchronous_commit TO OFF 를 건다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **클라이언트** (actor): 200 과 토큰을 받는다. Evidence: L207–L213.
|
||||
- **Keycloak** (service): 트랜잭션마다 synchronous_commit 을 끈다. Evidence: L211–L215.
|
||||
- **PostgreSQL** (datastore): COMMIT 을 즉시 반환한다. WAL 은 아직 메모리에 있다. Evidence: L211–L215.
|
||||
- **디스크의 WAL** (datastore): 여기 닿아야 살아남는다. Evidence: L207–L215.
|
||||
- **클라이언트** (actor): 200 과 토큰을 받는다. Evidence: L513–L518.
|
||||
- **Keycloak** (service): 트랜잭션마다 synchronous_commit 을 끈다. Evidence: L516–L518.
|
||||
- **PostgreSQL** (datastore): COMMIT 을 즉시 반환한다. WAL 은 아직 메모리에 있다. Evidence: L516–L518.
|
||||
- **디스크의 WAL** (datastore): 여기 닿아야 살아남는다. Evidence: L513–L518.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **클라이언트 → Keycloak:** 로그인 요청. Evidence: L207–L211.
|
||||
- **Keycloak → PostgreSQL:** 세션 INSERT · COMMIT. Evidence: L211–L215.
|
||||
- **PostgreSQL → 디스크의 WAL:** 지연된 WAL 기록. Evidence: L211–L215.
|
||||
- **클라이언트 → Keycloak:** 로그인 요청. Evidence: L513–L516.
|
||||
- **Keycloak → PostgreSQL:** 세션 INSERT · COMMIT. Evidence: L516–L518.
|
||||
- **PostgreSQL → 디스크의 WAL:** 지연된 WAL 기록. Evidence: L516–L518.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a3-commit-to-disk-gap" name="200 과 디스크 사이의 빈 구간">
|
||||
<mxGraphModel dx="680" dy="742" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1358" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1358" math="0" shadow="0">
|
||||
<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">
|
||||
<mxGeometry x="134.5" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_client" value="클라이언트<br/>성공 응답 153건" tooltip="200 과 토큰을 받는다. | Evidence: L513-L518" 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="35.0" y="145.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">
|
||||
<mxGeometry x="70.0" y="240.0" width="279.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_keycloak" value="Keycloak<br/>SET LOCAL synchronous_commit TO OFF" tooltip="트랜잭션마다 synchronous_commit 을 끈다. | Evidence: L516-L518" 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="41.5" y="455.0" width="279.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_pg" value="PostgreSQL<br/>wal_writer_delay 200ms" tooltip="COMMIT 을 즉시 반환한다. WAL 은 아직 메모리에 있다. | Evidence: L516-L518" 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="41.5" y="894.0" width="188.0" height="71.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">
|
||||
<mxGeometry x="134.5" y="600.0" width="150.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_disk" value="디스크의 WAL<br/>DB 존재 149건" tooltip="여기 닿아야 살아남는다. | Evidence: L513-L518" 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="41.5" y="1242.0" width="150.0" height="71.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="192.0" as="offset"/>
|
||||
<mxPoint x="145.5" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_k-pg" value="세션 INSERT · COMMIT" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_keycloak" target="n_pg">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="359.0" as="offset"/>
|
||||
<mxPoint x="158.2" y="682.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_pg-disk" value="지연된 WAL 기록" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_pg" target="n_disk">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="233.5" y="552.0" as="offset"/>
|
||||
<mxPoint x="126.0" y="1075.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-k",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 229.0,
|
||||
"width": 71.0,
|
||||
"height": 226.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
71.0,
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
71.0,
|
||||
226.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-k",
|
||||
"type": "text",
|
||||
"x": 188.5,
|
||||
"y": 180.0,
|
||||
"x": 100.5,
|
||||
"y": 302.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-k-pg",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 135.5,
|
||||
"y": 526.0,
|
||||
"width": 45.5,
|
||||
"height": 368.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
45.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
45.5,
|
||||
184.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
184.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
368.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-k-pg",
|
||||
"type": "text",
|
||||
"x": 161.5,
|
||||
"y": 347.0,
|
||||
"x": 86.25,
|
||||
"y": 670.0,
|
||||
"width": 144,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-pg-disk",
|
||||
"type": "arrow",
|
||||
"x": 209.5,
|
||||
"y": 504.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 116.5,
|
||||
"y": 965.0,
|
||||
"width": 19.0,
|
||||
"height": 277.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -230,20 +230,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
19.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
19.0,
|
||||
138.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
138.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
277.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-pg-disk",
|
||||
"type": "text",
|
||||
"x": 188.5,
|
||||
"y": 540.0,
|
||||
"x": 81.0,
|
||||
"y": 1063.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-client",
|
||||
"type": "rectangle",
|
||||
"x": 134.5,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-client",
|
||||
"type": "text",
|
||||
"x": 144.5,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-keycloak",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"x": 41.5,
|
||||
"y": 455.0,
|
||||
"width": 279.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-keycloak",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"x": 51.5,
|
||||
"y": 465.0,
|
||||
"width": 259.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,10 +441,10 @@
|
||||
{
|
||||
"id": "node-pg",
|
||||
"type": "rectangle",
|
||||
"x": 115.5,
|
||||
"y": 407.0,
|
||||
"x": 41.5,
|
||||
"y": 894.0,
|
||||
"width": 188.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -471,10 +471,10 @@
|
||||
{
|
||||
"id": "node-label-pg",
|
||||
"type": "text",
|
||||
"x": 125.5,
|
||||
"y": 417.0,
|
||||
"x": 51.5,
|
||||
"y": 904.0,
|
||||
"width": 168.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -510,10 +510,10 @@
|
||||
{
|
||||
"id": "node-disk",
|
||||
"type": "rectangle",
|
||||
"x": 134.5,
|
||||
"y": 600.0,
|
||||
"x": 41.5,
|
||||
"y": 1242.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-label-disk",
|
||||
"type": "text",
|
||||
"x": 144.5,
|
||||
"y": 610.0,
|
||||
"x": 51.5,
|
||||
"y": 1252.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a3-commit-to-disk-gap",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "1dfb52c8df3953df103ea2d515cf5a4f3cfed507d286e093fad31b583a851ce2",
|
||||
"spec_sha256": "a0c44206073887a8248aba92dcb37a69aed2084c72215b2c76fefdb266807c3e",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-2 · A-3 — DB 가 멈출 때와 죽을 때",
|
||||
"line": 201
|
||||
"line": 506
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"a3-commit-to-disk-gap.excalidraw",
|
||||
"a3-commit-to-disk-gap.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="742" viewBox="0 0 680 742" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1358" viewBox="0 0 680 1358" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">200 과 디스크 사이의 빈 구간</title>
|
||||
<desc id="diagram-description">Keycloak 은 트랜잭션마다 SET LOCAL synchronous_commit TO OFF 를 건다. PostgreSQL 은 COMMIT 을 WAL 디스크 기록을 기다리지 않고 즉시 반환하고, 클라이언트는 200 과 토큰을 받는다. 실측된 wal_writer_delay 는 200ms 이며 그 사이에 프로세스가 죽으면 그만큼이 사라진다. 153건 중 4건이 그렇게 유실됐다. 버그가 아니라 의도된 설계이고 그 대가를 숫자로 확인한 것이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a3-commit-to-disk-gap","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-2 · A-3 — DB 가 멈출 때와 죽을 때","line":201}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a3-commit-to-disk-gap","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-2 · A-3 — DB 가 멈출 때와 죽을 때","line":506}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="742" />
|
||||
<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="207-211" />
|
||||
<rect class="edge-label-bg" x="204.4" y="178.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="193.0">로그인 요청</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="209.5,311.0 209.5,359.0 209.5,359.0 209.5,407.0" data-evidence="211-215" />
|
||||
<rect class="edge-label-bg" x="164.2" y="345.0" width="138.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="360.0">세션 INSERT · COMMIT</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="209.5,504.0 209.5,552.0 209.5,552.0 209.5,600.0" data-evidence="211-215" />
|
||||
<rect class="edge-label-bg" x="191.0" y="538.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="233.5" y="553.0">지연된 WAL 기록</text>
|
||||
<rect class="canvas" width="680" height="1358" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,229.0 110.0,342.0 181.0,342.0 181.0,455.0" data-evidence="513-516" />
|
||||
<rect class="edge-label-bg" x="116.4" y="300.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="145.5" y="315.0">로그인 요청</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="181.0,526.0 181.0,710.0 135.5,710.0 135.5,894.0" data-evidence="516-518" />
|
||||
<rect class="edge-label-bg" x="88.9" y="668.0" width="138.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="158.2" y="683.0">세션 INSERT · COMMIT</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="135.5,965.0 135.5,1103.5 116.5,1103.5 116.5,1242.0" data-evidence="516-518" />
|
||||
<rect class="edge-label-bg" x="83.5" y="1061.5" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="126.0" y="1076.5">지연된 WAL 기록</text>
|
||||
<g id="node-client">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="207-213"><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-primary" data-evidence="513-518"><circle cx="110.0" cy="165.0" r="11.0" /><line x1="110.0" y1="181.0" x2="110.0" y2="200.0" /><line x1="92.0" y1="191.0" x2="128.0" y2="191.0" /><line x1="110.0" y1="200.0" x2="95.0" y2="217.0" /><line x1="110.0" y1="200.0" x2="125.0" y2="217.0" /></g>
|
||||
<text class="node-label" x="110.0" y="222.0">클라이언트</text>
|
||||
</g>
|
||||
<g id="node-keycloak">
|
||||
<rect class="node-shape kind-service emphasis-normal role-control" data-evidence="211-215" x="70.0" y="240.0" width="279.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="209.5" y="267.0">Keycloak</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">SET LOCAL synchronous_commit TO OFF</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-control" data-evidence="516-518" x="41.5" y="455.0" width="279.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="181.0" y="482.0">Keycloak</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="503.0" x2="306.5" y2="503.0" />
|
||||
<text class="node-detail" x="57.5" y="520.0">SET LOCAL synchronous_commit TO OFF</text>
|
||||
</g>
|
||||
<g id="node-pg">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-control" data-evidence="211-215" x="115.5" y="420.0" width="188.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-control" cx="209.5" cy="420.0" rx="94.0" ry="13.0" /><path class="storage-bottom" d="M 115.5 491.0 A 94.0 13.0 0 0 0 303.5 491.0" />
|
||||
<text class="node-label" x="209.5" y="447.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="129.5" y1="468.0" x2="289.5" y2="468.0" />
|
||||
<text class="node-detail" x="131.5" y="485.0">wal_writer_delay 200ms</text>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-control" data-evidence="516-518" x="41.5" y="905.8" width="188.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-control" cx="135.5" cy="905.8" rx="94.0" ry="11.8" /><path class="storage-bottom" d="M 41.5 953.2 A 94.0 11.8 0 0 0 229.5 953.2" />
|
||||
<text class="node-label" x="135.5" y="921.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="942.0" x2="215.5" y2="942.0" />
|
||||
<text class="node-detail" x="57.5" y="959.0">wal_writer_delay 200ms</text>
|
||||
</g>
|
||||
<g id="node-disk">
|
||||
<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>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="513-518" x="41.5" y="1253.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="116.5" cy="1253.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 41.5 1301.2 A 75.0 11.8 0 0 0 191.5 1301.2" />
|
||||
<text class="node-label" x="116.5" y="1269.0">디스크의 WAL</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="1290.0" x2="177.5" y2="1290.0" />
|
||||
<text class="node-detail" x="57.5" y="1307.0">DB 존재 149건</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -10,13 +10,13 @@ kc-lab-2 를 잃으면 keycloak-1 은 살아 있지만 PostgreSQL 이 같이 죽
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **외부 요청** (actor): 사용자. Evidence: L221–L228.
|
||||
- **진입 경로 (kc-lab-1)** (gateway): traefik 과 API 서버. 잃으면 000 이다. Evidence: L221–L231.
|
||||
- **keycloak-0 (kc-lab-2)** (service): 4b 에서는 계속 돌고 있었다. Evidence: L221–L231.
|
||||
- **PostgreSQL (kc-lab-2)** (datastore): 4a 에서 함께 죽는다. 잃으면 503 이다. Evidence: L221–L233.
|
||||
- **외부 요청** (actor): 사용자. Evidence: L533–L538.
|
||||
- **진입 경로 (kc-lab-1)** (gateway): traefik 과 API 서버. 잃으면 000 이다. Evidence: L533–L540.
|
||||
- **keycloak-0 (kc-lab-2)** (service): 4b 에서는 계속 돌고 있었다. Evidence: L533–L540.
|
||||
- **PostgreSQL (kc-lab-2)** (datastore): 4a 에서 함께 죽는다. 잃으면 503 이다. Evidence: L533–L543.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **외부 요청 → 진입 경로 (kc-lab-1):** 요청. Evidence: L221–L228.
|
||||
- **진입 경로 (kc-lab-1) → keycloak-0 (kc-lab-2):** 라우팅. Evidence: L221–L228.
|
||||
- **keycloak-0 (kc-lab-2) → PostgreSQL (kc-lab-2):** 세션 조회. Evidence: L221–L233.
|
||||
- **외부 요청 → 진입 경로 (kc-lab-1):** 요청. Evidence: L533–L538.
|
||||
- **진입 경로 (kc-lab-1) → keycloak-0 (kc-lab-2):** 라우팅. Evidence: L533–L538.
|
||||
- **keycloak-0 (kc-lab-2) → PostgreSQL (kc-lab-2):** 세션 조회. Evidence: L533–L543.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a4-two-node-losses" name="노드를 잃는 두 가지">
|
||||
<mxGraphModel dx="680" dy="716" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1222" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1222" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<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 id="n_client" value="외부 요청" tooltip="사용자. | Evidence: L533-L538" 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="35.0" y="145.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">
|
||||
<mxGeometry x="92.5" y="240.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_entry" value="진입 경로 (kc-lab-1)<br/>kubectl 불통" tooltip="traefik 과 API 서버. 잃으면 000 이다. | Evidence: L533-L540" 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="41.5" y="455.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">
|
||||
<mxGeometry x="77.0" y="407.0" width="181.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_workload" value="keycloak-0 (kc-lab-2)<br/>도달 불가" tooltip="4b 에서는 계속 돌고 있었다. | Evidence: L533-L540" 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="41.5" y="765.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">
|
||||
<mxGeometry x="70.0" y="574.0" width="195.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_store" value="PostgreSQL (kc-lab-2)<br/>local-path PVC · 재배치 불가" tooltip="4a 에서 함께 죽는다. 잃으면 503 이다. | Evidence: L533-L543" 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="41.5" y="1106.0" width="195.0" height="71.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="192.0" as="offset"/>
|
||||
<mxPoint x="113.2" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_e-w" value="라우팅" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_entry" target="n_workload">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="359.0" as="offset"/>
|
||||
<mxPoint x="124.2" y="617.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_w-s" value="세션 조회" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_workload" target="n_store">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="526.0" as="offset"/>
|
||||
<mxPoint x="135.5" y="943.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-e",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 229.0,
|
||||
"width": 6.5,
|
||||
"height": 226.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
6.5,
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
6.5,
|
||||
226.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-e",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 180.0,
|
||||
"x": 68.25,
|
||||
"y": 302.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-e-w",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 116.5,
|
||||
"y": 526.0,
|
||||
"width": 15.5,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -136,15 +136,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
15.5,
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
15.5,
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-e-w",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 347.0,
|
||||
"x": 79.25,
|
||||
"y": 605.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-w-s",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 478.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 132.0,
|
||||
"y": 836.0,
|
||||
"width": 7.0,
|
||||
"height": 270.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -235,15 +235,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
135.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
7.0,
|
||||
135.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
7.0,
|
||||
270.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-w-s",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 514.0,
|
||||
"x": 90.5,
|
||||
"y": 931.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-client",
|
||||
"type": "rectangle",
|
||||
"x": 92.5,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-client",
|
||||
"type": "text",
|
||||
"x": 102.5,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-entry",
|
||||
"type": "rectangle",
|
||||
"x": 92.5,
|
||||
"y": 240.0,
|
||||
"x": 41.5,
|
||||
"y": 455.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-entry",
|
||||
"type": "text",
|
||||
"x": 102.5,
|
||||
"y": 250.0,
|
||||
"x": 51.5,
|
||||
"y": 465.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-workload",
|
||||
"type": "rectangle",
|
||||
"x": 77.0,
|
||||
"y": 407.0,
|
||||
"x": 41.5,
|
||||
"y": 765.0,
|
||||
"width": 181.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-workload",
|
||||
"type": "text",
|
||||
"x": 87.0,
|
||||
"y": 417.0,
|
||||
"x": 51.5,
|
||||
"y": 775.0,
|
||||
"width": 161.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,10 +510,10 @@
|
||||
{
|
||||
"id": "node-store",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 574.0,
|
||||
"x": 41.5,
|
||||
"y": 1106.0,
|
||||
"width": 195.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-label-store",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 584.0,
|
||||
"x": 51.5,
|
||||
"y": 1116.0,
|
||||
"width": 175.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a4-two-node-losses",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "3c9bc2fcbc853f176e8a38fc838062a36779083d1affe8e46a560484e230233b",
|
||||
"spec_sha256": "247acbd7b3438959cb84e1d84475eb7e45d39c92835d4d6dae400ff8f9137186",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-4 · 노드 상실 — 둘 다 전면 장애지만 이유가 다르다",
|
||||
"line": 223
|
||||
"line": 533
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"a4-two-node-losses.excalidraw",
|
||||
"a4-two-node-losses.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="716" viewBox="0 0 680 716" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1222" viewBox="0 0 680 1222" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">노드를 잃는 두 가지</title>
|
||||
<desc id="diagram-description">kc-lab-2 를 잃으면 keycloak-1 은 살아 있지만 PostgreSQL 이 같이 죽어 외부 응답이 503 이 된다. kc-lab-1 을 잃으면 keycloak-0 은 계속 돌고 있는데 traefik 과 API 서버가 사라져 연결 자체가 안 된다. 워크로드가 멀쩡한데 도달할 수 없어 장애다. 진입점이 단일 노드에 있으면 워크로드 이중화는 의미가 없다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a4-two-node-losses","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-4 · 노드 상실 — 둘 다 전면 장애지만 이유가 다르다","line":223}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a4-two-node-losses","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-4 · 노드 상실 — 둘 다 전면 장애지만 이유가 다르다","line":533}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="716" />
|
||||
<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="221-228" />
|
||||
<rect class="edge-label-bg" x="169.5" y="178.0" width="44.0" 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="221-228" />
|
||||
<rect class="edge-label-bg" x="169.5" y="345.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="191.5" y="360.0">라우팅</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="167.5,478.0 167.5,526.0 167.5,526.0 167.5,574.0" data-evidence="221-233" />
|
||||
<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>
|
||||
<rect class="canvas" width="680" height="1222" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,229.0 110.0,342.0 116.5,342.0 116.5,455.0" data-evidence="533-538" />
|
||||
<rect class="edge-label-bg" x="91.2" y="300.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="113.2" y="315.0">요청</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="116.5,526.0 116.5,645.5 132.0,645.5 132.0,765.0" data-evidence="533-538" />
|
||||
<rect class="edge-label-bg" x="102.2" y="603.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="124.2" y="618.5">라우팅</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="132.0,836.0 132.0,971.0 139.0,971.0 139.0,1106.0" data-evidence="533-543" />
|
||||
<rect class="edge-label-bg" x="109.8" y="929.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="135.5" y="944.0">세션 조회</text>
|
||||
<g id="node-client">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="221-228"><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 class="actor-symbol emphasis-primary" data-evidence="533-538"><circle cx="110.0" cy="165.0" r="11.0" /><line x1="110.0" y1="181.0" x2="110.0" y2="200.0" /><line x1="92.0" y1="191.0" x2="128.0" y2="191.0" /><line x1="110.0" y1="200.0" x2="95.0" y2="217.0" /><line x1="110.0" y1="200.0" x2="125.0" y2="217.0" /></g>
|
||||
<text class="node-label" x="110.0" y="222.0">외부 요청</text>
|
||||
</g>
|
||||
<g id="node-entry">
|
||||
<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>
|
||||
<polygon class="node-shape kind-gateway emphasis-warning role-control" data-evidence="533-540" points="116.5,455.0 191.5,490.5 116.5,526.0 41.5,490.5" />
|
||||
<text class="node-label" x="116.5" y="482.0">진입 경로 (kc-lab-1)</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="503.0" x2="177.5" y2="503.0" />
|
||||
<text class="node-detail" x="57.5" y="520.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>
|
||||
<rect class="node-shape kind-service emphasis-normal role-control" data-evidence="533-540" x="41.5" y="765.0" width="181.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="132.0" y="792.0">keycloak-0 (kc-lab-2)</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="813.0" x2="208.5" y2="813.0" />
|
||||
<text class="node-detail" x="57.5" y="830.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>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="533-543" x="41.5" y="1117.8" width="195.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="139.0" cy="1117.8" rx="97.5" ry="11.8" /><path class="storage-bottom" d="M 41.5 1165.2 A 97.5 11.8 0 0 0 236.5 1165.2" />
|
||||
<text class="node-label" x="139.0" y="1133.0">PostgreSQL (kc-lab-2)</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="1154.0" x2="222.5" y2="1154.0" />
|
||||
<text class="node-detail" x="57.5" y="1171.0">local-path PVC · 재배치 불가</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -10,13 +10,13 @@ JGroups 는 한 방향이 막혀도 열린 방향으로 재연결한다. 그래
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **keycloak-0** (service): 코디네이터. 분단돼도 살아남는다. Evidence: L226–L236.
|
||||
- **막은 방향** (channel): raw 테이블 PREROUTING 에서 끊는다. Evidence: L226–L236.
|
||||
- **keycloak-1** (service): 분단되면 스스로 로드밸런서에서 빠진다. Evidence: L226–L236.
|
||||
- **열린 반대 방향** (channel): 비대칭 차단에서는 여기로 재연결한다. Evidence: L226–L232.
|
||||
- **keycloak-0** (service): 코디네이터. 분단돼도 살아남는다. Evidence: L536–L545.
|
||||
- **막은 방향** (channel): raw 테이블 PREROUTING 에서 끊는다. Evidence: L536–L545.
|
||||
- **keycloak-1** (service): 분단되면 스스로 로드밸런서에서 빠진다. Evidence: L536–L545.
|
||||
- **열린 반대 방향** (channel): 비대칭 차단에서는 여기로 재연결한다. Evidence: L536–L542.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **막은 방향 → keycloak-1:** 차단. Evidence: L226–L232.
|
||||
- **keycloak-0 → 막은 방향:** JGroups 메시지. Evidence: L226–L232.
|
||||
- **keycloak-1 → 열린 반대 방향:** 재연결. Evidence: L226–L232.
|
||||
- **막은 방향 → keycloak-1:** 차단. Evidence: L536–L542.
|
||||
- **keycloak-0 → 막은 방향:** JGroups 메시지. Evidence: L536–L542.
|
||||
- **keycloak-1 → 열린 반대 방향:** 재연결. Evidence: L536–L542.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a5-partition-asymmetry" name="비대칭 차단은 가르지 못한다">
|
||||
<mxGraphModel dx="680" dy="677" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1191" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1191" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_k0" value="keycloak-0<br/>cluster_size 1" tooltip="코디네이터. 분단돼도 살아남는다. | Evidence: L226-L236" 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="60.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_k0" value="keycloak-0<br/>cluster_size 1" tooltip="코디네이터. 분단돼도 살아남는다. | Evidence: L536-L545" 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="35.0" y="145.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_fwd" value="막은 방향<br/>conntrack 보다 먼저" tooltip="raw 테이블 PREROUTING 에서 끊는다. | Evidence: L226-L236" 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="227.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_fwd" value="막은 방향<br/>conntrack 보다 먼저" tooltip="raw 테이블 PREROUTING 에서 끊는다. | Evidence: L536-L545" 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="35.0" y="455.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_k1" value="keycloak-1<br/>readiness 실패" tooltip="분단되면 스스로 로드밸런서에서 빠진다. | Evidence: L536-L545" 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="35.0" y="765.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">
|
||||
<mxGeometry x="70.0" y="561.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_rev" value="열린 반대 방향<br/>재연결 경로" tooltip="비대칭 차단에서는 여기로 재연결한다. | Evidence: L536-L542" 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="35.0" y="1075.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="169.0" y="346.0" as="offset"/>
|
||||
<mxPoint x="134.0" y="645.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_k0-f" value="JGroups 메시지" 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_fwd">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="169.0" y="179.0" as="offset"/>
|
||||
<mxPoint x="134.0" y="335.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_k1-rev" value="재연결" 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_rev">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="169.0" y="513.0" as="offset"/>
|
||||
<mxPoint x="134.0" y="955.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-f-k1",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 298.0,
|
||||
"x": 110.0,
|
||||
"y": 526.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-f-k1",
|
||||
"type": "text",
|
||||
"x": 124.0,
|
||||
"y": 334.0,
|
||||
"x": 89.0,
|
||||
"y": 633.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-k0-f",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 131.0,
|
||||
"x": 110.0,
|
||||
"y": 216.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -136,15 +136,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-k0-f",
|
||||
"type": "text",
|
||||
"x": 124.0,
|
||||
"y": 167.0,
|
||||
"x": 89.0,
|
||||
"y": 323.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-k1-rev",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 465.0,
|
||||
"x": 110.0,
|
||||
"y": 836.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -235,15 +235,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-k1-rev",
|
||||
"type": "text",
|
||||
"x": 124.0,
|
||||
"y": 501.0,
|
||||
"x": 89.0,
|
||||
"y": 943.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-k0",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-k0",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-fwd",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 227.0,
|
||||
"x": 35.0,
|
||||
"y": 455.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-fwd",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 237.0,
|
||||
"x": 45.0,
|
||||
"y": 465.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-k1",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 394.0,
|
||||
"x": 35.0,
|
||||
"y": 765.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-k1",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 404.0,
|
||||
"x": 45.0,
|
||||
"y": 775.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-rev",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 561.0,
|
||||
"x": 35.0,
|
||||
"y": 1075.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-rev",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 571.0,
|
||||
"x": 45.0,
|
||||
"y": 1085.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a5-partition-asymmetry",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "d971af080c366e171592b1cfe7e62120eb0f4e0b5193e241e018e9440a44536b",
|
||||
"spec_sha256": "0aa3da3e5c7c54040cf3edc7d09ab27531b6cef57d03bd7b3e6e944757431443",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-5 · 비대칭 분단 — 전면 장애 경로가 없다",
|
||||
"line": 246
|
||||
"line": 576
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"a5-partition-asymmetry.svg",
|
||||
"a5-partition-asymmetry.drawio",
|
||||
"a5-partition-asymmetry.mmd",
|
||||
"a5-partition-asymmetry.d2",
|
||||
"a5-partition-asymmetry.dot",
|
||||
"a5-partition-asymmetry.drawio",
|
||||
"a5-partition-asymmetry.excalidraw",
|
||||
"a5-partition-asymmetry.alt.md"
|
||||
],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="677" viewBox="0 0 680 677" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1191" viewBox="0 0 680 1191" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">비대칭 차단은 가르지 못한다</title>
|
||||
<desc id="diagram-description">JGroups 는 한 방향이 막혀도 열린 방향으로 재연결한다. 그래서 비대칭 차단으로는 cluster_size 가 2 로 유지된다. 양방향을 다 막으면 양쪽 모두 멤버 1개가 되지만 한쪽만 DOWN 이 된다. 코디네이터 쪽이 살아남고 분단된 쪽은 스스로 readiness 를 떨어뜨려 로드밸런서에서 빠지므로 외부 응답은 200 을 유지한다. 전면 장애 경로가 없다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a5-partition-asymmetry","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-5 · 비대칭 분단 — 전면 장애 경로가 없다","line":246}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a5-partition-asymmetry","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-5 · 비대칭 분단 — 전면 장애 경로가 없다","line":576}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,38 +49,38 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</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="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>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="145.0,465.0 145.0,513.0 145.0,513.0 145.0,561.0" data-evidence="226-232" />
|
||||
<rect class="edge-label-bg" x="147.0" y="499.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="169.0" y="514.0">재연결</text>
|
||||
<rect class="canvas" width="680" height="1191" />
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="110.0,526.0 110.0,645.5 110.0,645.5 110.0,765.0" data-evidence="536-542" />
|
||||
<rect class="edge-label-bg" x="112.0" y="631.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="134.0" y="646.5">차단</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,216.0 110.0,335.5 110.0,335.5 110.0,455.0" data-evidence="536-542" />
|
||||
<rect class="edge-label-bg" x="88.2" y="321.5" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="134.0" y="336.5">JGroups 메시지</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,836.0 110.0,955.5 110.0,955.5 110.0,1075.0" data-evidence="536-542" />
|
||||
<rect class="edge-label-bg" x="112.0" y="941.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="134.0" y="956.5">재연결</text>
|
||||
<g id="node-k0">
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="226-236" x="70.0" y="60.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="87.0">keycloak-0</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="108.0" x2="206.0" y2="108.0" />
|
||||
<text class="node-detail" x="86.0" y="125.0">cluster_size 1</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="536-545" x="35.0" y="145.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="172.0">keycloak-0</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="193.0" x2="171.0" y2="193.0" />
|
||||
<text class="node-detail" x="51.0" y="210.0">cluster_size 1</text>
|
||||
</g>
|
||||
<g id="node-fwd">
|
||||
<rect class="node-shape kind-channel emphasis-warning role-control" data-evidence="226-236" x="70.0" y="227.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="254.0">막은 방향</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="275.0" x2="206.0" y2="275.0" />
|
||||
<text class="node-detail" x="86.0" y="292.0">conntrack 보다 먼저</text>
|
||||
<rect class="node-shape kind-channel emphasis-warning role-control" data-evidence="536-545" x="35.0" y="455.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="482.0">막은 방향</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="503.0" x2="171.0" y2="503.0" />
|
||||
<text class="node-detail" x="51.0" y="520.0">conntrack 보다 먼저</text>
|
||||
</g>
|
||||
<g id="node-k1">
|
||||
<rect class="node-shape kind-service emphasis-normal role-target" data-evidence="226-236" x="70.0" y="394.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="421.0">keycloak-1</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="442.0" x2="206.0" y2="442.0" />
|
||||
<text class="node-detail" x="86.0" y="459.0">readiness 실패</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-target" data-evidence="536-545" x="35.0" y="765.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="792.0">keycloak-1</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="813.0" x2="171.0" y2="813.0" />
|
||||
<text class="node-detail" x="51.0" y="830.0">readiness 실패</text>
|
||||
</g>
|
||||
<g id="node-rev">
|
||||
<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>
|
||||
<rect class="node-shape kind-channel emphasis-normal role-support" data-evidence="536-542" x="35.0" y="1075.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="1102.0">열린 반대 방향</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="1123.0" x2="171.0" y2="1123.0" />
|
||||
<text class="node-detail" x="51.0" y="1140.0">재연결 경로</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -10,13 +10,13 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **주입한 지연** (process): tc netem 으로 넣는다. Evidence: L253–L258.
|
||||
- **왕복 횟수만큼 누적** (process): 로그인 한 번이 DB 를 여러 번 왕복한다. Evidence: L259–L263.
|
||||
- **커넥션 풀 대기** (component): 앞 요청이 커넥션을 붙들어 뒤가 밀린다. Evidence: L264–L268.
|
||||
- **readiness 실패** (process): 느린 노드가 로드밸런서에서 빠진다. Evidence: L269–L273.
|
||||
- **주입한 지연** (process): tc netem 으로 넣는다. Evidence: L590–L593.
|
||||
- **왕복 횟수만큼 누적** (process): 로그인 한 번이 DB 를 여러 번 왕복한다. Evidence: L594–L598.
|
||||
- **커넥션 풀 대기** (component): 앞 요청이 커넥션을 붙들어 뒤가 밀린다. Evidence: L600–L602.
|
||||
- **readiness 실패** (process): 느린 노드가 로드밸런서에서 빠진다. Evidence: L604–L605.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **주입한 지연 → 왕복 횟수만큼 누적:** 왕복당 누적. Evidence: L253–L263.
|
||||
- **커넥션 풀 대기 → readiness 실패:** 프로브도 타임아웃. Evidence: L264–L273.
|
||||
- **왕복 횟수만큼 누적 → 커넥션 풀 대기:** 커넥션 점유. Evidence: L259–L268.
|
||||
- **주입한 지연 → 왕복 횟수만큼 누적:** 왕복당 누적. Evidence: L590–L598.
|
||||
- **커넥션 풀 대기 → readiness 실패:** 프로브도 타임아웃. Evidence: L600–L605.
|
||||
- **왕복 횟수만큼 누적 → 커넥션 풀 대기:** 커넥션 점유. Evidence: L594–L602.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a6-latency-multiplication" name="지연이 곱해지는 두 단계">
|
||||
<mxGraphModel dx="680" dy="677" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1194" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1194" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_inject" value="주입한 지연<br/>200 ms" tooltip="tc netem 으로 넣는다. | Evidence: L253-L258" 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="60.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_inject" value="주입한 지연<br/>200 ms" tooltip="tc netem 으로 넣는다. | Evidence: L590-L593" 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="35.0" y="145.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_roundtrips" value="왕복 횟수만큼 누적<br/>66ms → 1,872ms" tooltip="로그인 한 번이 DB 를 여러 번 왕복한다. | Evidence: L259-L263" 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="71.5" y="227.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_roundtrips" value="왕복 횟수만큼 누적<br/>66ms → 1,872ms" tooltip="로그인 한 번이 DB 를 여러 번 왕복한다. | Evidence: L594-L598" 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="35.0" y="455.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_pool" value="커넥션 풀 대기<br/>획득 대기 최대 20,000ms" tooltip="앞 요청이 커넥션을 붙들어 뒤가 밀린다. | Evidence: L264-L268" 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="394.0" width="153.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_pool" value="커넥션 풀 대기<br/>획득 대기 최대 20,000ms" tooltip="앞 요청이 커넥션을 붙들어 뒤가 밀린다. | Evidence: L600-L602" 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="35.0" y="765.0" width="153.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_probe" value="readiness 실패<br/>동시 20건 최대 22.2초" tooltip="느린 노드가 로드밸런서에서 빠진다. | Evidence: L604-L605" 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="35.0" y="1078.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="170.5" y="179.0" as="offset"/>
|
||||
<mxPoint x="134.0" y="335.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_p-pr" value="프로브도 타임아웃" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_pool" target="n_probe">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="170.5" y="513.0" as="offset"/>
|
||||
<mxPoint x="110.8" y="929.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="170.5" y="346.0" as="offset"/>
|
||||
<mxPoint x="110.8" y="617.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-i-r",
|
||||
"type": "arrow",
|
||||
"x": 146.5,
|
||||
"y": 131.0,
|
||||
"x": 110.0,
|
||||
"y": 216.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-i-r",
|
||||
"type": "text",
|
||||
"x": 125.5,
|
||||
"y": 167.0,
|
||||
"x": 89.0,
|
||||
"y": 323.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-p-pr",
|
||||
"type": "arrow",
|
||||
"x": 146.5,
|
||||
"y": 465.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 836.0,
|
||||
"width": 1.5,
|
||||
"height": 242.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
1.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
1.5,
|
||||
121.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
121.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
242.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-p-pr",
|
||||
"type": "text",
|
||||
"x": 125.5,
|
||||
"y": 501.0,
|
||||
"x": 65.75,
|
||||
"y": 917.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-r-p",
|
||||
"type": "arrow",
|
||||
"x": 146.5,
|
||||
"y": 298.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 526.0,
|
||||
"width": 1.5,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -235,15 +235,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
1.5,
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
1.5,
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-r-p",
|
||||
"type": "text",
|
||||
"x": 125.5,
|
||||
"y": 334.0,
|
||||
"x": 65.75,
|
||||
"y": 605.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-inject",
|
||||
"type": "rectangle",
|
||||
"x": 71.5,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-inject",
|
||||
"type": "text",
|
||||
"x": 81.5,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-roundtrips",
|
||||
"type": "rectangle",
|
||||
"x": 71.5,
|
||||
"y": 227.0,
|
||||
"x": 35.0,
|
||||
"y": 455.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-roundtrips",
|
||||
"type": "text",
|
||||
"x": 81.5,
|
||||
"y": 237.0,
|
||||
"x": 45.0,
|
||||
"y": 465.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-pool",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 394.0,
|
||||
"x": 35.0,
|
||||
"y": 765.0,
|
||||
"width": 153.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-pool",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 404.0,
|
||||
"x": 45.0,
|
||||
"y": 775.0,
|
||||
"width": 133.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-probe",
|
||||
"type": "rectangle",
|
||||
"x": 71.5,
|
||||
"y": 561.0,
|
||||
"x": 35.0,
|
||||
"y": 1078.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-probe",
|
||||
"type": "text",
|
||||
"x": 81.5,
|
||||
"y": 571.0,
|
||||
"x": 45.0,
|
||||
"y": 1088.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a6-latency-multiplication",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "0b2994cb0a306f6fc1bd7fb2dae907ed79b6fe0bb6e908dba8509b0db1ca9f2b",
|
||||
"spec_sha256": "16cd29e25db3517215f28225fcddc07430da40b9e187aa51ea6da2472a2260f6",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-6 · 지연 주입 — 200밀리초가 22초가 된다",
|
||||
"line": 255
|
||||
"line": 590
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="677" viewBox="0 0 680 677" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1194" viewBox="0 0 680 1194" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">지연이 곱해지는 두 단계</title>
|
||||
<desc id="diagram-description">주입한 지연은 200ms 인데 로그인 응답이 1,872ms 가 됐다. 로그인 한 번이 데이터베이스 왕복을 여러 번 하므로 왕복 횟수만큼 더해진다. 동시 20건에서는 22.2초가 됐다. 요청이 커넥션을 오래 붙들어 뒤의 요청이 풀에서 대기하기 때문이며 커넥션 획득 대기 최대가 20,000ms 였다. 마지막으로 readiness 프로브가 타임아웃으로 실패해 느린 노드가 로드밸런서에서 빠진다. 느림이 장애로 승격된다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a6-latency-multiplication","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-6 · 지연 주입 — 200밀리초가 22초가 된다","line":255}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a6-latency-multiplication","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-6 · 지연 주입 — 200밀리초가 22초가 된다","line":590}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,38 +49,38 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</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="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="141.4" y="332.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="170.5" y="347.0">커넥션 점유</text>
|
||||
<rect class="canvas" width="680" height="1194" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,216.0 110.0,335.5 110.0,335.5 110.0,455.0" data-evidence="590-598" />
|
||||
<rect class="edge-label-bg" x="104.9" y="321.5" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="134.0" y="336.5">왕복당 누적</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="111.5,836.0 111.5,957.0 110.0,957.0 110.0,1078.0" data-evidence="600-605" />
|
||||
<rect class="edge-label-bg" x="71.6" y="915.0" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="110.8" y="930.0">프로브도 타임아웃</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,526.0 110.0,645.5 111.5,645.5 111.5,765.0" data-evidence="594-602" />
|
||||
<rect class="edge-label-bg" x="81.7" y="603.5" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="110.8" y="618.5">커넥션 점유</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>
|
||||
<line class="node-detail-divider" x1="85.5" y1="108.0" x2="207.5" y2="108.0" />
|
||||
<text class="node-detail" x="87.5" y="125.0">200 ms</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-source" data-evidence="590-593" x="35.0" y="145.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="172.0">주입한 지연</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="193.0" x2="171.0" y2="193.0" />
|
||||
<text class="node-detail" x="51.0" y="210.0">200 ms</text>
|
||||
</g>
|
||||
<g id="node-roundtrips">
|
||||
<rect class="node-shape kind-process emphasis-normal role-control" data-evidence="259-263" x="71.5" y="227.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="146.5" y="254.0">왕복 횟수만큼 누적</text>
|
||||
<line class="node-detail-divider" x1="85.5" y1="275.0" x2="207.5" y2="275.0" />
|
||||
<text class="node-detail" x="87.5" y="292.0">66ms → 1,872ms</text>
|
||||
<rect class="node-shape kind-process emphasis-normal role-control" data-evidence="594-598" x="35.0" y="455.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="482.0">왕복 횟수만큼 누적</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="503.0" x2="171.0" y2="503.0" />
|
||||
<text class="node-detail" x="51.0" y="520.0">66ms → 1,872ms</text>
|
||||
</g>
|
||||
<g id="node-pool">
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="264-268" x="70.0" y="394.0" width="153.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="146.5" y="421.0">커넥션 풀 대기</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="442.0" x2="209.0" y2="442.0" />
|
||||
<text class="node-detail" x="86.0" y="459.0">획득 대기 최대 20,000ms</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="600-602" x="35.0" y="765.0" width="153.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="111.5" y="792.0">커넥션 풀 대기</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="813.0" x2="174.0" y2="813.0" />
|
||||
<text class="node-detail" x="51.0" y="830.0">획득 대기 최대 20,000ms</text>
|
||||
</g>
|
||||
<g id="node-probe">
|
||||
<rect class="node-shape kind-process emphasis-warning role-target" data-evidence="269-273" x="71.5" y="561.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="146.5" y="588.0">readiness 실패</text>
|
||||
<line class="node-detail-divider" x1="85.5" y1="609.0" x2="207.5" y2="609.0" />
|
||||
<text class="node-detail" x="87.5" y="626.0">동시 20건 최대 22.2초</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-target" data-evidence="604-605" x="35.0" y="1078.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="1105.0">readiness 실패</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="1126.0" x2="171.0" y2="1126.0" />
|
||||
<text class="node-detail" x="51.0" y="1143.0">동시 20건 최대 22.2초</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -10,13 +10,13 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **롤링 재시작** (process): 한 번에 한 파드씩 교체한다. Evidence: L270–L274.
|
||||
- **Infinispan sessions 캐시** (component): 파드와 함께 사라진다. Evidence: L270–L278.
|
||||
- **PostgreSQL 세션 행** (datastore): 재시작과 무관하다. Evidence: L270–L278.
|
||||
- **재시작 전 발급한 refresh token** (component): 여전히 통한다. Evidence: L270–L278.
|
||||
- **롤링 재시작** (process): 한 번에 한 파드씩 교체한다. Evidence: L612–L614.
|
||||
- **Infinispan sessions 캐시** (component): 파드와 함께 사라진다. Evidence: L612–L618.
|
||||
- **PostgreSQL 세션 행** (datastore): 재시작과 무관하다. Evidence: L612–L618.
|
||||
- **재시작 전 발급한 refresh token** (component): 여전히 통한다. Evidence: L612–L618.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **PostgreSQL 세션 행 → 재시작 전 발급한 refresh token:** 세션 조회. Evidence: L270–L278.
|
||||
- **롤링 재시작 → Infinispan sessions 캐시:** 캐시 초기화. Evidence: L270–L278.
|
||||
- **롤링 재시작 → PostgreSQL 세션 행:** 변경 없음. Evidence: L270–L278.
|
||||
- **PostgreSQL 세션 행 → 재시작 전 발급한 refresh token:** 세션 조회. Evidence: L612–L618.
|
||||
- **롤링 재시작 → Infinispan sessions 캐시:** 캐시 초기화. Evidence: L612–L618.
|
||||
- **롤링 재시작 → PostgreSQL 세션 행:** 변경 없음. Evidence: L612–L618.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 재시작이 지우는 것과 남기는 것
|
||||
# Question: 롤링 재시작 뒤에도 로그인이 유지되는 이유는 무엇인가
|
||||
direction: down
|
||||
direction: right
|
||||
n0: "롤링 재시작" {
|
||||
shape: rectangle
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="롤링 재시작", shape=box, style="rounded,filled"];
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="a8-cache-vs-session" name="재시작이 지우는 것과 남기는 것">
|
||||
<mxGraphModel dx="680" dy="556" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="971" dy="319" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="971" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_restart" value="롤링 재시작<br/>kubectl rollout restart" tooltip="한 번에 한 파드씩 교체한다. | Evidence: L270-L274" 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="171.5" y="60.0" width="195.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_restart" value="롤링 재시작<br/>kubectl rollout restart" tooltip="한 번에 한 파드씩 교체한다. | Evidence: L612-L614" 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="131.5" width="195.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_cache" value="Infinispan sessions 캐시<br/>entries_unique → 0" tooltip="파드와 함께 사라진다. | Evidence: L270-L278" 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="188.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_cache" value="Infinispan sessions 캐시<br/>entries_unique → 0" tooltip="파드와 함께 사라진다. | Evidence: L612-L618" 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="425.0" y="60.0" width="188.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_db" value="PostgreSQL 세션 행<br/>151 → 151" tooltip="재시작과 무관하다. | Evidence: L270-L278" 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="318.0" y="227.0" width="150.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_db" value="PostgreSQL 세션 행<br/>151 → 151" tooltip="재시작과 무관하다. | Evidence: L612-L618" 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="444.0" y="203.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_refresh" value="재시작 전 발급한 refresh token<br/>200" tooltip="여전히 통한다. | Evidence: L612-L618" 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="773.0" y="121.5" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="331.0" y="344.0" as="offset"/>
|
||||
<mxPoint x="707.5" y="202.8" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="212.0" y="157.5" as="offset"/>
|
||||
<mxPoint x="369.0" y="126.8" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="335.5" y="151.0" as="offset"/>
|
||||
<mxPoint x="378.5" y="207.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-db-rt",
|
||||
"type": "arrow",
|
||||
"x": 269.0,
|
||||
"y": 324.0,
|
||||
"width": 124.0,
|
||||
"height": 96.0,
|
||||
"x": 594.0,
|
||||
"y": 167.0,
|
||||
"width": 179.0,
|
||||
"height": 71.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -32,20 +32,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
124.0,
|
||||
0.0,
|
||||
71.5
|
||||
],
|
||||
[
|
||||
89.5,
|
||||
71.5
|
||||
],
|
||||
[
|
||||
89.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
124.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
179.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-db-rt",
|
||||
"type": "text",
|
||||
"x": 286.0,
|
||||
"y": 332.0,
|
||||
"x": 662.5,
|
||||
"y": 190.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-r-c",
|
||||
"type": "arrow",
|
||||
"x": 164.0,
|
||||
"y": 131.0,
|
||||
"width": 96.0,
|
||||
"height": 109.0,
|
||||
"x": 265.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
96.0,
|
||||
0.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
109.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-r-c",
|
||||
"type": "text",
|
||||
"x": 167.0,
|
||||
"y": 145.5,
|
||||
"x": 324.0,
|
||||
"y": 114.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-r-db",
|
||||
"type": "arrow",
|
||||
"x": 278.0,
|
||||
"y": 131.0,
|
||||
"width": 115.0,
|
||||
"height": 96.0,
|
||||
"x": 265.0,
|
||||
"y": 176.0,
|
||||
"width": 179.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -234,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
89.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
115.0,
|
||||
48.0
|
||||
89.5,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
115.0,
|
||||
96.0
|
||||
179.0,
|
||||
62.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-r-db",
|
||||
"type": "text",
|
||||
"x": 290.5,
|
||||
"y": 139.0,
|
||||
"x": 333.5,
|
||||
"y": 195.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-restart",
|
||||
"type": "rectangle",
|
||||
"x": 171.5,
|
||||
"y": 60.0,
|
||||
"x": 70.0,
|
||||
"y": 131.5,
|
||||
"width": 195.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-restart",
|
||||
"type": "text",
|
||||
"x": 181.5,
|
||||
"y": 70.0,
|
||||
"x": 80.0,
|
||||
"y": 141.5,
|
||||
"width": 175.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-cache",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"x": 425.0,
|
||||
"y": 60.0,
|
||||
"width": 188.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-cache",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"x": 435.0,
|
||||
"y": 70.0,
|
||||
"width": 168.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,10 +441,10 @@
|
||||
{
|
||||
"id": "node-db",
|
||||
"type": "rectangle",
|
||||
"x": 318.0,
|
||||
"y": 227.0,
|
||||
"x": 444.0,
|
||||
"y": 203.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -471,10 +471,10 @@
|
||||
{
|
||||
"id": "node-label-db",
|
||||
"type": "text",
|
||||
"x": 328.0,
|
||||
"y": 237.0,
|
||||
"x": 454.0,
|
||||
"y": 213.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-refresh",
|
||||
"type": "rectangle",
|
||||
"x": 192.5,
|
||||
"y": 420.0,
|
||||
"x": 773.0,
|
||||
"y": 121.5,
|
||||
"width": 153.0,
|
||||
"height": 91.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-refresh",
|
||||
"type": "text",
|
||||
"x": 202.5,
|
||||
"y": 430.0,
|
||||
"x": 783.0,
|
||||
"y": 131.5,
|
||||
"width": 133.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "a8-cache-vs-session",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "02e971c0aedba252dce01773d7896eb2a751cd8752eb14d643b43e8f96646102",
|
||||
"spec_sha256": "fdf8bd6de23fdc5ed6a3895a53aeb26bb02bb36322f8214314a5dbd3aa098918",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-8 · 롤링 재시작 — 세션은 살아남고 캐시만 사라진다",
|
||||
"line": 272
|
||||
"line": 612
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%% 재시작이 지우는 것과 남기는 것
|
||||
%% question: 롤링 재시작 뒤에도 로그인이 유지되는 이유는 무엇인가
|
||||
flowchart TB
|
||||
flowchart LR
|
||||
n0["롤링 재시작"]
|
||||
n1["Infinispan sessions 캐시"]
|
||||
n2[("PostgreSQL 세션 행")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="556" viewBox="0 0 680 556" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="971" height="319" viewBox="0 0 971 319" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">재시작이 지우는 것과 남기는 것</title>
|
||||
<desc id="diagram-description">롤링 재시작은 한 번에 한 파드씩 바꾼다. 파드가 죽으면 그 노드의 Infinispan sessions 캐시는 0 으로 초기화된다. 그러나 PostgreSQL 의 세션 행은 151개가 그대로 남았고 재시작 전에 발급한 refresh token 이 여전히 200 을 받는다. 전 구간 서비스 중단이 없었다. volatile 모드였다면 같은 재시작에서 400 Session not active 가 되어 전원 로그아웃이 된다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a8-cache-vs-session","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-8 · 롤링 재시작 — 세션은 살아남고 캐시만 사라진다","line":272}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"a8-cache-vs-session","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-8 · 롤링 재시작 — 세션은 살아남고 캐시만 사라진다","line":612}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,39 +49,39 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</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="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="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="309.8" y="137.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="335.5" y="152.0">변경 없음</text>
|
||||
<rect class="canvas" width="971" height="319" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="594.0,238.5 683.5,238.5 683.5,167.0 773.0,167.0" data-evidence="612-618" />
|
||||
<rect class="edge-label-bg" x="681.8" y="188.8" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="707.5" y="203.8">세션 조회</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="265.0,158.0 345.0,158.0 345.0,95.5 425.0,95.5" data-evidence="612-618" />
|
||||
<rect class="edge-label-bg" x="339.9" y="112.8" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="369.0" y="127.8">캐시 초기화</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="265.0,176.0 354.5,176.0 354.5,238.5 444.0,238.5" data-evidence="612-618" />
|
||||
<rect class="edge-label-bg" x="352.8" y="193.2" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="378.5" y="208.2">변경 없음</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>
|
||||
<line class="node-detail-divider" x1="185.5" y1="108.0" x2="352.5" y2="108.0" />
|
||||
<text class="node-detail" x="187.5" y="125.0">kubectl rollout restart</text>
|
||||
<rect class="node-shape kind-process emphasis-primary role-source" data-evidence="612-614" x="70.0" y="131.5" width="195.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="167.5" y="158.5">롤링 재시작</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="179.5" x2="251.0" y2="179.5" />
|
||||
<text class="node-detail" x="86.0" y="196.5">kubectl rollout restart</text>
|
||||
</g>
|
||||
<g id="node-cache">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="270-278" x="70.0" y="240.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="164.0" y="267.0">Infinispan sessions 캐시</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="244.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">entries_unique → 0</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="612-618" x="425.0" y="60.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="519.0" y="87.0">Infinispan sessions 캐시</text>
|
||||
<line class="node-detail-divider" x1="439.0" y1="108.0" x2="599.0" y2="108.0" />
|
||||
<text class="node-detail" x="441.0" y="125.0">entries_unique → 0</text>
|
||||
</g>
|
||||
<g id="node-db">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="270-278" x="318.0" y="240.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="393.0" cy="240.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 318.0 311.0 A 75.0 13.0 0 0 0 468.0 311.0" />
|
||||
<text class="node-label" x="393.0" y="267.0">PostgreSQL 세션 행</text>
|
||||
<line class="node-detail-divider" x1="332.0" y1="288.0" x2="454.0" y2="288.0" />
|
||||
<text class="node-detail" x="334.0" y="305.0">151 → 151</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="612-618" x="444.0" y="214.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="519.0" cy="214.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 444.0 262.2 A 75.0 11.8 0 0 0 594.0 262.2" />
|
||||
<text class="node-label" x="519.0" y="230.0">PostgreSQL 세션 행</text>
|
||||
<line class="node-detail-divider" x1="458.0" y1="251.0" x2="580.0" y2="251.0" />
|
||||
<text class="node-detail" x="460.0" y="268.0">151 → 151</text>
|
||||
</g>
|
||||
<g id="node-refresh">
|
||||
<rect class="node-shape kind-component emphasis-primary role-target" data-evidence="270-278" x="192.5" y="420.0" width="153.0" height="91.0" rx="7" />
|
||||
<text class="node-label" x="269.0" y="447.0">재시작 전 발급한 refresh</text>
|
||||
<text class="node-label" x="269.0" y="465.0">token</text>
|
||||
<line class="node-detail-divider" x1="206.5" y1="486.0" x2="331.5" y2="486.0" />
|
||||
<text class="node-detail" x="208.5" y="503.0">200</text>
|
||||
<rect class="node-shape kind-component emphasis-primary role-target" data-evidence="612-618" x="773.0" y="121.5" width="153.0" height="91.0" rx="7" />
|
||||
<text class="node-label" x="849.5" y="148.5">재시작 전 발급한 refresh</text>
|
||||
<text class="node-label" x="849.5" y="166.5">token</text>
|
||||
<line class="node-detail-divider" x1="787.0" y1="187.5" x2="912.0" y2="187.5" />
|
||||
<text class="node-detail" x="789.0" y="204.5">200</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -10,13 +10,13 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **브라우저 A 세션** (component): 세션 id 가 다르다. Evidence: L360–L366.
|
||||
- **브라우저 B 세션** (component): 세션 id 가 다르다. Evidence: L360–L366.
|
||||
- **토큰 행** (datastore): 기본키에 세션 id 가 없어 둘이 같은 행을 쓴다. Evidence: L370–L377.
|
||||
- **로그아웃 후 남는 것** (datastore): Redis 세션은 0 키인데 여기는 1 행이 남는다. Evidence: L378–L384.
|
||||
- **브라우저 A 세션** (component): 세션 id 가 다르다. Evidence: L745–L747.
|
||||
- **브라우저 B 세션** (component): 세션 id 가 다르다. Evidence: L745–L747.
|
||||
- **토큰 행** (datastore): 기본키에 세션 id 가 없어 둘이 같은 행을 쓴다. Evidence: L776–L781.
|
||||
- **로그아웃 후 남는 것** (datastore): Redis 세션은 0 키인데 여기는 1 행이 남는다. Evidence: L789–L794.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **브라우저 A 세션 → 토큰 행:** principal 이름. Evidence: L370–L377.
|
||||
- **브라우저 B 세션 → 토큰 행:** 같은 키 · 덮어쓰기. Evidence: L370–L377.
|
||||
- **토큰 행 → 로그아웃 후 남는 것:** 로그아웃 미정리. Evidence: L378–L384.
|
||||
- **브라우저 A 세션 → 토큰 행:** principal 이름. Evidence: L776–L781.
|
||||
- **브라우저 B 세션 → 토큰 행:** 같은 키 · 덮어쓰기. Evidence: L776–L781.
|
||||
- **토큰 행 → 로그아웃 후 남는 것:** 로그아웃 미정리. Evidence: L789–L794.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 덮어쓰기를 만드는 기본키
|
||||
# Question: 같은 사용자의 두 브라우저가 서로의 토큰을 지우는 이유는 무엇인가
|
||||
direction: down
|
||||
direction: right
|
||||
n0: "브라우저 A 세션" {
|
||||
shape: rectangle
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="브라우저 A 세션", shape=box, style="rounded,filled"];
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="b2-primary-key-overwrite" name="덮어쓰기를 만드는 기본키">
|
||||
<mxGraphModel dx="680" dy="555" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="1035" dy="305" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1035" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_s1" value="브라우저 A 세션" tooltip="세션 id 가 다르다. | Evidence: L360-L366" 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">
|
||||
<mxCell id="n_s1" value="브라우저 A 세션" tooltip="세션 id 가 다르다. | Evidence: L745-L747" 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="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_s2" value="브라우저 B 세션" tooltip="세션 id 가 다르다. | Evidence: L360-L366" 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="280.0" y="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_s2" value="브라우저 B 세션" tooltip="세션 id 가 다르다. | Evidence: L745-L747" 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="196.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_token-row" value="토큰 행<br/>PRIMARY KEY (client_registration_id, principal_name)" tooltip="기본키에 세션 id 가 없어 둘이 같은 행을 쓴다. | Evidence: L370-L377" 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="100.0" y="220.0" width="300.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_token-row" value="토큰 행<br/>PRIMARY KEY (client_registration_id, principal_name)" tooltip="기본키에 세션 id 가 없어 둘이 같은 행을 쓴다. | Evidence: L776-L781" 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="380.0" y="124.5" width="300.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<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 id="n_leftover" value="로그아웃 후 남는 것<br/>평문 refresh token" tooltip="Redis 세션은 0 키인데 여기는 1 행이 남는다. | Evidence: L789-L794" 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="840.0" y="124.5" width="150.0" height="71.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="193.0" y="144.0" as="offset"/>
|
||||
<mxPoint x="324.0" y="121.5" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="307.0" y="144.0" as="offset"/>
|
||||
<mxPoint x="324.0" y="198.5" 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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="274.0" y="365.0" as="offset"/>
|
||||
<mxPoint x="760.0" y="132.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-s1-t",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 124.0,
|
||||
"width": 96.0,
|
||||
"height": 96.0,
|
||||
"x": 220.0,
|
||||
"y": 92.0,
|
||||
"width": 160.0,
|
||||
"height": 59.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -36,16 +36,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
48.0
|
||||
80.0,
|
||||
59.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
96.0
|
||||
160.0,
|
||||
59.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-s1-t",
|
||||
"type": "text",
|
||||
"x": 145.0,
|
||||
"y": 132.0,
|
||||
"x": 276.0,
|
||||
"y": 109.5,
|
||||
"width": 96,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-s2-t",
|
||||
"type": "arrow",
|
||||
"x": 259.0,
|
||||
"y": 124.0,
|
||||
"width": 96.0,
|
||||
"height": 96.0,
|
||||
"x": 220.0,
|
||||
"y": 169.0,
|
||||
"width": 160.0,
|
||||
"height": 59.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
96.0,
|
||||
0.0,
|
||||
59.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
59.0
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-s2-t",
|
||||
"type": "text",
|
||||
"x": 262.0,
|
||||
"y": 132.0,
|
||||
"x": 279.0,
|
||||
"y": 186.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-t-l",
|
||||
"type": "arrow",
|
||||
"x": 250.0,
|
||||
"y": 317.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 680.0,
|
||||
"y": 160.0,
|
||||
"width": 160.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -234,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-t-l",
|
||||
"type": "text",
|
||||
"x": 229.0,
|
||||
"y": 353.0,
|
||||
"x": 715.0,
|
||||
"y": 120.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-s2",
|
||||
"type": "rectangle",
|
||||
"x": 280.0,
|
||||
"y": 60.0,
|
||||
"x": 70.0,
|
||||
"y": 196.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-s2",
|
||||
"type": "text",
|
||||
"x": 290.0,
|
||||
"y": 70.0,
|
||||
"x": 80.0,
|
||||
"y": 206.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
@@ -441,10 +441,10 @@
|
||||
{
|
||||
"id": "node-token-row",
|
||||
"type": "rectangle",
|
||||
"x": 100.0,
|
||||
"y": 220.0,
|
||||
"x": 380.0,
|
||||
"y": 124.5,
|
||||
"width": 300.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -471,10 +471,10 @@
|
||||
{
|
||||
"id": "node-label-token-row",
|
||||
"type": "text",
|
||||
"x": 110.0,
|
||||
"y": 230.0,
|
||||
"x": 390.0,
|
||||
"y": 134.5,
|
||||
"width": 280.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -510,10 +510,10 @@
|
||||
{
|
||||
"id": "node-leftover",
|
||||
"type": "rectangle",
|
||||
"x": 175.0,
|
||||
"y": 413.0,
|
||||
"x": 840.0,
|
||||
"y": 124.5,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-label-leftover",
|
||||
"type": "text",
|
||||
"x": 185.0,
|
||||
"y": 423.0,
|
||||
"x": 850.0,
|
||||
"y": 134.5,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b2-primary-key-overwrite",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "88795858ea78e4c8a0f6fca3a6131c36883a2378246384b83b9d31b636a7b5a4",
|
||||
"spec_sha256": "aff371821f0477e1dead5fdd7bf81eb08ffd8344c331d23f0fdcfca8d263eb11",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-2 · 저장소를 나눠 풀자 다른 두 문제가 남았다",
|
||||
"line": 365
|
||||
"line": 771
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%% 덮어쓰기를 만드는 기본키
|
||||
%% question: 같은 사용자의 두 브라우저가 서로의 토큰을 지우는 이유는 무엇인가
|
||||
flowchart TB
|
||||
flowchart LR
|
||||
n0["브라우저 A 세션"]
|
||||
n1["브라우저 B 세션"]
|
||||
n2[("토큰 행")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="555" viewBox="0 0 680 555" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1035" height="305" viewBox="0 0 1035 305" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">덮어쓰기를 만드는 기본키</title>
|
||||
<desc id="diagram-description">토큰을 PostgreSQL 로 옮겨 다중 인스턴스 문제는 풀렸다. 그러나 기본키가 client_registration_id 와 principal_name 의 조합이고 세션 id 가 들어 있지 않다. 같은 사용자의 두 세션이 같은 행을 쓰므로 나중 로그인이 앞의 토큰을 덮어쓴다. 그리고 로그아웃하면 Redis 세션은 0 키로 정리되지만 PostgreSQL 에는 평문 refresh token 이 한 행 그대로 남는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b2-primary-key-overwrite","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"B-2 · 저장소를 나눠 풀자 다른 두 문제가 남았다","line":365}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b2-primary-key-overwrite","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-2 · 저장소를 나눠 풀자 다른 두 문제가 남았다","line":771}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,34 +49,34 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</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="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="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="238.2" y="351.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="274.0" y="366.0">로그아웃 미정리</text>
|
||||
<rect class="canvas" width="1035" height="305" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="220.0,92.0 300.0,92.0 300.0,151.0 380.0,151.0" data-evidence="776-781" />
|
||||
<rect class="edge-label-bg" x="274.8" y="107.5" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="324.0" y="122.5">principal 이름</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="220.0,228.0 300.0,228.0 300.0,169.0 380.0,169.0" data-evidence="776-781" />
|
||||
<rect class="edge-label-bg" x="278.1" y="184.5" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="324.0" y="199.5">같은 키 · 덮어쓰기</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="680.0,160.0 760.0,160.0 760.0,160.0 840.0,160.0" data-evidence="789-794" />
|
||||
<rect class="edge-label-bg" x="724.2" y="118.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="760.0" y="133.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" />
|
||||
<rect class="node-shape kind-component emphasis-primary role-source" data-evidence="745-747" 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>
|
||||
</g>
|
||||
<g id="node-s2">
|
||||
<rect class="node-shape kind-component emphasis-primary role-source" data-evidence="360-366" x="280.0" y="60.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="355.0" y="90.0">브라우저 B 세션</text>
|
||||
<rect class="node-shape kind-component emphasis-primary role-source" data-evidence="745-747" x="70.0" y="196.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="226.0">브라우저 B 세션</text>
|
||||
</g>
|
||||
<g id="node-token-row">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="370-377" x="100.0" y="233.0" width="300.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="250.0" cy="233.0" rx="150.0" ry="13.0" /><path class="storage-bottom" d="M 100.0 304.0 A 150.0 13.0 0 0 0 400.0 304.0" />
|
||||
<text class="node-label" x="250.0" y="260.0">토큰 행</text>
|
||||
<line class="node-detail-divider" x1="114.0" y1="281.0" x2="386.0" y2="281.0" />
|
||||
<text class="node-detail" x="116.0" y="298.0">PRIMARY KEY (client_registration_id, principal_name)</text>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="776-781" x="380.0" y="136.3" width="300.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="530.0" cy="136.3" rx="150.0" ry="11.8" /><path class="storage-bottom" d="M 380.0 183.7 A 150.0 11.8 0 0 0 680.0 183.7" />
|
||||
<text class="node-label" x="530.0" y="151.5">토큰 행</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="172.5" x2="666.0" y2="172.5" />
|
||||
<text class="node-detail" x="396.0" y="189.5">PRIMARY KEY (client_registration_id, principal_name)</text>
|
||||
</g>
|
||||
<g id="node-leftover">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="378-384" x="175.0" y="426.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="250.0" cy="426.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 175.0 497.0 A 75.0 13.0 0 0 0 325.0 497.0" />
|
||||
<text class="node-label" x="250.0" y="453.0">로그아웃 후 남는 것</text>
|
||||
<line class="node-detail-divider" x1="189.0" y1="474.0" x2="311.0" y2="474.0" />
|
||||
<text class="node-detail" x="191.0" y="491.0">평문 refresh token</text>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="789-794" x="840.0" y="136.3" width="150.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="915.0" cy="136.3" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 840.0 183.7 A 75.0 11.8 0 0 0 990.0 183.7" />
|
||||
<text class="node-label" x="915.0" y="151.5">로그아웃 후 남는 것</text>
|
||||
<line class="node-detail-divider" x1="854.0" y1="172.5" x2="976.0" y2="172.5" />
|
||||
<text class="node-detail" x="856.0" y="189.5">평문 refresh token</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -10,13 +10,13 @@ revokeRefreshToken 을 켜고 refreshTokenMaxReuse 를 0 으로 둔 상태에서
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **동시 refresh 5건** (actor): 같은 refresh token 을 쓴다. Evidence: L369–L375.
|
||||
- **회전 검사** (process): 이미 쓴 토큰인지 본다. Evidence: L369–L377.
|
||||
- **client session** (component): 경쟁이 감지되면 지워진다. Evidence: L378–L384.
|
||||
- **이긴 요청의 새 토큰** (component): 세션이 지워져 쓸 수 없다. Evidence: L378–L384.
|
||||
- **동시 refresh 5건** (actor): 같은 refresh token 을 쓴다. Evidence: L776–L781.
|
||||
- **회전 검사** (process): 이미 쓴 토큰인지 본다. Evidence: L776–L781.
|
||||
- **client session** (component): 경쟁이 감지되면 지워진다. Evidence: L789–L794.
|
||||
- **이긴 요청의 새 토큰** (component): 세션이 지워져 쓸 수 없다. Evidence: L789–L794.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **동시 refresh 5건 → 회전 검사:** 동시 도착. Evidence: L369–L377.
|
||||
- **client session → 이긴 요청의 새 토큰:** 사용 불가. Evidence: L378–L384.
|
||||
- **회전 검사 → client session:** 경쟁 감지 시 삭제. Evidence: L378–L384.
|
||||
- **동시 refresh 5건 → 회전 검사:** 동시 도착. Evidence: L776–L781.
|
||||
- **client session → 이긴 요청의 새 토큰:** 사용 불가. Evidence: L789–L794.
|
||||
- **회전 검사 → client session:** 경쟁 감지 시 삭제. Evidence: L789–L794.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="b3-rotation-contention" name="회전 경쟁에서 이긴 요청도 진다">
|
||||
<mxGraphModel dx="680" dy="676" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1334" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1334" math="0" shadow="0">
|
||||
<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="145.0" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_concurrent" value="동시 refresh 5건<br/>& + wait 필요" tooltip="같은 refresh token 을 쓴다. | Evidence: L776-L781" 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="35.0" y="145.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"/>
|
||||
<mxCell id="n_rotation" value="회전 검사<br/>revokeRefreshToken=true · refreshTokenMaxReuse=0" tooltip="이미 쓴 토큰인지 본다. | Evidence: L776-L781" 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="41.5" y="455.0" width="300.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_client-session" value="client session" tooltip="경쟁이 감지되면 지워진다. | Evidence: L378-L384" 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="145.0" y="407.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_client-session" value="client session" tooltip="경쟁이 감지되면 지워진다. | Evidence: L789-L794" 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="45.0" y="915.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_new-token" value="이긴 요청의 새 토큰" tooltip="세션이 지워져 쓸 수 없다. | Evidence: L378-L384" 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="145.0" y="567.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_new-token" value="이긴 요청의 새 토큰" tooltip="세션이 지워져 쓸 수 없다. | Evidence: L789-L794" 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="45.0" y="1225.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_c-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_concurrent" target="n_rotation">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="244.0" y="192.0" as="offset"/>
|
||||
<mxPoint x="150.8" y="314.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">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="244.0" y="519.0" as="offset"/>
|
||||
<mxPoint x="144.0" y="1102.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-cs" value="경쟁 감지 시 삭제" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_rotation" target="n_client-session">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="244.0" y="359.0" as="offset"/>
|
||||
<mxPoint x="155.8" y="692.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-r",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 229.0,
|
||||
"width": 81.5,
|
||||
"height": 226.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
81.5,
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
81.5,
|
||||
226.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-r",
|
||||
"type": "text",
|
||||
"x": 199.0,
|
||||
"y": 180.0,
|
||||
"x": 105.75,
|
||||
"y": 302.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-cs-nt",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 471.0,
|
||||
"x": 120.0,
|
||||
"y": 979.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 246.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -136,15 +136,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
123.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
123.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
246.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-cs-nt",
|
||||
"type": "text",
|
||||
"x": 199.0,
|
||||
"y": 507.0,
|
||||
"x": 99.0,
|
||||
"y": 1090.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-r-cs",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 120.0,
|
||||
"y": 526.0,
|
||||
"width": 71.5,
|
||||
"height": 389.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -230,20 +230,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
71.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
71.5,
|
||||
194.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
194.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
389.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-r-cs",
|
||||
"type": "text",
|
||||
"x": 199.0,
|
||||
"y": 347.0,
|
||||
"x": 110.75,
|
||||
"y": 680.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-concurrent",
|
||||
"type": "rectangle",
|
||||
"x": 145.0,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-concurrent",
|
||||
"type": "text",
|
||||
"x": 155.0,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-rotation",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"x": 41.5,
|
||||
"y": 455.0,
|
||||
"width": 300.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-rotation",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"x": 51.5,
|
||||
"y": 465.0,
|
||||
"width": 280.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-client-session",
|
||||
"type": "rectangle",
|
||||
"x": 145.0,
|
||||
"y": 407.0,
|
||||
"x": 45.0,
|
||||
"y": 915.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-client-session",
|
||||
"type": "text",
|
||||
"x": 155.0,
|
||||
"y": 417.0,
|
||||
"x": 55.0,
|
||||
"y": 925.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-new-token",
|
||||
"type": "rectangle",
|
||||
"x": 145.0,
|
||||
"y": 567.0,
|
||||
"x": 45.0,
|
||||
"y": 1225.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-new-token",
|
||||
"type": "text",
|
||||
"x": 155.0,
|
||||
"y": 577.0,
|
||||
"x": 55.0,
|
||||
"y": 1235.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b3-rotation-contention",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "31779474e5400d5e719faf81a62da35a657994d952e4e0324a1c76c5d18183e4",
|
||||
"spec_sha256": "511db5ddd2312ac2ee0eff68694683e9de62a6ced55f2ba9a828abff6f952f72",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-3 · Refresh Token Rotation 경쟁 (Q2)",
|
||||
"line": 391
|
||||
"line": 806
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"b3-rotation-contention.excalidraw",
|
||||
"b3-rotation-contention.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="676" viewBox="0 0 680 676" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1334" viewBox="0 0 680 1334" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">회전 경쟁에서 이긴 요청도 진다</title>
|
||||
<desc id="diagram-description">revokeRefreshToken 을 켜고 refreshTokenMaxReuse 를 0 으로 둔 상태에서 같은 refresh token 으로 동시에 5건을 보냈다. 순차로 돌리면 재현되지 않으며 백그라운드로 띄우고 wait 해야 경합이 생긴다. 이긴 요청이 받은 새 토큰조차 쓸 수 없었다. Keycloak 이 경쟁을 감지하면 client session 을 지우기 때문이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b3-rotation-contention","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"B-3 · Refresh Token Rotation 경쟁 (Q2)","line":391}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b3-rotation-contention","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-3 · Refresh Token Rotation 경쟁 (Q2)","line":806}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,32 +49,32 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="676" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,144.0 220.0,192.0 220.0,192.0 220.0,240.0" data-evidence="369-377" />
|
||||
<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="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>
|
||||
<rect class="canvas" width="680" height="1334" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,229.0 110.0,342.0 191.5,342.0 191.5,455.0" data-evidence="776-781" />
|
||||
<rect class="edge-label-bg" x="125.0" y="300.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="150.8" y="315.0">동시 도착</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="120.0,979.0 120.0,1102.0 120.0,1102.0 120.0,1225.0" data-evidence="789-794" />
|
||||
<rect class="edge-label-bg" x="118.2" y="1088.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="144.0" y="1103.0">사용 불가</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="191.5,526.0 191.5,720.5 120.0,720.5 120.0,915.0" data-evidence="789-794" />
|
||||
<rect class="edge-label-bg" x="113.2" y="678.5" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="155.8" y="693.5">경쟁 감지 시 삭제</text>
|
||||
<g id="node-concurrent">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="369-375"><circle cx="220.0" cy="80.0" r="11.0" /><line x1="220.0" y1="96.0" x2="220.0" y2="105.0" /><line x1="202.0" y1="106.0" x2="238.0" y2="106.0" /><line x1="220.0" y1="105.0" x2="205.0" y2="122.0" /><line x1="220.0" y1="105.0" x2="235.0" y2="122.0" /></g>
|
||||
<text class="node-label" x="220.0" y="137.0">동시 refresh 5건</text>
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="776-781"><circle cx="110.0" cy="165.0" r="11.0" /><line x1="110.0" y1="181.0" x2="110.0" y2="200.0" /><line x1="92.0" y1="191.0" x2="128.0" y2="191.0" /><line x1="110.0" y1="200.0" x2="95.0" y2="217.0" /><line x1="110.0" y1="200.0" x2="125.0" y2="217.0" /></g>
|
||||
<text class="node-label" x="110.0" y="222.0">동시 refresh 5건</text>
|
||||
</g>
|
||||
<g id="node-rotation">
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="369-377" x="70.0" y="240.0" width="300.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="220.0" y="267.0">회전 검사</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="356.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">revokeRefreshToken=true · refreshTokenMaxReuse=0</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="776-781" x="41.5" y="455.0" width="300.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="191.5" y="482.0">회전 검사</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="503.0" x2="327.5" y2="503.0" />
|
||||
<text class="node-detail" x="57.5" y="520.0">revokeRefreshToken=true · refreshTokenMaxReuse=0</text>
|
||||
</g>
|
||||
<g id="node-client-session">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="378-384" x="145.0" y="407.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="220.0" y="437.0">client session</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="789-794" x="45.0" y="915.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="120.0" y="945.0">client session</text>
|
||||
</g>
|
||||
<g id="node-new-token">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="378-384" x="145.0" y="567.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="220.0" y="597.0">이긴 요청의 새 토큰</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="789-794" x="45.0" y="1225.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="120.0" y="1255.0">이긴 요청의 새 토큰</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -1,22 +1,22 @@
|
||||
# 지우지 않으면 통과한다
|
||||
# 헤더 도착과 인가 우회는 다르다
|
||||
|
||||
## Alternative text
|
||||
|
||||
밖에서 들어온 위조 헤더가 프록시를 그대로 통과해 앱에 닿는 구성. 프록시가 그 이름을 설정할 때만 덮어쓴다.
|
||||
외부 위조 헤더가 nginx 를 지나 permitAll echo 에서는 200으로 도착하지만 JWT 보호 경로에서는 401로 막히는 분기.
|
||||
|
||||
## Long description
|
||||
|
||||
nginx 는 자기가 proxy_set_header 로 설정한 헤더만 덮어쓴다. 설정하지 않은 이름은 클라이언트가 보낸 값이 그대로 지나간다. 그래서 X-Auth-Request-Roles 같은 헤더를 앱이 믿으려면 프록시가 그 이름을 먼저 빈 값으로 지워야 한다. 그리고 IdP 에서 클레임을 바꿔도 반영되지 않는다. 12회 요청 6초 동안 옛 값이 갔고 세션을 지워 재인증한 뒤에야 새 값이 왔다. 세션은 로그인 시점의 스냅샷이다.
|
||||
위조 헤더는 app1.hyeonworks.com/api 의 permitAll echo 앱까지 그대로 도착했다. 같은 헤더로 JWT 를 요구하는 /api/me 와 /api/protected 를 호출하면 401이었다. 따라서 헤더 도착과 인가 우회는 다른 사건이다. proxy_set_header 로 먼저 지우는 것은 문서에 적힌 처방이지만 이 실험대에서는 적용하지 않아 미검증이다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **외부 위조 헤더** (actor): 밖에서 들어온 요청이 앱이 믿는 헤더 이름을 그대로 쓴다. Evidence: L393–L400.
|
||||
- **nginx** (gateway): 설정하지 않은 이름은 덮어쓰지 않는다. Evidence: L393–L400.
|
||||
- **oauth2-proxy** (gateway): 인증 결과를 헤더로 넣는다. Evidence: L393–L400.
|
||||
- **앱** (service): 헤더를 믿고 인가한다. Evidence: L401–L408.
|
||||
- **외부 위조 헤더** (actor): 앱이 신뢰할 수 있는 이름과 같은 헤더를 보낸다. Evidence: L823–L830.
|
||||
- **nginx** (gateway): 설정하지 않은 동명 헤더를 덮어쓰지 않는다. Evidence: L829–L830.
|
||||
- **permitAll echo** (service): 위조 헤더가 도착하는 것을 관측한 경로다. Evidence: L825–L840.
|
||||
- **JWT 보호 경로** (service): 같은 위조 헤더만으로는 통과하지 못했다. Evidence: L832–L840.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **외부 위조 헤더 → nginx:** 위조 헤더. Evidence: L393–L400.
|
||||
- **nginx → oauth2-proxy:** 미삭제 시 통과. Evidence: L393–L400.
|
||||
- **oauth2-proxy → 앱:** 인가 헤더. Evidence: L393–L408.
|
||||
- **외부 위조 헤더 → nginx:** 위조 헤더. Evidence: L823–L830.
|
||||
- **nginx → permitAll echo:** 그대로 전달. Evidence: L825–L840.
|
||||
- **nginx → JWT 보호 경로:** 같은 헤더. Evidence: L832–L840.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# 지우지 않으면 통과한다
|
||||
# Question: Edge 가 넣어주는 인가 헤더를 앱이 믿어도 되는가
|
||||
direction: down
|
||||
# 헤더 도착과 인가 우회는 다르다
|
||||
# Question: 같은 위조 헤더가 도착하는 것과 인가를 우회하는 것은 같은가
|
||||
direction: right
|
||||
n0: "외부 위조 헤더" {
|
||||
shape: person
|
||||
}
|
||||
n1: "nginx" {
|
||||
shape: diamond
|
||||
}
|
||||
n2: "oauth2-proxy" {
|
||||
shape: diamond
|
||||
n2: "permitAll echo" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "앱" {
|
||||
n3: "JWT 보호 경로" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "위조 헤더"
|
||||
n1 -> n2: "미삭제 시 통과"
|
||||
n2 -> n3: "인가 헤더"
|
||||
n1 -> n2: "그대로 전달"
|
||||
n1 -> n3: "같은 헤더"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="외부 위조 헤더", shape=box, style="rounded,dashed,filled"];
|
||||
n1 [label="nginx", shape=diamond, style="rounded,filled"];
|
||||
n2 [label="oauth2-proxy", shape=diamond, style="rounded,filled"];
|
||||
n3 [label="앱", shape=box, style="rounded,filled"];
|
||||
n2 [label="permitAll echo", shape=box, style="rounded,filled"];
|
||||
n3 [label="JWT 보호 경로", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="위조 헤더", style=solid];
|
||||
n1 -> n2 [label="미삭제 시 통과", style=solid];
|
||||
n2 -> n3 [label="인가 헤더", style=solid];
|
||||
n1 -> n2 [label="그대로 전달", style=solid];
|
||||
n1 -> n3 [label="같은 헤더", style=solid];
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="b4-header-trust-boundary" name="지우지 않으면 통과한다">
|
||||
<mxGraphModel dx="680" dy="690" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<diagram id="b4-header-trust-boundary" name="헤더 도착과 인가 우회는 다르다">
|
||||
<mxGraphModel dx="999" dy="319" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="999" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<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="82.5" y="60.0" width="170.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_attacker" value="외부 위조 헤더<br/>X-Auth-Request-Roles" tooltip="앱이 신뢰할 수 있는 이름과 같은 헤더를 보낸다. | Evidence: L823-L830" 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="70.0" y="125.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="195.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_nginx" value="nginx<br/>동명 헤더 미삭제" tooltip="설정하지 않은 동명 헤더를 덮어쓰지 않는다. | Evidence: L829-L830" 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="400.0" y="131.5" width="150.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="87.5" y="407.0" width="160.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_echo" value="permitAll echo<br/>/api/echo · HTTP 200" tooltip="위조 헤더가 도착하는 것을 관측한 경로다. | Evidence: L825-L840" 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="745.0" y="60.0" width="174.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="92.5" y="574.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_protected" value="JWT 보호 경로<br/>/api/me · /api/protected · 401" tooltip="같은 위조 헤더만으로는 통과하지 못했다. | Evidence: L832-L840" 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="710.0" y="203.0" width="244.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="191.5" y="192.0" as="offset"/>
|
||||
<mxPoint x="320.0" y="139.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-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_nginx" target="n_echo">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="359.0" as="offset"/>
|
||||
<mxPoint x="671.5" y="126.8" 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">
|
||||
<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_protected">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="191.5" y="526.0" as="offset"/>
|
||||
<mxPoint x="654.0" y="207.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-a-n",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 240.0,
|
||||
"y": 167.0,
|
||||
"width": 160.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -36,16 +36,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-a-n",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 180.0,
|
||||
"x": 275.0,
|
||||
"y": 127.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -102,13 +102,112 @@
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-n-e",
|
||||
"type": "arrow",
|
||||
"x": 550.0,
|
||||
"y": 95.5,
|
||||
"width": 195.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 148101747,
|
||||
"version": 1,
|
||||
"versionNonce": 318567733,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
97.5,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
97.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
195.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-nginx",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-echo",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-n-e",
|
||||
"type": "text",
|
||||
"x": 626.5,
|
||||
"y": 114.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 934715894,
|
||||
"version": 1,
|
||||
"versionNonce": 426907020,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "그대로 전달",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "그대로 전달",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-n-p",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 550.0,
|
||||
"y": 176.0,
|
||||
"width": 160.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -135,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
62.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -154,7 +253,7 @@
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-proxy",
|
||||
"elementId": "node-protected",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
@@ -165,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-n-p",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 347.0,
|
||||
"x": 609.0,
|
||||
"y": 195.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -193,118 +292,19 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "미삭제 시 통과",
|
||||
"text": "같은 헤더",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "미삭제 시 통과",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-p-a",
|
||||
"type": "arrow",
|
||||
"x": 167.5,
|
||||
"y": 478.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 579930019,
|
||||
"version": 1,
|
||||
"versionNonce": 141204879,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-proxy",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-app",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-p-a",
|
||||
"type": "text",
|
||||
"x": 146.5,
|
||||
"y": 514.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1931736220,
|
||||
"version": 1,
|
||||
"versionNonce": 15055194,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "인가 헤더",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "인가 헤더",
|
||||
"originalText": "같은 헤더",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-attacker",
|
||||
"type": "rectangle",
|
||||
"x": 82.5,
|
||||
"y": 60.0,
|
||||
"x": 70.0,
|
||||
"y": 125.0,
|
||||
"width": 170.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-attacker",
|
||||
"type": "text",
|
||||
"x": 92.5,
|
||||
"y": 70.0,
|
||||
"x": 80.0,
|
||||
"y": 135.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,9 +372,9 @@
|
||||
{
|
||||
"id": "node-nginx",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"width": 195.0,
|
||||
"x": 400.0,
|
||||
"y": 131.5,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -402,9 +402,9 @@
|
||||
{
|
||||
"id": "node-label-nginx",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"width": 175.0,
|
||||
"x": 410.0,
|
||||
"y": 141.5,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -430,89 +430,20 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "nginx\nproxy_set_header ... \"\"",
|
||||
"text": "nginx\n동명 헤더 미삭제",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "nginx\nproxy_set_header ... \"\"",
|
||||
"originalText": "nginx\n동명 헤더 미삭제",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-proxy",
|
||||
"id": "node-echo",
|
||||
"type": "rectangle",
|
||||
"x": 87.5,
|
||||
"y": 407.0,
|
||||
"width": 160.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#fff4e6",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 254112210,
|
||||
"version": 1,
|
||||
"versionNonce": 42167050,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-proxy",
|
||||
"type": "text",
|
||||
"x": 97.5,
|
||||
"y": 417.0,
|
||||
"width": 140.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 418528669,
|
||||
"version": 1,
|
||||
"versionNonce": 1299542283,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "oauth2-proxy\n--set-xauthrequest",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "oauth2-proxy\n--set-xauthrequest",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-app",
|
||||
"type": "rectangle",
|
||||
"x": 92.5,
|
||||
"y": 574.0,
|
||||
"width": 150.0,
|
||||
"x": 745.0,
|
||||
"y": 60.0,
|
||||
"width": 174.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -528,9 +459,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 594277328,
|
||||
"seed": 625814709,
|
||||
"version": 1,
|
||||
"versionNonce": 293358427,
|
||||
"versionNonce": 210846281,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -538,11 +469,11 @@
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-app",
|
||||
"id": "node-label-echo",
|
||||
"type": "text",
|
||||
"x": 102.5,
|
||||
"y": 584.0,
|
||||
"width": 130.0,
|
||||
"x": 755.0,
|
||||
"y": 70.0,
|
||||
"width": 154.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -558,9 +489,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1055843055,
|
||||
"seed": 1277422196,
|
||||
"version": 1,
|
||||
"versionNonce": 1025494149,
|
||||
"versionNonce": 1238585544,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -568,11 +499,80 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "앱\n로그인 시점 스냅샷",
|
||||
"text": "permitAll echo\n/api/echo · HTTP 200",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "앱\n로그인 시점 스냅샷",
|
||||
"originalText": "permitAll echo\n/api/echo · HTTP 200",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-protected",
|
||||
"type": "rectangle",
|
||||
"x": 710.0,
|
||||
"y": 203.0,
|
||||
"width": 244.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1165546669,
|
||||
"version": 1,
|
||||
"versionNonce": 1511744997,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-protected",
|
||||
"type": "text",
|
||||
"x": 720.0,
|
||||
"y": 213.0,
|
||||
"width": 224.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 101710672,
|
||||
"version": 1,
|
||||
"versionNonce": 1663076746,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "JWT 보호 경로\n/api/me · /api/protected · 401",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "JWT 보호 경로\n/api/me · /api/protected · 401",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b4-header-trust-boundary",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "ad0e26787a9e508ddce08e196ceeaacf2dfe1ef2caa047537f4355b5e34739a1",
|
||||
"spec_sha256": "e395b6fd6e149bcaf143b2a8c71685720ecc099f79a443dd98690f2b19aae141",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-4 · Edge 인가의 범위 (Q4)",
|
||||
"line": 401
|
||||
"line": 821
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"b4-header-trust-boundary.svg",
|
||||
"b4-header-trust-boundary.drawio",
|
||||
"b4-header-trust-boundary.mmd",
|
||||
"b4-header-trust-boundary.d2",
|
||||
"b4-header-trust-boundary.dot",
|
||||
"b4-header-trust-boundary.drawio",
|
||||
"b4-header-trust-boundary.excalidraw",
|
||||
"b4-header-trust-boundary.alt.md"
|
||||
],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
%% 지우지 않으면 통과한다
|
||||
%% question: Edge 가 넣어주는 인가 헤더를 앱이 믿어도 되는가
|
||||
flowchart TB
|
||||
%% 헤더 도착과 인가 우회는 다르다
|
||||
%% question: 같은 위조 헤더가 도착하는 것과 인가를 우회하는 것은 같은가
|
||||
flowchart LR
|
||||
n0(["외부 위조 헤더"])
|
||||
n1{"nginx"}
|
||||
n2{"oauth2-proxy"}
|
||||
n3["앱"]
|
||||
n2["permitAll echo"]
|
||||
n3["JWT 보호 경로"]
|
||||
n0 -->|"위조 헤더"| n1
|
||||
n1 -->|"미삭제 시 통과"| n2
|
||||
n2 -->|"인가 헤더"| n3
|
||||
n1 -->|"그대로 전달"| n2
|
||||
n1 -->|"같은 헤더"| n3
|
||||
classDef external stroke-dasharray: 6 4
|
||||
class n0 external
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="690" viewBox="0 0 680 690" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">지우지 않으면 통과한다</title>
|
||||
<desc id="diagram-description">nginx 는 자기가 proxy_set_header 로 설정한 헤더만 덮어쓴다. 설정하지 않은 이름은 클라이언트가 보낸 값이 그대로 지나간다. 그래서 X-Auth-Request-Roles 같은 헤더를 앱이 믿으려면 프록시가 그 이름을 먼저 빈 값으로 지워야 한다. 그리고 IdP 에서 클레임을 바꿔도 반영되지 않는다. 12회 요청 6초 동안 옛 값이 갔고 세션을 지워 재인증한 뒤에야 새 값이 왔다. 세션은 로그인 시점의 스냅샷이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b4-header-trust-boundary","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"B-4 · Edge 인가의 범위 (Q4)","line":401}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="999" height="319" viewBox="0 0 999 319" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">헤더 도착과 인가 우회는 다르다</title>
|
||||
<desc id="diagram-description">위조 헤더는 app1.hyeonworks.com/api 의 permitAll echo 앱까지 그대로 도착했다. 같은 헤더로 JWT 를 요구하는 /api/me 와 /api/protected 를 호출하면 401이었다. 따라서 헤더 도착과 인가 우회는 다른 사건이다. proxy_set_header 로 먼저 지우는 것은 문서에 적힌 처방이지만 이 실험대에서는 적용하지 않아 미검증이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b4-header-trust-boundary","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-4 · Edge 인가의 범위 (Q4)","line":821}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="690" />
|
||||
<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>
|
||||
<rect class="canvas" width="999" height="319" />
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="240.0,167.0 320.0,167.0 320.0,167.0 400.0,167.0" data-evidence="823-830" />
|
||||
<rect class="edge-label-bg" x="294.2" y="125.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="320.0" y="140.0">위조 헤더</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="550.0,158.0 647.5,158.0 647.5,95.5 745.0,95.5" data-evidence="825-840" />
|
||||
<rect class="edge-label-bg" x="642.4" y="112.8" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="671.5" y="127.8">그대로 전달</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="550.0,176.0 630.0,176.0 630.0,238.5 710.0,238.5" data-evidence="832-840" />
|
||||
<rect class="edge-label-bg" x="628.2" y="193.2" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="654.0" y="208.2">같은 헤더</text>
|
||||
<g id="node-attacker">
|
||||
<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 class="actor-symbol emphasis-warning" data-evidence="823-830"><circle cx="155.0" cy="145.0" r="11.0" /><line x1="155.0" y1="161.0" x2="155.0" y2="180.0" /><line x1="137.0" y1="171.0" x2="173.0" y2="171.0" /><line x1="155.0" y1="180.0" x2="140.0" y2="197.0" /><line x1="155.0" y1="180.0" x2="170.0" y2="197.0" /></g>
|
||||
<text class="node-label" x="155.0" y="202.0">외부 위조 헤더</text>
|
||||
</g>
|
||||
<g id="node-nginx">
|
||||
<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>
|
||||
<polygon class="node-shape kind-gateway emphasis-warning role-control" data-evidence="829-830" points="475.0,131.5 550.0,167.0 475.0,202.5 400.0,167.0" />
|
||||
<text class="node-label" x="475.0" y="158.5">nginx</text>
|
||||
<line class="node-detail-divider" x1="414.0" y1="179.5" x2="536.0" y2="179.5" />
|
||||
<text class="node-detail" x="416.0" y="196.5">동명 헤더 미삭제</text>
|
||||
</g>
|
||||
<g id="node-proxy">
|
||||
<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 id="node-echo">
|
||||
<rect class="node-shape kind-service emphasis-normal role-target" data-evidence="825-840" x="745.0" y="60.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="832.0" y="87.0">permitAll echo</text>
|
||||
<line class="node-detail-divider" x1="759.0" y1="108.0" x2="905.0" y2="108.0" />
|
||||
<text class="node-detail" x="761.0" y="125.0">/api/echo · HTTP 200</text>
|
||||
</g>
|
||||
<g id="node-app">
|
||||
<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 id="node-protected">
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="832-840" x="710.0" y="203.0" width="244.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="832.0" y="230.0">JWT 보호 경로</text>
|
||||
<line class="node-detail-divider" x1="724.0" y1="251.0" x2="940.0" y2="251.0" />
|
||||
<text class="node-detail" x="726.0" y="268.0">/api/me · /api/protected · 401</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -1,22 +1,24 @@
|
||||
# 볼륨 없는 영속화와 유예 없는 회전
|
||||
# 볼륨 없는 영속화와 replica별 키 회전
|
||||
|
||||
## Alternative text
|
||||
|
||||
Redis 의 데이터 디렉터리가 컨테이너 파일시스템일 때 영속화 설정이 무의미해지는 구성과, 새 kid 를 만난 검증기가 JWKS 를 재조회하는 구성.
|
||||
운영 변경에서 Redis AOF는 볼륨 없는 데이터 디렉터리로 이어져 소멸하고, realm 키 회전은 replica별 JWKS cache 차이로 401과 200이 갈리는 두 경로.
|
||||
|
||||
## Long description
|
||||
|
||||
redis-cli config set appendonly yes 를 켜도 아무것도 달라지지 않았다. /data 가 컨테이너 파일시스템이라 컨테이너와 함께 죽기 때문이다. appendonlydir 이 만들어졌다가 그대로 버려진다. 볼륨 없는 영속화 설정은 장식이다. realm 키를 회전했을 때는 JWKS 캐시의 유예 구간을 기대했는데 없었다. NimbusJwtDecoder 는 모르는 kid 를 만나면 JWKS 를 다시 가져온다.
|
||||
B-5에서는 appendonly yes 설정이 적용돼도 /data가 컨테이너 파일시스템이라 재시작 때 사라졌다. B-6에서는 옛 키를 지운 뒤 같은 토큰을 반복 요청하자 401과 200이 교대로 나왔다. echo replica 둘의 JWKS cache가 독립적이라 한쪽은 새 목록을 받아 옛 키를 잃었고 다른 쪽은 아직 들고 있었기 때문이다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **appendonly yes** (process): 설정은 적용된다. Evidence: L406–L412.
|
||||
- **/data** (datastore): 컨테이너 파일시스템이다. Evidence: L406–L412.
|
||||
- **컨테이너** (component): 죽으면 /data 도 같이 사라진다. Evidence: L406–L412.
|
||||
- **PersistentVolume** (datastore): 여기 있어야 설정이 뜻을 갖는다. Evidence: L406–L412.
|
||||
- **운영 변경** (process): 저장 설정과 realm 키 회전을 각각 수행한다. Evidence: L861–L872.
|
||||
- **Redis /data** (datastore): AOF가 기록되는 컨테이너 파일시스템이다. Evidence: L861–L863.
|
||||
- **Redis 재시작** (component): 볼륨이 없어 데이터가 함께 사라진다. Evidence: L861–L863.
|
||||
- **echo replica 2개** (service): JWKS cache를 인스턴스마다 따로 가진다. Evidence: L868–L872.
|
||||
- **교차 응답** (component): 같은 옛 토큰의 검증 결과가 replica에 따라 갈린다. Evidence: L868–L877.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **appendonly yes → /data:** AOF 기록. Evidence: L406–L412.
|
||||
- **/data → 컨테이너:** 컨테이너와 함께 소멸. Evidence: L406–L412.
|
||||
- **/data → PersistentVolume:** 볼륨 마운트. Evidence: L406–L412.
|
||||
- **운영 변경 → Redis /data:** AOF 설정. Evidence: L861–L863.
|
||||
- **운영 변경 → echo replica 2개:** 키 회전. Evidence: L865–L872.
|
||||
- **Redis /data → Redis 재시작:** 볼륨 없음. Evidence: L861–L863.
|
||||
- **echo replica 2개 → 교차 응답:** cache 상태. Evidence: L868–L877.
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
# 볼륨 없는 영속화와 유예 없는 회전
|
||||
# Question: 설정만으로 영속화와 무중단 키 교체가 되는가
|
||||
direction: down
|
||||
n0: "appendonly yes" {
|
||||
# 볼륨 없는 영속화와 replica별 키 회전
|
||||
# Question: 설정을 켰다는 사실만으로 영속화와 일관된 키 회전을 기대할 수 있는가
|
||||
direction: right
|
||||
n0: "운영 변경" {
|
||||
shape: rectangle
|
||||
}
|
||||
n1: "/data" {
|
||||
n1: "Redis /data" {
|
||||
shape: cylinder
|
||||
}
|
||||
n2: "컨테이너" {
|
||||
n2: "Redis 재시작" {
|
||||
shape: rectangle
|
||||
}
|
||||
n3: "PersistentVolume" {
|
||||
shape: cylinder
|
||||
n3: "echo replica 2개" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "AOF 기록"
|
||||
n1 -> n2: "컨테이너와 함께 소멸"
|
||||
n1 -> n3: "볼륨 마운트"
|
||||
n4: "교차 응답" {
|
||||
shape: rectangle
|
||||
}
|
||||
n0 -> n1: "AOF 설정"
|
||||
n1 -> n2: "볼륨 없음"
|
||||
n0 -> n3: "키 회전"
|
||||
n3 -> n4: "cache 상태"
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="appendonly yes", shape=box, style="rounded,filled"];
|
||||
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 [label="운영 변경", shape=box, style="rounded,filled"];
|
||||
n1 [label="Redis /data", shape=cylinder, style="rounded,filled"];
|
||||
n2 [label="Redis 재시작", shape=box, style="rounded,filled"];
|
||||
n3 [label="echo replica 2개", shape=box, style="rounded,filled"];
|
||||
n4 [label="교차 응답", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="AOF 설정", style=solid];
|
||||
n1 -> n2 [label="볼륨 없음", style=solid];
|
||||
n0 -> n3 [label="키 회전", style=solid];
|
||||
n3 -> n4 [label="cache 상태", style=solid];
|
||||
}
|
||||
|
||||
@@ -1,35 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="b5-b6-storage-and-keys" name="볼륨 없는 영속화와 유예 없는 회전">
|
||||
<mxGraphModel dx="680" dy="555" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<diagram id="b5-b6-storage-and-keys" name="볼륨 없는 영속화와 replica별 키 회전">
|
||||
<mxGraphModel dx="923" dy="319" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="923" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<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 id="n_change" value="운영 변경<br/>B-5 · B-6" tooltip="저장 설정과 realm 키 회전을 각각 수행한다. | Evidence: L861-L872" 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="131.5" width="150.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="175.0" y="227.0" width="150.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_datadir" value="Redis /data<br/>appendonly yes · 볼륨 없음" tooltip="AOF가 기록되는 컨테이너 파일시스템이다. | Evidence: L861-L863" 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="380.0" y="60.0" width="188.0" height="71.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">
|
||||
<mxGeometry x="70.0" y="429.5" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_replicas" value="echo replica 2개<br/>replica별 cache" tooltip="JWKS cache를 인스턴스마다 따로 가진다. | Evidence: L868-L872" 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="399.0" y="203.0" 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 id="n_lost" value="Redis 재시작<br/>영속화 실패" tooltip="볼륨이 없어 데이터가 함께 사라진다. | Evidence: L861-L863" 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="728.0" y="60.0" width="150.0" height="71.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="n_mixed" value="교차 응답<br/>401 · 200 · 반복" tooltip="같은 옛 토큰의 검증 결과가 replica에 따라 갈린다. | Evidence: L868-L877" 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="728.0" y="203.0" width="150.0" height="71.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_change" target="n_datadir">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="274.0" y="179.0" as="offset"/>
|
||||
<mxPoint x="324.0" y="126.8" 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_c-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_change" target="n_replicas">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="193.0" y="348.8" as="offset"/>
|
||||
<mxPoint x="333.5" y="207.2" 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-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_datadir" target="n_lost">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="307.0" y="344.0" as="offset"/>
|
||||
<mxPoint x="648.0" y="67.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-m" value="cache 상태" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_replicas" target="n_mixed">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="638.5" y="210.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-d",
|
||||
"type": "arrow",
|
||||
"x": 250.0,
|
||||
"y": 131.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 220.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -33,24 +33,24 @@
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-config",
|
||||
"elementId": "node-change",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-d",
|
||||
"type": "text",
|
||||
"x": 229.0,
|
||||
"y": 167.0,
|
||||
"x": 279.0,
|
||||
"y": 114.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -94,21 +94,21 @@
|
||||
"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
|
||||
},
|
||||
{
|
||||
"id": "edge-d-c",
|
||||
"id": "edge-c-r",
|
||||
"type": "arrow",
|
||||
"x": 145.0,
|
||||
"y": 324.0,
|
||||
"width": 96.0,
|
||||
"height": 105.5,
|
||||
"x": 220.0,
|
||||
"y": 176.0,
|
||||
"width": 179.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -121,9 +121,9 @@
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 1710772655,
|
||||
"seed": 1593849987,
|
||||
"version": 1,
|
||||
"versionNonce": 825890023,
|
||||
"versionNonce": 148545859,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -131,30 +131,30 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
96.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
52.75
|
||||
89.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.75
|
||||
89.5,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
105.5
|
||||
179.0,
|
||||
62.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-datadir",
|
||||
"elementId": "node-change",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-container",
|
||||
"elementId": "node-replicas",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
@@ -163,10 +163,10 @@
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-d-c",
|
||||
"id": "edge-label-c-r",
|
||||
"type": "text",
|
||||
"x": 148.0,
|
||||
"y": 336.75,
|
||||
"x": 288.5,
|
||||
"y": 195.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -183,9 +183,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1795748316,
|
||||
"seed": 1032385081,
|
||||
"version": 1,
|
||||
"versionNonce": 1772431002,
|
||||
"versionNonce": 839461234,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -193,21 +193,21 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "컨테이너와 함께 소멸",
|
||||
"text": "키 회전",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "컨테이너와 함께 소멸",
|
||||
"originalText": "키 회전",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "edge-d-v",
|
||||
"id": "edge-d-l",
|
||||
"type": "arrow",
|
||||
"x": 259.0,
|
||||
"y": 324.0,
|
||||
"width": 96.0,
|
||||
"height": 96.0,
|
||||
"x": 568.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -220,9 +220,9 @@
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 1778945409,
|
||||
"seed": 1898062713,
|
||||
"version": 1,
|
||||
"versionNonce": 128234071,
|
||||
"versionNonce": 349178508,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -234,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
96.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -253,7 +253,7 @@
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-volume",
|
||||
"elementId": "node-lost",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
@@ -262,10 +262,10 @@
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-d-v",
|
||||
"id": "edge-label-d-l",
|
||||
"type": "text",
|
||||
"x": 262.0,
|
||||
"y": 332.0,
|
||||
"x": 603.0,
|
||||
"y": 55.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -282,9 +282,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1825891785,
|
||||
"seed": 160907104,
|
||||
"version": 1,
|
||||
"versionNonce": 120899807,
|
||||
"versionNonce": 1439173985,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -292,20 +292,119 @@
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "볼륨 마운트",
|
||||
"text": "볼륨 없음",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "볼륨 마운트",
|
||||
"originalText": "볼륨 없음",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-config",
|
||||
"id": "edge-r-m",
|
||||
"type": "arrow",
|
||||
"x": 549.0,
|
||||
"y": 238.5,
|
||||
"width": 179.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": null,
|
||||
"seed": 566697716,
|
||||
"version": 1,
|
||||
"versionNonce": 531785984,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
89.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
89.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
179.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "node-replicas",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "node-mixed",
|
||||
"focus": 0,
|
||||
"gap": 4
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "edge-label-r-m",
|
||||
"type": "text",
|
||||
"x": 593.5,
|
||||
"y": 198.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1733315186,
|
||||
"version": 1,
|
||||
"versionNonce": 1533067753,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 13,
|
||||
"fontFamily": 5,
|
||||
"text": "cache 상태",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "cache 상태",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-change",
|
||||
"type": "rectangle",
|
||||
"x": 163.0,
|
||||
"y": 60.0,
|
||||
"width": 174.0,
|
||||
"x": 70.0,
|
||||
"y": 131.5,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -321,9 +420,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 736776605,
|
||||
"seed": 262945989,
|
||||
"version": 1,
|
||||
"versionNonce": 1424542118,
|
||||
"versionNonce": 1104944359,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -331,11 +430,11 @@
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-config",
|
||||
"id": "node-label-change",
|
||||
"type": "text",
|
||||
"x": 173.0,
|
||||
"y": 70.0,
|
||||
"width": 154.0,
|
||||
"x": 80.0,
|
||||
"y": 141.5,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
@@ -351,9 +450,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 802906507,
|
||||
"seed": 193631094,
|
||||
"version": 1,
|
||||
"versionNonce": 72227637,
|
||||
"versionNonce": 157184716,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -361,21 +460,21 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "appendonly yes\nredis-cli config set",
|
||||
"text": "운영 변경\nB-5 · B-6",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "appendonly yes\nredis-cli config set",
|
||||
"originalText": "운영 변경\nB-5 · B-6",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-datadir",
|
||||
"type": "rectangle",
|
||||
"x": 175.0,
|
||||
"y": 227.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"x": 380.0,
|
||||
"y": 60.0,
|
||||
"width": 188.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -402,10 +501,10 @@
|
||||
{
|
||||
"id": "node-label-datadir",
|
||||
"type": "text",
|
||||
"x": 185.0,
|
||||
"y": 237.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"x": 390.0,
|
||||
"y": 70.0,
|
||||
"width": 168.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -430,19 +529,19 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "/data\nappendonlydir",
|
||||
"text": "Redis /data\nappendonly yes · 볼륨 없음",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "/data\nappendonlydir",
|
||||
"originalText": "Redis /data\nappendonly yes · 볼륨 없음",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-container",
|
||||
"id": "node-replicas",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 429.5,
|
||||
"x": 399.0,
|
||||
"y": 203.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -459,9 +558,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 899340409,
|
||||
"seed": 1255192028,
|
||||
"version": 1,
|
||||
"versionNonce": 719103033,
|
||||
"versionNonce": 1720096067,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -469,10 +568,10 @@
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-container",
|
||||
"id": "node-label-replicas",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 439.5,
|
||||
"x": 409.0,
|
||||
"y": 213.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -489,9 +588,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 625860718,
|
||||
"seed": 471343967,
|
||||
"version": 1,
|
||||
"versionNonce": 292100911,
|
||||
"versionNonce": 1781276630,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -499,24 +598,24 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "컨테이너\n볼륨 없음",
|
||||
"text": "echo replica 2개\nreplica별 cache",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "컨테이너\n볼륨 없음",
|
||||
"originalText": "echo replica 2개\nreplica별 cache",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-volume",
|
||||
"id": "node-lost",
|
||||
"type": "rectangle",
|
||||
"x": 280.0,
|
||||
"y": 420.0,
|
||||
"x": 728.0,
|
||||
"y": 60.0,
|
||||
"width": 150.0,
|
||||
"height": 90.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
@@ -528,9 +627,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 690472711,
|
||||
"seed": 1036926306,
|
||||
"version": 1,
|
||||
"versionNonce": 62320937,
|
||||
"versionNonce": 1013846721,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -538,12 +637,12 @@
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-volume",
|
||||
"id": "node-label-lost",
|
||||
"type": "text",
|
||||
"x": 290.0,
|
||||
"y": 430.0,
|
||||
"x": 738.0,
|
||||
"y": 70.0,
|
||||
"width": 130.0,
|
||||
"height": 70.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -558,9 +657,9 @@
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 964975471,
|
||||
"seed": 1899718311,
|
||||
"version": 1,
|
||||
"versionNonce": 313361807,
|
||||
"versionNonce": 638763424,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
@@ -568,11 +667,80 @@
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "PersistentVolume",
|
||||
"text": "Redis 재시작\n영속화 실패",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "PersistentVolume",
|
||||
"originalText": "Redis 재시작\n영속화 실패",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "node-mixed",
|
||||
"type": "rectangle",
|
||||
"x": 728.0,
|
||||
"y": 203.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1694771516,
|
||||
"version": 1,
|
||||
"versionNonce": 301343372,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "node-label-mixed",
|
||||
"type": "text",
|
||||
"x": 738.0,
|
||||
"y": 213.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": null,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1468593009,
|
||||
"version": 1,
|
||||
"versionNonce": 77181802,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 0,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"fontSize": 15,
|
||||
"fontFamily": 5,
|
||||
"text": "교차 응답\n401 · 200 · 반복",
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "교차 응답\n401 · 200 · 반복",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b5-b6-storage-and-keys",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "6be204030cc95c5d9aec2ec4f07eae62bd88e7f344d00c2e014a20e18d6f8dba",
|
||||
"spec_sha256": "84e25e1b1ee45fb426dc29ababf1e14f1898da8de253e5bfa2611fa92a6fa607",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-5 · B-6 — 저장소 상실과 키 회전",
|
||||
"line": 415
|
||||
"line": 859
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
%% 볼륨 없는 영속화와 유예 없는 회전
|
||||
%% question: 설정만으로 영속화와 무중단 키 교체가 되는가
|
||||
flowchart TB
|
||||
n0["appendonly yes"]
|
||||
n1[("/data")]
|
||||
n2["컨테이너"]
|
||||
n3[("PersistentVolume")]
|
||||
n0 -->|"AOF 기록"| n1
|
||||
n1 -->|"컨테이너와 함께 소멸"| n2
|
||||
n1 -->|"볼륨 마운트"| n3
|
||||
%% 볼륨 없는 영속화와 replica별 키 회전
|
||||
%% question: 설정을 켰다는 사실만으로 영속화와 일관된 키 회전을 기대할 수 있는가
|
||||
flowchart LR
|
||||
n0["운영 변경"]
|
||||
n1[("Redis /data")]
|
||||
n2["Redis 재시작"]
|
||||
n3["echo replica 2개"]
|
||||
n4["교차 응답"]
|
||||
n0 -->|"AOF 설정"| n1
|
||||
n1 -->|"볼륨 없음"| n2
|
||||
n0 -->|"키 회전"| n3
|
||||
n3 -->|"cache 상태"| n4
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="555" viewBox="0 0 680 555" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">볼륨 없는 영속화와 유예 없는 회전</title>
|
||||
<desc id="diagram-description">redis-cli config set appendonly yes 를 켜도 아무것도 달라지지 않았다. /data 가 컨테이너 파일시스템이라 컨테이너와 함께 죽기 때문이다. appendonlydir 이 만들어졌다가 그대로 버려진다. 볼륨 없는 영속화 설정은 장식이다. realm 키를 회전했을 때는 JWKS 캐시의 유예 구간을 기대했는데 없었다. NimbusJwtDecoder 는 모르는 kid 를 만나면 JWKS 를 다시 가져온다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b5-b6-storage-and-keys","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"B-5 · B-6 — 저장소 상실과 키 회전","line":415}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="923" height="319" viewBox="0 0 923 319" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">볼륨 없는 영속화와 replica별 키 회전</title>
|
||||
<desc id="diagram-description">B-5에서는 appendonly yes 설정이 적용돼도 /data가 컨테이너 파일시스템이라 재시작 때 사라졌다. B-6에서는 옛 키를 지운 뒤 같은 토큰을 반복 요청하자 401과 200이 교대로 나왔다. echo replica 둘의 JWKS cache가 독립적이라 한쪽은 새 목록을 받아 옛 키를 잃었고 다른 쪽은 아직 들고 있었기 때문이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b5-b6-storage-and-keys","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-5 · B-6 — 저장소 상실과 키 회전","line":859}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,47 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</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="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="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="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>
|
||||
<line class="node-detail-divider" x1="177.0" y1="108.0" x2="323.0" y2="108.0" />
|
||||
<text class="node-detail" x="179.0" y="125.0">redis-cli config set</text>
|
||||
<rect class="canvas" width="923" height="319" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="220.0,158.0 300.0,158.0 300.0,95.5 380.0,95.5" data-evidence="861-863" />
|
||||
<rect class="edge-label-bg" x="294.9" y="112.8" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="324.0" y="127.8">AOF 설정</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,176.0 309.5,176.0 309.5,238.5 399.0,238.5" data-evidence="865-872" />
|
||||
<rect class="edge-label-bg" x="311.1" y="193.2" width="44.8" height="22" rx="3" />
|
||||
<text class="edge-label" x="333.5" y="208.2">키 회전</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="568.0,95.5 648.0,95.5 648.0,95.5 728.0,95.5" data-evidence="861-863" />
|
||||
<rect class="edge-label-bg" x="622.2" y="53.5" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="648.0" y="68.5">볼륨 없음</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="549.0,238.5 638.5,238.5 638.5,238.5 728.0,238.5" data-evidence="868-877" />
|
||||
<rect class="edge-label-bg" x="602.7" y="196.5" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="638.5" y="211.5">cache 상태</text>
|
||||
<g id="node-change">
|
||||
<rect class="node-shape kind-process emphasis-normal role-source" data-evidence="861-872" x="70.0" y="131.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="158.5">운영 변경</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="179.5" x2="206.0" y2="179.5" />
|
||||
<text class="node-detail" x="86.0" y="196.5">B-5 · B-6</text>
|
||||
</g>
|
||||
<g id="node-datadir">
|
||||
<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="189.0" y1="288.0" x2="311.0" y2="288.0" />
|
||||
<text class="node-detail" x="191.0" y="305.0">appendonlydir</text>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-control" data-evidence="861-863" x="380.0" y="71.8" width="188.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-control" cx="474.0" cy="71.8" rx="94.0" ry="11.8" /><path class="storage-bottom" d="M 380.0 119.2 A 94.0 11.8 0 0 0 568.0 119.2" />
|
||||
<text class="node-label" x="474.0" y="87.0">Redis /data</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="108.0" x2="554.0" y2="108.0" />
|
||||
<text class="node-detail" x="396.0" y="125.0">appendonly yes · 볼륨 없음</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>
|
||||
<g id="node-replicas">
|
||||
<rect class="node-shape kind-service emphasis-warning role-control" data-evidence="868-872" x="399.0" y="203.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="474.0" y="230.0">echo replica 2개</text>
|
||||
<line class="node-detail-divider" x1="413.0" y1="251.0" x2="535.0" y2="251.0" />
|
||||
<text class="node-detail" x="415.0" y="268.0">replica별 cache</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" />
|
||||
<text class="node-label" x="355.0" y="463.0">PersistentVolume</text>
|
||||
<g id="node-lost">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="861-863" x="728.0" y="60.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="803.0" y="87.0">Redis 재시작</text>
|
||||
<line class="node-detail-divider" x1="742.0" y1="108.0" x2="864.0" y2="108.0" />
|
||||
<text class="node-detail" x="744.0" y="125.0">영속화 실패</text>
|
||||
</g>
|
||||
<g id="node-mixed">
|
||||
<rect class="node-shape kind-component emphasis-primary role-target" data-evidence="868-877" x="728.0" y="203.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="803.0" y="230.0">교차 응답</text>
|
||||
<line class="node-detail-divider" x1="742.0" y1="251.0" x2="864.0" y2="251.0" />
|
||||
<text class="node-detail" x="744.0" y="268.0">401 · 200 · 반복</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -6,17 +6,17 @@
|
||||
|
||||
## Long description
|
||||
|
||||
oauth2-proxy 는 세션 전체를 쿠키에 담고 replica 는 같은 k8s Secret 을 읽을 뿐이다. 공유할 서버 상태가 없으니 콜백이 다른 replica 로 가도 된다. 대신 --cookie-secret 이 단수라 옛 secret 도 당분간 받아준다가 불가능하고 교체하는 순간 모든 쿠키가 한꺼번에 무효가 된다. Redis 저장소를 켜면 쿠키에는 티켓만 남는데, 티켓 안에 세션 id 가 있으므로 secret 을 바꾸면 어느 Redis 키를 지울지도 모르게 된다.
|
||||
cookie-only 모드에서는 세션 전체가 쿠키에 있어 replica가 같은 secret만 읽으면 된다. Redis session store를 켜면 쿠키에는 ticket만 남고 실제 session은 Redis에 생긴다. secret을 바꾸면 ticket을 풀 수 없어 어느 Redis key를 지울지 알 수 없고 고아 session이 남는다. refresh:disabled에서는 TTL이 생성 시각의 함수라 회전 시각과 비교해 고아를 골라낼 수 있지만 --cookie-refresh를 켜면 이 역산은 성립하지 않는다.
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **k8s Secret** (datastore): 모든 replica 가 같은 값을 읽는다. Evidence: L419–L428.
|
||||
- **쿠키의 티켓** (component): 세션 id 와 복호화 키가 함께 암호화되어 있다. Evidence: L429–L438.
|
||||
- **Redis 의 세션** (datastore): 티켓을 못 풀면 어느 키인지도 모른다. Evidence: L429–L440.
|
||||
- **TTL** (component): 요청으로 갱신되지 않아 생성 시각의 함수다. Evidence: L441–L452.
|
||||
- **k8s Secret** (datastore): 모든 replica 가 같은 값을 읽는다. Evidence: L863–L867.
|
||||
- **쿠키의 티켓** (component): 세션 id 와 복호화 키가 함께 암호화되어 있다. Evidence: L923–L932.
|
||||
- **Redis 의 세션** (datastore): 티켓을 못 풀면 어느 키인지도 모른다. Evidence: L923–L932.
|
||||
- **TTL** (component): 요청으로 갱신되지 않아 생성 시각의 함수다. Evidence: L935–L946.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Redis 의 세션 → TTL:** 생성 시각. Evidence: L441–L452.
|
||||
- **k8s Secret → 쿠키의 티켓:** 티켓 암호화. Evidence: L419–L432.
|
||||
- **쿠키의 티켓 → Redis 의 세션:** 키 이름 생성. Evidence: L429–L438.
|
||||
- **Redis 의 세션 → TTL:** 생성 시각. Evidence: L935–L946.
|
||||
- **k8s Secret → 쿠키의 티켓:** 티켓 암호화. Evidence: L919–L925.
|
||||
- **쿠키의 티켓 → Redis 의 세션:** 키 이름 생성. Evidence: L923–L932.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="b7-cookie-session-tradeoff" name="쿠키에 담으면 공유할 것이 없다">
|
||||
<mxGraphModel dx="680" dy="729" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1225" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1225" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_secret" value="k8s Secret<br/>--cookie-secret 은 단수" tooltip="모든 replica 가 같은 값을 읽는다. | Evidence: L419-L428" 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="70.0" y="60.0" width="174.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_secret" value="k8s Secret<br/>--cookie-secret 은 단수" tooltip="모든 replica 가 같은 값을 읽는다. | Evidence: L863-L867" 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="35.0" y="145.0" width="174.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_ticket" value="쿠키의 티켓<br/>secret 교체 시 복호화 불가" tooltip="세션 id 와 복호화 키가 함께 암호화되어 있다. | Evidence: L429-L438" 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="77.0" y="253.0" width="160.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_ticket" value="쿠키의 티켓<br/>secret 교체 시 복호화 불가" tooltip="세션 id 와 복호화 키가 함께 암호화되어 있다. | Evidence: L923-L932" 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="35.0" y="479.0" width="160.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_redis-session" value="Redis 의 세션<br/>고아 세션" tooltip="티켓을 못 풀면 어느 키인지도 모른다. | Evidence: L429-L440" 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="82.0" y="420.0" width="150.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_redis-session" value="Redis 의 세션<br/>고아 세션" tooltip="티켓을 못 풀면 어느 키인지도 모른다. | Evidence: L923-L932" 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="35.0" y="799.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_ttl" value="TTL<br/>생성 시각 역산" tooltip="요청으로 갱신되지 않아 생성 시각의 함수다. | Evidence: L441-L452" 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="82.0" y="613.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_ttl" value="TTL<br/>생성 시각 역산" tooltip="요청으로 갱신되지 않아 생성 시각의 함수다. | Evidence: L935-L946" 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="35.0" y="1109.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-ttl" value="생성 시각" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_redis-session" target="n_ttl">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="565.0" as="offset"/>
|
||||
<mxPoint x="134.0" y="989.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_s-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_secret" target="n_ticket">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="205.0" as="offset"/>
|
||||
<mxPoint x="118.5" y="319.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_t-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_ticket" target="n_redis-session">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="372.0" as="offset"/>
|
||||
<mxPoint x="112.5" y="646.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-r-ttl",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 517.0,
|
||||
"x": 110.0,
|
||||
"y": 870.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -37,15 +37,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-r-ttl",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 553.0,
|
||||
"x": 89.0,
|
||||
"y": 977.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-s-t",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 157.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 115.0,
|
||||
"y": 216.0,
|
||||
"width": 7.0,
|
||||
"height": 263.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
7.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
7.0,
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
263.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-s-t",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 193.0,
|
||||
"x": 73.5,
|
||||
"y": 307.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-t-r",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 324.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 550.0,
|
||||
"width": 5.0,
|
||||
"height": 249.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -230,20 +230,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
5.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
5.0,
|
||||
124.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
124.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
249.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-t-r",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 360.0,
|
||||
"x": 67.5,
|
||||
"y": 634.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,10 +303,10 @@
|
||||
{
|
||||
"id": "node-secret",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 174.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -333,10 +333,10 @@
|
||||
{
|
||||
"id": "node-label-secret",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 154.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-ticket",
|
||||
"type": "rectangle",
|
||||
"x": 77.0,
|
||||
"y": 253.0,
|
||||
"x": 35.0,
|
||||
"y": 479.0,
|
||||
"width": 160.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-ticket",
|
||||
"type": "text",
|
||||
"x": 87.0,
|
||||
"y": 263.0,
|
||||
"x": 45.0,
|
||||
"y": 489.0,
|
||||
"width": 140.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,10 +441,10 @@
|
||||
{
|
||||
"id": "node-redis-session",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 420.0,
|
||||
"x": 35.0,
|
||||
"y": 799.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -471,10 +471,10 @@
|
||||
{
|
||||
"id": "node-label-redis-session",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 430.0,
|
||||
"x": 45.0,
|
||||
"y": 809.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-ttl",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 613.0,
|
||||
"x": 35.0,
|
||||
"y": 1109.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-ttl",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 623.0,
|
||||
"x": 45.0,
|
||||
"y": 1119.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "b7-cookie-session-tradeoff",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "9a594212157672a9188e296e73a0ab9af3d53992c94afdbfe2e1aeedaf8998bc",
|
||||
"spec_sha256": "27651564d83168008bdeef9aa1782631988abfbbaeffa80073aa3b854f237bf9",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-7 · B-7a — 쿠키에 담는 세션, 그리고 그 대가",
|
||||
"line": 424
|
||||
"line": 884
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"b7-cookie-session-tradeoff.excalidraw",
|
||||
"b7-cookie-session-tradeoff.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="729" viewBox="0 0 680 729" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1225" viewBox="0 0 680 1225" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">쿠키에 담으면 공유할 것이 없다</title>
|
||||
<desc id="diagram-description">oauth2-proxy 는 세션 전체를 쿠키에 담고 replica 는 같은 k8s Secret 을 읽을 뿐이다. 공유할 서버 상태가 없으니 콜백이 다른 replica 로 가도 된다. 대신 --cookie-secret 이 단수라 옛 secret 도 당분간 받아준다가 불가능하고 교체하는 순간 모든 쿠키가 한꺼번에 무효가 된다. Redis 저장소를 켜면 쿠키에는 티켓만 남는데, 티켓 안에 세션 id 가 있으므로 secret 을 바꾸면 어느 Redis 키를 지울지도 모르게 된다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b7-cookie-session-tradeoff","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"B-7 · B-7a — 쿠키에 담는 세션, 그리고 그 대가","line":424}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<desc id="diagram-description">cookie-only 모드에서는 세션 전체가 쿠키에 있어 replica가 같은 secret만 읽으면 된다. Redis session store를 켜면 쿠키에는 ticket만 남고 실제 session은 Redis에 생긴다. secret을 바꾸면 ticket을 풀 수 없어 어느 Redis key를 지울지 알 수 없고 고아 session이 남는다. refresh:disabled에서는 TTL이 생성 시각의 함수라 회전 시각과 비교해 고아를 골라낼 수 있지만 --cookie-refresh를 켜면 이 역산은 성립하지 않는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"b7-cookie-session-tradeoff","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-7 · B-7a — 쿠키에 담는 세션, 그리고 그 대가","line":884}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,38 +49,38 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="729" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="157.0,517.0 157.0,565.0 157.0,565.0 157.0,613.0" data-evidence="441-452" />
|
||||
<rect class="edge-label-bg" x="155.2" y="551.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="566.0">생성 시각</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="157.0,157.0 157.0,205.0 157.0,205.0 157.0,253.0" data-evidence="419-432" />
|
||||
<rect class="edge-label-bg" x="151.9" y="191.0" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="206.0">티켓 암호화</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="157.0,324.0 157.0,372.0 157.0,372.0 157.0,420.0" data-evidence="429-438" />
|
||||
<rect class="edge-label-bg" x="148.6" y="358.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="373.0">키 이름 생성</text>
|
||||
<rect class="canvas" width="680" height="1225" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="110.0,870.0 110.0,989.5 110.0,989.5 110.0,1109.0" data-evidence="935-946" />
|
||||
<rect class="edge-label-bg" x="108.2" y="975.5" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="134.0" y="990.5">생성 시각</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="122.0,216.0 122.0,347.5 115.0,347.5 115.0,479.0" data-evidence="919-925" />
|
||||
<rect class="edge-label-bg" x="89.4" y="305.5" width="58.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="118.5" y="320.5">티켓 암호화</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="115.0,550.0 115.0,674.5 110.0,674.5 110.0,799.0" data-evidence="923-932" />
|
||||
<rect class="edge-label-bg" x="80.0" y="632.5" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="112.5" y="647.5">키 이름 생성</text>
|
||||
<g id="node-secret">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-source" data-evidence="419-428" x="70.0" y="73.0" width="174.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-source" cx="157.0" cy="73.0" rx="87.0" ry="13.0" /><path class="storage-bottom" d="M 70.0 144.0 A 87.0 13.0 0 0 0 244.0 144.0" />
|
||||
<text class="node-label" x="157.0" y="100.0">k8s Secret</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="121.0" x2="230.0" y2="121.0" />
|
||||
<text class="node-detail" x="86.0" y="138.0">--cookie-secret 은 단수</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-source" data-evidence="863-867" x="35.0" y="156.8" width="174.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-source" cx="122.0" cy="156.8" rx="87.0" ry="11.8" /><path class="storage-bottom" d="M 35.0 204.2 A 87.0 11.8 0 0 0 209.0 204.2" />
|
||||
<text class="node-label" x="122.0" y="172.0">k8s Secret</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="193.0" x2="195.0" y2="193.0" />
|
||||
<text class="node-detail" x="51.0" y="210.0">--cookie-secret 은 단수</text>
|
||||
</g>
|
||||
<g id="node-ticket">
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="429-438" x="77.0" y="253.0" width="160.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="280.0">쿠키의 티켓</text>
|
||||
<line class="node-detail-divider" x1="91.0" y1="301.0" x2="223.0" y2="301.0" />
|
||||
<text class="node-detail" x="93.0" y="318.0">secret 교체 시 복호화 불가</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="923-932" x="35.0" y="479.0" width="160.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="115.0" y="506.0">쿠키의 티켓</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="527.0" x2="181.0" y2="527.0" />
|
||||
<text class="node-detail" x="51.0" y="544.0">secret 교체 시 복호화 불가</text>
|
||||
</g>
|
||||
<g id="node-redis-session">
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="429-440" x="82.0" y="433.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="157.0" cy="433.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 82.0 504.0 A 75.0 13.0 0 0 0 232.0 504.0" />
|
||||
<text class="node-label" x="157.0" y="460.0">Redis 의 세션</text>
|
||||
<line class="node-detail-divider" x1="96.0" y1="481.0" x2="218.0" y2="481.0" />
|
||||
<text class="node-detail" x="98.0" y="498.0">고아 세션</text>
|
||||
<rect class="node-shape kind-datastore emphasis-warning role-target" data-evidence="923-932" x="35.0" y="810.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-warning role-target" cx="110.0" cy="810.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 35.0 858.2 A 75.0 11.8 0 0 0 185.0 858.2" />
|
||||
<text class="node-label" x="110.0" y="826.0">Redis 의 세션</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="847.0" x2="171.0" y2="847.0" />
|
||||
<text class="node-detail" x="51.0" y="864.0">고아 세션</text>
|
||||
</g>
|
||||
<g id="node-ttl">
|
||||
<rect class="node-shape kind-component emphasis-primary role-target" data-evidence="441-452" x="82.0" y="613.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="640.0">TTL</text>
|
||||
<line class="node-detail-divider" x1="96.0" y1="661.0" x2="218.0" y2="661.0" />
|
||||
<text class="node-detail" x="98.0" y="678.0">생성 시각 역산</text>
|
||||
<rect class="node-shape kind-component emphasis-primary role-target" data-evidence="935-946" x="35.0" y="1109.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="110.0" y="1136.0">TTL</text>
|
||||
<line class="node-detail-divider" x1="49.0" y1="1157.0" x2="171.0" y2="1157.0" />
|
||||
<text class="node-detail" x="51.0" y="1174.0">생성 시각 역산</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
@@ -10,15 +10,15 @@ Spring Security 의 자동 구성은 세션을 서블릿 컨테이너 메모리
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **브라우저 요청** (actor): 쿠키에 세션 id 를 담아 온다. Evidence: L326–L331.
|
||||
- **Application Session** (component): 누가 로그인했는지를 담는다. 세션 id 로 찾는다. Evidence: L335–L341.
|
||||
- **OAuth2AuthorizedClient** (component): access · refresh token 을 담는다. principal 이름으로 찾는다. Evidence: L335–L341.
|
||||
- **Redis** (datastore): 세션을 옮긴 곳. B-1 에서 여기까지는 옮겨졌다. Evidence: L347–L351.
|
||||
- **PostgreSQL** (datastore): 토큰을 옮긴 곳. B-2 에서 따로 옮겨야 했다. Evidence: L355–L358.
|
||||
- **브라우저 요청** (actor): 쿠키에 세션 id 를 담아 온다. Evidence: L719–L722.
|
||||
- **Application Session** (component): 누가 로그인했는지를 담는다. 세션 id 로 찾는다. Evidence: L727–L732.
|
||||
- **OAuth2AuthorizedClient** (component): access · refresh token 을 담는다. principal 이름으로 찾는다. Evidence: L727–L732.
|
||||
- **Redis** (datastore): 세션을 옮긴 곳. B-1 에서 여기까지는 옮겨졌다. Evidence: L744–L747.
|
||||
- **PostgreSQL** (datastore): 토큰을 옮긴 곳. B-2 에서 따로 옮겨야 했다. Evidence: L773–L776.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **OAuth2AuthorizedClient → PostgreSQL:** 저장. Evidence: L355–L358.
|
||||
- **브라우저 요청 → OAuth2AuthorizedClient:** 조회. Evidence: L332–L341.
|
||||
- **브라우저 요청 → Application Session:** 조회. Evidence: L326–L331.
|
||||
- **Application Session → Redis:** 저장. Evidence: L347–L351.
|
||||
- **OAuth2AuthorizedClient → PostgreSQL:** 저장. Evidence: L773–L776.
|
||||
- **브라우저 요청 → OAuth2AuthorizedClient:** 조회. Evidence: L725–L732.
|
||||
- **브라우저 요청 → Application Session:** 조회. Evidence: L719–L722.
|
||||
- **Application Session → Redis:** 저장. Evidence: L744–L747.
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="bff-store-lookup-keys" name="BFF 가 저장하는 두 가지와 그 조회 키">
|
||||
<mxGraphModel dx="1038" dy="371" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1038" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="1038" dy="319" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1038" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_request" value="브라우저 요청<br/>JSESSIONID" tooltip="쿠키에 세션 id 를 담아 온다. | Evidence: L326-L331" 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="70.0" y="151.0" width="150.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_request" value="브라우저 요청<br/>JSESSIONID" tooltip="쿠키에 세션 id 를 담아 온다. | Evidence: L719-L722" 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="70.0" y="125.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_app-session" value="Application Session<br/>조회 키: 세션 id" tooltip="누가 로그인했는지를 담는다. 세션 id 로 찾는다. | Evidence: L335-L341" 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="390.5" y="86.0" width="167.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_app-session" value="Application Session<br/>조회 키: 세션 id" tooltip="누가 로그인했는지를 담는다. 세션 id 로 찾는다. | Evidence: L727-L732" 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="390.5" y="60.0" width="167.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_authorized-client" value="OAuth2AuthorizedClient<br/>조회 키: principal 이름" tooltip="access · refresh token 을 담는다. principal 이름으로 찾는다. | Evidence: L335-L341" 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="380.0" y="229.0" width="188.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_authorized-client" value="OAuth2AuthorizedClient<br/>조회 키: principal 이름" tooltip="access · refresh token 을 담는다. principal 이름으로 찾는다. | Evidence: L727-L732" 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="380.0" y="203.0" width="188.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_redis" value="Redis<br/>SPRING_SESSION_STORE_TYPE=redis" tooltip="세션을 옮긴 곳. B-1 에서 여기까지는 옮겨졌다. | Evidence: L347-L351" 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="735.0" y="60.0" width="251.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_redis" value="Redis<br/>SPRING_SESSION_STORE_TYPE=redis" tooltip="세션을 옮긴 곳. B-1 에서 여기까지는 옮겨졌다. | Evidence: L744-L747" 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="735.0" y="60.0" width="251.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_postgres" value="PostgreSQL<br/>JdbcOAuth2AuthorizedClientService" tooltip="토큰을 옮긴 곳. B-2 에서 따로 옮겨야 했다. | Evidence: L355-L358" 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="728.0" y="229.0" width="265.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_postgres" value="PostgreSQL<br/>JdbcOAuth2AuthorizedClientService" tooltip="토큰을 옮긴 곳. B-2 에서 따로 옮겨야 했다. | Evidence: L773-L776" 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="728.0" y="203.0" width="265.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_client-store" value="저장" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_authorized-client" target="n_postgres">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="672.0" y="271.0" as="offset"/>
|
||||
<mxPoint x="648.0" y="210.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_lookup-client" value="조회" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_request" target="n_authorized-client">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="324.0" y="233.2" as="offset"/>
|
||||
<mxPoint x="324.0" y="207.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_lookup-session" value="조회" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_request" target="n_app-session">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="329.2" y="152.8" as="offset"/>
|
||||
<mxPoint x="329.2" y="126.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_session-store" value="저장" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_app-session" target="n_redis">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="670.2" y="115.0" as="offset"/>
|
||||
<mxPoint x="646.2" y="67.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
"id": "edge-client-store",
|
||||
"type": "arrow",
|
||||
"x": 568.0,
|
||||
"y": 264.5,
|
||||
"y": 238.5,
|
||||
"width": 160.0,
|
||||
"height": 13.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -41,11 +41,11 @@
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
13.0
|
||||
0.0
|
||||
],
|
||||
[
|
||||
160.0,
|
||||
13.0
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-client-store",
|
||||
"type": "text",
|
||||
"x": 627.0,
|
||||
"y": 259.0,
|
||||
"x": 603.0,
|
||||
"y": 198.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -106,7 +106,7 @@
|
||||
"id": "edge-lookup-client",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 202.0,
|
||||
"y": 176.0,
|
||||
"width": 160.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
@@ -166,7 +166,7 @@
|
||||
"id": "edge-label-lookup-client",
|
||||
"type": "text",
|
||||
"x": 279.0,
|
||||
"y": 221.25,
|
||||
"y": 195.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -205,7 +205,7 @@
|
||||
"id": "edge-lookup-session",
|
||||
"type": "arrow",
|
||||
"x": 220.0,
|
||||
"y": 121.5,
|
||||
"y": 95.5,
|
||||
"width": 170.5,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
@@ -265,7 +265,7 @@
|
||||
"id": "edge-label-lookup-session",
|
||||
"type": "text",
|
||||
"x": 284.25,
|
||||
"y": 140.75,
|
||||
"y": 114.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -304,9 +304,9 @@
|
||||
"id": "edge-session-store",
|
||||
"type": "arrow",
|
||||
"x": 557.5,
|
||||
"y": 108.5,
|
||||
"y": 95.5,
|
||||
"width": 177.5,
|
||||
"height": 13.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -330,11 +330,11 @@
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
13.0
|
||||
0.0
|
||||
],
|
||||
[
|
||||
88.75,
|
||||
13.0
|
||||
0.0
|
||||
],
|
||||
[
|
||||
88.75,
|
||||
@@ -363,8 +363,8 @@
|
||||
{
|
||||
"id": "edge-label-session-store",
|
||||
"type": "text",
|
||||
"x": 625.25,
|
||||
"y": 103.0,
|
||||
"x": 601.25,
|
||||
"y": 55.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -403,7 +403,7 @@
|
||||
"id": "node-request",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 151.0,
|
||||
"y": 125.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -433,7 +433,7 @@
|
||||
"id": "node-label-request",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 161.0,
|
||||
"y": 135.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -472,7 +472,7 @@
|
||||
"id": "node-app-session",
|
||||
"type": "rectangle",
|
||||
"x": 390.5,
|
||||
"y": 86.0,
|
||||
"y": 60.0,
|
||||
"width": 167.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -502,7 +502,7 @@
|
||||
"id": "node-label-app-session",
|
||||
"type": "text",
|
||||
"x": 400.5,
|
||||
"y": 96.0,
|
||||
"y": 70.0,
|
||||
"width": 147.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -541,7 +541,7 @@
|
||||
"id": "node-authorized-client",
|
||||
"type": "rectangle",
|
||||
"x": 380.0,
|
||||
"y": 229.0,
|
||||
"y": 203.0,
|
||||
"width": 188.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -571,7 +571,7 @@
|
||||
"id": "node-label-authorized-client",
|
||||
"type": "text",
|
||||
"x": 390.0,
|
||||
"y": 239.0,
|
||||
"y": 213.0,
|
||||
"width": 168.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -612,7 +612,7 @@
|
||||
"x": 735.0,
|
||||
"y": 60.0,
|
||||
"width": 251.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -642,7 +642,7 @@
|
||||
"x": 745.0,
|
||||
"y": 70.0,
|
||||
"width": 231.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -679,9 +679,9 @@
|
||||
"id": "node-postgres",
|
||||
"type": "rectangle",
|
||||
"x": 728.0,
|
||||
"y": 229.0,
|
||||
"y": 203.0,
|
||||
"width": 265.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -709,9 +709,9 @@
|
||||
"id": "node-label-postgres",
|
||||
"type": "text",
|
||||
"x": 738.0,
|
||||
"y": 239.0,
|
||||
"y": 213.0,
|
||||
"width": 245.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "bff-store-lookup-keys",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "196f000c18bd775c5cfbdd1c571d723e4797bba6c32865e13e67197fb57faba1",
|
||||
"spec_sha256": "bff11d8ff3042bae62503194d6d5209c024a57956ca113824041657917b86d2f",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "609353e10bfd37a9bbb6a79ecf2a32f3d3c02d5d161879a14ad4713e49e7e5e8",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "B-1 · 세션만 Redis 로 옮기면 — 반쪽만 옮겨진다",
|
||||
"line": 345
|
||||
"line": 742
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"bff-store-lookup-keys.svg",
|
||||
"bff-store-lookup-keys.drawio",
|
||||
"bff-store-lookup-keys.mmd",
|
||||
"bff-store-lookup-keys.d2",
|
||||
"bff-store-lookup-keys.dot",
|
||||
"bff-store-lookup-keys.drawio",
|
||||
"bff-store-lookup-keys.excalidraw",
|
||||
"bff-store-lookup-keys.alt.md"
|
||||
],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1038" height="371" viewBox="0 0 1038 371" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1038" height="319" viewBox="0 0 1038 319" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">BFF 가 저장하는 두 가지와 그 조회 키</title>
|
||||
<desc id="diagram-description">Spring Security 의 자동 구성은 세션을 서블릿 컨테이너 메모리에, authorized client 를 InMemoryOAuth2AuthorizedClientService 에 둔다. 조회 경로가 다른데 이름이 비슷해 하나로 오해하기 쉽다. AuthenticatedPrincipalOAuth2AuthorizedClientRepository 는 principal 이름으로 찾고 조회 키에 session id 가 없다. 그래서 SPRING_SESSION_STORE_TYPE 을 redis 로 바꿔 세션을 옮겨도 토큰은 인스턴스 메모리에 남는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"bff-store-lookup-keys","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"609353e10bfd37a9bbb6a79ecf2a32f3d3c02d5d161879a14ad4713e49e7e5e8","anchor":{"kind":"heading","value":"B-1 · 세션만 Redis 로 옮기면 — 반쪽만 옮겨진다","line":345}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"bff-store-lookup-keys","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"B-1 · 세션만 Redis 로 옮기면 — 반쪽만 옮겨진다","line":742}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,45 +49,45 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="1038" height="371" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="568.0,264.5 648.0,264.5 648.0,277.5 728.0,277.5" data-evidence="355-358" />
|
||||
<rect class="edge-label-bg" x="650.0" y="257.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="672.0" y="272.0">저장</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="220.0,202.0 300.0,202.0 300.0,264.5 380.0,264.5" data-evidence="332-341" />
|
||||
<rect class="edge-label-bg" x="302.0" y="219.2" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="324.0" y="234.2">조회</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="220.0,184.0 305.2,184.0 305.2,121.5 390.5,121.5" data-evidence="326-331" />
|
||||
<rect class="edge-label-bg" x="307.2" y="138.8" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="329.2" y="153.8">조회</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="557.5,121.5 646.2,121.5 646.2,108.5 735.0,108.5" data-evidence="347-351" />
|
||||
<rect class="edge-label-bg" x="648.2" y="101.0" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="670.2" y="116.0">저장</text>
|
||||
<rect class="canvas" width="1038" height="319" />
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="568.0,238.5 648.0,238.5 648.0,238.5 728.0,238.5" data-evidence="773-776" />
|
||||
<rect class="edge-label-bg" x="626.0" y="196.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="648.0" y="211.5">저장</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="220.0,176.0 300.0,176.0 300.0,238.5 380.0,238.5" data-evidence="725-732" />
|
||||
<rect class="edge-label-bg" x="302.0" y="193.2" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="324.0" y="208.2">조회</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="220.0,158.0 305.2,158.0 305.2,95.5 390.5,95.5" data-evidence="719-722" />
|
||||
<rect class="edge-label-bg" x="307.2" y="112.8" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="329.2" y="127.8">조회</text>
|
||||
<polyline class="edge kind-write style-solid emphasis-normal" points="557.5,95.5 646.2,95.5 646.2,95.5 735.0,95.5" data-evidence="744-747" />
|
||||
<rect class="edge-label-bg" x="624.2" y="53.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="646.2" y="68.5">저장</text>
|
||||
<g id="node-request">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="326-331"><circle cx="145.0" cy="171.0" r="11.0" /><line x1="145.0" y1="187.0" x2="145.0" y2="196.0" /><line x1="127.0" y1="197.0" x2="163.0" y2="197.0" /><line x1="145.0" y1="196.0" x2="130.0" y2="213.0" /><line x1="145.0" y1="196.0" x2="160.0" y2="213.0" /></g>
|
||||
<text class="node-label" x="145.0" y="228.0">브라우저 요청</text>
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="719-722"><circle cx="145.0" cy="145.0" r="11.0" /><line x1="145.0" y1="161.0" x2="145.0" y2="180.0" /><line x1="127.0" y1="171.0" x2="163.0" y2="171.0" /><line x1="145.0" y1="180.0" x2="130.0" y2="197.0" /><line x1="145.0" y1="180.0" x2="160.0" y2="197.0" /></g>
|
||||
<text class="node-label" x="145.0" y="202.0">브라우저 요청</text>
|
||||
</g>
|
||||
<g id="node-app-session">
|
||||
<rect class="node-shape kind-component emphasis-primary role-store" data-evidence="335-341" x="390.5" y="86.0" width="167.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="474.0" y="113.0">Application Session</text>
|
||||
<line class="node-detail-divider" x1="404.5" y1="134.0" x2="543.5" y2="134.0" />
|
||||
<text class="node-detail" x="406.5" y="151.0">조회 키: 세션 id</text>
|
||||
<rect class="node-shape kind-component emphasis-primary role-store" data-evidence="727-732" x="390.5" y="60.0" width="167.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="474.0" y="87.0">Application Session</text>
|
||||
<line class="node-detail-divider" x1="404.5" y1="108.0" x2="543.5" y2="108.0" />
|
||||
<text class="node-detail" x="406.5" y="125.0">조회 키: 세션 id</text>
|
||||
</g>
|
||||
<g id="node-authorized-client">
|
||||
<rect class="node-shape kind-component emphasis-warning role-store" data-evidence="335-341" x="380.0" y="229.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="474.0" y="256.0">OAuth2AuthorizedClient</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="277.0" x2="554.0" y2="277.0" />
|
||||
<text class="node-detail" x="396.0" y="294.0">조회 키: principal 이름</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-store" data-evidence="727-732" x="380.0" y="203.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="474.0" y="230.0">OAuth2AuthorizedClient</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="251.0" x2="554.0" y2="251.0" />
|
||||
<text class="node-detail" x="396.0" y="268.0">조회 키: principal 이름</text>
|
||||
</g>
|
||||
<g id="node-redis">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="347-351" x="735.0" y="73.0" width="251.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="860.5" cy="73.0" rx="125.5" ry="13.0" /><path class="storage-bottom" d="M 735.0 144.0 A 125.5 13.0 0 0 0 986.0 144.0" />
|
||||
<text class="node-label" x="860.5" y="100.0">Redis</text>
|
||||
<line class="node-detail-divider" x1="749.0" y1="121.0" x2="972.0" y2="121.0" />
|
||||
<text class="node-detail" x="751.0" y="138.0">SPRING_SESSION_STORE_TYPE=redis</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="744-747" x="735.0" y="71.8" width="251.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="860.5" cy="71.8" rx="125.5" ry="11.8" /><path class="storage-bottom" d="M 735.0 119.2 A 125.5 11.8 0 0 0 986.0 119.2" />
|
||||
<text class="node-label" x="860.5" y="87.0">Redis</text>
|
||||
<line class="node-detail-divider" x1="749.0" y1="108.0" x2="972.0" y2="108.0" />
|
||||
<text class="node-detail" x="751.0" y="125.0">SPRING_SESSION_STORE_TYPE=redis</text>
|
||||
</g>
|
||||
<g id="node-postgres">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="355-358" x="728.0" y="242.0" width="265.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="860.5" cy="242.0" rx="132.5" ry="13.0" /><path class="storage-bottom" d="M 728.0 313.0 A 132.5 13.0 0 0 0 993.0 313.0" />
|
||||
<text class="node-label" x="860.5" y="269.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="742.0" y1="290.0" x2="979.0" y2="290.0" />
|
||||
<text class="node-detail" x="744.0" y="307.0">JdbcOAuth2AuthorizedClientService</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="773-776" x="728.0" y="214.8" width="265.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="860.5" cy="214.8" rx="132.5" ry="11.8" /><path class="storage-bottom" d="M 728.0 262.2 A 132.5 11.8 0 0 0 993.0 262.2" />
|
||||
<text class="node-label" x="860.5" y="230.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="742.0" y1="251.0" x2="979.0" y2="251.0" />
|
||||
<text class="node-detail" x="744.0" y="268.0">JdbcOAuth2AuthorizedClientService</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -10,13 +10,13 @@ C-1 에서 로그아웃이 다른 앱으로 퍼지지 않는 것을 관측했고
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **한 앱의 로그아웃** (actor): 한 앱에서 로그아웃하면 Keycloak 세션이 끝난다. Evidence: L458–L465.
|
||||
- **backchannelLogoutUrl** (component): 클라이언트 설정. 비어 있었다. Evidence: L458–L466.
|
||||
- **앱의 수신 엔드포인트** (component): 소스에 없었다. Evidence: L458–L466.
|
||||
- **다른 앱의 세션** (component): 그대로 남는다. Evidence: L458–L466.
|
||||
- **한 앱의 로그아웃** (actor): 한 앱에서 로그아웃하면 Keycloak 세션이 끝난다. Evidence: L952–L957.
|
||||
- **backchannelLogoutUrl** (component): 클라이언트 설정. 비어 있었다. Evidence: L952–L966.
|
||||
- **앱의 수신 엔드포인트** (component): 소스에 없었다. Evidence: L952–L966.
|
||||
- **다른 앱의 세션** (component): 그대로 남는다. Evidence: L952–L966.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **backchannelLogoutUrl → 앱의 수신 엔드포인트:** 호출 (주소 필요). Evidence: L458–L466.
|
||||
- **앱의 수신 엔드포인트 → 다른 앱의 세션:** 세션 삭제 (엔드포인트 필요). Evidence: L458–L466.
|
||||
- **한 앱의 로그아웃 → backchannelLogoutUrl:** 통지 주소 조회. Evidence: L458–L466.
|
||||
- **backchannelLogoutUrl → 앱의 수신 엔드포인트:** 호출 (주소 필요). Evidence: L952–L966.
|
||||
- **앱의 수신 엔드포인트 → 다른 앱의 세션:** 세션 삭제 (엔드포인트 필요). Evidence: L952–L966.
|
||||
- **한 앱의 로그아웃 → backchannelLogoutUrl:** 통지 주소 조회. Evidence: L952–L966.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="c2-backchannel-both-sides" name="백채널 로그아웃은 양쪽이 있어야 한다">
|
||||
<mxGraphModel dx="680" dy="690" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1215" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1215" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_logout" value="한 앱의 로그아웃" tooltip="한 앱에서 로그아웃하면 Keycloak 세션이 끝난다. | Evidence: L458-L465" 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="82.0" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_logout" value="한 앱의 로그아웃" tooltip="한 앱에서 로그아웃하면 Keycloak 세션이 끝난다. | Evidence: L952-L957" 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="35.0" y="145.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_kc-config" value="backchannelLogoutUrl<br/>주소 없음" tooltip="클라이언트 설정. 비어 있었다. | Evidence: L458-L466" 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="174.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_kc-config" value="backchannelLogoutUrl<br/>주소 없음" tooltip="클라이언트 설정. 비어 있었다. | Evidence: L952-L966" 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="41.5" y="455.0" width="174.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_app-endpoint" value="앱의 수신 엔드포인트<br/>oidcLogout 설정 없음" tooltip="소스에 없었다. | Evidence: L458-L466" 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="82.0" y="407.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_app-endpoint" value="앱의 수신 엔드포인트<br/>oidcLogout 설정 없음" tooltip="소스에 없었다. | Evidence: L952-L966" 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="41.5" y="789.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_app-session" value="다른 앱의 세션<br/>네트워크 도달 200" tooltip="그대로 남는다. | Evidence: L458-L466" 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="82.0" y="574.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_app-session" value="다른 앱의 세션<br/>네트워크 도달 200" tooltip="그대로 남는다. | Evidence: L952-L966" 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="41.5" y="1099.0" width="150.0" height="71.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_kc-config" target="n_app-endpoint">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="359.0" as="offset"/>
|
||||
<mxPoint x="122.5" y="629.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_e-s" value="세션 삭제 (엔드포인트 필요)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_app-endpoint" target="n_app-session">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="526.0" as="offset"/>
|
||||
<mxPoint x="140.5" y="979.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_l-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_logout" target="n_kc-config">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="181.0" y="192.0" as="offset"/>
|
||||
<mxPoint x="119.2" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-e",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 116.5,
|
||||
"y": 526.0,
|
||||
"width": 12.0,
|
||||
"height": 263.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -32,20 +32,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
12.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
12.0,
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
131.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
263.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-e",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 347.0,
|
||||
"x": 77.5,
|
||||
"y": 617.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-e-s",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 478.0,
|
||||
"x": 116.5,
|
||||
"y": 860.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"height": 239.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -136,15 +136,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
119.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
239.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-e-s",
|
||||
"type": "text",
|
||||
"x": 117.0,
|
||||
"y": 514.0,
|
||||
"x": 76.5,
|
||||
"y": 967.5,
|
||||
"width": 128,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-l-c",
|
||||
"type": "arrow",
|
||||
"x": 157.0,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 229.0,
|
||||
"width": 18.5,
|
||||
"height": 226.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -235,15 +235,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
18.5,
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
18.5,
|
||||
226.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-l-c",
|
||||
"type": "text",
|
||||
"x": 136.0,
|
||||
"y": 180.0,
|
||||
"x": 74.25,
|
||||
"y": 302.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-logout",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-logout",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-kc-config",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"x": 41.5,
|
||||
"y": 455.0,
|
||||
"width": 174.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-kc-config",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"x": 51.5,
|
||||
"y": 465.0,
|
||||
"width": 154.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-app-endpoint",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 407.0,
|
||||
"x": 41.5,
|
||||
"y": 789.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-app-endpoint",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 417.0,
|
||||
"x": 51.5,
|
||||
"y": 799.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,8 +510,8 @@
|
||||
{
|
||||
"id": "node-app-session",
|
||||
"type": "rectangle",
|
||||
"x": 82.0,
|
||||
"y": 574.0,
|
||||
"x": 41.5,
|
||||
"y": 1099.0,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -540,8 +540,8 @@
|
||||
{
|
||||
"id": "node-label-app-session",
|
||||
"type": "text",
|
||||
"x": 92.0,
|
||||
"y": 584.0,
|
||||
"x": 51.5,
|
||||
"y": 1109.0,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,26 +2,26 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "c2-backchannel-both-sides",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "7197b688cc0a3bdd859bda6c2bc4c32af3e562e2bd165d27d50e66aa63bc942d",
|
||||
"spec_sha256": "cc66cd77f323c21b74389a31312ef247f37a21ae84780157948d92975d952466",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "C층 — SSO 와 로그아웃 전파",
|
||||
"line": 464
|
||||
"line": 964
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"c2-backchannel-both-sides.svg",
|
||||
"c2-backchannel-both-sides.drawio",
|
||||
"c2-backchannel-both-sides.mmd",
|
||||
"c2-backchannel-both-sides.d2",
|
||||
"c2-backchannel-both-sides.dot",
|
||||
"c2-backchannel-both-sides.drawio",
|
||||
"c2-backchannel-both-sides.excalidraw",
|
||||
"c2-backchannel-both-sides.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="690" viewBox="0 0 680 690" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1215" viewBox="0 0 680 1215" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">백채널 로그아웃은 양쪽이 있어야 한다</title>
|
||||
<desc id="diagram-description">C-1 에서 로그아웃이 다른 앱으로 퍼지지 않는 것을 관측했고 원인은 단순했다. 두 클라이언트 모두 backchannelLogoutUrl 이 비어 있어 Keycloak 은 부를 주소를 몰랐고, BFF 소스에는 oidcLogout 설정이 없어 주소를 알려줘도 받을 곳이 없었다. Keycloak 파드에서 앱 URL 로 요청하면 200 이 돌아오므로 네트워크 문제도 아니다. 설정이 빠졌다와 기능이 없다는 다르게 고쳐야 하는데 여기는 둘 다였다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"c2-backchannel-both-sides","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"C층 — SSO 와 로그아웃 전파","line":464}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"c2-backchannel-both-sides","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"C층 — SSO 와 로그아웃 전파","line":964}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="690" />
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="157.0,311.0 157.0,359.0 157.0,359.0 157.0,407.0" data-evidence="458-466" />
|
||||
<rect class="edge-label-bg" x="138.5" y="345.0" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="360.0">호출 (주소 필요)</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="157.0,478.0 157.0,526.0 157.0,526.0 157.0,574.0" data-evidence="458-466" />
|
||||
<rect class="edge-label-bg" x="118.4" y="512.0" width="125.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="527.0">세션 삭제 (엔드포인트 필요)</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="157.0,144.0 157.0,192.0 157.0,192.0 157.0,240.0" data-evidence="458-466" />
|
||||
<rect class="edge-label-bg" x="145.2" y="178.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="181.0" y="193.0">통지 주소 조회</text>
|
||||
<rect class="canvas" width="680" height="1215" />
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="128.5,526.0 128.5,657.5 116.5,657.5 116.5,789.0" data-evidence="952-966" />
|
||||
<rect class="edge-label-bg" x="80.0" y="615.5" width="85.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="122.5" y="630.5">호출 (주소 필요)</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="116.5,860.0 116.5,979.5 116.5,979.5 116.5,1099.0" data-evidence="952-966" />
|
||||
<rect class="edge-label-bg" x="77.9" y="965.5" width="125.2" height="22" rx="3" />
|
||||
<text class="edge-label" x="140.5" y="980.5">세션 삭제 (엔드포인트 필요)</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="110.0,229.0 110.0,342.0 128.5,342.0 128.5,455.0" data-evidence="952-966" />
|
||||
<rect class="edge-label-bg" x="83.5" y="300.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="119.2" y="315.0">통지 주소 조회</text>
|
||||
<g id="node-logout">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="458-465"><circle cx="157.0" cy="80.0" r="11.0" /><line x1="157.0" y1="96.0" x2="157.0" y2="105.0" /><line x1="139.0" y1="106.0" x2="175.0" y2="106.0" /><line x1="157.0" y1="105.0" x2="142.0" y2="122.0" /><line x1="157.0" y1="105.0" x2="172.0" y2="122.0" /></g>
|
||||
<text class="node-label" x="157.0" y="137.0">한 앱의 로그아웃</text>
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="952-957"><circle cx="110.0" cy="165.0" r="11.0" /><line x1="110.0" y1="181.0" x2="110.0" y2="200.0" /><line x1="92.0" y1="191.0" x2="128.0" y2="191.0" /><line x1="110.0" y1="200.0" x2="95.0" y2="217.0" /><line x1="110.0" y1="200.0" x2="125.0" y2="217.0" /></g>
|
||||
<text class="node-label" x="110.0" y="222.0">한 앱의 로그아웃</text>
|
||||
</g>
|
||||
<g id="node-kc-config">
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="458-466" x="70.0" y="240.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="267.0">backchannelLogoutUrl</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="230.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">주소 없음</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="952-966" x="41.5" y="455.0" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="128.5" y="482.0">backchannelLogoutUrl</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="503.0" x2="201.5" y2="503.0" />
|
||||
<text class="node-detail" x="57.5" y="520.0">주소 없음</text>
|
||||
</g>
|
||||
<g id="node-app-endpoint">
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="458-466" x="82.0" y="407.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="434.0">앱의 수신 엔드포인트</text>
|
||||
<line class="node-detail-divider" x1="96.0" y1="455.0" x2="218.0" y2="455.0" />
|
||||
<text class="node-detail" x="98.0" y="472.0">oidcLogout 설정 없음</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-control" data-evidence="952-966" x="41.5" y="789.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="116.5" y="816.0">앱의 수신 엔드포인트</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="837.0" x2="177.5" y2="837.0" />
|
||||
<text class="node-detail" x="57.5" y="854.0">oidcLogout 설정 없음</text>
|
||||
</g>
|
||||
<g id="node-app-session">
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="458-466" x="82.0" y="574.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="157.0" y="601.0">다른 앱의 세션</text>
|
||||
<line class="node-detail-divider" x1="96.0" y1="622.0" x2="218.0" y2="622.0" />
|
||||
<text class="node-detail" x="98.0" y="639.0">네트워크 도달 200</text>
|
||||
<rect class="node-shape kind-component emphasis-warning role-target" data-evidence="952-966" x="41.5" y="1099.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="116.5" y="1126.0">다른 앱의 세션</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="1147.0" x2="177.5" y2="1147.0" />
|
||||
<text class="node-detail" x="57.5" y="1164.0">네트워크 도달 200</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -10,13 +10,13 @@ volatile 모드에서 로그인은 SQL 을 0개 쏜다. refresh 는 딱 한 문
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **로그인 · refresh 요청** (actor): 같은 명령이 캐시 상태에 따라 다른 답을 받는다. Evidence: L284–L296.
|
||||
- **CLIENT 조회** (process): 냉시동에서 여기서 실패한다. Evidence: L306–L312.
|
||||
- **CLIENT_SCOPE_CLIENT 조회** (process): refresh 만 쏘고 첫 번째만 쏜다. Evidence: L284–L292.
|
||||
- **PostgreSQL** (datastore): 세운 상태다. 여기 닿는 조회만 실패한다. Evidence: L296–L302.
|
||||
- **로그인 · refresh 요청** (actor): 같은 명령이 캐시 상태에 따라 다른 답을 받는다. Evidence: L646–L657.
|
||||
- **CLIENT 조회** (process): 냉시동에서 여기서 실패한다. Evidence: L667–L673.
|
||||
- **CLIENT_SCOPE_CLIENT 조회** (process): refresh 만 쏘고 첫 번째만 쏜다. Evidence: L646–L652.
|
||||
- **PostgreSQL** (datastore): 세운 상태다. 여기 닿는 조회만 실패한다. Evidence: L657–L662.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **CLIENT 조회 → CLIENT_SCOPE_CLIENT 조회:** 스코프 재계산. Evidence: L284–L292.
|
||||
- **로그인 · refresh 요청 → CLIENT 조회:** 클라이언트 확인. Evidence: L306–L312.
|
||||
- **CLIENT_SCOPE_CLIENT 조회 → PostgreSQL:** 캐시 미스. Evidence: L296–L302.
|
||||
- **CLIENT 조회 → CLIENT_SCOPE_CLIENT 조회:** 스코프 재계산. Evidence: L646–L652.
|
||||
- **로그인 · refresh 요청 → CLIENT 조회:** 클라이언트 확인. Evidence: L667–L673.
|
||||
- **CLIENT_SCOPE_CLIENT 조회 → PostgreSQL:** 캐시 미스. Evidence: L657–L662.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="cache-temperature-outcomes" name="캐시 온도가 결과를 가른다">
|
||||
<mxGraphModel dx="680" dy="716" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="680" dy="1302" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1302" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_request" value="로그인 · refresh 요청" tooltip="같은 명령이 캐시 상태에 따라 다른 답을 받는다. | Evidence: L284-L296" 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="106.5" y="60.0" width="150.0" height="84.0" as="geometry"/>
|
||||
<mxCell id="n_request" value="로그인 · refresh 요청" tooltip="같은 명령이 캐시 상태에 따라 다른 답을 받는다. | Evidence: L646-L657" 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="35.0" y="145.0" width="150.0" height="84.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_client-lookup" value="CLIENT 조회<br/>select ce1_0.ID from CLIENT" tooltip="냉시동에서 여기서 실패한다. | Evidence: L306-L312" 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="223.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_client-lookup" value="CLIENT 조회<br/>select ce1_0.ID from CLIENT" tooltip="냉시동에서 여기서 실패한다. | Evidence: L667-L673" 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="41.5" y="455.0" width="223.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_scope-lookup" value="CLIENT_SCOPE_CLIENT 조회<br/>DEFAULT_SCOPE='f'" tooltip="refresh 만 쏘고 첫 번째만 쏜다. | Evidence: L284-L292" 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="87.5" y="407.0" width="188.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_scope-lookup" value="CLIENT_SCOPE_CLIENT 조회<br/>DEFAULT_SCOPE='f'" tooltip="refresh 만 쏘고 첫 번째만 쏜다. | Evidence: L646-L652" 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="41.5" y="838.0" width="188.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_db" value="PostgreSQL<br/>캐시 미스 시에만 도달" tooltip="세운 상태다. 여기 닿는 조회만 실패한다. | Evidence: L296-L302" 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="106.5" y="574.0" width="150.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_db" value="PostgreSQL<br/>캐시 미스 시에만 도달" tooltip="세운 상태다. 여기 닿는 조회만 실패한다. | Evidence: L657-L662" 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="41.5" y="1186.0" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_c-s" 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-lookup" target="n_scope-lookup">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="205.5" y="359.0" as="offset"/>
|
||||
<mxPoint x="144.2" y="654.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_request" target="n_client-lookup">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="205.5" y="192.0" as="offset"/>
|
||||
<mxPoint x="131.5" y="314.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_s-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_scope-lookup" target="n_db">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="205.5" y="526.0" as="offset"/>
|
||||
<mxPoint x="126.0" y="1019.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-c-s",
|
||||
"type": "arrow",
|
||||
"x": 181.5,
|
||||
"y": 311.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 135.5,
|
||||
"y": 526.0,
|
||||
"width": 17.5,
|
||||
"height": 312.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -32,20 +32,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
17.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
17.5,
|
||||
156.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
156.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
312.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-c-s",
|
||||
"type": "text",
|
||||
"x": 160.5,
|
||||
"y": 347.0,
|
||||
"x": 99.25,
|
||||
"y": 642.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-r-c",
|
||||
"type": "arrow",
|
||||
"x": 181.5,
|
||||
"y": 144.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 110.0,
|
||||
"y": 229.0,
|
||||
"width": 43.0,
|
||||
"height": 226.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -136,15 +136,15 @@
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
43.0,
|
||||
113.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
43.0,
|
||||
226.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-r-c",
|
||||
"type": "text",
|
||||
"x": 160.5,
|
||||
"y": 180.0,
|
||||
"x": 86.5,
|
||||
"y": 302.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-s-db",
|
||||
"type": "arrow",
|
||||
"x": 181.5,
|
||||
"y": 478.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 116.5,
|
||||
"y": 909.0,
|
||||
"width": 19.0,
|
||||
"height": 277.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -230,20 +230,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0.0,
|
||||
19.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
19.0,
|
||||
138.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
138.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
277.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-s-db",
|
||||
"type": "text",
|
||||
"x": 160.5,
|
||||
"y": 514.0,
|
||||
"x": 81.0,
|
||||
"y": 1007.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,8 +303,8 @@
|
||||
{
|
||||
"id": "node-request",
|
||||
"type": "rectangle",
|
||||
"x": 106.5,
|
||||
"y": 60.0,
|
||||
"x": 35.0,
|
||||
"y": 145.0,
|
||||
"width": 150.0,
|
||||
"height": 84.0,
|
||||
"angle": 0,
|
||||
@@ -333,8 +333,8 @@
|
||||
{
|
||||
"id": "node-label-request",
|
||||
"type": "text",
|
||||
"x": 116.5,
|
||||
"y": 70.0,
|
||||
"x": 45.0,
|
||||
"y": 155.0,
|
||||
"width": 130.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -372,8 +372,8 @@
|
||||
{
|
||||
"id": "node-client-lookup",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 240.0,
|
||||
"x": 41.5,
|
||||
"y": 455.0,
|
||||
"width": 223.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-label-client-lookup",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 250.0,
|
||||
"x": 51.5,
|
||||
"y": 465.0,
|
||||
"width": 203.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -441,8 +441,8 @@
|
||||
{
|
||||
"id": "node-scope-lookup",
|
||||
"type": "rectangle",
|
||||
"x": 87.5,
|
||||
"y": 407.0,
|
||||
"x": 41.5,
|
||||
"y": 838.0,
|
||||
"width": 188.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-label-scope-lookup",
|
||||
"type": "text",
|
||||
"x": 97.5,
|
||||
"y": 417.0,
|
||||
"x": 51.5,
|
||||
"y": 848.0,
|
||||
"width": 168.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -510,10 +510,10 @@
|
||||
{
|
||||
"id": "node-db",
|
||||
"type": "rectangle",
|
||||
"x": 106.5,
|
||||
"y": 574.0,
|
||||
"x": 41.5,
|
||||
"y": 1186.0,
|
||||
"width": 150.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-label-db",
|
||||
"type": "text",
|
||||
"x": 116.5,
|
||||
"y": 584.0,
|
||||
"x": 51.5,
|
||||
"y": 1196.0,
|
||||
"width": 130.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "cache-temperature-outcomes",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "ef12517d7e1783c23b34a19ae285946eaf73ec600e5487c5d9bca1c086e0c22d",
|
||||
"spec_sha256": "9619cc1cfa6d1304be675294d4759d5e216d78b68f4b8dbc59a82e28654751c5",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "A-7 · A-7a — 전부 뒤집는 설정 하나, 그리고 그 표에도 조건이 있었다",
|
||||
"line": 283
|
||||
"line": 644
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"cache-temperature-outcomes.excalidraw",
|
||||
"cache-temperature-outcomes.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="716" viewBox="0 0 680 716" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1302" viewBox="0 0 680 1302" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">캐시 온도가 결과를 가른다</title>
|
||||
<desc id="diagram-description">volatile 모드에서 로그인은 SQL 을 0개 쏜다. refresh 는 딱 한 문장을 쏘는데 CLIENT_SCOPE_CLIENT 의 선택적 스코프 조회이며, 그것도 첫 번째만 쏘고 이후 캐시된다. 그래서 DB 를 세웠을 때 완전 냉시동이면 클라이언트 조회부터 실패해 로그인이 400 이고, CLIENT 캐시만 더우면 refresh 가 500 이며, 완전히 더우면 둘 다 200 이다. A-7 이 표에 적은 것은 그 사이의 한 상태였다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"cache-temperature-outcomes","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"A-7 · A-7a — 전부 뒤집는 설정 하나, 그리고 그 표에도 조건이 있었다","line":283}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"cache-temperature-outcomes","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"A-7 · A-7a — 전부 뒤집는 설정 하나, 그리고 그 표에도 조건이 있었다","line":644}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,36 +49,36 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="716" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="181.5,311.0 181.5,359.0 181.5,359.0 181.5,407.0" data-evidence="284-292" />
|
||||
<rect class="edge-label-bg" x="173.1" y="345.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="205.5" y="360.0">스코프 재계산</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="181.5,144.0 181.5,192.0 181.5,192.0 181.5,240.0" data-evidence="306-312" />
|
||||
<rect class="edge-label-bg" x="169.7" y="178.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="205.5" y="193.0">클라이언트 확인</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="181.5,478.0 181.5,526.0 181.5,526.0 181.5,574.0" data-evidence="296-302" />
|
||||
<rect class="edge-label-bg" x="179.8" y="512.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="205.5" y="527.0">캐시 미스</text>
|
||||
<rect class="canvas" width="680" height="1302" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="153.0,526.0 153.0,682.0 135.5,682.0 135.5,838.0" data-evidence="646-652" />
|
||||
<rect class="edge-label-bg" x="111.8" y="640.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="144.2" y="655.0">스코프 재계산</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="110.0,229.0 110.0,342.0 153.0,342.0 153.0,455.0" data-evidence="667-673" />
|
||||
<rect class="edge-label-bg" x="95.7" y="300.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="131.5" y="315.0">클라이언트 확인</text>
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="135.5,909.0 135.5,1047.5 116.5,1047.5 116.5,1186.0" data-evidence="657-662" />
|
||||
<rect class="edge-label-bg" x="100.2" y="1005.5" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="126.0" y="1020.5">캐시 미스</text>
|
||||
<g id="node-request">
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="284-296"><circle cx="181.5" cy="80.0" r="11.0" /><line x1="181.5" y1="96.0" x2="181.5" y2="105.0" /><line x1="163.5" y1="106.0" x2="199.5" y2="106.0" /><line x1="181.5" y1="105.0" x2="166.5" y2="122.0" /><line x1="181.5" y1="105.0" x2="196.5" y2="122.0" /></g>
|
||||
<text class="node-label" x="181.5" y="137.0">로그인 · refresh 요청</text>
|
||||
<g class="actor-symbol emphasis-primary" data-evidence="646-657"><circle cx="110.0" cy="165.0" r="11.0" /><line x1="110.0" y1="181.0" x2="110.0" y2="200.0" /><line x1="92.0" y1="191.0" x2="128.0" y2="191.0" /><line x1="110.0" y1="200.0" x2="95.0" y2="217.0" /><line x1="110.0" y1="200.0" x2="125.0" y2="217.0" /></g>
|
||||
<text class="node-label" x="110.0" y="222.0">로그인 · refresh 요청</text>
|
||||
</g>
|
||||
<g id="node-client-lookup">
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="306-312" x="70.0" y="240.0" width="223.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="181.5" y="267.0">CLIENT 조회</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="288.0" x2="279.0" y2="288.0" />
|
||||
<text class="node-detail" x="86.0" y="305.0">select ce1_0.ID from CLIENT</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="667-673" x="41.5" y="455.0" width="223.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="153.0" y="482.0">CLIENT 조회</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="503.0" x2="250.5" y2="503.0" />
|
||||
<text class="node-detail" x="57.5" y="520.0">select ce1_0.ID from CLIENT</text>
|
||||
</g>
|
||||
<g id="node-scope-lookup">
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="284-292" x="87.5" y="407.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="181.5" y="434.0">CLIENT_SCOPE_CLIENT 조회</text>
|
||||
<line class="node-detail-divider" x1="101.5" y1="455.0" x2="261.5" y2="455.0" />
|
||||
<text class="node-detail" x="103.5" y="472.0">DEFAULT_SCOPE='f'</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="646-652" x="41.5" y="838.0" width="188.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="135.5" y="865.0">CLIENT_SCOPE_CLIENT 조회</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="886.0" x2="215.5" y2="886.0" />
|
||||
<text class="node-detail" x="57.5" y="903.0">DEFAULT_SCOPE='f'</text>
|
||||
</g>
|
||||
<g id="node-db">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="296-302" x="106.5" y="587.0" width="150.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="181.5" cy="587.0" rx="75.0" ry="13.0" /><path class="storage-bottom" d="M 106.5 658.0 A 75.0 13.0 0 0 0 256.5 658.0" />
|
||||
<text class="node-label" x="181.5" y="614.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="120.5" y1="635.0" x2="242.5" y2="635.0" />
|
||||
<text class="node-detail" x="122.5" y="652.0">캐시 미스 시에만 도달</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-target" data-evidence="657-662" x="41.5" y="1197.8" width="150.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-target" cx="116.5" cy="1197.8" rx="75.0" ry="11.8" /><path class="storage-bottom" d="M 41.5 1245.2 A 75.0 11.8 0 0 0 191.5 1245.2" />
|
||||
<text class="node-label" x="116.5" y="1213.0">PostgreSQL</text>
|
||||
<line class="node-detail-divider" x1="55.5" y1="1234.0" x2="177.5" y2="1234.0" />
|
||||
<text class="node-detail" x="57.5" y="1251.0">캐시 미스 시에만 도달</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -10,23 +10,23 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **QEMU** (service): KVM 핸들을 열고 VM 과 vCPU 를 만드는 유저공간 프로세스. vcpu ioctl 은 그 vcpu 를 만든 스레드에서 낸다. Evidence: L1102–L1108.
|
||||
- **VFIO** (service): IOMMU 보호 아래 장치 접근을 유저공간에 여는 프레임워크. 소유 단위가 IOMMU 그룹이다. Evidence: L1131–L1146.
|
||||
- **물리 PCI 장치** (service): 호스트 드라이버에서 떼어 vfio-pci 에 묶은 장치. Evidence: L1131–L1133, L1144–L1146.
|
||||
- **IOMMU** (service): 장치가 아무 메모리나 건드리지 못하게 막는 리매핑 장치. Evidence: L1144–L1146.
|
||||
- **KVM** (service): mmap 한 공유 메모리로 나온 이유를 유저공간에 알리는 커널 쪽. Evidence: L1105–L1108.
|
||||
- **물리 CPU** (service): VM entry 때 guest-state 영역에서 상태를 싣고 VM exit 때 그리로 저장하는 프로세서. Evidence: L1111–L1113.
|
||||
- **게스트 코드** (service): VMX non-root 로 물리 CPU 에서 도는 게스트 명령. Evidence: L1111–L1113.
|
||||
- **게스트 virtio 드라이버** (service): 게스트가 보는 반가상화 장치의 드라이버. 주고받는 통로가 virtqueue 다. Evidence: L1116–L1118.
|
||||
- **vhost** (service): virtqueue 를 QEMU 밖과 나누는 커널 구현. vhost-user 규약이 이것을 제어하는 ioctl 인터페이스를 보완한다. Evidence: L1125–L1129.
|
||||
- **QEMU** (service): KVM 핸들을 열고 VM 과 vCPU 를 만드는 유저공간 프로세스. vcpu ioctl 은 그 vcpu 를 만든 스레드에서 낸다. Evidence: L3441–L3447.
|
||||
- **VFIO** (service): IOMMU 보호 아래 장치 접근을 유저공간에 여는 프레임워크. 소유 단위가 IOMMU 그룹이다. Evidence: L3470–L3485.
|
||||
- **물리 PCI 장치** (service): 호스트 드라이버에서 떼어 vfio-pci 에 묶은 장치. Evidence: L3470–L3472, L3483–L3485.
|
||||
- **IOMMU** (service): 장치가 아무 메모리나 건드리지 못하게 막는 리매핑 장치. Evidence: L3483–L3485.
|
||||
- **KVM** (service): mmap 한 공유 메모리로 나온 이유를 유저공간에 알리는 커널 쪽. Evidence: L3444–L3447.
|
||||
- **물리 CPU** (service): VM entry 때 guest-state 영역에서 상태를 싣고 VM exit 때 그리로 저장하는 프로세서. Evidence: L3450–L3452.
|
||||
- **게스트 코드** (service): VMX non-root 로 물리 CPU 에서 도는 게스트 명령. Evidence: L3450–L3452.
|
||||
- **게스트 virtio 드라이버** (service): 게스트가 보는 반가상화 장치의 드라이버. 주고받는 통로가 virtqueue 다. Evidence: L3455–L3457.
|
||||
- **vhost** (service): virtqueue 를 QEMU 밖과 나누는 커널 구현. vhost-user 규약이 이것을 제어하는 ioctl 인터페이스를 보완한다. Evidence: L3464–L3468.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **물리 CPU → 게스트 코드:** VMX non-root. Evidence: L1111–L1112.
|
||||
- **물리 PCI 장치 → IOMMU:** DMA. Evidence: L1144–L1146.
|
||||
- **KVM → 물리 CPU:** VM entry. Evidence: L1111–L1112.
|
||||
- **QEMU → VFIO:** 장치 fd. Evidence: L1138–L1141.
|
||||
- **QEMU → KVM:** KVM_RUN. Evidence: L1104–L1106.
|
||||
- **QEMU → vhost:** 제어 ioctl. Evidence: L1125–L1129.
|
||||
- **QEMU → 게스트 virtio 드라이버:** virtqueue. Evidence: L1116–L1118, L1128–L1128.
|
||||
- **VFIO → 물리 PCI 장치:** vfio-pci 결합. Evidence: L1144–L1146.
|
||||
- **물리 CPU → 게스트 코드:** VMX non-root. Evidence: L3450–L3451.
|
||||
- **물리 PCI 장치 → IOMMU:** DMA. Evidence: L3483–L3485.
|
||||
- **KVM → 물리 CPU:** VM entry. Evidence: L3450–L3451.
|
||||
- **QEMU → VFIO:** 장치 fd. Evidence: L3477–L3480.
|
||||
- **QEMU → KVM:** KVM_RUN. Evidence: L3443–L3445.
|
||||
- **QEMU → vhost:** 제어 ioctl. Evidence: L3464–L3468.
|
||||
- **QEMU → 게스트 virtio 드라이버:** virtqueue. Evidence: L3455–L3457, L3467–L3467.
|
||||
- **VFIO → 물리 PCI 장치:** vfio-pci 결합. Evidence: L3483–L3485.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 게스트가 하드웨어에 닿는 갈래는 셋이고 깊이가 다르다
|
||||
# Question: 게스트의 CPU 실행·virtio I/O·직접 할당한 장치는 각각 어디까지 내려가고 호스트 유저공간을 지나는가
|
||||
direction: down
|
||||
direction: right
|
||||
n0: "QEMU" {
|
||||
shape: rectangle
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="QEMU", shape=box, style="rounded,filled"];
|
||||
n1 [label="VFIO", shape=box, style="rounded,filled"];
|
||||
n2 [label="물리 PCI 장치", shape=box, style="rounded,filled"];
|
||||
n3 [label="IOMMU", shape=box, style="rounded,filled"];
|
||||
n4 [label="KVM", shape=box, style="rounded,filled"];
|
||||
n5 [label="물리 CPU", shape=box, style="rounded,filled"];
|
||||
n6 [label="게스트 코드", shape=box, style="rounded,filled"];
|
||||
n7 [label="게스트 virtio 드라이버", shape=box, style="rounded,filled"];
|
||||
n8 [label="vhost", shape=box, style="rounded,filled"];
|
||||
n0 -> n1 [label="장치 fd", style=solid];
|
||||
n1 -> n2 [label="vfio-pci 결합", style=solid];
|
||||
n2 -> n3 [label="DMA", style=solid];
|
||||
n0 -> n4 [label="KVM_RUN", style=solid];
|
||||
n4 -> n5 [label="VM entry", style=solid];
|
||||
n5 -> n6 [label="VMX non-root", style=solid];
|
||||
n0 -> n7 [label="virtqueue", style=solid];
|
||||
n0 -> n8 [label="제어 ioctl", style=solid];
|
||||
}
|
||||
@@ -1,75 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="cpu-io-passthrough-paths" name="게스트가 하드웨어에 닿는 갈래는 셋이고 깊이가 다르다">
|
||||
<mxGraphModel dx="985" dy="762" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="985" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="1393" dy="690" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1393" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_qemu" value="QEMU<br/>/dev/kvm<br/>KVM_CREATE_VM · KVM_CREATE_VCPU" tooltip="KVM 핸들을 열고 VM 과 vCPU 를 만드는 유저공간 프로세스. vcpu ioctl 은 그 vcpu 를 만든 스레드에서 낸다. | Evidence: L1102-L1108" 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="379.5" y="60.0" width="251.0" height="88.0" as="geometry"/>
|
||||
<mxCell id="n_qemu" value="QEMU<br/>/dev/kvm<br/>KVM_CREATE_VM · KVM_CREATE_VCPU" tooltip="KVM 핸들을 열고 VM 과 vCPU 를 만드는 유저공간 프로세스. vcpu ioctl 은 그 vcpu 를 만든 스레드에서 낸다. | Evidence: L3441-L3447" 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="308.5" width="251.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_kvm" value="KVM<br/>struct kvm_run<br/>KVM_GET_VCPU_MMAP_SIZE" tooltip="mmap 한 공유 메모리로 나온 이유를 유저공간에 알리는 커널 쪽. | Evidence: L1105-L1108" 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="252.5" width="188.0" height="88.0" as="geometry"/>
|
||||
<mxCell id="n_kvm" value="KVM<br/>struct kvm_run<br/>KVM_GET_VCPU_MMAP_SIZE" tooltip="mmap 한 공유 메모리로 나온 이유를 유저공간에 알리는 커널 쪽. | Evidence: L3444-L3447" 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="488.0" y="60.0" width="188.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_vfio" value="VFIO<br/>vfio-pci<br/>IOMMU 그룹<br/>/dev/vfio/vfio" tooltip="IOMMU 보호 아래 장치 접근을 유저공간에 여는 프레임워크. 소유 단위가 IOMMU 그룹이다. | Evidence: L1131-L1146" 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="318.0" y="244.0" width="150.0" height="105.0" as="geometry"/>
|
||||
<mxCell id="n_vfio" value="VFIO<br/>vfio-pci<br/>IOMMU 그룹<br/>/dev/vfio/vfio" tooltip="IOMMU 보호 아래 장치 접근을 유저공간에 여는 프레임워크. 소유 단위가 IOMMU 그룹이다. | Evidence: L3470-L3485" 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="507.0" y="220.0" width="150.0" height="105.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_vhost" value="vhost<br/>리눅스 커널 구현<br/>virtqueue 공유" tooltip="virtqueue 를 QEMU 밖과 나누는 커널 구현. vhost-user 규약이 이것을 제어하는 ioctl 인터페이스를 보완한다. | Evidence: L1125-L1129" 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="528.0" y="252.5" width="150.0" height="88.0" as="geometry"/>
|
||||
<mxCell id="n_vhost" value="vhost<br/>리눅스 커널 구현<br/>virtqueue 공유" tooltip="virtqueue 를 QEMU 밖과 나누는 커널 구현. vhost-user 규약이 이것을 제어하는 ioctl 인터페이스를 보완한다. | Evidence: L3464-L3468" 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="507.0" y="397.0" width="150.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_virtio-driver" value="게스트 virtio 드라이버<br/>virtio-pci · virtio-mmio<br/>virtqueue" tooltip="게스트가 보는 반가상화 장치의 드라이버. 주고받는 통로가 virtqueue 다. | Evidence: L1116-L1118" 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="738.0" y="252.5" width="202.0" height="88.0" as="geometry"/>
|
||||
<mxCell id="n_virtio-driver" value="게스트 virtio 드라이버<br/>virtio-pci · virtio-mmio<br/>virtqueue" tooltip="게스트가 보는 반가상화 장치의 드라이버. 주고받는 통로가 virtqueue 다. | Evidence: L3455-L3457" 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="481.0" y="557.0" width="202.0" height="88.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_physical-cpu" value="물리 CPU<br/>VMX root · VMX non-root<br/>VM entry · VM exit<br/>guest-state 영역" tooltip="VM entry 때 guest-state 영역에서 상태를 싣고 VM exit 때 그리로 저장하는 프로세서. | Evidence: L1111-L1113" 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="302.5" y="445.0" width="195.0" height="105.0" as="geometry"/>
|
||||
<mxCell id="n_physical-cpu" value="물리 CPU<br/>VMX root · VMX non-root<br/>VM entry · VM exit<br/>guest-state 영역" tooltip="VM entry 때 guest-state 영역에서 상태를 싣고 VM exit 때 그리로 저장하는 프로세서. | Evidence: L3450-L3452" 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="843.0" y="228.5" width="195.0" height="105.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_pci-device" value="물리 PCI 장치<br/>게스트 직접 할당" tooltip="호스트 드라이버에서 떼어 vfio-pci 에 묶은 장치. | Evidence: L1131-L1133, L1144-L1146" 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="557.5" y="462.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_pci-device" value="물리 PCI 장치<br/>게스트 직접 할당" tooltip="호스트 드라이버에서 떼어 vfio-pci 에 묶은 장치. | Evidence: L3470-L3472, L3483-L3485" 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="865.5" y="405.5" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_guest-code" value="게스트 코드<br/>vCPU" tooltip="VMX non-root 로 물리 CPU 에서 도는 게스트 명령. | Evidence: L1111-L1113" 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="325.0" y="646.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_guest-code" value="게스트 코드<br/>vCPU" tooltip="VMX non-root 로 물리 CPU 에서 도는 게스트 명령. | Evidence: L3450-L3452" 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="1198.0" y="245.5" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_iommu" value="IOMMU<br/>DMA · 인터럽트 리매핑" tooltip="장치가 아무 메모리나 건드리지 못하게 막는 리매핑 장치. | Evidence: L1144-L1146" 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="535.0" y="646.0" width="150.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_iommu" value="IOMMU<br/>DMA · 인터럽트 리매핑" tooltip="장치가 아무 메모리나 건드리지 못하게 막는 리매핑 장치. | Evidence: L3483-L3485" 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="1198.0" y="388.5" width="150.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_cpu-runs-guest" value="VMX non-root" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_physical-cpu" target="n_guest-code">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="424.0" y="598.0" as="offset"/>
|
||||
<mxPoint x="1118.0" y="253.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_device-dma" value="DMA" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_pci-device" target="n_iommu">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="621.2" y="561.5" as="offset"/>
|
||||
<mxPoint x="1130.8" y="432.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_kvm-vm-entry" value="VM entry" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_kvm" target="n_physical-cpu">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="282.0" y="364.8" as="offset"/>
|
||||
<mxPoint x="783.5" y="192.5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_qemu-device-fd" value="장치 fd" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu" target="n_vfio">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="444.5" y="168.0" as="offset"/>
|
||||
<mxPoint x="438.0" y="308.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_qemu-kvm-run" value="KVM_RUN" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu" target="n_kvm">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="321.0" y="172.2" as="offset"/>
|
||||
<mxPoint x="428.5" y="214.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_qemu-vhost-ioctl" value="제어 ioctl" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu" target="n_vhost">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="558.5" y="172.2" as="offset"/>
|
||||
<mxPoint x="438.0" y="401.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_qemu-virtqueue" value="virtqueue" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu" target="n_virtio-driver">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="685.5" y="172.2" as="offset"/>
|
||||
<mxPoint x="425.0" y="490.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_vfio-binds-device" value="vfio-pci 결합" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_vfio" target="n_pci-device">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="512.8" y="377.5" as="offset"/>
|
||||
<mxPoint x="785.2" y="356.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-cpu-runs-guest",
|
||||
"type": "arrow",
|
||||
"x": 400.0,
|
||||
"y": 550.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 1038.0,
|
||||
"y": 281.0,
|
||||
"width": 160.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -36,16 +36,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-cpu-runs-guest",
|
||||
"type": "text",
|
||||
"x": 376.0,
|
||||
"y": 586.0,
|
||||
"x": 1070.0,
|
||||
"y": 241.0,
|
||||
"width": 96,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-device-dma",
|
||||
"type": "arrow",
|
||||
"x": 610.0,
|
||||
"y": 533.0,
|
||||
"width": 22.5,
|
||||
"height": 113.0,
|
||||
"x": 1015.5,
|
||||
"y": 424.0,
|
||||
"width": 182.5,
|
||||
"height": 17.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -131,20 +131,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
22.5,
|
||||
0.0,
|
||||
17.0
|
||||
],
|
||||
[
|
||||
91.25,
|
||||
17.0
|
||||
],
|
||||
[
|
||||
91.25,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
22.5,
|
||||
56.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
56.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
113.0
|
||||
182.5,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-device-dma",
|
||||
"type": "text",
|
||||
"x": 576.25,
|
||||
"y": 549.5,
|
||||
"x": 1085.75,
|
||||
"y": 420.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-kvm-vm-entry",
|
||||
"type": "arrow",
|
||||
"x": 164.0,
|
||||
"y": 340.5,
|
||||
"width": 236.0,
|
||||
"height": 104.5,
|
||||
"x": 676.0,
|
||||
"y": 104.0,
|
||||
"width": 167.0,
|
||||
"height": 177.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -234,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.25
|
||||
83.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
236.0,
|
||||
52.25
|
||||
83.5,
|
||||
177.0
|
||||
],
|
||||
[
|
||||
236.0,
|
||||
104.5
|
||||
167.0,
|
||||
177.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-kvm-vm-entry",
|
||||
"type": "text",
|
||||
"x": 237.0,
|
||||
"y": 352.75,
|
||||
"x": 738.5,
|
||||
"y": 180.5,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,10 +303,10 @@
|
||||
{
|
||||
"id": "edge-qemu-device-fd",
|
||||
"type": "arrow",
|
||||
"x": 393.0,
|
||||
"y": 148.0,
|
||||
"width": 103.0,
|
||||
"height": 96.0,
|
||||
"x": 321.0,
|
||||
"y": 272.5,
|
||||
"width": 186.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -329,20 +329,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
103.0,
|
||||
0.0,
|
||||
71.0
|
||||
],
|
||||
[
|
||||
93.0,
|
||||
71.0
|
||||
],
|
||||
[
|
||||
93.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
103.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
186.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -363,8 +363,8 @@
|
||||
{
|
||||
"id": "edge-label-qemu-device-fd",
|
||||
"type": "text",
|
||||
"x": 399.5,
|
||||
"y": 156.0,
|
||||
"x": 393.0,
|
||||
"y": 296.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -402,10 +402,10 @@
|
||||
{
|
||||
"id": "edge-qemu-kvm-run",
|
||||
"type": "arrow",
|
||||
"x": 164.0,
|
||||
"y": 148.0,
|
||||
"width": 314.0,
|
||||
"height": 104.5,
|
||||
"x": 321.0,
|
||||
"y": 104.0,
|
||||
"width": 167.0,
|
||||
"height": 221.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -428,20 +428,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
314.0,
|
||||
0.0,
|
||||
221.5
|
||||
],
|
||||
[
|
||||
83.5,
|
||||
221.5
|
||||
],
|
||||
[
|
||||
83.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
314.0,
|
||||
52.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.25
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
104.5
|
||||
167.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -462,8 +462,8 @@
|
||||
{
|
||||
"id": "edge-label-qemu-kvm-run",
|
||||
"type": "text",
|
||||
"x": 276.0,
|
||||
"y": 160.25,
|
||||
"x": 383.5,
|
||||
"y": 202.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -501,10 +501,10 @@
|
||||
{
|
||||
"id": "edge-qemu-vhost-ioctl",
|
||||
"type": "arrow",
|
||||
"x": 514.0,
|
||||
"y": 148.0,
|
||||
"width": 89.0,
|
||||
"height": 104.5,
|
||||
"x": 321.0,
|
||||
"y": 361.5,
|
||||
"width": 186.0,
|
||||
"height": 79.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -531,16 +531,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.25
|
||||
93.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
89.0,
|
||||
52.25
|
||||
93.0,
|
||||
79.5
|
||||
],
|
||||
[
|
||||
89.0,
|
||||
104.5
|
||||
186.0,
|
||||
79.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -561,8 +561,8 @@
|
||||
{
|
||||
"id": "edge-label-qemu-vhost-ioctl",
|
||||
"type": "text",
|
||||
"x": 513.5,
|
||||
"y": 160.25,
|
||||
"x": 393.0,
|
||||
"y": 389.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -600,10 +600,10 @@
|
||||
{
|
||||
"id": "edge-qemu-virtqueue",
|
||||
"type": "arrow",
|
||||
"x": 532.0,
|
||||
"y": 148.0,
|
||||
"width": 307.0,
|
||||
"height": 104.5,
|
||||
"x": 321.0,
|
||||
"y": 379.5,
|
||||
"width": 160.0,
|
||||
"height": 221.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -630,16 +630,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
52.25
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
307.0,
|
||||
52.25
|
||||
80.0,
|
||||
221.5
|
||||
],
|
||||
[
|
||||
307.0,
|
||||
104.5
|
||||
160.0,
|
||||
221.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -660,8 +660,8 @@
|
||||
{
|
||||
"id": "edge-label-qemu-virtqueue",
|
||||
"type": "text",
|
||||
"x": 640.5,
|
||||
"y": 160.25,
|
||||
"x": 380.0,
|
||||
"y": 478.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -699,10 +699,10 @@
|
||||
{
|
||||
"id": "edge-vfio-binds-device",
|
||||
"type": "arrow",
|
||||
"x": 393.0,
|
||||
"y": 349.0,
|
||||
"width": 239.5,
|
||||
"height": 113.0,
|
||||
"x": 657.0,
|
||||
"y": 272.5,
|
||||
"width": 208.5,
|
||||
"height": 168.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -729,16 +729,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
56.5
|
||||
104.25,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
239.5,
|
||||
56.5
|
||||
104.25,
|
||||
168.5
|
||||
],
|
||||
[
|
||||
239.5,
|
||||
113.0
|
||||
208.5,
|
||||
168.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -759,8 +759,8 @@
|
||||
{
|
||||
"id": "edge-label-vfio-binds-device",
|
||||
"type": "text",
|
||||
"x": 467.75,
|
||||
"y": 365.5,
|
||||
"x": 740.25,
|
||||
"y": 344.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -798,8 +798,8 @@
|
||||
{
|
||||
"id": "node-qemu",
|
||||
"type": "rectangle",
|
||||
"x": 379.5,
|
||||
"y": 60.0,
|
||||
"x": 70.0,
|
||||
"y": 308.5,
|
||||
"width": 251.0,
|
||||
"height": 88.0,
|
||||
"angle": 0,
|
||||
@@ -828,8 +828,8 @@
|
||||
{
|
||||
"id": "node-label-qemu",
|
||||
"type": "text",
|
||||
"x": 389.5,
|
||||
"y": 70.0,
|
||||
"x": 80.0,
|
||||
"y": 318.5,
|
||||
"width": 231.0,
|
||||
"height": 68.0,
|
||||
"angle": 0,
|
||||
@@ -867,8 +867,8 @@
|
||||
{
|
||||
"id": "node-kvm",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 252.5,
|
||||
"x": 488.0,
|
||||
"y": 60.0,
|
||||
"width": 188.0,
|
||||
"height": 88.0,
|
||||
"angle": 0,
|
||||
@@ -897,8 +897,8 @@
|
||||
{
|
||||
"id": "node-label-kvm",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 262.5,
|
||||
"x": 498.0,
|
||||
"y": 70.0,
|
||||
"width": 168.0,
|
||||
"height": 68.0,
|
||||
"angle": 0,
|
||||
@@ -936,8 +936,8 @@
|
||||
{
|
||||
"id": "node-vfio",
|
||||
"type": "rectangle",
|
||||
"x": 318.0,
|
||||
"y": 244.0,
|
||||
"x": 507.0,
|
||||
"y": 220.0,
|
||||
"width": 150.0,
|
||||
"height": 105.0,
|
||||
"angle": 0,
|
||||
@@ -966,8 +966,8 @@
|
||||
{
|
||||
"id": "node-label-vfio",
|
||||
"type": "text",
|
||||
"x": 328.0,
|
||||
"y": 254.0,
|
||||
"x": 517.0,
|
||||
"y": 230.0,
|
||||
"width": 130.0,
|
||||
"height": 85.0,
|
||||
"angle": 0,
|
||||
@@ -1005,8 +1005,8 @@
|
||||
{
|
||||
"id": "node-vhost",
|
||||
"type": "rectangle",
|
||||
"x": 528.0,
|
||||
"y": 252.5,
|
||||
"x": 507.0,
|
||||
"y": 397.0,
|
||||
"width": 150.0,
|
||||
"height": 88.0,
|
||||
"angle": 0,
|
||||
@@ -1035,8 +1035,8 @@
|
||||
{
|
||||
"id": "node-label-vhost",
|
||||
"type": "text",
|
||||
"x": 538.0,
|
||||
"y": 262.5,
|
||||
"x": 517.0,
|
||||
"y": 407.0,
|
||||
"width": 130.0,
|
||||
"height": 68.0,
|
||||
"angle": 0,
|
||||
@@ -1074,8 +1074,8 @@
|
||||
{
|
||||
"id": "node-virtio-driver",
|
||||
"type": "rectangle",
|
||||
"x": 738.0,
|
||||
"y": 252.5,
|
||||
"x": 481.0,
|
||||
"y": 557.0,
|
||||
"width": 202.0,
|
||||
"height": 88.0,
|
||||
"angle": 0,
|
||||
@@ -1104,8 +1104,8 @@
|
||||
{
|
||||
"id": "node-label-virtio-driver",
|
||||
"type": "text",
|
||||
"x": 748.0,
|
||||
"y": 262.5,
|
||||
"x": 491.0,
|
||||
"y": 567.0,
|
||||
"width": 182.0,
|
||||
"height": 68.0,
|
||||
"angle": 0,
|
||||
@@ -1143,8 +1143,8 @@
|
||||
{
|
||||
"id": "node-physical-cpu",
|
||||
"type": "rectangle",
|
||||
"x": 302.5,
|
||||
"y": 445.0,
|
||||
"x": 843.0,
|
||||
"y": 228.5,
|
||||
"width": 195.0,
|
||||
"height": 105.0,
|
||||
"angle": 0,
|
||||
@@ -1173,8 +1173,8 @@
|
||||
{
|
||||
"id": "node-label-physical-cpu",
|
||||
"type": "text",
|
||||
"x": 312.5,
|
||||
"y": 455.0,
|
||||
"x": 853.0,
|
||||
"y": 238.5,
|
||||
"width": 175.0,
|
||||
"height": 85.0,
|
||||
"angle": 0,
|
||||
@@ -1212,8 +1212,8 @@
|
||||
{
|
||||
"id": "node-pci-device",
|
||||
"type": "rectangle",
|
||||
"x": 557.5,
|
||||
"y": 462.0,
|
||||
"x": 865.5,
|
||||
"y": 405.5,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -1242,8 +1242,8 @@
|
||||
{
|
||||
"id": "node-label-pci-device",
|
||||
"type": "text",
|
||||
"x": 567.5,
|
||||
"y": 472.0,
|
||||
"x": 875.5,
|
||||
"y": 415.5,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -1281,8 +1281,8 @@
|
||||
{
|
||||
"id": "node-guest-code",
|
||||
"type": "rectangle",
|
||||
"x": 325.0,
|
||||
"y": 646.0,
|
||||
"x": 1198.0,
|
||||
"y": 245.5,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -1311,8 +1311,8 @@
|
||||
{
|
||||
"id": "node-label-guest-code",
|
||||
"type": "text",
|
||||
"x": 335.0,
|
||||
"y": 656.0,
|
||||
"x": 1208.0,
|
||||
"y": 255.5,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -1350,8 +1350,8 @@
|
||||
{
|
||||
"id": "node-iommu",
|
||||
"type": "rectangle",
|
||||
"x": 535.0,
|
||||
"y": 646.0,
|
||||
"x": 1198.0,
|
||||
"y": 388.5,
|
||||
"width": 150.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -1380,8 +1380,8 @@
|
||||
{
|
||||
"id": "node-label-iommu",
|
||||
"type": "text",
|
||||
"x": 545.0,
|
||||
"y": 656.0,
|
||||
"x": 1208.0,
|
||||
"y": 398.5,
|
||||
"width": 130.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,25 +2,26 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "cpu-io-passthrough-paths",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "981204dcf528aa1c3b0a400d3ea0d11fb5c26250adaf6e7929f9f1bf39b72f3a",
|
||||
"spec_sha256": "ea1f7be7592152fc3e9b51bb7c8c1486d851443c7c8e539cce67145362872cae",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "0ae5674723f25dc85d5069529890f07be1a11b768b56901103fa3ea6ac3dcf55",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "이 층 아래의 구조 — 조사한 것",
|
||||
"line": 1091
|
||||
"line": 3430
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"cpu-io-passthrough-paths.svg",
|
||||
"cpu-io-passthrough-paths.drawio",
|
||||
"cpu-io-passthrough-paths.mmd",
|
||||
"cpu-io-passthrough-paths.d2",
|
||||
"cpu-io-passthrough-paths.dot",
|
||||
"cpu-io-passthrough-paths.drawio",
|
||||
"cpu-io-passthrough-paths.excalidraw",
|
||||
"cpu-io-passthrough-paths.alt.md"
|
||||
],
|
||||
"lint_issue_count": 5,
|
||||
"lint_issue_count": 2,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%% 게스트가 하드웨어에 닿는 갈래는 셋이고 깊이가 다르다
|
||||
%% question: 게스트의 CPU 실행·virtio I/O·직접 할당한 장치는 각각 어디까지 내려가고 호스트 유저공간을 지나는가
|
||||
flowchart TB
|
||||
flowchart LR
|
||||
n0["QEMU"]
|
||||
n1["VFIO"]
|
||||
n2["물리 PCI 장치"]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="985" height="762" viewBox="0 0 985 762" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1393" height="690" viewBox="0 0 1393 690" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">게스트가 하드웨어에 닿는 갈래는 셋이고 깊이가 다르다</title>
|
||||
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 QEMU 에서 갈래 셋이 갈린다. 왼쪽 CPU 갈래는 KVM_RUN 으로 커널의 KVM 에 들어가고 KVM 이 VM entry 로 물리 CPU 에 올리며 게스트 코드가 VMX non-root 로 거기서 돈다. 물리 CPU 상자에 VM exit 이 함께 적혀 있고 나온 이유는 KVM 상자의 struct kvm_run 으로 유저공간에 돌아간다. 가운데 패스스루 갈래는 QEMU 가 VFIO 로 장치 fd 를 받고 vfio-pci 가 장치를 묶으며 그 장치의 DMA 가 IOMMU 를 지난다. 오른쪽 virtio 갈래는 virtqueue 가 게스트 드라이버에 닿고 같은 virtqueue 를 커널 vhost 와 나누는 길이 따로 있다. 앞의 둘은 이 실험대가 쓰고 패스스루는 쓰지 않는다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"cpu-io-passthrough-paths","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"0ae5674723f25dc85d5069529890f07be1a11b768b56901103fa3ea6ac3dcf55","anchor":{"kind":"heading","value":"이 층 아래의 구조 — 조사한 것","line":1091}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"cpu-io-passthrough-paths","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"이 층 아래의 구조 — 조사한 것","line":3430}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,91 +49,91 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="985" height="762" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="400.0,550.0 400.0,598.0 400.0,598.0 400.0,646.0" data-evidence="1111-1112" />
|
||||
<rect class="edge-label-bg" x="374.8" y="584.0" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="424.0" y="599.0">VMX non-root</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="632.5,533.0 632.5,589.5 610.0,589.5 610.0,646.0" data-evidence="1144-1146" />
|
||||
<rect class="edge-label-bg" x="599.2" y="547.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="621.2" y="562.5">DMA</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="164.0,340.5 164.0,392.8 400.0,392.8 400.0,445.0" data-evidence="1111-1112" />
|
||||
<rect class="edge-label-bg" x="246.2" y="350.8" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="282.0" y="365.8">VM entry</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="496.0,148.0 496.0,196.0 393.0,196.0 393.0,244.0" data-evidence="1138-1141" />
|
||||
<rect class="edge-label-bg" x="418.8" y="154.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="444.5" y="169.0">장치 fd</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="478.0,148.0 478.0,200.2 164.0,200.2 164.0,252.5" data-evidence="1104-1106" />
|
||||
<rect class="edge-label-bg" x="288.6" y="158.2" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="321.0" y="173.2">KVM_RUN</text>
|
||||
<polyline class="edge kind-control style-dashed emphasis-normal" points="514.0,148.0 514.0,200.2 603.0,200.2 603.0,252.5" data-evidence="1125-1129" />
|
||||
<rect class="edge-label-bg" x="522.7" y="158.2" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="558.5" y="173.2">제어 ioctl</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="532.0,148.0 532.0,200.2 839.0,200.2 839.0,252.5" data-evidence="1116-1118,1128-1128" />
|
||||
<rect class="edge-label-bg" x="646.4" y="158.2" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="685.5" y="173.2">virtqueue</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="393.0,349.0 393.0,405.5 632.5,405.5 632.5,462.0" data-evidence="1144-1146" />
|
||||
<rect class="edge-label-bg" x="466.9" y="363.5" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="512.8" y="378.5">vfio-pci 결합</text>
|
||||
<rect class="canvas" width="1393" height="690" />
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="1038.0,281.0 1118.0,281.0 1118.0,281.0 1198.0,281.0" data-evidence="3450-3451" />
|
||||
<rect class="edge-label-bg" x="1068.8" y="239.0" width="98.4" height="22" rx="3" />
|
||||
<text class="edge-label" x="1118.0" y="254.0">VMX non-root</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="1015.5,441.0 1106.8,441.0 1106.8,424.0 1198.0,424.0" data-evidence="3483-3485" />
|
||||
<rect class="edge-label-bg" x="1108.8" y="418.5" width="44.0" height="22" rx="3" />
|
||||
<text class="edge-label" x="1130.8" y="433.5">DMA</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="676.0,104.0 759.5,104.0 759.5,281.0 843.0,281.0" data-evidence="3450-3451" />
|
||||
<rect class="edge-label-bg" x="747.7" y="178.5" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="783.5" y="193.5">VM entry</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="321.0,343.5 414.0,343.5 414.0,272.5 507.0,272.5" data-evidence="3477-3480" />
|
||||
<rect class="edge-label-bg" x="412.2" y="294.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="438.0" y="309.0">장치 fd</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="321.0,325.5 404.5,325.5 404.5,104.0 488.0,104.0" data-evidence="3443-3445" />
|
||||
<rect class="edge-label-bg" x="396.1" y="200.8" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="428.5" y="215.8">KVM_RUN</text>
|
||||
<polyline class="edge kind-control style-dashed emphasis-normal" points="321.0,361.5 414.0,361.5 414.0,441.0 507.0,441.0" data-evidence="3464-3468" />
|
||||
<rect class="edge-label-bg" x="402.2" y="387.2" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="438.0" y="402.2">제어 ioctl</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="321.0,379.5 401.0,379.5 401.0,601.0 481.0,601.0" data-evidence="3455-3457,3467-3467" />
|
||||
<rect class="edge-label-bg" x="385.9" y="476.2" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="425.0" y="491.2">virtqueue</text>
|
||||
<polyline class="edge kind-control style-solid emphasis-normal" points="657.0,272.5 761.2,272.5 761.2,441.0 865.5,441.0" data-evidence="3483-3485" />
|
||||
<rect class="edge-label-bg" x="739.4" y="342.8" width="91.7" height="22" rx="3" />
|
||||
<text class="edge-label" x="785.2" y="357.8">vfio-pci 결합</text>
|
||||
<g id="node-qemu">
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="1102-1108" x="379.5" y="60.0" width="251.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="505.0" y="87.0">QEMU</text>
|
||||
<line class="node-detail-divider" x1="393.5" y1="108.0" x2="616.5" y2="108.0" />
|
||||
<text class="node-detail" x="395.5" y="125.0">/dev/kvm</text>
|
||||
<text class="node-detail" x="395.5" y="141.0">KVM_CREATE_VM · KVM_CREATE_VCPU</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="3441-3447" x="70.0" y="308.5" width="251.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="195.5" y="335.5">QEMU</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="356.5" x2="307.0" y2="356.5" />
|
||||
<text class="node-detail" x="86.0" y="373.5">/dev/kvm</text>
|
||||
<text class="node-detail" x="86.0" y="389.5">KVM_CREATE_VM · KVM_CREATE_VCPU</text>
|
||||
</g>
|
||||
<g id="node-kvm">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1105-1108" x="70.0" y="252.5" width="188.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="164.0" y="279.5">KVM</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="300.5" x2="244.0" y2="300.5" />
|
||||
<text class="node-detail" x="86.0" y="317.5">struct kvm_run</text>
|
||||
<text class="node-detail" x="86.0" y="333.5">KVM_GET_VCPU_MMAP_SIZE</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="3444-3447" x="488.0" y="60.0" width="188.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="582.0" y="87.0">KVM</text>
|
||||
<line class="node-detail-divider" x1="502.0" y1="108.0" x2="662.0" y2="108.0" />
|
||||
<text class="node-detail" x="504.0" y="125.0">struct kvm_run</text>
|
||||
<text class="node-detail" x="504.0" y="141.0">KVM_GET_VCPU_MMAP_SIZE</text>
|
||||
</g>
|
||||
<g id="node-vfio">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1131-1146" x="318.0" y="244.0" width="150.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="393.0" y="271.0">VFIO</text>
|
||||
<line class="node-detail-divider" x1="332.0" y1="292.0" x2="454.0" y2="292.0" />
|
||||
<text class="node-detail" x="334.0" y="309.0">vfio-pci</text>
|
||||
<text class="node-detail" x="334.0" y="325.0">IOMMU 그룹</text>
|
||||
<text class="node-detail" x="334.0" y="341.0">/dev/vfio/vfio</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="3470-3485" x="507.0" y="220.0" width="150.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="582.0" y="247.0">VFIO</text>
|
||||
<line class="node-detail-divider" x1="521.0" y1="268.0" x2="643.0" y2="268.0" />
|
||||
<text class="node-detail" x="523.0" y="285.0">vfio-pci</text>
|
||||
<text class="node-detail" x="523.0" y="301.0">IOMMU 그룹</text>
|
||||
<text class="node-detail" x="523.0" y="317.0">/dev/vfio/vfio</text>
|
||||
</g>
|
||||
<g id="node-vhost">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="1125-1129" x="528.0" y="252.5" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="603.0" y="279.5">vhost</text>
|
||||
<line class="node-detail-divider" x1="542.0" y1="300.5" x2="664.0" y2="300.5" />
|
||||
<text class="node-detail" x="544.0" y="317.5">리눅스 커널 구현</text>
|
||||
<text class="node-detail" x="544.0" y="333.5">virtqueue 공유</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="3464-3468" x="507.0" y="397.0" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="582.0" y="424.0">vhost</text>
|
||||
<line class="node-detail-divider" x1="521.0" y1="445.0" x2="643.0" y2="445.0" />
|
||||
<text class="node-detail" x="523.0" y="462.0">리눅스 커널 구현</text>
|
||||
<text class="node-detail" x="523.0" y="478.0">virtqueue 공유</text>
|
||||
</g>
|
||||
<g id="node-virtio-driver">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="1116-1118" x="738.0" y="252.5" width="202.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="839.0" y="279.5">게스트 virtio 드라이버</text>
|
||||
<line class="node-detail-divider" x1="752.0" y1="300.5" x2="926.0" y2="300.5" />
|
||||
<text class="node-detail" x="754.0" y="317.5">virtio-pci · virtio-mmio</text>
|
||||
<text class="node-detail" x="754.0" y="333.5">virtqueue</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="3455-3457" x="481.0" y="557.0" width="202.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="582.0" y="584.0">게스트 virtio 드라이버</text>
|
||||
<line class="node-detail-divider" x1="495.0" y1="605.0" x2="669.0" y2="605.0" />
|
||||
<text class="node-detail" x="497.0" y="622.0">virtio-pci · virtio-mmio</text>
|
||||
<text class="node-detail" x="497.0" y="638.0">virtqueue</text>
|
||||
</g>
|
||||
<g id="node-physical-cpu">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1111-1113" x="302.5" y="445.0" width="195.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="400.0" y="472.0">물리 CPU</text>
|
||||
<line class="node-detail-divider" x1="316.5" y1="493.0" x2="483.5" y2="493.0" />
|
||||
<text class="node-detail" x="318.5" y="510.0">VMX root · VMX non-root</text>
|
||||
<text class="node-detail" x="318.5" y="526.0">VM entry · VM exit</text>
|
||||
<text class="node-detail" x="318.5" y="542.0">guest-state 영역</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="3450-3452" x="843.0" y="228.5" width="195.0" height="105.0" rx="7" />
|
||||
<text class="node-label" x="940.5" y="255.5">물리 CPU</text>
|
||||
<line class="node-detail-divider" x1="857.0" y1="276.5" x2="1024.0" y2="276.5" />
|
||||
<text class="node-detail" x="859.0" y="293.5">VMX root · VMX non-root</text>
|
||||
<text class="node-detail" x="859.0" y="309.5">VM entry · VM exit</text>
|
||||
<text class="node-detail" x="859.0" y="325.5">guest-state 영역</text>
|
||||
</g>
|
||||
<g id="node-pci-device">
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1131-1133,1144-1146" x="557.5" y="462.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="632.5" y="489.0">물리 PCI 장치</text>
|
||||
<line class="node-detail-divider" x1="571.5" y1="510.0" x2="693.5" y2="510.0" />
|
||||
<text class="node-detail" x="573.5" y="527.0">게스트 직접 할당</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="3470-3472,3483-3485" x="865.5" y="405.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="940.5" y="432.5">물리 PCI 장치</text>
|
||||
<line class="node-detail-divider" x1="879.5" y1="453.5" x2="1001.5" y2="453.5" />
|
||||
<text class="node-detail" x="881.5" y="470.5">게스트 직접 할당</text>
|
||||
</g>
|
||||
<g id="node-guest-code">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="1111-1113" x="325.0" y="646.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="400.0" y="673.0">게스트 코드</text>
|
||||
<line class="node-detail-divider" x1="339.0" y1="694.0" x2="461.0" y2="694.0" />
|
||||
<text class="node-detail" x="341.0" y="711.0">vCPU</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="3450-3452" x="1198.0" y="245.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="1273.0" y="272.5">게스트 코드</text>
|
||||
<line class="node-detail-divider" x1="1212.0" y1="293.5" x2="1334.0" y2="293.5" />
|
||||
<text class="node-detail" x="1214.0" y="310.5">vCPU</text>
|
||||
</g>
|
||||
<g id="node-iommu">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="1144-1146" x="535.0" y="646.0" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="610.0" y="673.0">IOMMU</text>
|
||||
<line class="node-detail-divider" x1="549.0" y1="694.0" x2="671.0" y2="694.0" />
|
||||
<text class="node-detail" x="551.0" y="711.0">DMA · 인터럽트 리매핑</text>
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="3483-3485" x="1198.0" y="388.5" width="150.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="1273.0" y="415.5">IOMMU</text>
|
||||
<line class="node-detail-divider" x1="1212.0" y1="436.5" x2="1334.0" y2="436.5" />
|
||||
<text class="node-detail" x="1214.0" y="453.5">DMA · 인터럽트 리매핑</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -10,15 +10,15 @@
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **옛 버전 롤백** (process): 이미지 태그를 옛 버전으로 되돌린다. Evidence: L483–L490.
|
||||
- **Liquibase 검증** (process): 새 버전이 남긴 체크섬을 거부한다. Evidence: L483–L491.
|
||||
- **StatefulSet 롤링 업데이트** (process): 첫 파드가 실패하면 거기서 멈춘다. Evidence: L492–L497.
|
||||
- **외부 서비스** (service): 남은 파드가 계속 200 을 낸다. Evidence: L492–L498.
|
||||
- **databasechangelog 행 수** (datastore): 전후로 같으면 롤백된다. Evidence: L499–L505.
|
||||
- **옛 버전 롤백** (process): 이미지 태그를 옛 버전으로 되돌린다. Evidence: L988–L995.
|
||||
- **Liquibase 검증** (process): 새 버전이 남긴 체크섬을 거부한다. Evidence: L988–L996.
|
||||
- **StatefulSet 롤링 업데이트** (process): 첫 파드가 실패하면 거기서 멈춘다. Evidence: L997–L999.
|
||||
- **외부 서비스** (service): 남은 파드가 계속 200 을 낸다. Evidence: L997–L1003.
|
||||
- **databasechangelog 행 수** (datastore): 전후로 같으면 롤백된다. Evidence: L1006–L1008.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Liquibase 검증 → databasechangelog 행 수:** 판단 기준. Evidence: L499–L505.
|
||||
- **Liquibase 검증 → StatefulSet 롤링 업데이트:** 중단 지점. Evidence: L492–L497.
|
||||
- **옛 버전 롤백 → Liquibase 검증:** 기동 시 검증. Evidence: L483–L491.
|
||||
- **StatefulSet 롤링 업데이트 → 외부 서비스:** 잔여 파드 응답. Evidence: L492–L498.
|
||||
- **Liquibase 검증 → databasechangelog 행 수:** 판단 기준. Evidence: L1006–L1008.
|
||||
- **Liquibase 검증 → StatefulSet 롤링 업데이트:** 중단 지점. Evidence: L997–L999.
|
||||
- **옛 버전 롤백 → Liquibase 검증:** 기동 시 검증. Evidence: L988–L996.
|
||||
- **StatefulSet 롤링 업데이트 → 외부 서비스:** 잔여 파드 응답. Evidence: L997–L1003.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 방향에 따라 갈리는 업그레이드
|
||||
# Question: 되돌릴 수 있는지는 무엇이 결정하는가
|
||||
direction: down
|
||||
direction: right
|
||||
n0: "옛 버전 롤백" {
|
||||
shape: rectangle
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
digraph techviz {
|
||||
graph [rankdir=TB, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
graph [rankdir=LR, splines=ortho, nodesep=0.55, ranksep=0.85];
|
||||
node [fontname=Helvetica, fontsize=11, margin="0.18,0.12", style="rounded,filled", fillcolor=white, color="#2d4357", penwidth=1.5];
|
||||
edge [fontname=Helvetica, fontsize=10, color="#364b5f", penwidth=1.4, arrowsize=0.75];
|
||||
n0 [label="옛 버전 롤백", shape=box, style="rounded,filled"];
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="d2-upgrade-direction" name="방향에 따라 갈리는 업그레이드">
|
||||
<mxGraphModel dx="680" dy="689" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="1376" dy="319" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1376" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_rollback" value="옛 버전 롤백" tooltip="이미지 태그를 옛 버전으로 되돌린다. | Evidence: L483-L490" 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="199.0" y="60.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_rollback" value="옛 버전 롤백" tooltip="이미지 태그를 옛 버전으로 되돌린다. | Evidence: L988-L995" 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="135.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_liquibase" value="Liquibase 검증<br/>ValidationFailedException: 1 changesets check sum" tooltip="새 버전이 남긴 체크섬을 거부한다. | Evidence: L483-L491" 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="124.0" y="220.0" width="300.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_liquibase" value="Liquibase 검증<br/>ValidationFailedException: 1 changesets check sum" tooltip="새 버전이 남긴 체크섬을 거부한다. | Evidence: L988-L996" 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="380.0" y="131.5" width="300.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_changelog" value="databasechangelog 행 수<br/>전후 행 수 비교" tooltip="전후로 같으면 롤백된다. | Evidence: L499-L505" 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="70.0" y="387.0" width="181.0" height="97.0" as="geometry"/>
|
||||
<mxCell id="n_changelog" value="databasechangelog 행 수<br/>전후 행 수 비교" tooltip="전후로 같으면 롤백된다. | Evidence: L1006-L1008" 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="840.0" y="60.0" width="181.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_rolling" value="StatefulSet 롤링 업데이트<br/>나머지 파드 유지" tooltip="첫 파드가 실패하면 거기서 멈춘다. | Evidence: L492-L497" 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="311.0" y="400.0" width="167.0" height="71.0" as="geometry"/>
|
||||
<mxCell id="n_rolling" value="StatefulSet 롤링 업데이트<br/>나머지 파드 유지" tooltip="첫 파드가 실패하면 거기서 멈춘다. | Evidence: L997-L999" 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="847.0" y="203.0" width="167.0" height="71.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_service" value="외부 서비스" tooltip="남은 파드가 계속 200 을 낸다. | Evidence: L492-L498" 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="199.0" y="580.0" width="150.0" height="64.0" as="geometry"/>
|
||||
<mxCell id="n_service" value="외부 서비스" tooltip="남은 파드가 계속 200 을 낸다. | Evidence: L997-L1003" 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="1181.0" y="135.0" width="150.0" height="64.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_l-cl" value="판단 기준" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_liquibase" target="n_changelog">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="212.8" y="311.0" as="offset"/>
|
||||
<mxPoint x="784.0" y="126.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_l-ro" value="중단 지점" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_liquibase" target="n_rolling">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="338.8" y="317.5" as="offset"/>
|
||||
<mxPoint x="787.5" y="207.2" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_r-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_rollback" target="n_liquibase">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="298.0" y="172.0" as="offset"/>
|
||||
<mxPoint x="300.0" y="139.0" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_ro-s" value="잔여 파드 응답" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_rolling" target="n_service">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="334.2" y="497.5" as="offset"/>
|
||||
<mxPoint x="1121.5" y="202.8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{
|
||||
"id": "edge-l-cl",
|
||||
"type": "arrow",
|
||||
"x": 160.5,
|
||||
"y": 291.0,
|
||||
"width": 104.5,
|
||||
"height": 96.0,
|
||||
"x": 680.0,
|
||||
"y": 95.5,
|
||||
"width": 160.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -32,20 +32,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
104.5,
|
||||
0.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
104.5,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -66,8 +66,8 @@
|
||||
{
|
||||
"id": "edge-label-l-cl",
|
||||
"type": "text",
|
||||
"x": 167.75,
|
||||
"y": 299.0,
|
||||
"x": 739.0,
|
||||
"y": 114.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -105,10 +105,10 @@
|
||||
{
|
||||
"id": "edge-l-ro",
|
||||
"type": "arrow",
|
||||
"x": 283.0,
|
||||
"y": 291.0,
|
||||
"width": 111.5,
|
||||
"height": 109.0,
|
||||
"x": 680.0,
|
||||
"y": 176.0,
|
||||
"width": 167.0,
|
||||
"height": 62.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -135,16 +135,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
54.5
|
||||
83.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
111.5,
|
||||
54.5
|
||||
83.5,
|
||||
62.5
|
||||
],
|
||||
[
|
||||
111.5,
|
||||
109.0
|
||||
167.0,
|
||||
62.5
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -165,8 +165,8 @@
|
||||
{
|
||||
"id": "edge-label-l-ro",
|
||||
"type": "text",
|
||||
"x": 293.75,
|
||||
"y": 305.5,
|
||||
"x": 742.5,
|
||||
"y": 195.25,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -204,10 +204,10 @@
|
||||
{
|
||||
"id": "edge-r-l",
|
||||
"type": "arrow",
|
||||
"x": 274.0,
|
||||
"y": 124.0,
|
||||
"width": 0.0,
|
||||
"height": 96.0,
|
||||
"x": 220.0,
|
||||
"y": 167.0,
|
||||
"width": 160.0,
|
||||
"height": 0.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -234,16 +234,16 @@
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
48.0
|
||||
80.0,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
96.0
|
||||
160.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -264,8 +264,8 @@
|
||||
{
|
||||
"id": "edge-label-r-l",
|
||||
"type": "text",
|
||||
"x": 253.0,
|
||||
"y": 160.0,
|
||||
"x": 255.0,
|
||||
"y": 127.0,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -303,10 +303,10 @@
|
||||
{
|
||||
"id": "edge-ro-s",
|
||||
"type": "arrow",
|
||||
"x": 274.0,
|
||||
"y": 471.0,
|
||||
"width": 120.5,
|
||||
"height": 109.0,
|
||||
"x": 1014.0,
|
||||
"y": 167.0,
|
||||
"width": 167.0,
|
||||
"height": 71.5,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -329,20 +329,20 @@
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
120.5,
|
||||
0.0,
|
||||
71.5
|
||||
],
|
||||
[
|
||||
83.5,
|
||||
71.5
|
||||
],
|
||||
[
|
||||
83.5,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
120.5,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
109.0
|
||||
167.0,
|
||||
0.0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
@@ -363,8 +363,8 @@
|
||||
{
|
||||
"id": "edge-label-ro-s",
|
||||
"type": "text",
|
||||
"x": 289.25,
|
||||
"y": 485.5,
|
||||
"x": 1076.5,
|
||||
"y": 190.75,
|
||||
"width": 90,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
@@ -402,8 +402,8 @@
|
||||
{
|
||||
"id": "node-rollback",
|
||||
"type": "rectangle",
|
||||
"x": 199.0,
|
||||
"y": 60.0,
|
||||
"x": 70.0,
|
||||
"y": 135.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -432,8 +432,8 @@
|
||||
{
|
||||
"id": "node-label-rollback",
|
||||
"type": "text",
|
||||
"x": 209.0,
|
||||
"y": 70.0,
|
||||
"x": 80.0,
|
||||
"y": 145.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
@@ -471,8 +471,8 @@
|
||||
{
|
||||
"id": "node-liquibase",
|
||||
"type": "rectangle",
|
||||
"x": 124.0,
|
||||
"y": 220.0,
|
||||
"x": 380.0,
|
||||
"y": 131.5,
|
||||
"width": 300.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -501,8 +501,8 @@
|
||||
{
|
||||
"id": "node-label-liquibase",
|
||||
"type": "text",
|
||||
"x": 134.0,
|
||||
"y": 230.0,
|
||||
"x": 390.0,
|
||||
"y": 141.5,
|
||||
"width": 280.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -540,10 +540,10 @@
|
||||
{
|
||||
"id": "node-changelog",
|
||||
"type": "rectangle",
|
||||
"x": 70.0,
|
||||
"y": 387.0,
|
||||
"x": 840.0,
|
||||
"y": 60.0,
|
||||
"width": 181.0,
|
||||
"height": 97.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
@@ -570,10 +570,10 @@
|
||||
{
|
||||
"id": "node-label-changelog",
|
||||
"type": "text",
|
||||
"x": 80.0,
|
||||
"y": 397.0,
|
||||
"x": 850.0,
|
||||
"y": 70.0,
|
||||
"width": 161.0,
|
||||
"height": 77.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
@@ -609,8 +609,8 @@
|
||||
{
|
||||
"id": "node-rolling",
|
||||
"type": "rectangle",
|
||||
"x": 311.0,
|
||||
"y": 400.0,
|
||||
"x": 847.0,
|
||||
"y": 203.0,
|
||||
"width": 167.0,
|
||||
"height": 71.0,
|
||||
"angle": 0,
|
||||
@@ -639,8 +639,8 @@
|
||||
{
|
||||
"id": "node-label-rolling",
|
||||
"type": "text",
|
||||
"x": 321.0,
|
||||
"y": 410.0,
|
||||
"x": 857.0,
|
||||
"y": 213.0,
|
||||
"width": 147.0,
|
||||
"height": 51.0,
|
||||
"angle": 0,
|
||||
@@ -678,8 +678,8 @@
|
||||
{
|
||||
"id": "node-service",
|
||||
"type": "rectangle",
|
||||
"x": 199.0,
|
||||
"y": 580.0,
|
||||
"x": 1181.0,
|
||||
"y": 135.0,
|
||||
"width": 150.0,
|
||||
"height": 64.0,
|
||||
"angle": 0,
|
||||
@@ -708,8 +708,8 @@
|
||||
{
|
||||
"id": "node-label-service",
|
||||
"type": "text",
|
||||
"x": 209.0,
|
||||
"y": 590.0,
|
||||
"x": 1191.0,
|
||||
"y": 145.0,
|
||||
"width": 130.0,
|
||||
"height": 44.0,
|
||||
"angle": 0,
|
||||
|
||||
@@ -2,26 +2,26 @@
|
||||
"harness_version": "0.2.0",
|
||||
"spec_id": "d2-upgrade-direction",
|
||||
"spec_version": "1.1",
|
||||
"spec_sha256": "1900af5e51837783471656df7ad19888dbca5f323396175b7d4f1a23d8a6f444",
|
||||
"spec_sha256": "0cebfcc3d0db59dfb864ed62ee9b08be860e5f4b845990180ea998d918538259",
|
||||
"source_context": {
|
||||
"document": "docs/keycloak-session-store/final/document.md",
|
||||
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
|
||||
"document_sha256": "28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560",
|
||||
"anchor": {
|
||||
"kind": "heading",
|
||||
"value": "D-1 · D-2 — 백업과 업그레이드",
|
||||
"line": 481
|
||||
"line": 986
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
"d2-upgrade-direction.svg",
|
||||
"d2-upgrade-direction.drawio",
|
||||
"d2-upgrade-direction.mmd",
|
||||
"d2-upgrade-direction.d2",
|
||||
"d2-upgrade-direction.dot",
|
||||
"d2-upgrade-direction.drawio",
|
||||
"d2-upgrade-direction.excalidraw",
|
||||
"d2-upgrade-direction.alt.md"
|
||||
],
|
||||
"lint_issue_count": 0,
|
||||
"lint_issue_count": 1,
|
||||
"assumption_count": 0,
|
||||
"assumptions_allowed": false,
|
||||
"composition_profile": "component-flow",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%% 방향에 따라 갈리는 업그레이드
|
||||
%% question: 되돌릴 수 있는지는 무엇이 결정하는가
|
||||
flowchart TB
|
||||
flowchart LR
|
||||
n0["옛 버전 롤백"]
|
||||
n1["Liquibase 검증"]
|
||||
n2["StatefulSet 롤링 업데이트"]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="689" viewBox="0 0 680 689" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1376" height="319" viewBox="0 0 1376 319" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">방향에 따라 갈리는 업그레이드</title>
|
||||
<desc id="diagram-description">26.7.0 에서 26.7.3 으로 올리는 것은 무중단이었고 87회 요청이 전부 200 이었다. 되돌리기는 Liquibase 가 체크섬 검증에서 막았다. 새 버전이 남긴 체크섬을 옛 버전이 거부하기 때문이다. 그런데 서비스는 살아 있었다. StatefulSet 롤링 업데이트가 첫 파드에서 멈추고 나머지를 건드리지 않았기 때문이다. 롤백 불가는 조건부이며 판단 기준은 databasechangelog 의 행 수가 업그레이드 전후로 같은가이다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"d2-upgrade-direction","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe","anchor":{"kind":"heading","value":"D-1 · D-2 — 백업과 업그레이드","line":481}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"d2-upgrade-direction","profile":"component-flow"},"source_context":{"document":"docs/keycloak-session-store/final/document.md","document_sha256":"28aef96a2bbb94fbb10ade26a71238fee62a5a4d9fa6e7749ae98cfd0a65e560","anchor":{"kind":"heading","value":"D-1 · D-2 — 백업과 업그레이드","line":986}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
@@ -49,43 +49,43 @@
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="680" height="689" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="265.0,291.0 265.0,339.0 160.5,339.0 160.5,387.0" data-evidence="499-505" />
|
||||
<rect class="edge-label-bg" x="187.0" y="297.0" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="212.8" y="312.0">판단 기준</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="283.0,291.0 283.0,345.5 394.5,345.5 394.5,400.0" data-evidence="492-497" />
|
||||
<rect class="edge-label-bg" x="313.0" y="303.5" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="338.8" y="318.5">중단 지점</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="274.0,124.0 274.0,172.0 274.0,172.0 274.0,220.0" data-evidence="483-491" />
|
||||
<rect class="edge-label-bg" x="265.6" y="158.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="298.0" y="173.0">기동 시 검증</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="394.5,471.0 394.5,525.5 274.0,525.5 274.0,580.0" data-evidence="492-498" />
|
||||
<rect class="edge-label-bg" x="298.4" y="483.5" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="334.2" y="498.5">잔여 파드 응답</text>
|
||||
<rect class="canvas" width="1376" height="319" />
|
||||
<polyline class="edge kind-read style-solid emphasis-normal" points="680.0,158.0 760.0,158.0 760.0,95.5 840.0,95.5" data-evidence="1006-1008" />
|
||||
<rect class="edge-label-bg" x="758.2" y="112.8" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="784.0" y="127.8">판단 기준</text>
|
||||
<polyline class="edge kind-blocked style-solid emphasis-normal" points="680.0,176.0 763.5,176.0 763.5,238.5 847.0,238.5" data-evidence="997-999" />
|
||||
<rect class="edge-label-bg" x="761.8" y="193.2" width="51.5" height="22" rx="3" />
|
||||
<text class="edge-label" x="787.5" y="208.2">중단 지점</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,167.0 300.0,167.0 300.0,167.0 380.0,167.0" data-evidence="988-996" />
|
||||
<rect class="edge-label-bg" x="267.6" y="125.0" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="300.0" y="140.0">기동 시 검증</text>
|
||||
<polyline class="edge kind-request style-solid emphasis-normal" points="1014.0,238.5 1097.5,238.5 1097.5,167.0 1181.0,167.0" data-evidence="997-1003" />
|
||||
<rect class="edge-label-bg" x="1085.7" y="188.8" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="1121.5" y="203.8">잔여 파드 응답</text>
|
||||
<g id="node-rollback">
|
||||
<rect class="node-shape kind-process emphasis-warning role-source" data-evidence="483-490" x="199.0" y="60.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="274.0" y="90.0">옛 버전 롤백</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-source" data-evidence="988-995" x="70.0" y="135.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="145.0" y="165.0">옛 버전 롤백</text>
|
||||
</g>
|
||||
<g id="node-liquibase">
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="483-491" x="124.0" y="220.0" width="300.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="274.0" y="247.0">Liquibase 검증</text>
|
||||
<line class="node-detail-divider" x1="138.0" y1="268.0" x2="410.0" y2="268.0" />
|
||||
<text class="node-detail" x="140.0" y="285.0">ValidationFailedException: 1 changesets check sum</text>
|
||||
<rect class="node-shape kind-process emphasis-warning role-control" data-evidence="988-996" x="380.0" y="131.5" width="300.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="530.0" y="158.5">Liquibase 검증</text>
|
||||
<line class="node-detail-divider" x1="394.0" y1="179.5" x2="666.0" y2="179.5" />
|
||||
<text class="node-detail" x="396.0" y="196.5">ValidationFailedException: 1 changesets check sum</text>
|
||||
</g>
|
||||
<g id="node-changelog">
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-support" data-evidence="499-505" x="70.0" y="400.0" width="181.0" height="71.0" /><ellipse class="node-shape kind-datastore emphasis-primary role-support" cx="160.5" cy="400.0" rx="90.5" ry="13.0" /><path class="storage-bottom" d="M 70.0 471.0 A 90.5 13.0 0 0 0 251.0 471.0" />
|
||||
<text class="node-label" x="160.5" y="427.0">databasechangelog 행 수</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="448.0" x2="237.0" y2="448.0" />
|
||||
<text class="node-detail" x="86.0" y="465.0">전후 행 수 비교</text>
|
||||
<rect class="node-shape kind-datastore emphasis-primary role-support" data-evidence="1006-1008" x="840.0" y="71.8" width="181.0" height="47.3" /><ellipse class="node-shape kind-datastore emphasis-primary role-support" cx="930.5" cy="71.8" rx="90.5" ry="11.8" /><path class="storage-bottom" d="M 840.0 119.2 A 90.5 11.8 0 0 0 1021.0 119.2" />
|
||||
<text class="node-label" x="930.5" y="87.0">databasechangelog 행 수</text>
|
||||
<line class="node-detail-divider" x1="854.0" y1="108.0" x2="1007.0" y2="108.0" />
|
||||
<text class="node-detail" x="856.0" y="125.0">전후 행 수 비교</text>
|
||||
</g>
|
||||
<g id="node-rolling">
|
||||
<rect class="node-shape kind-process emphasis-primary role-control" data-evidence="492-497" x="311.0" y="400.0" width="167.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="394.5" y="427.0">StatefulSet 롤링 업데이트</text>
|
||||
<line class="node-detail-divider" x1="325.0" y1="448.0" x2="464.0" y2="448.0" />
|
||||
<text class="node-detail" x="327.0" y="465.0">나머지 파드 유지</text>
|
||||
<rect class="node-shape kind-process emphasis-primary role-control" data-evidence="997-999" x="847.0" y="203.0" width="167.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="930.5" y="230.0">StatefulSet 롤링 업데이트</text>
|
||||
<line class="node-detail-divider" x1="861.0" y1="251.0" x2="1000.0" y2="251.0" />
|
||||
<text class="node-detail" x="863.0" y="268.0">나머지 파드 유지</text>
|
||||
</g>
|
||||
<g id="node-service">
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="492-498" x="199.0" y="580.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="274.0" y="610.0">외부 서비스</text>
|
||||
<rect class="node-shape kind-service emphasis-primary role-target" data-evidence="997-1003" x="1181.0" y="135.0" width="150.0" height="64.0" rx="7" />
|
||||
<text class="node-label" x="1256.0" y="165.0">외부 서비스</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
@@ -10,11 +10,11 @@ kubectl get secret -o yaml 이 보여주는 base64 는 인코딩이지 암호화
|
||||
|
||||
## Elements and evidence
|
||||
|
||||
- **k8s Secret** (datastore): base64 로 담긴다. Evidence: L495–L501.
|
||||
- **etcd** (datastore): 평문으로 있다. Evidence: L495–L501.
|
||||
- **파드 환경 변수** (component): env 로 그대로 읽힌다. Evidence: L495–L501.
|
||||
- **k8s Secret** (datastore): base64 로 담긴다. Evidence: L1002–L1006.
|
||||
- **etcd** (datastore): 평문으로 있다. Evidence: L1002–L1006.
|
||||
- **파드 환경 변수** (component): env 로 그대로 읽힌다. Evidence: L1002–L1006.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **k8s Secret → etcd:** 저장. Evidence: L495–L501.
|
||||
- **k8s Secret → 파드 환경 변수:** 주입. Evidence: L495–L501.
|
||||
- **k8s Secret → etcd:** 저장. Evidence: L1002–L1006.
|
||||
- **k8s Secret → 파드 환경 변수:** 주입. Evidence: L1002–L1006.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# base64 는 암호화가 아니다
|
||||
# Question: Secret 에 넣으면 값이 가려지는가
|
||||
direction: down
|
||||
direction: right
|
||||
n0: "k8s Secret" {
|
||||
shape: cylinder
|
||||
}
|
||||
|
||||