메인 콘텐츠로 건너뛰기

테스트 유형

ClickHouse에는 다음과 같은 테스트가 있습니다.

기능 테스트

기능 테스트는 가장 간단하고 사용하기 편리한 테스트입니다. ClickHouse의 대부분의 기능은 기능 테스트로 검증할 수 있으며, 이런 방식으로 테스트할 수 있는 ClickHouse 코드 변경에는 반드시 기능 테스트를 사용해야 합니다. 각 기능 테스트는 실행 중인 ClickHouse 서버에 하나 이상의 쿼리를 보내고 그 결과를 기준 결과와 비교합니다. 테스트는 ./tests/queries 디렉터리에 있습니다. 각 테스트는 .sql.sh 두 가지 유형 중 하나입니다.
  • .sql 테스트는 clickhouse-client에 파이프로 전달되는 간단한 SQL 스크립트입니다.
  • .sh 테스트는 독립적으로 실행되는 스크립트입니다.
일반적으로는 .sh 테스트보다 SQL 테스트를 사용하는 것이 좋습니다. 순수 SQL만으로 검증할 수 없는 기능을 테스트해야 할 때만 .sh 테스트를 사용하십시오. 예를 들어 입력 데이터를 clickhouse-client로 파이프하거나 clickhouse-local을 테스트하는 경우가 이에 해당합니다.
데이터 타입 DateTimeDateTime64를 테스트할 때 흔히 하는 실수는 서버가 특정 시간대(예: “UTC”)를 사용한다고 가정하는 것입니다. 실제로는 그렇지 않습니다. CI 테스트 실행에서는 시간대가 의도적으로 무작위로 설정됩니다. 가장 쉬운 해결 방법은 테스트 값에 시간대를 명시적으로 지정하는 것입니다. 예를 들어 toDateTime64(val, 3, 'Europe/Amsterdam')와 같이 지정할 수 있습니다.

로컬에서 테스트 실행하기

기본 포트(9000)로 수신 대기하도록 로컬에서 ClickHouse 서버를 시작합니다. 예를 들어 01428_hash_set_nan_key 테스트를 실행하려면 리포지토리 폴더로 이동한 후 다음 명령을 실행합니다:
PATH=<path to clickhouse-client>:$PATH tests/clickhouse-test 01428_hash_set_nan_key
테스트 결과(stderrstdout)는 테스트 파일과 같은 위치에 있는 01428_hash_set_nan_key.[stderr|stdout] 파일에 기록됩니다(queries/0_stateless/foo.sql의 경우 출력은 queries/0_stateless/foo.stdout에 기록됩니다). clickhouse-test의 모든 옵션은 tests/clickhouse-test --help를 참조하십시오. 테스트 이름 filter를 지정하여 모든 테스트를 실행하거나 일부 테스트만 실행할 수 있습니다: ./clickhouse-test substring. 테스트를 병렬로 실행하거나 무작위 순서로 실행하는 옵션도 있습니다.

빠른 테스트 실행

테스트 하위 집합(「빠른 테스트」라고 함)을 실행하려면 어느 정도 성능이 좋은 머신이 필요할 수 있습니다. 아래 내용은 100 GB 스토리지를 갖춘 t3.2xlarge AWS amd64 Ubuntu 인스턴스에서 작동합니다.
  1. 필수 구성 요소를 설치한 후 다시 로그인하세요.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
  1. 소스 코드를 가져오세요.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. 코드를 빌드한 후 “빠른 테스트”를 실행합니다.
python -m ci.praktika run fast
다음과 같은 결과가 표시되어야 합니다
Failed: 0, Passed: 7394, Skipped: 1795
실행을 무인 상태로 둘 경우, ssh 연결이 끊어진 후에도 계속 실행되도록 nohup 또는 disown을 사용할 수 있습니다.

상태 비저장 테스트 실행

상태 비저장 테스트를 실행하려면 어느 정도 성능이 좋은 머신이 필요할 수 있습니다. 아래 구성은 200GB 스토리지를 갖춘 m7i.8xlarge AWS amd64 Ubuntu instance에서 작동합니다.
  1. 필수 구성 요소를 설치한 뒤 다시 로그인하세요.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
sudo tee /etc/docker/daemon.json <<'EOF'
{
  "ipv6": true,
  "ip6tables": true
}
EOF
sudo systemctl restart docker
  1. 소스 코드를 가져옵니다.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. 코드를 빌드하세요.
python -m ci.praktika run build_debug
cp ci/tmp/build/programs/clickhouse ci/tmp
  1. 병렬 실행이 가능한 상태 비저장 테스트를 실행합니다.
python -m ci.praktika run functional
다음과 같은 결과가 나와야 합니다
Failed: 0, Passed: 8497, Skipped: 103
참고: python -m ci.praktika run 명령은 특정 CI 작업을 실행합니다. ClickHouse CI에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

새 테스트 추가

새 테스트를 추가하려면 먼저 queries/0_stateless 디렉터리에 .sql 또는 .sh 파일을 생성합니다. 그런 다음 clickhouse-client < 12345_test.sql > 12345_test.reference 또는 ./12345_test.sh > ./12345_test.reference를 사용해 해당 .reference 파일을 생성합니다. 테스트에서는 미리 자동 생성되는 test 데이터베이스에서만 테이블 생성, 삭제, 조회 등의 작업을 수행해야 합니다. 임시 테이블을 사용해도 됩니다. 로컬에서 CI와 동일한 환경을 설정하려면 테스트 구성을 설치합니다(이 구성은 Zookeeper 모의 구현을 사용하며 일부 설정을 조정합니다)
cd <repository>/tests/config
sudo ./install.sh
테스트는 다음과 같아야 합니다.
  • 최소화: 꼭 필요한 최소한의 테이블, 컬럼, 복잡성만 생성합니다.
  • 신속성: 몇 초를 넘기지 않아야 하며(더 바람직하게는 1초 미만),
  • 정확성과 결정성: 테스트 대상 기능이 작동하지 않을 때에만 실패해야 합니다.
  • 격리/무상태성: 환경이나 타이밍에 의존하지 않아야 합니다.
  • 철저함: 0, NULL, 빈 집합, 예외와 같은 경계 사례까지 다뤄야 합니다(음수 테스트에는 -- { serverError xyz }-- { clientError xyz } 구문을 사용하십시오),
  • 테스트 끝에 테이블을 정리합니다(남은 것이 있을 수 있으므로).
  • 다른 테스트가 동일한 내용을 테스트하지 않는지 확인합니다(즉, 먼저 grep해 보십시오).

테스트 실행 제한

테스트에는 CI에서 어떤 환경에서 실행될지를 제한하는 태그를 0개 이상 지정할 수 있습니다. .sql 테스트에서는 태그를 첫 번째 줄의 SQL 주석에 넣습니다:
-- Tags: no-fasttest, no-replicated-database
-- no-fasttest: <여기에_태그_사용_이유를_작성하세요>
-- no-replicated-database: <여기에_이유를_작성하세요>

SELECT 1
.sh 테스트에서는 태그를 두 번째 줄에 주석으로 작성합니다:
#!/usr/bin/env bash
# Tags: no-fasttest, no-replicated-database
# - no-fasttest: <여기에_태그_사용_이유를_작성하세요>
# - no-replicated-database: <여기에_이유를_작성하세요>
사용 가능한 태그 목록:
태그 이름설명사용 예시
disabled테스트를 실행하지 않습니다
long테스트 실행 시간이 1분에서 10분으로 늘어납니다
deadlock테스트를 오랫동안 반복 실행합니다
racedeadlock와 동일합니다. deadlock 사용을 권장합니다
shard서버가 127.0.0.*에서 수신 대기해야 합니다
distributedshard와 동일합니다. shard 사용을 권장합니다
globalshard와 동일합니다. shard 사용을 권장합니다
zookeeper테스트를 실행하려면 Zookeeper 또는 ClickHouse Keeper가 필요합니다테스트에서 ReplicatedMergeTree를 사용합니다
replicazookeeper와 동일합니다. zookeeper 사용을 권장합니다
no-fasttest빠른 테스트에서는 테스트를 실행하지 않습니다테스트에서 빠른 테스트에 비활성화된 MySQL 테이블 엔진을 사용합니다
fasttest-only테스트를 빠른 테스트에서만 실행합니다
no-[asan, tsan, msan, ubsan]sanitizers가 적용된 빌드에서는 테스트를 비활성화합니다테스트가 sanitizer와 호환되지 않는 QEMU에서 실행됩니다
no-replicated-database기본 데이터베이스가 ReplicatedDatabaseEngine를 사용할 때 테스트를 비활성화합니다
no-ordinary-database기본 데이터베이스 엔진이 Ordinary일 때 테스트를 비활성화합니다
no-parallel이 테스트와 다른 테스트를 병렬로 실행하지 않도록 합니다테스트가 system 테이블에서 읽기 때문에 불변식이 깨질 수 있습니다
no-parallel-replicas병렬 레플리카가 활성화된 경우 테스트를 비활성화합니다
no-debugDebug 빌드에서는 테스트를 비활성화합니다
no-releaseRelease 빌드에서는 테스트를 비활성화합니다
no-darwinmacOS(Darwin)에서는 테스트를 비활성화합니다테스트가 분산 쿼리, procfs, HTTP 서버와 같은 Linux 전용 기능에 의존합니다
다음 옵션도 지원됩니다: no-stress, no-polymorphic-parts, no-random-settings, no-random-merge-tree-settings, no-backward-compatibility-check, no-cpu-x86_64, no-cpu-aarch64, no-cpu-ppc64le, no-s3-storage. 위 설정에 더해, 특정 ClickHouse 기능 사용 여부를 정의하기 위해 system.build_optionsUSE_* 플래그도 사용할 수 있습니다. 예를 들어 테스트에서 MySQL 테이블을 사용하는 경우 use-mysql 태그를 추가해야 합니다.

무작위 설정에 대한 제한 지정

테스트 실행 중 무작위화할 수 있는 설정의 허용 최소값과 최대값을 테스트에서 지정할 수 있습니다. .sh 테스트에서는 제한을 태그 옆 줄의 주석에 작성하거나, 태그를 지정하지 않은 경우 둘째 줄의 주석에 작성합니다:
#!/usr/bin/env bash
# Tags: no-fasttest
# 랜덤 설정 제한: max_block_size=(1000, 10000); index_granularity=(100, None)
.sql 테스트에서는 태그를 tags 옆줄이나 첫 번째 줄에 SQL 주석으로 넣습니다.
-- Tags: no-fasttest
-- Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
SELECT 1
하나의 제한만 지정하면 되는 경우, 나머지 하나에는 None을 사용할 수 있습니다.

테스트 이름 선택

테스트 이름은 00422_hash_function_constexpr.sql과 같이 5자리 prefix로 시작하며, 그 뒤에 설명적인 이름이 붙습니다. prefix를 선택하려면 디렉터리에 이미 있는 가장 큰 prefix를 찾은 다음 1을 더하십시오.
ls tests/queries/0_stateless/[0-9]*.reference | tail -n 1
그동안 같은 숫자 접두사를 사용하는 다른 테스트가 추가될 수 있지만, 이는 문제가 되지 않으므로 나중에 변경할 필요는 없습니다.

반드시 발생해야 하는 오류 확인

잘못된 쿼리에서 서버 오류가 발생하는지 테스트해야 하는 경우가 있습니다. 이를 위해 SQL 테스트에서는 다음 형식의 특수 어노테이션을 지원합니다:
SELECT x; -- { serverError 49 }
이 테스트는 서버가 알 수 없는 컬럼 x에 대해 코드 49의 오류를 반환하는지 확인합니다. 오류가 발생하지 않거나 다른 오류가 발생하면 테스트는 실패합니다. 클라이언트 측에서 오류가 발생하는지 확인하려면 대신 clientError 어노테이션을 사용하십시오. 오류 메시지의 특정 문구는 확인하지 마십시오. 향후 변경될 수 있으므로 테스트가 불필요하게 실패할 수 있습니다. 오류 코드만 확인하십시오. 기존 오류 코드가 필요에 비해 충분히 정확하지 않다면 새 코드를 추가하는 것을 고려하십시오.

분산 쿼리 테스트하기

기능 테스트에서 분산 쿼리를 사용하려면 서버가 자기 자신에게 쿼리하도록 127.0.0.{1..2} 주소와 함께 remote 테이블 함수를 활용할 수 있습니다. 또는 서버 설정 파일에 미리 정의된 test_shard_localhost 같은 테스트 클러스터를 사용할 수도 있습니다. 서버가 분산 쿼리를 지원하도록 구성된 올바른 환경에서 CI가 테스트를 실행할 수 있도록 테스트 이름에 shard 또는 distributed라는 단어를 추가해야 합니다.

임시 파일 작업

셸 테스트에서는 작업에 사용할 파일을 즉석에서 만들어야 할 때가 있습니다. 일부 CI 검사는 테스트를 병렬로 실행하므로, 스크립트에서 고유한 이름 없이 임시 파일을 만들거나 삭제하면 Flaky와 같은 일부 CI 검사가 실패할 수 있다는 점에 유의하십시오. 이 문제를 피하려면 환경 변수 $CLICKHOUSE_TEST_UNIQUE_NAME를 사용해 현재 실행 중인 테스트마다 임시 파일에 고유한 이름을 지정해야 합니다. 그러면 설정 중에 만들거나 정리 중에 삭제하는 파일이 해당 테스트에서만 사용하는 파일이며, 병렬로 실행 중인 다른 테스트의 파일이 아님을 확실히 할 수 있습니다.

알려진 버그

기능 테스트로 쉽게 재현할 수 있는 버그를 파악한 경우, 미리 준비된 기능 테스트를 tests/queries/bugs 디렉터리에 배치합니다. 이 테스트는 버그가 수정되면 tests/queries/0_stateless로 이동됩니다.

통합 테스트

통합 테스트를 사용하면 클러스터 구성에서 ClickHouse를 테스트하고, MySQL, Postgres, MongoDB와 같은 다른 서버와 ClickHouse가 상호작용하는 방식도 테스트할 수 있습니다. 이 테스트는 네트워크 분할, 패킷 손실 등을 에뮬레이션하는 데 유용합니다. 이 테스트는 Docker에서 실행되며, 다양한 소프트웨어를 포함한 여러 컨테이너를 생성합니다. 이 테스트를 실행하는 방법은 tests/integration/README.md를 참조하십시오. ClickHouse와 서드파티 드라이버 간 통합은 테스트하지 않습니다. 또한 현재는 JDBC 및 ODBC 드라이버에 대한 통합 테스트도 제공하지 않습니다.

단위 테스트

단위 테스트는 ClickHouse 전체가 아니라, 분리된 단일 라이브러리나 클래스를 테스트하려는 경우에 유용합니다. ENABLE_TESTS CMake 옵션으로 테스트 빌드를 활성화하거나 비활성화할 수 있습니다. 단위 테스트(및 기타 테스트 프로그램)는 코드베이스 전반의 tests 하위 디렉터리에 있습니다. 단위 테스트를 실행하려면 ninja test를 입력하십시오. 일부 테스트는 gtest를 사용하지만, 일부는 테스트가 실패하면 0이 아닌 종료 코드를 반환하는 단순한 프로그램입니다. 코드가 이미 기능 테스트로 커버된다면 단위 테스트를 반드시 작성할 필요는 없습니다(그리고 기능 테스트는 일반적으로 사용하기가 훨씬 더 간단합니다). 개별 gtest 테스트 항목은 실행 파일을 직접 호출하여 실행할 수 있습니다. 예시는 다음과 같습니다:
$ ./src/unit_tests_dbms --gtest_filter=LocalAddress*

성능 테스트

성능 테스트를 사용하면 합성 쿼리에서 ClickHouse의 특정한 독립 영역에 대한 성능을 측정하고 비교할 수 있습니다. 성능 테스트는 tests/performance/에 있습니다. 각 테스트는 테스트 케이스 설명이 포함된 .xml 파일로 구성됩니다. 테스트는 docker/test/performance-comparison 도구로 실행됩니다. 실행 방법은 readme 파일을 참조하십시오. 각 테스트는 하나 이상의 쿼리(매개변수 조합을 포함할 수 있음)를 루프에서 실행합니다. 특정 시나리오에서 ClickHouse의 성능을 개선하려고 하며, 그 개선 효과를 단순한 쿼리로 확인할 수 있다면 성능 테스트를 작성할 것을 강력히 권장합니다. 또한 비교적 독립적이고 지나치게 난해하지 않은 SQL 함수를 추가하거나 수정할 때도 성능 테스트를 작성하는 것이 좋습니다. 테스트 중에는 언제나 perf top 또는 다른 perf 도구를 함께 사용하는 것이 유용합니다.

테스트 도구와 스크립트

tests 디렉터리의 일부 프로그램은 미리 준비된 테스트가 아니라 테스트 도구입니다. 예를 들어 Lexer에는 stdin을 토큰화한 뒤 결과를 색상 적용된 형태로 stdout에 출력하는 src/Parsers/tests/lexer 도구가 있습니다. 이런 도구는 코드 예시로 활용할 수 있을 뿐 아니라, 기능을 살펴보거나 수동으로 테스트할 때도 사용할 수 있습니다.

기타 테스트

tests/external_models에는 머신 러닝 모델에 대한 테스트가 있습니다. 이 테스트는 더 이상 업데이트되지 않으므로 통합 테스트로 옮겨야 합니다. 쿼럼 삽입을 위한 별도의 테스트도 있습니다. 이 테스트는 별도의 서버에서 ClickHouse 클러스터를 실행하고 다양한 장애 상황을 에뮬레이션합니다. 예를 들어 네트워크 분리, 패킷 손실(ClickHouse 노드 간, ClickHouse와 ZooKeeper 간, ClickHouse 서버와 클라이언트 간 등), kill -9, kill -STOP, kill -CONTJepsen과 유사한 방식으로 수행합니다. 그런 다음 승인된 모든 삽입은 기록되었고 거부된 모든 삽입은 기록되지 않았는지 확인합니다.

수동 테스트

새 기능을 개발할 때는 수동으로도 테스트하는 것이 좋습니다. 다음 단계에 따라 수행할 수 있습니다. ClickHouse를 빌드합니다. 터미널에서 ClickHouse를 실행합니다. programs/clickhouse-server로 디렉터리를 변경한 뒤 ./clickhouse-server를 실행합니다. 그러면 기본적으로 현재 디렉터리의 구성(config.xml, users.xml, config.dusers.d 디렉터리 내부의 파일)을 사용합니다. ClickHouse 서버에 연결하려면 programs/clickhouse-client/clickhouse-client를 실행합니다. 모든 clickhouse 도구(server, client 등)는 실제로 clickhouse라는 단일 바이너리를 가리키는 심볼릭 링크일 뿐입니다. 이 바이너리는 programs/clickhouse에 있습니다. 모든 도구는 clickhouse-tool 대신 clickhouse tool 형식으로도 호출할 수 있습니다. 또는 ClickHouse 패키지를 설치할 수도 있습니다. ClickHouse 리포지토리의 안정 릴리스를 사용하거나, ClickHouse 소스 루트에서 ./release를 실행해 직접 패키지를 빌드할 수 있습니다. 그런 다음 sudo clickhouse start로 서버를 시작합니다(서버를 중지하려면 stop을 사용합니다). 로그는 /etc/clickhouse-server/clickhouse-server.log에서 확인하십시오. 시스템에 ClickHouse가 이미 설치되어 있다면, 새 clickhouse 바이너리를 빌드한 다음 기존 바이너리로 대체할 수 있습니다:
$ sudo clickhouse stop
$ sudo cp ./clickhouse /usr/bin/
$ sudo clickhouse start
또한 시스템의 clickhouse-server를 중지한 뒤, 동일한 구성을 사용하되 로그가 터미널에 출력되도록 자체 인스턴스를 실행할 수 있습니다:
$ sudo clickhouse stop
$ sudo -u clickhouse /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
gdb 사용 예시:
$ sudo -u clickhouse gdb --args /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
시스템에서 clickhouse-server가 이미 실행 중이고 이를 중지하고 싶지 않다면, config.xml에서 포트 번호를 변경하거나 (config.d 디렉터리의 파일에서 재정의할 수도 있습니다), 적절한 데이터 경로를 설정한 뒤 실행할 수 있습니다. clickhouse 실행 파일은 의존성이 거의 없어서 다양한 Linux 배포판에서 동작합니다. 서버에서 변경 사항을 빠르게 시험해 보려면, 새로 빌드한 clickhouse 실행 파일을 서버로 scp한 다음 위의 예시처럼 실행하면 됩니다.

빌드 테스트

빌드 테스트를 사용하면 다양한 대체 구성과 일부 이기종 시스템에서 빌드가 깨지지 않았는지 확인할 수 있습니다. 이러한 테스트도 자동화되어 있습니다. 예시:
  • Darwin x86_64(macOS)용 크로스 컴파일
  • FreeBSD x86_64용 크로스 컴파일
  • Linux AArch64용 크로스 컴파일
  • 시스템 패키지의 라이브러리를 사용해 Ubuntu에서 빌드(권장되지 않음)
  • 라이브러리를 공유 링크 방식으로 빌드(권장되지 않음)
예를 들어, 시스템 패키지를 사용해 빌드하는 것은 바람직하지 않은 방식입니다. 시스템에 정확히 어떤 버전의 패키지가 설치되어 있을지 보장할 수 없기 때문입니다. 하지만 Debian maintainer에게는 이것이 꼭 필요합니다. 이 때문에 최소한 이 빌드 변형은 지원해야 합니다. 또 다른 예로, 공유 링크 방식은 흔히 문제를 일으키지만 일부 사용자에게는 필요합니다. 모든 빌드 변형에서 모든 테스트를 실행할 수는 없지만, 적어도 다양한 빌드 변형이 깨지지 않았는지는 확인하고자 합니다. 이를 위해 빌드 테스트를 사용합니다. 또한 컴파일 단위가 너무 길어 컴파일하기 어렵거나 RAM을 지나치게 많이 요구하는 경우가 없는지도 테스트합니다. 또한 스택 프레임이 지나치게 큰 경우가 없는지도 테스트합니다.

프로토콜 호환성 테스트

ClickHouse 네트워크 프로토콜을 확장할 때는 이전 clickhouse-client가 새로운 clickhouse-server와, 새로운 clickhouse-client가 이전 clickhouse-server와 호환되는지를 수동으로 테스트합니다(해당 패키지의 바이너리를 실행해 확인하는 방식입니다). 또한 일부 사례는 통합 테스트로 자동 검증합니다:
  • 이전 버전의 ClickHouse에서 기록한 데이터를 새 버전에서 성공적으로 읽을 수 있는지
  • 서로 다른 ClickHouse 버전으로 구성된 클러스터에서 분산 쿼리가 정상적으로 작동하는지

컴파일러의 도움

주요 ClickHouse 코드(src 디렉터리에 있음)는 -Wall -Wextra -Werror와 몇 가지 추가 경고 옵션을 활성화한 상태로 빌드됩니다. 다만 이러한 옵션은 서드파티 라이브러리에는 활성화되지 않습니다. Clang에는 훨씬 더 유용한 경고가 많습니다. -Weverything으로 확인한 뒤, 그중 일부를 기본 빌드에 포함하도록 선택할 수 있습니다. ClickHouse는 개발 환경과 프로덕션 환경 모두에서 항상 clang으로 빌드합니다. 로컬 머신에서는 디버그 모드로 빌드할 수 있습니다(노트북 배터리를 절약하기 위해서입니다). 하지만 더 나은 제어 흐름 및 프로시저 간 분석 덕분에, 컴파일러는 -O3에서 더 많은 경고를 생성할 수 있다는 점에 유의하십시오. clang으로 디버그 모드에서 빌드하면 libc++의 디버그 버전이 사용되며, 이를 통해 런타임에 더 많은 오류를 포착할 수 있습니다.

Sanitizers

로컬에서 실행할 때 프로세스(ClickHouse 서버 또는 클라이언트)가 시작 시점에 충돌하면 주소 공간 배치 난수화(ASLR)를 비활성화해야 할 수 있습니다: sudo sysctl kernel.randomize_va_space=0

Address 새니타이저

커밋이 있을 때마다 ASan 환경에서 기능, 통합, 스트레스 테스트 및 단위 테스트를 실행합니다.

스레드 새니타이저

각 커밋마다 TSan으로 기능, 통합, 스트레스 및 단위 테스트를 실행합니다.

메모리 새니타이저

각 커밋마다 MSan 환경에서 기능 테스트, 통합 테스트, 스트레스 테스트, 단위 테스트를 실행합니다.

정의되지 않은 동작 새니타이저

각 커밋마다 UBSan으로 기능, 통합, 스트레스 및 단위 테스트를 실행합니다. 일부 서드파티 라이브러리 코드는 UB에 대한 새니타이저 처리가 되어 있지 않습니다.

Valgrind (memcheck)

예전에는 Valgrind로 기능 테스트를 밤새 실행했지만, 지금은 더 이상 그렇게 하지 않습니다. 몇 시간이 걸립니다. 현재 re2 라이브러리에는 알려진 오탐이 1건 있습니다. 자세한 내용은 이 글을 참조하십시오.

퍼징

ClickHouse 퍼징은 libFuzzer와 무작위 SQL 쿼리를 모두 사용해 구현됩니다. 모든 퍼즈 테스트는 새니타이저(Address 및 Undefined)와 함께 수행해야 합니다. libFuzzer는 라이브러리 코드를 격리된 상태에서 퍼즈 테스트하는 데 사용됩니다. 퍼저는 테스트 코드의 일부로 구현되며, 이름 끝에 “_fuzzer” 접미사가 붙습니다. 퍼저 예시는 src/Parsers/fuzzers/lexer_fuzzer.cpp에서 확인할 수 있습니다. libFuzzer 전용 구성, dictionaries, corpus는 tests/fuzz에 저장됩니다. 사용자 입력을 처리하는 모든 기능에 대해 퍼즈 테스트를 작성할 것을 권장합니다. 퍼저는 기본적으로 빌드되지 않습니다. 퍼저를 빌드하려면 -DENABLE_FUZZING=1-DENABLE_TESTS=1 옵션을 모두 설정해야 합니다. 퍼저를 빌드할 때는 Jemalloc을 비활성화하는 것을 권장합니다. ClickHouse 퍼징을 Google OSS-Fuzz에 통합하는 데 사용하는 구성은 docker/fuzz에서 확인할 수 있습니다. 또한 무작위 SQL 쿼리를 생성하고, 이를 실행해도 server가 종료되지 않는지 확인하는 간단한 퍼즈 테스트도 사용합니다. 이는 00746_sql_fuzzy.pl에서 확인할 수 있습니다. 이 테스트는 지속적으로(하룻밤 이상 장시간) 실행해야 합니다. 또한 많은 코너 케이스를 찾아낼 수 있는 정교한 AST 기반 쿼리 퍼저도 사용합니다. 이 퍼저는 쿼리 AST에서 무작위 순열과 치환을 수행합니다. 이전 테스트의 AST 노드를 기억해 두었다가, 이를 무작위 순서로 처리하면서 이후 테스트를 퍼징하는 데 활용합니다. 이 퍼저에 대해 자세히 알아보려면 이 블로그 글을 참조하십시오.

스트레스 테스트

스트레스 테스트는 퍼징의 또 다른 한 형태입니다. 단일 서버에서 모든 기능 테스트를 무작위 순서로 병렬 실행합니다. 테스트 결과는 검증하지 않습니다. 다음 사항을 확인합니다:
  • 서버가 충돌하지 않고, 디버그 또는 새니타이저 트랩이 발생하지 않습니다.
  • 교착 상태가 없습니다.
  • 데이터베이스 구조의 일관성이 유지됩니다.
  • 테스트 후 서버를 예외 없이 정상적으로 중지한 뒤 다시 시작할 수 있습니다.
5가지 변형(Debug, ASan, TSan, MSan, UBSan)이 있습니다.

Thread fuzzer

Thread Fuzzer(Thread Sanitizer와 혼동하지 마십시오)는 thread의 실행 순서를 무작위로 바꿀 수 있게 해 주는 또 다른 종류의 퍼징입니다. 이를 통해 더욱 다양한 특수 사례를 찾아낼 수 있습니다.

보안 감사

보안 팀에서 보안 관점에서 ClickHouse의 기능을 기본 수준으로 검토했습니다.

정적 분석기

커밋마다 clang-tidy를 실행합니다. clang-static-analyzer 검사도 활성화되어 있습니다. 일부 스타일 검사에도 clang-tidy를 사용합니다. clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL을 평가했습니다. 사용 방법에 대한 지침은 tests/instructions/ 디렉터리에서 확인할 수 있습니다. CLion을 IDE로 사용하는 경우 일부 clang-tidy 검사를 별도 설정 없이 활용할 수 있습니다. 셸 스크립트의 정적 분석에도 shellcheck를 사용합니다.

강화

디버그 빌드에서는 사용자 수준 할당에 ASLR을 적용하는 사용자 지정 allocator를 사용합니다. 또한 할당 후 읽기 전용이어야 하는 메모리 영역을 수동으로 보호합니다. 디버그 빌드에서는 “유해한”(obsolete, insecure, not thread-safe) 함수가 호출되지 않도록 libc를 사용자 지정합니다. 디버그 어설션을 광범위하게 사용합니다. 디버그 빌드에서는 “logical error” 코드의 예외(즉, 버그를 의미함)가 발생하면 프로그램을 즉시 종료합니다. 이를 통해 릴리스 빌드에서는 예외를 사용할 수 있고, 디버그 빌드에서는 이를 어설션처럼 처리할 수 있습니다. 디버그 빌드에는 jemalloc의 디버그 버전을 사용합니다. 디버그 빌드에는 libc++의 디버그 버전을 사용합니다.

런타임 무결성 검사

디스크에 저장된 데이터는 체크섬으로 검증됩니다. MergeTree 테이블의 데이터는 세 가지 방식으로 동시에 체크섬으로 검증됩니다* (압축 데이터 블록, 비압축 데이터 블록, 블록 전체에 대한 총 체크섬). 클라이언트와 서버 사이 또는 서버 간에 네트워크를 통해 전송되는 데이터에도 체크섬이 적용됩니다. 복제는 레플리카 간에 비트 단위까지 동일한 데이터를 보장합니다. 이는 결함이 있는 하드웨어(저장 매체의 비트 로트(bit rot), 서버 RAM의 비트 플립, 네트워크 컨트롤러 RAM의 비트 플립, 네트워크 스위치 RAM의 비트 플립, 클라이언트 RAM의 비트 플립, wire 상의 비트 플립)로부터 보호하기 위해 필요합니다. 비트 플립은 흔하게 발생하며, ECC RAM을 사용하고 TCP 체크섬이 있어도 발생할 가능성이 높다는 점에 유의하십시오(매일 페타바이트 규모의 데이터를 처리하는 수천 대의 서버를 운영하는 경우). 동영상 보기(러시아어). ClickHouse는 운영 엔지니어가 결함이 있는 하드웨어를 찾아내는 데 도움이 되는 진단 기능을 제공합니다.
  • 그리고 성능 저하도 거의 없습니다.

코드 스타일

코드 스타일 규칙은 여기에 설명되어 있습니다. 일반적인 스타일 위반 사항 몇 가지를 확인하려면 utils/check-style 스크립트를 사용할 수 있습니다. 코드 스타일을 올바르게 적용하려면 clang-format을 사용할 수 있습니다. .clang-format 파일은 소스 루트에 있습니다. 이 파일은 대체로 실제 코드 스타일과 일치합니다. 하지만 기존 파일에 clang-format을 적용하면 포맷팅이 오히려 나빠질 수 있으므로 권장하지 않습니다. clang 소스 리포지토리에서 clang-format-diff 도구를 찾을 수 있습니다. 또는 uncrustify 도구를 사용해 코드를 다시 포맷할 수도 있습니다. 구성은 소스 루트의 uncrustify.cfg에 있습니다. 이 도구는 clang-format만큼 충분히 테스트되지는 않았습니다. CLion에는 자체 코드 포매터가 있으며, 코드 스타일에 맞게 조정해야 합니다. 또한 코드의 오탈자를 찾기 위해 codespell도 사용합니다. 이 역시 자동화되어 있습니다.

테스트 커버리지

테스트 커버리지도 추적하지만, 기능 테스트와 clickhouse-server에 한해서만 추적합니다. 이 작업은 매일 수행됩니다.

테스트를 검증하는 테스트

플레이키 테스트를 확인하는 자동 검사 기능이 있습니다. 이 기능은 모든 새 테스트를 100회(기능 테스트) 또는 10회(통합 테스트) 실행합니다. 테스트가 단 한 번이라도 실패하면 플레이키한 테스트로 간주됩니다.

테스트 자동화

테스트는 GitHub Actions를 사용해 실행합니다. 빌드 작업과 테스트는 각 커밋마다 Sandbox에서 실행됩니다. 생성된 패키지와 테스트 결과는 GitHub에 게시되며 direct link로 다운로드할 수 있습니다. artifact는 몇 개월 동안 저장됩니다. GitHub에서 pull request를 보내면 “can be tested” 태그가 지정되며, CI 시스템이 ClickHouse 패키지(release, debug, address 새니타이저 포함 등)를 빌드해 줍니다.
마지막 수정일 2026년 6월 10일