SQLite는 어떻게 테스트되는가
(sqlite.org)- SQLite는 철저한 자동화 테스트 체계를 통해 높은 신뢰성과 견고성을 유지하며, 코드보다 590배 많은 테스트 코드가 존재함
- 네 가지 독립 테스트 하니스(TCL, TH3, SQL Logic Test, dbsqlfuzz) 가 핵심 라이브러리를 검증하며, 수억 건의 테스트를 수행
- 이상 상황 테스트(OOM, I/O 오류, 크래시 시뮬레이션) 와 퍼즈 테스트(fuzz testing) 를 통해 비정상 입력과 시스템 장애에도 안정적으로 동작함을 확인
- 100% 분기 및 MC/DC 커버리지, 리소스 누수 검출, Valgrind·정적 분석·체크리스트 등 다층적 검증 절차를 유지
- 이러한 체계적 테스트 덕분에 SQLite는 상용 DB 수준의 신뢰성과 품질을 확보한 오픈소스 데이터베이스로 평가됨
1. 개요
- SQLite의 신뢰성과 견고성은 세밀한 테스트 과정에서 비롯됨
- 버전 3.42.0 기준, SQLite는 약 155.8 KSLOC의 C 코드와 92053.1 KSLOC의 테스트 코드로 구성
- 테스트 체계는 4개의 독립 하니스, 100% 분기 커버리지, 수백만 건의 테스트 케이스를 포함
- OOM, I/O 오류, 크래시, 퍼즈, 경계값, 회귀, 비정상 DB 파일, 최적화 비활성화 테스트 등 다수 항목 포함
2. 테스트 하니스
-
TCL Tests
- SQLite 개발 중 주로 사용되는 공개 테스트 세트
- 27.2 KSLOC의 C 코드와 1390개 스크립트 파일(23.2MB)로 구성
- 약 5만여 개 테스트 케이스, 매개변수화로 전체 실행 시 수백만 건 수행
-
TH3
- 상용 C 기반 테스트 세트로 100% 분기 및 MC/DC 커버리지 달성
- 임베디드 환경에서도 동작하며, 1055.4 KSLOC·약 5만여 케이스 포함
- 전체 커버리지 테스트 시 약 2.4백만 건, 릴리스 전 2.48억 건 soak 테스트 수행
-
SQL Logic Test (SLT)
- SQLite와 PostgreSQL, MySQL, SQL Server, Oracle 10g 결과를 비교
- 7.2백만 쿼리, 1.12GB 데이터로 구성
-
dbsqlfuzz
- SQL과 데이터베이스 파일을 동시에 변형하는 libFuzzer 기반 퍼저
- 하루 약 10억 번의 변이 테스트 수행, 악의적 입력에 대한 견고성 검증
- 추가 도구
- speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz 등
- 모든 테스트는 다중 플랫폼·컴파일 설정에서 통과해야 릴리스 가능
3. 이상 상황 테스트
-
OOM 테스트
- malloc() 실패를 시뮬레이션하여 메모리 부족 시 정상 복구 여부 검증
- 실패 시점 카운터를 증가시키며 반복 수행
-
I/O 오류 테스트
- 가상 파일 시스템(VFS)을 이용해 디스크 오류를 시뮬레이션
- 오류 후
PRAGMA integrity_check로 데이터 손상 여부 확인
-
크래시 테스트
- 전원 차단·OS 크래시 상황을 시뮬레이션
- TCL 하니스는 자식 프로세스 기반, TH3는 메모리 기반 VFS 사용
- 트랜잭션의 완전 롤백 또는 완전 완료 여부 검증
-
복합 실패 테스트
- 크래시 후 OOM 또는 I/O 오류가 연속 발생하는 상황까지 검증
4. 퍼즈 테스트
-
SQL Fuzz
- 문법적으로 유효하지만 비정상적인 SQL을 생성해 SQLite 반응 검증
-
American Fuzzy Lop (AFL)
- 2014년 도입된 프로파일 기반 퍼저로, 새로운 제어 경로를 탐색
- SQLite의 assert 실패·크래시·잘못된 결과를 다수 발견
-
Google OSS Fuzz
- 2016년부터 Google 인프라에서 자동 퍼징 수행
- 신규 커밋에서 간헐적 문제를 탐지
-
dbsqlfuzz / jfuzz
- 2018년 이후 내부 퍼저로 도입, SQL과 DB 파일 동시 변이
- 하루 5억 건 이상 테스트, 외부 퍼저의 버그 리포트 거의 소멸
- 2024년부터 jfuzz가 JSONB 입력 검증 추가
-
타사 퍼저 및 fuzzcheck
- 외부 연구자(예: Manuel Rigger)가 잘못된 결과 계산 사례 다수 발견
- fuzzcheck 유틸리티가 과거 퍼즈 케이스 중 ‘흥미로운’ 수천 건을 재검증
-
MC/DC와 퍼즈 테스트의 긴장 관계
- MC/DC는 방어 코드 최소화, 퍼즈는 방어 코드 필요
- SQLite는 두 접근을 병행해 정상·악의적 입력 모두에 견고한 코드 유지
5. 회귀 테스트
- 보고된 버그는 수정 후 반드시 새 테스트 케이스로 추가
- 과거 버그의 재발 방지 목적
6. 자동 리소스 누수 검출
- TCL·TH3 하니스가 메모리·파일·스레드·뮤텍스 누수 자동 감시
- OOM·I/O 오류 후에도 메모리 누수가 없어야 함
7. 테스트 커버리지
- SQLite 코어는 TH3 기준 100% 분기 커버리지 달성
- FTS3, RTree 등 확장은 제외
-
Statement vs Branch Coverage
- 분기 커버리지는 문장 커버리지보다 엄격하며, 모든 조건 분기를 양방향으로 검증
-
방어 코드 커버리지
- ALWAYS(), NEVER() 매크로로 방어 조건을 명시
- 세 가지 정의 형태로 테스트를 반복해 일관성 검증
-
경계값 및 불리언 벡터 테스트
- testcase() 매크로로 조건의 양·음 결과 모두 검증
- 1184개 testcase() 사용
-
MC/DC 달성
- testcase() 매크로를 통해 모든 조건의 독립적 영향 검증
-
gcov 기반 측정
-
-fprofile-arcs -ftest-coverage옵션으로 커버리지 측정 - 결과 비교를 통해 컴파일러 버그나 정의되지 않은 동작 탐지
-
-
Mutation Testing
- 분기 명령을 변경해 테스트가 이를 감지하는지 확인
- 최적화 분기(
/*OPTIMIZATION-IF-TRUE*/)는 예외 처리
-
완전 커버리지 경험
- 모든 분기 테스트 덕분에 코드 변경 시 부작용 최소화
- 유지 비용은 높지만, 광범위 배포되는 인프라 라이브러리로서 정당화됨
8. 동적 분석
-
Assert()
- 6754개 assert 문으로 전·후조건 및 루프 불변식 검증
-
SQLITE_DEBUG빌드에서만 활성화
-
Valgrind
- 메모리 오류·스택 오버플로·초기화되지 않은 메모리 접근 탐지
- 릴리스 전 veryquick 및 TH3 테스트를 Valgrind로 실행
-
Memsys2
-
SQLITE_MEMDEBUG빌드 시 메모리 오류 감시용 래퍼 삽입 - Valgrind보다 빠르게 반복 검증 가능
-
-
Mutex Asserts
-
sqlite3_mutex_held()등으로 멀티스레드 동기화 검증
-
-
Journal Tests
- 롤백 저널이 DB보다 먼저 기록되는지 확인, 트랜잭션 원자성 보장
-
Undefined Behavior Checks
-
-ftrapv,-fsanitize=undefined,/RTC1등으로 비정의 동작 탐지 - 32/64비트, 엔디언, 다양한 CPU 아키텍처에서 반복 수행
-
9. 최적화 비활성화 테스트
-
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)으로 최적화 끄기- 최적화 유무에 관계없이 동일 결과를 산출해야 함
- 일부 성능 측정용 테스트는 예외
10. 체크리스트
- 릴리스 전 약 200개 항목의 수동 체크리스트 검증
- 일부는 수초, 일부는 수시간 소요
- 문제 발견 시 즉시 항목 추가, 지속적 개선
11. 정적 분석
- GCC·Clang·MSVC에서 경고 없이 컴파일
- Clang Static Analyzer에서도 유효 경고 없음
- 정적 분석은 실제 버그 탐지 효과가 제한적
12. 요약
- SQLite는 오픈소스임에도 상용 수준의 품질과 낮은 결함률을 유지
- 철저한 테스트와 코드 설계가 핵심 요인
- 모든 릴리스는 위 절차를 거쳐 미션 크리티컬 환경에서도 신뢰 가능한 DB 엔진으로 제공됨
함께 보면 좋은 글: SQLite의 알려지지 않은 이야기
SQLite 개발자 Richard Hipp의 인터뷰를 요약한 글입니다.
SQLite의 개발자들은 록웰 콜린스와 일하던 시기에 Do-178을 알게되어 이 절차를 따르기 시작했다고 합니다. 그 중 하나가 100%의 MC/DC 달성이구요.
Do-178은 정말로 쓸만한 지침서이니 개발자라면 누구나 읽어보시길 추천합니다.
링크하신건 Do-178 교육자료인것 같아요.
원 문서는 이 링크를 보시면 됩니다.
https://studylib.net/doc/27132454/rtca-do-178b
Hacker News 의견들
-
10여 년 전 SQLite의 유지보수자가 OSCON에서 테스트 관행에 대해 발표했음
특히 인상 깊었던 건 체크리스트(checklist) 의 힘이었음. 비행 전마다 파일럿이 사용하는 바로 그 도구임
그는 또한 국경없는의사회(Doctors Without Borders) 사례를 언급했는데, 의료진이 서로 이름도 모르고 언어도 달라서 수술 성과가 낮았다고 함
해결책은 간단했음 — 수술 전 체크리스트를 만들어 각자 이름과 역할을 말하게 한 것임. 이 작은 의식이 기술이 아닌 소통 개선을 통해 생존율을 높였다고 함
관련 자료: SQLite checklist 예시- 반면 이런 이야기가 불필요한 관료주의를 낳는다고 생각함. 항공, MSF, SQLite의 체크리스트는 훌륭하지만, 대부분의 조직은 쓸모없는 체크리스트로 시간을 낭비함
좋은 체크리스트와 나쁜 체크리스트의 차이에 대한 논의가 더 필요함. 수학의 아름다운 공식처럼 단순해 보이지만 발견하기는 어려운 것 같음 - 항공 운영과 공학에서 배울 점이 많다고 느껴왔음. 이런 원칙을 군대식 리더십과 결합한 IT 조직을 상상해봄
특히 미 육군의 FM22-100 문서를 여러 번 읽었는데, 놀라울 만큼 현대적이고 영감을 줌
FM22-100 문서 보기 - 좋은 체크리스트를 만드는 방법을 알고 싶다면 The Checklist Manifesto를 강력히 추천함
책 링크 - 대부분의 개발자들이 비프로그래밍적 단순한 일을 회피하려는 게 이해되지 않음
나는 테스트와 CI 외에도 Markdown으로 된 배포 체크리스트를 따라감. 결과를 저장하지도 않지만, 단계별로 수행함
이렇게 간단한 일을 왜 다른 사람들은 안 하는지 모르겠음 - 의식이 성과를 높인다는 점이 흥미로움. 요즘 회의에서도 참가자 소개 라운드를 하면 참여도가 확실히 높아짐
MSF 사례를 다룬 공식 페이지가 있다면 꼭 보고 싶음. 구글링으로는 찾지 못했음
- 반면 이런 이야기가 불필요한 관료주의를 낳는다고 생각함. 항공, MSF, SQLite의 체크리스트는 훌륭하지만, 대부분의 조직은 쓸모없는 체크리스트로 시간을 낭비함
-
SQLite의 테스트 관련 과거 토론들을 모아둠
2009~2024년 HN 스레드 목록
재게시가 1년 간격으로 반복되는 듯함 -
SQLite 같은 소프트웨어를 완벽하게 다듬는 과정이 부럽고 경이로움
장인정신이 느껴지는 작품임- 사실 누구나 그렇게 할 수 있음. 천천히, 제대로 만드는 걸로 해고된 적은 없음
시간이 지나면 품질 기준이 높아지고, 같은 노력으로 더 큰 보상을 얻게 됨
자신이 손댄 부분을 조금이라도 더 깨끗하게 남기는 사람을 싫어하는 이는 없음
- 사실 누구나 그렇게 할 수 있음. 천천히, 제대로 만드는 걸로 해고된 적은 없음
-
SQLite는 정말 훌륭한 소프트웨어임. 공식 웹사이트도 마케팅 대신 정보 중심이라 좋음
다만 최근 HN에 공식 사이트의 페이지들이 하나씩 올라오는 게 흥미로움- 아마 어제 simonw의 LLM 포팅 글이 화제가 되면서 관련 링크가 다시 주목받은 듯함
- HN에서는 이런 일이 주기적으로 반복됨. 예전엔 Haskell이 그랬고, 요즘은 Zig가 그 주기를 도는 중임
이런 링크들을 모아두면 재밌을 것 같음
-
SQLite가 공개 소프트웨어이면서도 비공개 테스트를 사용한다는 점이 흥미로움
오픈소스 프로젝트가 폐쇄형 테스트를 갖는 게 가능하다는 걸 이제야 깨달음
이런 모델이 오픈코어(open-core) 와 비슷한 새로운 비즈니스 모델이 될 수도 있을 것 같음- 실제로 테스트 스위트가 코드보다 더 가치 있는 경우가 많음. 예를 들어 Excel 같은 소프트웨어의 수많은 엣지 케이스를 문서화하는 건 구현보다 어렵기 때문임
- 나도 회사 프로젝트에서 비슷하게 함. GPL 이중 라이선스와 함께 테스트 및 데이터 생성기는 상용 라이선스 사용자에게만 공개함
예시: railgunlabs/unicorn 라이선스
-
SQLite의 100% 브랜치 커버리지는 프로젝트 자체만큼이나 인상적임
지속적으로 유지한다는 게 특히 대단함 -
테스트가 비공개라는 점이 흥미로움. LLM 기반 코딩이 발전하는 지금, 테스트가 구현보다 중요해지는 시대가 오고 있음
최근 simonw가 justHTML 엔진을 Python에서 JS로 거의 자동 변환한 사례를 보며 SQLite의 테스트 전략이 떠올랐음- 오픈소스 제품의 비즈니스 모델 관점에서 보면, 방대한 테스트 스위트는 효율적인 변경과 가치 창출의 핵심 자산이 됨
- SQLite의 How SQLite Is Tested 문서는 말 그대로 테스트의 성서처럼 느껴짐
-
최근 SQLite와 DuckDB 간 호환성을 고려하며 LLM과 논의했는데, 동시성 처리 측면에서는 SQLite가 더 낫다는 결론을 얻었음
-
SQLite의 테스트 문서에서 성능 회귀 테스트(performance regression) 에 대한 언급이 적은 게 의외였음
정확성도 중요하지만, 특정 쿼리 경로에서의 성능 저하는 치명적일 수 있음- HFT 분야에서 일했지만, 성능 보증을 내세우는 오픈소스 프로젝트는 거의 본 적이 없음
혹시 이런 목표를 핵심 사명으로 삼는 프로젝트가 있는지 궁금함
- HFT 분야에서 일했지만, 성능 보증을 내세우는 오픈소스 프로젝트는 거의 본 적이 없음
-
SQLite의 안정성을 보면 이상(anomaly) 테스트가 어떻게 이루어졌는지 더 알고 싶었음
하지만 글에서는 거의 언급이 없었음. 그럼에도 불구하고 SQLite는 모든 기기에서 쓰이는 가장 견고한 소프트웨어 중 하나임- 테스트 스위트 접근권이 연간 15만 달러 수준이라, 내부 내용을 공개할 가능성은 낮을 것 같음