jit-compilation

1 개의 포스트

Unraveling a Postgres segfault that uncovered an Arm64 JIT compiler bug (새 탭에서 열림)

Postgres 서버에서 발생한 'Segmentation fault(signal 11)' 오류의 원인을 추적하여, 이것이 Postgres 자체의 결함이 아니라 Arm64 아키텍처 환경에서 작동하는 LLVM(JIT 컴파일러)의 버그임을 밝혀낸 과정을 다루고 있습니다. 대규모 파티션 테이블을 조회할 때 JIT가 활성화되면서 크래시가 발생했으며, 이를 통해 업스트림 LLVM의 문제를 해결하는 성과를 거두었습니다. ## JIT 컴파일과 크래시의 상관관계 * **세그멘테이션 폴트 발생**: 최신 버전의 Postgres를 사용 중임에도 특정 쿼리(죽음의 쿼리)를 실행하면 서버가 즉시 종료되는 현상이 발생했습니다. * **스택 오염 확인**: 코어 덤프 분석 결과, 호출 스택(Backtrace)이 비정상적으로 짧고 깨져 있었으며, 이는 JIT 실행 함수인 `ExecRunCompiledExpr` 부근에서 문제가 발생했음을 시사했습니다. * **트리거 조건**: 64개의 파티션과 160만 개 이상의 행을 가진 대규모 테이블을 스캔할 때, Postgres의 비용 기반 휴리스틱에 의해 JIT 컴파일이 활성화되면서 오류가 유발되었습니다. ## Postgres JIT와 LLVM의 역할 * **성능 최적화**: Postgres는 반복적인 SQL 표현식 평가 오버헤드를 줄이기 위해 LLVM을 사용하여 런타임에 네이티브 머신 코드를 생성합니다. * **튜플 디포밍(Tuple Deforming)**: 디스크상의 데이터를 메모리 표현으로 변환하는 과정을 네이티브 코드로 컴파일하여 처리 효율을 극대화합니다. * **컴파일 오버헤드**: JIT는 실행 속도를 높이지만 컴파일 시간이 추가되므로, Postgres는 실행 비용이 높은 쿼리에 대해서만 선택적으로 JIT를 적용합니다. ## 문제 해결 및 근본 원인 파악 * **임시 조치**: `SET jit = off;` 설정을 통해 JIT 기능을 비활성화함으로써 쿼리 지연 시간의 큰 손해 없이 프로덕션 환경의 크래시를 즉시 중단시켰습니다. * **디버깅 결과**: 데이터 복제 및 로컬 환경 재현을 통해 분석한 결과, 특정 조건에서 LLVM이 Arm64 아키텍처용 머신 코드를 잘못 생성하는 버그가 있음을 확인했습니다. * **업스트림 기여**: 이 조사는 단순히 설정을 변경하는 것에 그치지 않고, 어셈블리 수준의 분석을 통해 LLVM 프로젝트의 코드 수정까지 이끌어내는 계기가 되었습니다. ## 권장 사항 Arm64 기반 클라우드 환경에서 Postgres를 운영 중인데 원인을 알 수 없는 Segmentation fault가 발생한다면, 우선적으로 JIT를 비활성화하여 안정성을 확보하십시오. 이후 시스템의 LLVM 라이브러리 버전을 확인하고 관련 아키텍처 패치가 적용되었는지 점검하는 것이 필요합니다.