Тестирование ClickHouse
Функциональные Тесты
Функциональные тесты являются наиболее простыми и удобными для использования. Большинство функций ClickHouse можно протестировать с помощью функциональных тестов, и они обязательны к использованию при каждом изменении в коде ClickHouse, которое можно протестировать таким образом.
Каждый функциональный тест отправляет один или несколько запросов на работающий сервер ClickHouse и сравнивает результат с эталонным.
Тесты находятся в директории queries
.
Существуют две подпапки: stateless
и stateful
.
- Stateless тесты выполняют запросы без предварительно загруженных тестовых данных - они часто создают небольшие синтетические наборы данных на лету, в пределах самого теста.
- Stateful тесты требуют предварительно загруженные тестовые данные из ClickHouse, которые доступны широкой публике.
Каждый тест может быть одного из двух типов: .sql
и .sh
.
- Тест
.sql
— это простой SQL-скрипт, который передается вclickhouse-client
. - Тест
.sh
— это скрипт, который выполняется самостоятельно.
SQL тесты обычно предпочтительнее .sh
тестов.
Вы должны использовать .sh
тесты только в случае, если нужно протестировать какую-либо функцию, которую нельзя проверить с помощью чистого SQL, например, при передаче каких-то входных данных в clickhouse-client
или тестировании clickhouse-local
.
Распространенной ошибкой при тестировании типов данных DateTime
и DateTime64
является предположение, что сервер использует определенный часовой пояс (например, "UTC"). Это не так, часовые пояса в CI-тестах намеренно рандомизированы. Самый простой обходной путь — явно указать часовой пояс для тестовых значений, например, toDateTime64(val, 3, 'Europe/Amsterdam')
.
Запуск теста локально
Запустите сервер ClickHouse локально, слушая на порту по умолчанию (9000).
Чтобы запустить, например, тест 01428_hash_set_nan_key
, перейдите в папку репозитория и выполните следующую команду:
Результаты теста (stderr
и stdout
) записываются в файлы 01428_hash_set_nan_key.[stderr|stdout]
, которые находятся рядом с самим тестом (для queries/0_stateless/foo.sql
вывод будет в queries/0_stateless/foo.stdout
).
Смотрите tests/clickhouse-test --help
для всех опций clickhouse-test
.
Вы можете запустить все тесты или выполнить подмножство тестов, предоставив фильтр для имен тестов: ./clickhouse-test substring
.
Также есть опции для запуска тестов параллельно или в случайном порядке.
Добавление нового теста
Чтобы добавить новый тест, сначала создайте файл .sql
или .sh
в директории queries/0_stateless
.
Затем сгенерируйте соответствующий .reference
файл с помощью clickhouse-client < 12345_test.sql > 12345_test.reference
или ./12345_test.sh > ./12345_test.reference
.
Тесты должны создавать, удалять, выполнять выборку из и т. д. таблицы в базе данных test
, которая автоматически создается заранее.
Использование временных таблиц допустимо.
Чтобы настроить ту же среду, что и в CI локально, установите конфигурации тестов (они будут использовать имитацию Zookeeper и настраивать некоторые параметры)
Тесты должны
- быть минимальными: создавать только минимально необходимые таблицы, колонки и сложность,
- быть быстрыми: не занимать больше нескольких секунд (лучше: менее секунды),
- быть корректными и детерминированными: давать сбой только в случае, если тестируемая функция не работает,
- быть изолированными/stateless: не полагаться на окружение и время,
- быть исчерпывающими: охватывать крайние случаи, такие как нули, null, пустые наборы, исключения (негативные тесты, используйте синтаксис
-- { serverError xyz }
и-- { clientError xyz }
для этого), - очищать таблицы в конце теста (в случае остатков),
- убедитесь, что другие тесты не тестируют то же самое (т.е. сначала grep).
Ограничение запусков тестов
У теста может быть ноль или больше тегов, указывающих ограничения, в каких контекстах тест запускается в CI.
Для тестов .sql
теги размещаются в первой строке в качестве SQL-комментария:
Для тестов .sh
теги записываются как комментарий на второй строке:
Список доступных тегов:
Имя тега | Что это делает | Пример использования |
---|---|---|
disabled | Тест не запускается | |
long | Исполнение теста увеличено с 1 до 10 минут | |
deadlock | Тест выполняется в цикле долго | |
race | То же самое, что и deadlock . Предпочитайте deadlock | |
shard | Сервер должен слушать 127.0.0.* | |
distributed | То же самое, что и shard . Предпочитайте shard | |
global | То же самое, что и shard . Предпочитайте shard | |
zookeeper | Тест требует Zookeeper или ClickHouse Keeper для запуска | Тест использует ReplicatedMergeTree |
replica | То же самое, что и zookeeper . Предпочитайте zookeeper | |
no-fasttest | Тест не запускается под быстрым тестом | Тест использует движок таблиц MySQL , который отключен в быстром тесте |
no-[asan, tsan, msan, ubsan] | Отключает тесты в сборке с санитайзерами | Тест выполняется под QEMU, который не работает с санитайзерами |
no-replicated-database | ||
no-ordinary-database | ||
no-parallel | Отключает выполнение других тестов параллельно с этим | Тест читает из system таблиц, и инварианты могут быть нарушены |
no-parallel-replicas | ||
no-debug | ||
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 |
В дополнение к вышеуказанным параметрам, вы можете использовать флаги USE_*
из system.build_options
, чтобы определить использование определенных функций ClickHouse.
Например, если ваш тест использует таблицу MySQL, вы должны добавить тег use-mysql
.
Указание ограничений для случайных настроек
Тест может указать минимальные и максимальные допустимые значения для настроек, которые могут быть рандомизированы в ходе тестирования.
Для тестов .sh
ограничения записываются как комментарий на строке рядом с тегами или на второй строке, если теги не указаны:
Для тестов .sql
теги размещаются в качестве SQL-комментария в строке рядом с тегами или в первой строке:
Если вам нужно указать только одно ограничение, вы можете использовать None
для другого.
Выбор имени теста
Имя теста начинается с пятизначного префикса, за которым следует описательное имя, например, 00422_hash_function_constexpr.sql
.
Чтобы выбрать префикс, найдите наибольший префикс, уже присутствующий в директории, и увеличьте его на единицу.
В то же время могут быть добавлены другие тесты с тем же числовым префиксом, но это нормально и не приведет к проблемам, вам не нужно изменять его позже.
Проверка на ошибку, которая должна возникнуть
Иногда вы хотите протестировать, что ошибка сервера возникает для некорректного запроса. Мы поддерживаем специальные аннотации для этого в SQL-тестах, в следующей форме:
Этот тест обеспечивает, чтобы сервер возвращал ошибку с кодом 49 о неизвестной колонке x
.
Если ошибки нет или ошибка отличается, тест завершится сбоем.
Если вы хотите убедиться, что ошибка возникает на стороне клиента, используйте аннотацию clientError
вместо.
Не проверяйте конкретную формулировку сообщения об ошибке, оно может измениться в будущем, и тест будет ненужным образом ломаться. Проверяйте только код ошибки. Если существующий код ошибки недостаточно точен для ваших нужд, рассмотрите возможность добавления нового.
Тестирование распределенного запроса
Если вы хотите использовать распределенные запросы в функциональных тестах, вы можете воспользоваться табличной функцией remote
с адресами 127.0.0.{1..2}
для обращения к серверу; или вы можете использовать предопределенные тестовые кластеры в конфигурационном файле сервера, такие как test_shard_localhost
.
Не забудьте добавить слова shard
или distributed
к имени теста, чтобы он запускался в CI в правильных конфигурациях, где сервер настроен на поддержку распределенных запросов.
Работа с Временными Файлами
Иногда в тесте оболочки вам может понадобиться создать файл на лету для работы с ним.
Имейте в виду, что некоторые проверки CI запускают тесты параллельно, поэтому, если вы создаете или удаляете временный файл в своем скрипте без уникального имени, это может привести к сбоям некоторых проверок CI, таких как Flaky.
Чтобы обойти это, вы должны использовать переменную окружения $CLICKHOUSE_TEST_UNIQUE_NAME
, чтобы дать временным файлам имя, уникальное для теста, который выполняется.
Таким образом, вы можете быть уверены, что файл, который вы создаете во время настройки или удаляете во время очистки, является единственным файлом, используемым этим тестом, и не каким-либо другим тестом, который выполняется параллельно.
Известные Ошибки
Если мы знаем о некоторых ошибках, которые можно легко воспроизвести с помощью функциональных тестов, мы помещаем подготовленные функциональные тесты в директорию tests/queries/bugs
.
Эти тесты будут перемещены в tests/queries/0_stateless
, когда ошибки будут исправлены.
Интеграционные Тесты
Интеграционные тесты позволяют протестировать ClickHouse в кластерной конфигурации и взаимодействие ClickHouse с другими серверами, такими как MySQL, Postgres, MongoDB. Они полезны для имитации сетевых разделений, потерь пакетов и т. д. Эти тесты выполняются в Docker и создают несколько контейнеров с различным программным обеспечением.
Смотрите tests/integration/README.md
о том, как запустить эти тесты.
Обратите внимание, что интеграция ClickHouse с драйверами сторонних разработчиков не тестируется. Также в настоящее время у нас нет интеграционных тестов с нашими JDBC и ODBC драйверами.
Модульные Тесты
Модульные тесты полезны, когда вы хотите протестировать не сам ClickHouse целиком, а отдельную изолированную библиотеку или класс.
Вы можете включить или отключить сборку тестов с помощью опции CMake ENABLE_TESTS
.
Модульные тесты (и другие тестовые программы) находятся в подкаталогах tests
по всему коду.
Чтобы запустить модульные тесты, введите ninja test
.
Некоторые тесты используют gtest
, но некоторые являются просто программами, которые возвращают ненулевой код выхода при сбое теста.
Необязательно иметь модульные тесты, если код уже покрыт функциональными тестами (и функциональные тесты обычно гораздо проще в использовании).
Вы можете запускать отдельные проверки gtest, вызывая исполняемый файл напрямую, например:
Производственные Тесты
Производственные тесты позволяют измерять и сравнивать производительность некоторых изолированных частей ClickHouse по синтетическим запросам.
Производственные тесты находятся в tests/performance/
.
Каждый тест представлен файлом .xml
с описанием тестового случая.
Тесты запускаются с помощью инструмента docker/test/performance-comparison
. Смотрите файл readme для вызова.
Каждый тест выполняет один или несколько запросов (возможно с комбинациями параметров) в цикле.
Если вы хотите улучшить производительность ClickHouse в каком-либо сценарии, и если улучшения можно наблюдать на простых запросах, настоятельно рекомендуется написать производственный тест.
Также рекомендуется писать производственные тесты, когда вы добавляете или модифицируете SQL-функции, которые относительно изолированы и не слишком неясны.
Всегда имеет смысл использовать perf top
или другие инструменты perf
во время ваших тестов.
Инструменты и Скрипты Тестирования
Некоторые программы в директории tests
не являются подготовленными тестами, а представляют собой инструменты тестирования.
Например, для Lexer
существует инструмент src/Parsers/tests/lexer
, который просто выполняет токенизацию stdin и выводит цветной результат в stdout.
Вы можете использовать эти инструменты в качестве примеров кода и для исследования и ручного тестирования.
Разнообразные Тесты
Существуют тесты для моделей машинного обучения в tests/external_models
.
Эти тесты не обновляются и должны быть перенесены в интеграционные тесты.
Существует отдельный тест для вставок по кворуму.
Этот тест запускает кластер ClickHouse на отдельных серверах и эмулирует различные случаи отказов: сетевое разделение, потерю пакетов (между узлами ClickHouse, между ClickHouse и ZooKeeper, между сервером ClickHouse и клиентом и т. д.), kill -9
, kill -STOP
и kill -CONT
, как в Jepsen. Затем тест проверяет, что все подтвержденные вставки были записаны, а все отклоненные вставки нет.
Тест кворума был написан отдельной командой до того, как ClickHouse был открыт. Эта команда больше не работает с ClickHouse. Тест был случайно написан на Java. По этим причинам тест кворума должен быть переписан и перенесен в интеграционные тесты.
Ручное Тестирование
Когда вы разрабатываете новую функцию, разумно также протестировать ее вручную. Вы можете сделать это, следуя следующим шагам:
Соберите ClickHouse. Запустите ClickHouse из терминала: смените директорию на programs/clickhouse-server
и запустите его с помощью ./clickhouse-server
. По умолчанию он будет использовать конфигурацию (config.xml
, users.xml
и файлы в директориях config.d
и users.d
) из текущей директории. Чтобы подключиться к серверу ClickHouse, запустите programs/clickhouse-client/clickhouse-client
.
Обратите внимание, что все инструменты clickhouse (сервер, клиент и т. д.) являются просто символьными ссылками на единственный бинарный файл с именем clickhouse
.
Вы можете найти этот бинарный файл в programs/clickhouse
.
Все инструменты также могут быть вызваны как clickhouse tool
вместо clickhouse-tool
.
В качестве альтернативы вы можете установить пакет ClickHouse: либо стабильный релиз из репозитория ClickHouse, либо вы можете собрать пакет для себя с помощью ./release
в корне исходного кода ClickHouse.
Затем запустите сервер с помощью sudo clickhouse start
(или остановите, чтобы остановить сервер).
Ищите логи по пути /etc/clickhouse-server/clickhouse-server.log
.
Когда ClickHouse уже установлен на вашей системе, вы можете собрать новый бинарный файл clickhouse
и заменить существующий бинарный файл:
Кроме того, вы можете остановить системный clickhouse-server и запустить свой с той же конфигурацией, но с логированием в терминал:
Пример с gdb:
Если системный clickhouse-server уже запущен и вы не хотите его останавливать, вы можете изменить номера портов в своем config.xml
(или переопределить их в файле в директории config.d
), предоставить соответствующий путь к данным и запустить его.
Бинарный файл clickhouse
практически не имеет зависимостей и работает в широком диапазоне дистрибутивов Linux.
Чтобы быстро и неформально протестировать ваши изменения на сервере, вы просто можете scp
свой свежесобранный бинарный файл clickhouse
на ваш сервер, а затем запустить его, как в приведенных выше примерах.
Тесты на Сборку
Тесты на сборку позволяют проверить, что сборка не сломана на различных альтернативных конфигурациях и на некоторых чужих системах. Эти тесты также автоматизированы.
Примеры:
- кросс-компиляция для Darwin x86_64 (macOS)
- кросс-компиляция для FreeBSD x86_64
- кросс-компиляция для Linux AArch64
- сборка на Ubuntu с библиотеками из системных пакетов (не рекомендуется)
- сборка со связанной компоновкой библиотек (не рекомендуется)
Например, сборка с системными пакетами является плохой практикой, потому что мы не можем гарантировать, какую именно версию пакетов будет иметь система. Но это действительно нужно для мейнтейнеров Debian. По этой причине мы по крайней мере должны поддерживать этот вариант сборки. Другой пример: совместная компоновка является общим источником проблем, но это нужно некоторым энтузиастам.
Хотя мы не можем запускать все тесты на всех вариантах сборок, мы хотим проверить по крайней мере, что различные варианты сборок не сломаны. Для этой цели мы используем тесты сборки.
Мы также тестируем, что нет единиц перевода, которые слишком длинные для компиляции или требуют слишком много ОЗУ.
Мы также тестируем, что нет слишком больших стековых кадров.
Тестирование на Совместимость Протоколов
Когда мы расширяем сетевой протокол ClickHouse, мы вручную тестируем, что старый clickhouse-client работает с новым clickhouse-server, и новый clickhouse-client работает со старым clickhouse-server (просто запуская бинарные файлы из соответствующих пакетов).
Мы также тестируем некоторые случаи автоматически с интеграционными тестами:
- если данные, записанные старой версией ClickHouse, могут быть успешно прочитаны новой версией;
- работают ли распределенные запросы в кластере с разными версиями ClickHouse.
Помощь от Компилятора
Основной код ClickHouse (который находится в директории src
) компилируется с -Wall -Wextra -Werror
и с некоторыми дополнительными включенными предупреждениями.
Хотя эти параметры не включены для сторонних библиотек.
Clang имеет еще более полезные предупреждения — вы можете найти их с помощью -Weverything
и выбрать что-то для стандартной сборки.
Мы всегда используем clang для сборки ClickHouse, как для разработки, так и для производства.
Вы можете собрать на своем компьютере в режиме отладки (чтобы сэкономить батарею вашего ноутбука), но обратите внимание, что компилятор может генерировать больше предупреждений с -O3
благодаря лучшему контролю потока и анализу межпроцедурных взаимодействий.
При сборке с помощью clang в режиме отладки используется отлаженная версия libc++
, которая позволяет ловить больше ошибок во время выполнения.
Санитайзеры
Если процесс (сервер или клиент ClickHouse) зависает при запуске локально, вам может потребоваться отключить рандомизацию макета адресного пространства: sudo sysctl kernel.randomize_va_space=0
Санитайзер адресов
Мы запускаем функциональные, интеграционные, стрессовые и модульные тесты под ASan на основе каждого коммита.
Санитайзер потоков
Мы запускаем функциональные, интеграционные, стрессовые и модульные тесты под TSan на основе каждого коммита.
Санитайзер памяти
Мы запускаем функциональные, интеграционные, стрессовые и модульные тесты под MSan на основе каждого коммита.
Санитайзер не определенного поведения
Мы запускаем функциональные, интеграционные, стрессовые и модульные тесты под UBSan на основе каждого коммита. Код некоторых сторонних библиотек не очищается на наличие UB.
Valgrind (Memcheck)
Ранее мы запускали функциональные тесты под Valgrind ночью, но больше не делаем этого.
Это занимает много часов.
В настоящее время существует одна известная ложная срабатывания в библиотеке re2
, см. эту статью.
Fuzzing
Fuzzing ClickHouse реализовано как с помощью libFuzzer, так и с помощью случайных SQL-запросов. Все тесты на Fuzzing должны выполняться с санитайзерами (Address и Undefined).
LibFuzzer используется для изолированного тестирования библиотеки кода на момент появления ошибок.
Fuzzer внедряются как часть тестового кода и имеют постфикс "_fuzzer".
Пример fuzzer можно найти в src/Parsers/fuzzers/lexer_fuzzer.cpp
.
Конфигурации, специфичные для LibFuzzer, словари и корпус хранятся в tests/fuzz
.
Мы призываем вас писать тесты на Fuzzing для каждой функциональности, которая обрабатывает ввод от пользователей.
Fuzzers не собираются по умолчанию.
Чтобы собрать fuzzers, необходимо установить оба параметра -DENABLE_FUZZING=1
и -DENABLE_TESTS=1
.
Рекомендуется отключить Jemalloc во время сборки fuzzers.
Конфигурация, используемая для интеграции Fuzzing ClickHouse в Google OSS-Fuzz, может быть найдена в docker/fuzz
.
Мы также используем простой тест Fuzzing для генерации случайных SQL-запросов и для проверки, что сервер не "гибнет", выполняя их.
Вы можете найти его в 00746_sql_fuzzy.pl
.
Этот тест должен выполняться непрерывно (ночью и дольше).
Мы также используем сложный fuzzer запросов на основе AST, который способен находить огромное количество крайних случаев. Он делает случайные перестановки и замены в запросах AST. Он запоминает узлы AST из предыдущих тестов, чтобы использовать их для Fuzzing последующих тестов, обрабатывая их в случайном порядке. Вы можете узнать больше об этом fuzzer в этой статье в блоге.
Стресс-тест
Стресс-тесты — это еще один случай Fuzzing. Он запускает все функциональные тесты параллельно в случайном порядке с единственным сервером. Результаты тестов не проверяются.
Проверяется, что:
- сервер не зависает, не возникает никаких отладочных или санитайзерных ловушек;
- отсутствуют взаимоблокировки;
- структура базы данных последовательна;
- сервер может успешно остановиться после теста и снова запуститься без исключений.
Существует пять вариантов (Debug, ASan, TSan, MSan, UBSan).
Fuzzer потоков
Fuzzer потоков (пожалуйста, не путайте с Санитайзером потоков) является еще одним видом Fuzzing, который позволяет рандомизировать порядок выполнения потоков. Это помогает находить еще больше специальных случаев.
Аудит Безопасности
Наша команда по безопасности провела некоторый базовый обзор возможностей ClickHouse с точки зрения безопасности.
Статические Анализаторы
Мы запускаем clang-tidy
на основе каждого коммита.
Проверки clang-static-analyzer
также включены.
clang-tidy
также используется для некоторых проверок стиля.
Мы оценили clang-tidy
, Coverity
, cppcheck
, PVS-Studio
, tscancode
, CodeQL
.
Вы найдете инструкции по использованию в директории tests/instructions/
.
Если вы используете CLion
в качестве IDE, вы можете использовать некоторые проверки clang-tidy
из коробки.
Мы также используем shellcheck
для статического анализа оболочки.
Укрепление
В сборке отладки мы используем кастомный аллокатор, который выполняет ASLR для выделений на уровне пользователя.
Мы также вручную защищаем области памяти, которые должны оставаться только для чтения после выделения.
В сборке отладки мы также используем модификацию libc, которая гарантирует, что никакие "вредные" (устаревшие, небезопасные, не потокобезопасные) функции не вызываются.
Отладочные утверждения используются широко.
В сборке отладки, если возникает исключение с кодом "логическая ошибка" (предполагает наличие ошибки), программа завершается преждевременно. Это позволяет использовать исключения в сборке выпуска, но делать их утверждением в сборке отладки.
В отладочной версии используется jemalloc. В отладочной версии используется libc++.
Проверки Целостности Во Время Выполнения
Данные, хранящиеся на диске, проверяются с контрольной суммой. Данные в таблицах MergeTree проверяются с контрольной суммой тремя способами одновременно* (сжатые блоки данных, несжатые блоки данных, общая контрольная сумма по блокам). Данные, передаваемые по сети между клиентом и сервером или между серверами, также проверяются с контрольной суммой. Репликация обеспечивает битово-идентичные данные на репликах.
Необходимо защитить от неисправного оборудования (битовые ошибки на носителях, битовые ошибки в ОП на сервере, битовые ошибки в ОП сетевого контроллера, битовые ошибки в ОП сетевого коммутатора, битовые ошибки в ОП клиента, битовые ошибки на проводах). Обратите внимание, что битовые ошибки являются распространенными и могут произойти даже для OОПЭК и при наличии контрольных сумм TCP (если вам удастся запустить тысячи серверов, обрабатывающих петабайты данных каждый день). Смотрите видео (по-русски).
ClickHouse предоставляет диагностику, которая поможет инженерам по эксплуатации найти неисправное оборудование.
* и это не медленно.
Стиль Кода
Правила стиля кода описаны здесь.
Для проверки некоторых распространенных нарушений стиля вы можете использовать скрипт utils/check-style
.
Чтобы заставить ваш код соответствовать правильному стилю, вы можете использовать clang-format
.
Файл .clang-format
находится в корне исходного кода.
Он в основном соответствует нашему фактическому стилю кода.
Но не рекомендуется применять clang-format
к существующим файлам, так как это ухудшает форматирование.
Вы можете использовать инструмент clang-format-diff
, который можно найти в репозитории исходного кода clang.
В качестве альтернативы вы можете попробовать инструмент uncrustify
для переформатирования вашего кода.
Конфигурация находится в uncrustify.cfg
в корне исходного кода.
Он менее протестирован, чем clang-format
.
CLion
имеет свой собственный форматировщик кода, который должен быть настроен под наш стиль кода.
Мы также используем codespell
для поиска опечаток в коде.
Это также автоматизировано.
Покрытие Тестами
Мы также отслеживаем покрытие тестами, но только для функциональных тестов и только для clickhouse-server. Это выполняется на ежедневной основе.
Тесты для Тестов
Существует автоматическая проверка на ненадежные тесты. Она запускает все новые тесты 100 раз (для функциональных тестов) или 10 раз (для интеграционных тестов). Если хотя бы один раз тест завершился с ошибкой, он считается ненадежным.
Автоматизация Тестов
Мы запускаем тесты с помощью GitHub Actions.
Работы по сборке и тесты запускаются в песочнице при каждом коммите. Результирующие пакеты и результаты тестов публикуются в GitHub и могут быть загружены по прямым ссылкам. Артефакты хранятся в течение нескольких месяцев. Когда вы отправляете запрос на внесение изменений в GitHub, мы помечаем его как "может быть протестирован", и наша CI-система будет собирать пакеты ClickHouse (релиз, отладка, с санитайзером адресов и т. д.) для вас.