TL;DR
- 스왑을 사용하는 환경에서 Go 가비지 컬렉터(GC)가 스왑된 메타데이터를 읽으면, 최악의 경우 40ms 정지 시간(stop-the-world pause)이 발생함.
- 40ms 정지 중 발생한 228건의 페이지 폴트(page fault)에 약 39ms가 소요됨.
- GC 메타데이터는 힙 외부에 할당되고 해제되지 않지만, 재사용되며 GC 주기마다 읽혀 스왑 대상이 될 수 있음.
- 메타데이터 페이지 폴트에 따른 정지는 모든 Go 실행 프로세서(P)를 멈추지만, 메시지 할당 지연은 할당을 수행하는 고루틴에 국한됨.
- 실험에서 511KiB 메시지 하나를 만드는 데 NVMe에서는 105ms, Hetzner 네트워크 볼륨에서는 903ms가 걸렸으며, Go 1.26의 Green Tea GC 메타데이터 읽기 방식 변경 효과는 미미함.
스왑 환경과 실험
- 프로덕션 메모리 급증에 대비해 스왑을 사용하려던 중, 한 제어 그룹(cgroup)에 두 프로세스를 두고 실험함. 하나는
io.ReadAll뒤proto.Unmarshal을 호출해 바이트 덩어리와 Go 할당기가 스캔 대상으로 표시하는 그래프 구조체를 만드는 Go 프로세스이며, 다른 하나는 대부분 유휴 상태인 HTTP 서버임. - GC는 실행될 때 스캔 대상 메모리 구간(span)을 포인터 단위로 읽고 처리 방식을 결정함. 메모리 압박 시 커널이 프로세스별이 아니라 cgroup 단위로 페이지를 스왑하므로, 커널과 GC 사이에서 스왑 인·아웃이 반복될 가능성은 작을 것으로 예상했으나 실제로는 그렇지 않았음.
- MGLRU를 활성화한 커널 6.8의 Hetzner 장비에서 모의 실험을 진행함. 실험 자료에는 그래프, 모의 할당기, BPF 스크립트와 파이썬 스크립트가 포함되며, 실험 자료 전체에서 확인할 수 있음.
- 일반적인 정지 시간 중앙값은 약 51µs였지만, 메타데이터가 NVMe에 스왑된 경우 최악의 정지 시간은 40ms였음.
정지 시간의 원인
- 세계 정지 중 페이지 폴트를 세는 BPF 스크립트로 지연을 추적함. 최악의 사례는 정지 시간 39,902µs, 페이지 폴트 228건, 폴트 처리 시간 39,013µs로, 약 40ms 가운데 39ms가 페이지 폴트에서 발생함.
- 폴트는 GC의 내부 관리 작업에서 발생함. 호출 경로는
runtime.(*spanSet).reset,runtime.finishsweep_m,runtime.gcStart.func2,runtime.systemstack,runtime.gcStart와runtime.nextMarkBitArenaEpoch,runtime.finishsweep_m을 포함함. - Go GC는 스윕 종료(sweep termination)와 마크 종료(mark termination) 시점에 세계를 정지해야 함. 실험에서는 30분 동안 이런 정지가 312회 발생함.
- 런타임이 메타데이터 페이지를 할당한 뒤 해제하지 않고 재사용하며, GC 주기마다 해당 페이지를 읽음. 커널은 접근 시점이 오래된 페이지를 스왑하므로 GC가 세계를 정지한 상태에서 해당 페이지를 다시 읽으면 메이저 페이지 폴트(major page fault)가 발생함.
- 메이저 폴트 처리에는 PTE 읽기,
do_swap_page호출, 새 프레임 확보, cgroup에 대한 메모리 요금 부과, 페이지 읽기, 블록 입출력(bio) 제출과 디스크 대기가 포함됨.
서비스에 미치는 영향
- 40ms는 일반적인 정지 시간 중앙값의 800배이며, 실험 중 메모리 급증 한 번마다 두세 차례 발생함.
- 세계 정지 중에는 모든 Go 실행 프로세서(P)가 멈춤. 따라서 입출력을 기다리던 고루틴의 입출력이 정지 중에 완료되더라도 이를 처리할 실행 주체가 없을 수 있음.
- 511KiB 메시지 하나를 만드는 작업은 보통 3~5ms가 걸리지만, NVMe에서는 105ms, Hetzner 네트워크 볼륨에서는 903ms로 늘어남. 이 지연은 메타데이터 정지 시간보다 크지만, 할당을 수행하는 고루틴에 국한되며 전역 정지는 아님.
- 메시지 생성 지연이 어디에서 발생하는지는 확인하지 못했으나, 스왑 사용 시 추가로 감당해야 할 비용임.
스왑 자체가 악은 아니지만, 이 실험에서는 GC와 잘 맞지 않았으며 프로덕션에서 GC 작업이 많음.
업데이트: 9월 14일
- Go 1.26의 Green Tea GC가 GC의 메타데이터 읽기 방식을 바꾸었는지 질문을 받아 측정했으며, 그 영향은 미미함.
- 실험 자료 전체는 저장소에서 확인할 수 있음.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요