8.7 KiB
id, kind, slug, title, topic, topicName, project, status, studio, sourceRevision, source
| id | kind | slug | title | topic | topicName | project | status | studio | sourceRevision | source | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| decbab8d-9c98-45c6-b5b8-e6162e407299 | REFERENCE | a-speedup-that-removed-durability-is-not-an-optimization | 빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다 | storage-virtualization | 스토리지 가상화 | virtualization | 초안 | https://hyeonworks.com/studio/documents/decbab8d-9c98-45c6-b5b8-e6162e407299/edit | no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다 |
|
빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다
durability 는 전원이 나가도 데이터가 남아 있는 영속성을 말한다. 쓰기 지연이 줄면 그 변경은 성능 개선으로 기록되지만, 무엇을 더 이상 보장하지 않게 됐는지는 아무 데도 적히지 않는다. 개념 문서는 fsync() 를 없애서 빨라졌다면 그것이 최적화가 아니라 durability contract 를 제거한 것일 수 있다고 적었다. 게스트가 요청한 FLUSH 에 완료로 응답했는데 데이터는 호스트 RAM 에만 있는 상태도 성능 문제가 아니라 correctness 문제로 놓았다. 이 기준은 빨라진 구성을 평가할 때 어느 단계가 빠졌는지를 먼저 적게 한다.
관계
- 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존 이 기준이 갈라 적으라고 하는 네 단계를 그 글이 설명한다.
- /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device 영속성을 어느 백엔드 위에서 이야기하는지 그 글이 설명한다.
- 이 VM 들의 QEMU disk cache mode 는 무엇인가 이 기준이 걸리는 설정 값을 이 환경에서 읽는 물음이다.
- Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가 영속성을 지킨 상태의 지연이 얼마인지 이 환경에서 재는 물음이다.
- 지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다 같은 스토리지 판독에 쓰는 기준이고, 그쪽은 자원을 가르고 이쪽은 보장을 가른다.
목적
스토리지 설정을 바꿔 쓰기 지연이 줄었을 때 그것을 성능 항목으로만 적으면 어떤 실패에 노출됐는지가 기록에 남지 않는다. 개념 문서가 가장 위험한 상황으로 든 것은 거짓 완료다. 게스트가 WRITE 와 FLUSH 를 요청했는데 데이터는 호스트 RAM 에 있고 물리 스토리지에는 옛 데이터가 있는 상태인데도 게스트에게 FLUSH 완료라고 응답하는 경우다. 그러면 PostgreSQL 은 영속성이 확보되었다고 판단하고, 직후 호스트 전원이 나가면 RAM 의 데이터가 사라진다.
이 기준은 빨라진 것을 되돌리라고 하지 않는다. 무엇이 빨라졌는지 옆에 어느 보장이 사라졌는지를 같이 적게 한다.
이것을 완료의 네 단계를 설명하는 개념 기록 안의 각주로 두지 않고 따로 뺀 것은 이것이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
규칙
1. 완료를 네 단계로 갈라 어디까지 보장되는지 적는다
개념 문서는 write() 완료와 writeback 완료와 fsync/flush 완료와 전원 장애에도 안전한 durability 를 서로 다른 것으로 놓았다. write(fd, data, size) 가 성공했다는 것만으로 정전 이후 생존이 보장되지 않으므로, 필요한 시점에 fsync(fd) 를 불러 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다. 어떤 구성을 적을 때 이 네 단계 중 어디까지가 보장되는지를 함께 적는다.
2. 빨라진 구성에서 사라진 경계를 이름으로 적는다
무엇이 몇 밀리초 줄었는지보다 먼저, 그 변경이 위 네 단계 중 어느 것을 건너뛰게 했는지를 적는다. 개념 문서가 그린 전달 경로는 게스트가 요구한 durability 에서 시작한다. 거기서 Guest Filesystem, Guest Block Layer, virtio, QEMU 와 backend, Host Storage, Device 로 이어진다. 이 경로는 어디에서 의미가 끊겨도 결과가 같아서, 끊긴 곳을 짚지 못하면 그 구성이 무엇을 보장하는지 다음 사람이 다시 조사해야 한다.
3. 거짓 완료를 성능 항목으로 분류하지 않는다
FLUSH 완료 응답을 받은 쪽은 그 데이터가 살아남는다고 가정하고 그 다음 동작을 한다. PostgreSQL 이라면 COMMIT 을 성공으로 처리한다. 이 가정이 틀린 상태는 느려지는 것이 아니라 응답한 내용이 사실과 다른 것이므로, 개념 문서는 이것을 durability contract 가 깨지는 correctness 문제로 적었다.
4. 캐시 모드를 이름만 보고 안전과 위험으로 가르지 않는다
개념 문서는 none 을 캐시 자체가 없음으로, writeback 을 무조건 위험으로 읽는 것이 부정확하다고 적었다. cache=writeback 을 골라도 정상적인 스택이라면 게스트의 fsync 와 FLUSH 는 virtio FLUSH 와 QEMU 와 백엔드를 지나 호스트의 sync 와 flush 로 전달된다. 필요한 완료 확인 뒤에 게스트로 완료가 돌아간다. 개념 문서는 정확한 표현을 이렇게 적었다 — writeback caching 에서는 volatile cache 가 존재할 수 있으므로 Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다.
5. DB 부하에서는 애플리케이션이 쓰는 영속성 규약을 함께 적는다
PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점에 스토리지 동기화를 수행한다. 가상 머신의 스토리지 계층이 flush 와 fsync 의 의미를 제대로 보존하지 않으면 애플리케이션이 전제한 영속성과 실제 스토리지 동작이 어긋난다. 그래서 설정만 적지 않고 그 위에서 도는 애플리케이션이 무엇을 언제 동기화하는지도 같이 적는다.
적용 조건
- 스토리지 관련 설정이나 코드를 바꿔 쓰기 지연이나 커밋 지연이 줄었을 때
- QEMU 의 캐시 모드를 고르기 전
- Direct I/O 를 켜거나 끄기 전
- fsync() 호출을 더하거나 뺄 때
- 파일시스템의 마운트 옵션을 고를 때
예외
- 영속성을 실제로 포기해도 되는 데이터가 있다. 다시 만들 수 있는 캐시나 파생 파일이 그렇고, 그때는 빨라진 것이 정당한 선택이다. 이 기준이 요구하는 것은 포기를 막는 것이 아니라 무엇을 포기했는지 적게 하는 것이다.
- cache=none 이나 Direct I/O 를 골랐다고 영속성이 확보되지도 않는다. 개념 문서는 Direct I/O 의 핵심이 페이지 캐시 우회이며 호스트 페이지 캐시를 우회하는 것이 즉시 durable media 반영을 뜻하지 않는다고 적었고, 그 뒤에 스토리지 컨트롤러나 장치가 휘발성 쓰기 캐시를 가질 수 있다고 덧붙였다.
- 개념 문서는 실제 ordering 과 durability semantics 가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
- 이 저장소에는 영속성을 실제로 재 본 실험이 없다. 여기 적은 것은 개념 문서가 서술한 구분이고 이 호스트에서 확인된 동작이 아니다.
예시
- write() 완료 : 애플리케이션 호출이 돌아온 상태
- writeback 완료 : 캐시에서 아래로 내려간 것까지다
- fsync/flush 완료 : 여기서도 장치 쪽 휘발성 캐시가 뒤에 남을 수 있다
- 전원 장애에도 안전한 상태 : 위 셋과 다른 단계로 센다
- 빨라졌다 : 어느 단계를 건너뛰었는지 옆에 적는다
- 포기해도 되는 데이터 : 다시 만들 수 있는 캐시 · 파생 파일
- 이 호스트에서 잰 영속성 측정 : x