feat: 가상화 문서들 추가

This commit is contained in:
DongHyeonka
2026-09-10 08:54:05 +09:00
parent e9f6a93327
commit 43e1aadef0
695 changed files with 153404 additions and 12754 deletions
@@ -0,0 +1,28 @@
# fsync() 의 요구가 물리 storage 까지 내려가는 사슬
## Alternative text
PostgreSQL 의 fsync() 가 Guest Filesystem, Guest Block Layer, virtio-blk, QEMU / Backend, Host Storage Stack 을 차례로 지나 Physical Storage 에 닿는 위에서 아래로 읽는 흐름도.
## Long description
위에서 아래로 읽는다. 맨 위 PostgreSQL 이 fsync() 를 부르고 그 요구가 Guest Filesystem 으로 내려간다. Guest Block Layer 에서 FLUSH 등의 요청으로 바뀌고, virtio-blk 를 지나 VM 경계를 넘어 QEMU / Backend 로 간다. 거기서 Host Storage Stack 을 지나 맨 아래 Physical Storage 까지 그 의미가 전달되어야 완료가 성립한다. 여섯 번 손이 바뀌는 동안 한 곳이라도 그 의미를 지키지 않으면 게스트가 받은 완료 응답이 거짓이 된다. write() 완료와 writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 다르다는 구분은 본문의 코드블록이 맡는다.
## Elements and evidence
- **PostgreSQL** (service): 필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스. Evidence: L6742L6742, L6726L6734.
- **Guest Filesystem** (service): fsync() 요구를 처음 받는 게스트 파일시스템. Evidence: L6746L6746.
- **Guest Block Layer** (service): 요구를 FLUSH 등의 block 요청으로 바꾸는 계층. Evidence: L6749L6751.
- **virtio-blk** (service): 그 요청을 VM 경계 너머로 옮기는 게스트 driver. Evidence: L6753L6753.
- **QEMU / Backend** (service): 요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리. Evidence: L6756L6756, L6686L6686.
- **Host Storage Stack** (service): 호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간. Evidence: L6759L6759, L6688L6692.
- **Physical Storage** (store): 요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다. Evidence: L6762L6765, L6729L6729.
## Relationships
- **PostgreSQL → Guest Filesystem:** fsync(). Evidence: L6744L6746.
- **Guest Filesystem → Guest Block Layer:** control. Evidence: L6746L6749.
- **Guest Block Layer → virtio-blk:** FLUSH 등. Evidence: L6751L6753.
- **virtio-blk → QEMU / Backend:** control. Evidence: L6753L6756.
- **QEMU / Backend → Host Storage Stack:** control. Evidence: L6756L6759.
- **Host Storage Stack → Physical Storage:** control. Evidence: L6759L6765.
@@ -0,0 +1,30 @@
# fsync() 의 요구가 물리 storage 까지 내려가는 사슬
# Question: 게스트가 fsync() 를 부르면 그 요구는 어느 계층까지, 몇 번 손이 바뀌어 내려가야 하는가
direction: down
n0: "PostgreSQL" {
shape: rectangle
}
n1: "Guest Filesystem" {
shape: rectangle
}
n2: "Guest Block Layer" {
shape: rectangle
}
n3: "virtio-blk" {
shape: rectangle
}
n4: "QEMU / Backend" {
shape: rectangle
}
n5: "Host Storage Stack" {
shape: rectangle
}
n6: "Physical Storage" {
shape: rectangle
}
n0 -> n1: "fsync()"
n1 -> n2
n2 -> n3: "FLUSH 등"
n3 -> n4
n4 -> n5
n5 -> n6
@@ -0,0 +1,62 @@
<?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="write-completion-boundaries" name="fsync() 의 요구가 물리 storage 까지 내려가는 사슬">
<mxGraphModel dx="680" dy="1153" 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">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="n_postgresql" value="PostgreSQL&lt;br/&gt;write()&lt;br/&gt;fsync()" tooltip="필요한 시점에 fsync() 를 불러 영속성 경계까지 반영을 요청하는 게스트 프로세스. | Evidence: L6742-L6742, L6726-L6734" 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="75.0" y="60.0" width="150.0" height="88.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-filesystem" value="Guest Filesystem" tooltip="fsync() 요구를 처음 받는 게스트 파일시스템. | Evidence: L6746-L6746" 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="75.0" y="244.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_guest-block-layer" value="Guest Block Layer" tooltip="요구를 FLUSH 등의 block 요청으로 바꾸는 계층. | Evidence: L6749-L6751" 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="73.5" y="404.0" width="153.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_virtio-blk" value="virtio-blk" tooltip="그 요청을 VM 경계 너머로 옮기는 게스트 driver. | Evidence: L6753-L6753" 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="75.0" y="564.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_qemu-backend" value="QEMU / Backend" tooltip="요청을 받아 호스트 쪽 파일 I/O 나 block I/O 로 잇는 자리. | Evidence: L6756-L6756, L6686-L6686" 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="75.0" y="724.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_host-storage-stack" value="Host Storage Stack" tooltip="호스트 Page Cache 와 파일시스템, Block Layer 가 놓인 구간. | Evidence: L6759-L6759, L6688-L6692" 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="884.0" width="160.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="n_physical-storage" value="Physical Storage" tooltip="요구가 여기까지 전달되어야 정전 이후 생존을 말할 수 있다. | Evidence: L6762-L6765, L6729-L6729" 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="75.0" y="1044.0" width="150.0" height="64.0" as="geometry"/>
</mxCell>
<mxCell id="e_app-to-guest-fs" value="fsync()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_postgresql" target="n_guest-filesystem">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="196.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_guest-fs-to-block-layer" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-filesystem" target="n_guest-block-layer">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="356.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_block-layer-to-virtio" value="FLUSH 등" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_guest-block-layer" target="n_virtio-blk">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="516.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_virtio-to-qemu" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_virtio-blk" target="n_qemu-backend">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="676.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_qemu-to-host-stack" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_qemu-backend" target="n_host-storage-stack">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="836.0" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_host-stack-to-physical" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeWidth=2;endArrow=block;endFill=1;" edge="1" parent="1" source="n_host-storage-stack" target="n_physical-storage">
<mxGeometry relative="1" as="geometry">
<mxPoint x="174.0" y="996.0" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -0,0 +1,934 @@
{
"type": "excalidraw",
"version": 2,
"source": "techviz-harness",
"elements": [
{
"id": "edge-app-to-guest-fs",
"type": "arrow",
"x": 150.0,
"y": 148.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": 812660886,
"version": 1,
"versionNonce": 98088779,
"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-postgresql",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-guest-filesystem",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-app-to-guest-fs",
"type": "text",
"x": 129.0,
"y": 184.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": 106795923,
"version": 1,
"versionNonce": 801195523,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "fsync()",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "fsync()",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-guest-fs-to-block-layer",
"type": "arrow",
"x": 150.0,
"y": 308.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": 1352950235,
"version": 1,
"versionNonce": 1614356579,
"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-guest-filesystem",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-guest-block-layer",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-block-layer-to-virtio",
"type": "arrow",
"x": 150.0,
"y": 468.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": 653332640,
"version": 1,
"versionNonce": 706884381,
"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-guest-block-layer",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-virtio-blk",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-label-block-layer-to-virtio",
"type": "text",
"x": 129.0,
"y": 504.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": 352541713,
"version": 1,
"versionNonce": 1745807882,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 13,
"fontFamily": 5,
"text": "FLUSH 등",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "FLUSH 등",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "edge-virtio-to-qemu",
"type": "arrow",
"x": 150.0,
"y": 628.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": 1913137791,
"version": 1,
"versionNonce": 1422107469,
"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-virtio-blk",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-qemu-backend",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-qemu-to-host-stack",
"type": "arrow",
"x": 150.0,
"y": 788.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": 1785251986,
"version": 1,
"versionNonce": 1697013013,
"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-qemu-backend",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-host-storage-stack",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "edge-host-stack-to-physical",
"type": "arrow",
"x": 150.0,
"y": 948.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": 574245066,
"version": 1,
"versionNonce": 83280906,
"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-host-storage-stack",
"focus": 0,
"gap": 4
},
"endBinding": {
"elementId": "node-physical-storage",
"focus": 0,
"gap": 4
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": true
},
{
"id": "node-postgresql",
"type": "rectangle",
"x": 75.0,
"y": 60.0,
"width": 150.0,
"height": 88.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": 51288073,
"version": 1,
"versionNonce": 91686637,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-postgresql",
"type": "text",
"x": 85.0,
"y": 70.0,
"width": 130.0,
"height": 68.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": 308492034,
"version": 1,
"versionNonce": 936735075,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "PostgreSQL\nwrite()\nfsync()",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "PostgreSQL\nwrite()\nfsync()",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-guest-filesystem",
"type": "rectangle",
"x": 75.0,
"y": 244.0,
"width": 150.0,
"height": 64.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": 1533824948,
"version": 1,
"versionNonce": 1692132304,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-guest-filesystem",
"type": "text",
"x": 85.0,
"y": 254.0,
"width": 130.0,
"height": 44.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": 21094774,
"version": 1,
"versionNonce": 893088149,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Guest Filesystem",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Guest Filesystem",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-guest-block-layer",
"type": "rectangle",
"x": 73.5,
"y": 404.0,
"width": 153.0,
"height": 64.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": 197573630,
"version": 1,
"versionNonce": 1359380081,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-guest-block-layer",
"type": "text",
"x": 83.5,
"y": 414.0,
"width": 133.0,
"height": 44.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": 1437945492,
"version": 1,
"versionNonce": 1941177307,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Guest Block Layer",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Guest Block Layer",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-virtio-blk",
"type": "rectangle",
"x": 75.0,
"y": 564.0,
"width": 150.0,
"height": 64.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": 805183008,
"version": 1,
"versionNonce": 387189353,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-virtio-blk",
"type": "text",
"x": 85.0,
"y": 574.0,
"width": 130.0,
"height": 44.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": 1466921391,
"version": 1,
"versionNonce": 178652417,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "virtio-blk",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "virtio-blk",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-qemu-backend",
"type": "rectangle",
"x": 75.0,
"y": 724.0,
"width": 150.0,
"height": 64.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": 88754261,
"version": 1,
"versionNonce": 960115709,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-qemu-backend",
"type": "text",
"x": 85.0,
"y": 734.0,
"width": 130.0,
"height": 44.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": 18150914,
"version": 1,
"versionNonce": 118285455,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "QEMU / Backend",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "QEMU / Backend",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-host-storage-stack",
"type": "rectangle",
"x": 70.0,
"y": 884.0,
"width": 160.0,
"height": 64.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": 185667777,
"version": 1,
"versionNonce": 165868748,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-host-storage-stack",
"type": "text",
"x": 80.0,
"y": 894.0,
"width": 140.0,
"height": 44.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": 1032766674,
"version": 1,
"versionNonce": 1275711067,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Host Storage Stack",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Host Storage Stack",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "node-physical-storage",
"type": "rectangle",
"x": 75.0,
"y": 1044.0,
"width": 150.0,
"height": 64.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": 748275136,
"version": 1,
"versionNonce": 1258638582,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false
},
{
"id": "node-label-physical-storage",
"type": "text",
"x": 85.0,
"y": 1054.0,
"width": 130.0,
"height": 44.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": 1619302994,
"version": 1,
"versionNonce": 56126538,
"isDeleted": false,
"boundElements": [],
"updated": 0,
"link": null,
"locked": false,
"fontSize": 15,
"fontFamily": 5,
"text": "Physical Storage",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Physical Storage",
"autoResize": true,
"lineHeight": 1.25
}
],
"appState": {
"gridSize": 10,
"viewBackgroundColor": "#ffffff",
"currentItemFontFamily": 5
},
"files": {}
}
@@ -0,0 +1,31 @@
{
"harness_version": "0.2.0",
"spec_id": "write-completion-boundaries",
"spec_version": "1.1",
"spec_sha256": "c7838bc8393b43c94ed4628db350f0fb26e90fc0d566201e471d49d533963699",
"source_context": {
"document": "docs/virtualization/final/document.md",
"document_sha256": "181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd",
"anchor": {
"kind": "heading",
"value": "150. `fsync()`가 필요한 이유",
"line": 6723
}
},
"outputs": [
"write-completion-boundaries.svg",
"write-completion-boundaries.drawio",
"write-completion-boundaries.mmd",
"write-completion-boundaries.d2",
"write-completion-boundaries.excalidraw",
"write-completion-boundaries.alt.md"
],
"lint_issue_count": 4,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,16 @@
%% fsync() 의 요구가 물리 storage 까지 내려가는 사슬
%% question: 게스트가 fsync() 를 부르면 그 요구는 어느 계층까지, 몇 번 손이 바뀌어 내려가야 하는가
flowchart TB
n0["PostgreSQL"]
n1["Guest Filesystem"]
n2["Guest Block Layer"]
n3["virtio-blk"]
n4["QEMU / Backend"]
n5["Host Storage Stack"]
n6["Physical Storage"]
n0 -->|"fsync()"| n1
n1 --> n2
n2 -->|"FLUSH 등"| n3
n3 --> n4
n4 --> n5
n5 --> n6
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="1153" viewBox="0 0 680 1153" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">fsync() 의 요구가 물리 storage 까지 내려가는 사슬</title>
<desc id="diagram-description">위에서 아래로 읽는다. 맨 위 PostgreSQL 이 fsync() 를 부르고 그 요구가 Guest Filesystem 으로 내려간다. Guest Block Layer 에서 FLUSH 등의 요청으로 바뀌고, virtio-blk 를 지나 VM 경계를 넘어 QEMU / Backend 로 간다. 거기서 Host Storage Stack 을 지나 맨 아래 Physical Storage 까지 그 의미가 전달되어야 완료가 성립한다. 여섯 번 손이 바뀌는 동안 한 곳이라도 그 의미를 지키지 않으면 게스트가 받은 완료 응답이 거짓이 된다. write() 완료와 writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 다르다는 구분은 본문의 코드블록이 맡는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;write-completion-boundaries&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;docs/virtualization/final/document.md&quot;,&quot;document_sha256&quot;:&quot;181e2b3cc8a45bae81e7e8193d026937c4d586ae4495d3b2c323eb7e6abcfadd&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;heading&quot;,&quot;value&quot;:&quot;150. `fsync()`가 필요한 이유&quot;,&quot;line&quot;:6723}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;: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" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="1153" />
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,148.0 150.0,196.0 150.0,196.0 150.0,244.0" data-evidence="6744-6746" />
<rect class="edge-label-bg" x="141.6" y="182.0" width="64.9" height="22" rx="3" />
<text class="edge-label" x="174.0" y="197.0">fsync()</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,308.0 150.0,356.0 150.0,356.0 150.0,404.0" data-evidence="6746-6749" />
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,468.0 150.0,516.0 150.0,516.0 150.0,564.0" data-evidence="6751-6753" />
<rect class="edge-label-bg" x="141.6" y="502.0" width="64.9" height="22" rx="3" />
<text class="edge-label" x="174.0" y="517.0">FLUSH 등</text>
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,628.0 150.0,676.0 150.0,676.0 150.0,724.0" data-evidence="6753-6756" />
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,788.0 150.0,836.0 150.0,836.0 150.0,884.0" data-evidence="6756-6759" />
<polyline class="edge kind-control style-solid emphasis-normal" points="150.0,948.0 150.0,996.0 150.0,996.0 150.0,1044.0" data-evidence="6759-6765" />
<g id="node-postgresql">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="6742-6742,6726-6734" x="75.0" y="60.0" width="150.0" height="88.0" rx="7" />
<text class="node-label" x="150.0" y="87.0">PostgreSQL</text>
<line class="node-detail-divider" x1="89.0" y1="108.0" x2="211.0" y2="108.0" />
<text class="node-detail" x="91.0" y="125.0">write()</text>
<text class="node-detail" x="91.0" y="141.0">fsync()</text>
</g>
<g id="node-guest-filesystem">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6746-6746" x="75.0" y="244.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="274.0">Guest Filesystem</text>
</g>
<g id="node-guest-block-layer">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6749-6751" x="73.5" y="404.0" width="153.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="434.0">Guest Block Layer</text>
</g>
<g id="node-virtio-blk">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6753-6753" x="75.0" y="564.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="594.0">virtio-blk</text>
</g>
<g id="node-qemu-backend">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6756-6756,6686-6686" x="75.0" y="724.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="754.0">QEMU / Backend</text>
</g>
<g id="node-host-storage-stack">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="6759-6759,6688-6692" x="70.0" y="884.0" width="160.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="914.0">Host Storage Stack</text>
</g>
<g id="node-physical-storage">
<rect class="node-shape kind-store emphasis-primary role-sink" data-evidence="6762-6765,6729-6729" x="75.0" y="1044.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="150.0" y="1074.0">Physical Storage</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB