최근 Rust 기반의 한 체스 분석 서비스에서 약 4만 건의 JSON 레코드를 SQLite 데이터베이스에 저장하는 과정에서 수 분이 소요되고 CPU 코어 하나가 완전히 포화되는 예상치 못한 병목 현상이 발생했습니다. 개발팀은 데이터 저장 작업이 밀리초 단위로 끝날 것으로 예상했지만, 실제로는 대규모 데이터 처리 시 서비스 진행을 막는 심각한 문제로 드러났습니다.
문제의 원인은 Linux용 SQLite를 컴파일할 때 사용된 Zig 언어의 표준 C 라이브러리(libc) 재구현에 있었습니다. Zig는 C 의존성을 줄이기 위해 musl을 대체하는 자체 라이브러리인 libzigc를 개발 중인데, 이 과정에서 재작성된 strspn과 같은 문자열 처리 함수들이 문자열 전체 길이를 첫 단계에서 측정하도록 변경되었습니다. SQLite가 JSON 데이터를 토큰화(tokenization)하는 과정에서 공백을 건너뛰고 다음 위치로 이동할 때마다 남은 문자열의 길이를 다시 측정하게 되면서, 반복적인 문자열 순회가 발생해 이차 시간 복잡도(quadratic time complexity) 문제가 발생했습니다. 이로 인해 1.2MB 파일의 토큰화는 약 12,000배 느려지는 결과를 초래했습니다. 흥미롭게도 Zig를 사용하지 않고 컴파일된 Mac 버전에서는 이런 문제가 나타나지 않았습니다.
이 사례는 추상화 수준이 높아지더라도 기본적인 알고리듬과 라이브러리 구현 방식이 전체 시스템 성능에 얼마나 큰 영향을 미칠 수 있는지 보여줍니다. 마치 페인트공 슐레미엘(Shlemiel)이 페인트통을 옮기지 않고 도로를 칠해 작업이 진행될수록 왕복 시간이 길어지는 비유처럼, 사소해 보이는 라이브러리 함수의 구현 변경이 대규모 데이터 처리 시 치명적인 병목으로 이어질 수 있음을 상기시킵니다. 개발팀은 우선 JSON 공백을 미리 제거하는 임시방편으로 문제를 완화한 뒤, 최종적으로 Linux 빌드를 Linux 컨테이너로 이전하여 해결했습니다. 이 경험은 예상치 못한 성능 문제를 방지하기 위해 낮은 수준의 구현 세부 사항에도 경계를 늦추지 않아야 한다는 중요한 교훈을 제공합니다.