Files
document-haness/docs/virtualization/tech-log-studio/storage-virtualization/question/question-guest-fsync-latency-vs-host-storage-latency.md
T
DongHyeonkaandClaude Opus 5 2109f726fe feat(pipeline): keycloak-session-store 25편·virtualization 59편을 S3→S5→S6 으로 돌린다
기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만
있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문
종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다.

SSOT 결함 둘을 고쳤다.

- kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이
  「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽
  `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다.
- virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데
  원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다.

기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은
둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더
든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다).

계약을 셋 고쳤다.

- kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/**
  28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는
  것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온
  것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수
  대조했을 때 가장 가까운 커밋도 28개가 어긋났다).
- virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고
  재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다.
- kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고
  넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다.

style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져
(실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고,
engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고
있었다(Question 기록에서 11.94 → 3.86).

verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:01:55 +09:00

10 KiB

id, kind, slug, title, topic, topicName, project, status, questionStatus, studio, sourceRevision, source
id kind slug title topic topicName project status questionStatus studio sourceRevision source
be3c2c58-1ed4-4eae-876a-3114faa61c94 QUESTION guest-fsync-latency-vs-host-storage-latency Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가 storage-virtualization 스토리지 가상화 virtualization 초안 OPEN https://hyeonworks.com/studio/documents/be3c2c58-1ed4-4eae-876a-3114faa61c94/edit no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-8
final/document.md#150-fsync-가-필요한-이유
final/document.md#169-storage-관측-명령어
final/document.md#170-postgresql-예시-wal과-durability
final/document.md#171-성능과-durability의-trade-off
final/document.md#148-write-완료와-영속화는-다르다

Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가

게스트 안의 fsync() 는 게스트 파일시스템, 블록 계층, virtio-blk, QEMU 를 지나 호스트 스토리지까지 의미가 전달되어야 하는 요청이다. 그 전달이 이 호스트에서 실제로 이어지는지는 두 계열을 같은 시간축에 놓고 봐야 갈린다. 이 물음은 게스트 쪽 애플리케이션 지연과 호스트 쪽 스토리지 지연을 같은 타임스탬프로 남겨, 둘이 함께 움직이는지를 확인한다.

관계

  • 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존 게스트가 받은 완료 응답이 어느 뜻인지를 그 개념이 가른다.
  • Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk fsync() 가 전달되어야 하는 앞쪽 경로를 그 개념이 단계로 설명한다.
  • 빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다 두 계열이 갈렸을 때 지연을 줄이는 방향으로 가려면 그 기준을 지나야 한다.
  • 지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다 게스트 지연이 올랐을 때 CPU 가 아니라 스토리지를 먼저 보는 순서를 그 기준이 요구한다.
  • 이 VM 들의 QEMU disk cache mode 는 무엇인가 그 값이 이 측정의 조건이므로 같은 기록에 함께 적는다.
  • VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가 같은 호스트 지표를 보지만 그쪽은 다른 가상 머신의 부하가 넘어오는지를 묻는다.
  • 호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가 호스트 쪽에서 스토리지를 미는 다른 원인을 그 물음이 따로 본다.

사실

  • §150 은 write(fd, data, size) 의 성공만으로는 정전 이후 생존을 보장하지 않고, 필요한 시점에 fsync(fd) 로 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다고 적었다.
  • §150 은 가상 머신에서 그 요청이 지나야 하는 경로를 이렇게 그렸다. PostgreSQL, fsync(), Guest Filesystem, Guest Block Layer, FLUSH 등, virtio-blk, QEMU/Backend, Host Storage Stack, Physical Storage
  • §148 은 write() 완료, writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 같지 않다고 못박았다.
  • §170 은 PostgreSQL 이 WAL 등의 durability protocol 을 사용하며 필요한 시점에 스토리지 동기화를 수행한다고 적었다. 가상 머신의 스토리지 계층이 flush 와 fsync 의 의미를 제대로 보존하지 않으면 PostgreSQL 이 전제한 영속성과 실제 스토리지 동작이 어긋날 수 있다.
  • §171 은 모든 쓰기에서 스토리지 동기화를 기다리면 지연이 커질 수 있고, 특히 DB 부하에서는 fsync() 지연이 트랜잭션 지연과 연결될 수 있다고 적었다.
  • §171 은 더 적극적인 캐싱으로 쓰기 지연을 개선할 수 있지만 durability semantics 는 반드시 보존해야 한다고 덧붙였다. fsync() 를 없애서 빨라졌다면 그것이 최적화가 아니라 durability contract 를 제거한 것일 수 있다.
  • §169 는 호스트 관측으로 iostat -xz 1 과 iotop 을 들고, 게스트 쪽 확인 명령으로 lsblk · mount · df -h · cat /proc/mounts · iostat -xz 1 을 적었다.
  • §175 OQ-8 은 게스트의 애플리케이션 지연과 호스트 iostat 를 시간축으로 함께 관찰하라고 했다.
  • 이 호스트에서 두 계열을 재 본 값은 개념 문서에 없다.

가정

  • 게스트에서 애플리케이션이나 데이터베이스의 지연을 시간축으로 기록할 수 있다고 본다. 그 기록에 fsync() 자체의 지연이 따로 나오는지, 아니면 트랜잭션 지연으로만 보이는지는 해 보기 전에 알 수 없다.
  • 호스트와 게스트의 시계가 맞아 두 계열을 같은 타임스탬프에 놓을 수 있다고 전제한다. 어긋나 있으면 함께 움직였다는 판정 자체가 성립하지 않는다.
  • 부하가 없는 구간과 있는 구간을 만들 수 있고, 두 구간에서 같은 방법으로 잴 수 있다고 본다.
  • 측정 중에 캐시 모드를 비롯한 디스크 설정이 바뀌지 않는다고 두고 두 구간을 견준다.
  • 같은 장치를 쓰는 다른 가상 머신이나 호스트 프로세스의 입출력이 섞일 수 있다고 보고, 어느 프로세스가 입출력을 내는지 iotop 으로 함께 남긴다.

미지수

  • 게스트의 fsync() 나 트랜잭션 지연이 커지는 구간과 호스트 스토리지 지연이 커지는 구간이 시간축에서 겹치는지.
  • 겹친다면 두 값이 같은 크기로 움직이는지, 게스트 쪽이 더 크게 벌어지는지.
  • 게스트 지연은 오르는데 호스트 스토리지 지연이 따라 오르지 않는 구간이 있는지. 있다면 그 차이가 게스트 쪽 대기에서 생기는지 QEMU 와 백엔드 쪽에서 생기는지.
  • 이때 걸려 있던 캐시 모드가 무엇인지. 그 값은 별도 물음이 읽는다.
  • 게스트 쪽 지연을 무엇으로 어떤 단위로 기록할지. §169 가 게스트 쪽에 적은 명령은 장치 입출력과 마운트를 보는 것이라, 애플리케이션이나 트랜잭션 지연은 거기서 나오지 않는다.

제약

  • 이 물음은 영속성을 줄여 지연을 낮추는 방향을 검토하지 않는다. §171 이 fsync() 를 없애 빨라진 것을 최적화로 부르지 말라고 적었고, 그런 방향은 별도 기준을 지나야 한다.
  • 두 계열을 같은 시각에 남기지 못하면 이 물음은 닫히지 않는다. 나중에 따로 잰 값을 겹쳐 놓고 함께 올랐다고 적지 않는다.
  • 측정 조건에 캐시 모드와 백엔드 형식과 장치 이름을 함께 적는다. 이 셋이 없으면 같은 값을 다른 환경과 견줄 수 없다.
  • 측정하는 동안 다른 스토리지 실험을 같은 장치 위에서 겹쳐 돌리지 않는다.
  • 호스트 지연이 오른 이유까지 이 물음이 가르지 않는다. 다른 가상 머신의 부하인지 호스트 메모리 압박인지는 그것을 재는 두 물음이 받는다.
  • 그 가운데 메모리 압박 쪽 물음과는 호스트 스토리지 지표를 같이 본다. 그래도 한 물음으로 합치지 않았다. 그쪽은 호스트에서 major fault 를 유도해 그것이 스토리지를 미는지를 보고, 이쪽은 게스트의 fsync() 가 호스트까지 전달되는지를 본다. 유도 방법도 견주는 계열도 달라서 한 실행으로 닫히지 않는다.

선택지

1. 부하 없는 구간과 있는 구간을 각각 찍어 두 계열을 겹친다

게스트 쪽 애플리케이션과 데이터베이스의 지연을 기록하면서 같은 시각에 호스트에서 iostat -xz 1 을 돌린다. 두 구간을 같은 방법으로 남기면 부하가 들어왔을 때 두 계열이 함께 움직였는지 그 자료에서 읽힌다. §175 OQ-8 이 적은 방법 그대로다.

게스트 쪽 지연을 무엇으로 기록할지 먼저 정해야 한다.

2. 데이터베이스 트랜잭션 부하만 걸고 fsync 쪽만 본다

§170 이 그린 WAL 경로를 따라 쓰기 위주의 트랜잭션을 돌리면 fsync() 가 지연에 직접 걸리는 구간을 만들기 쉽다. 두 계열이 어긋나는 구간을 찾기에도 이 부하가 낫다.

읽기 위주 구간에서는 어떻게 움직이는지 못 본다. §171 은 DB 부하를 예로 들었을 뿐 다른 부하를 배제하지 않았다.

3. 제외 — 캐시 모드를 바꿔 가며 두 계열을 비교한다

cache=none 과 cache=writeback 에서 두 계열이 어떻게 달라지는지 보면 전달 경로의 모양이 더 뚜렷해진다. 그러나 지금 필요한 것은 현재 구성에서 경로가 이어지는지 하나이고, 설정을 바꾸면 두 구간이 서로 다른 구성에서 나온 값이 된다. 현재 값이 무엇인지도 아직 읽지 않았다.

다음 검증

  1. 캐시 모드를 묻는 물음의 결과를 측정 조건으로 먼저 적는다. 함께 적을 것은 백엔드 형식과 이미지가 놓인 장치 이름이다.
  2. 게스트에서 애플리케이션이나 데이터베이스의 지연을 시간축으로 기록한다. 무엇으로 어떻게 기록했는지 함께 남긴다.
  3. 같은 시각에 호스트에서 iostat -xz 1 을 돌려 두 계열을 같은 타임스탬프로 남긴다 (§175 OQ-8).
  4. 부하가 없는 구간과 있는 구간을 모두 찍고, 어느 프로세스가 입출력을 내는지 iotop 으로 함께 적는다 (§169).

닫는 조건 : 두 계열이 같은 구간에서 함께 오르면 §150 이 그린 전달 경로가 이 환경에서 이어진다는 것을 확인한 것이고, 그 시계열이 Case 가 된다. 게스트의 fsync() 지연은 오르는데 호스트 스토리지 지연이 따라 오르지 않으면 그 차이가 어느 계층에서 생기는지 — 게스트 쪽 대기인지 QEMU 와 백엔드 쪽인지 — 를 다음 물음으로 넘기고 닫는다. 어느 쪽이든 성능을 위해 영속성을 줄이는 방향은 「빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다」를 지나야 한다.