Перейти к основному содержимому
Перейти к основному содержимому

Тестирование ClickHouse

Функциональные тесты

Функциональные тесты являются самыми простыми и удобными в использовании. Большинство возможностей ClickHouse можно протестировать с помощью функциональных тестов, и их обязательно использовать для каждого изменения в коде ClickHouse, которое можно протестировать таким образом.

Каждый функциональный тест отправляет один или несколько запросов к запущенному серверу ClickHouse и сравнивает результат со ссылкой.

Тесты находятся в директории queries. Существует две поддиректории: stateless и stateful.

  • Stateless тесты выполняют запросы без предварительно загруженных тестовых данных — они часто создают небольшие синтетические наборы данных на лету, в пределах самого теста.
  • Stateful тесты требуют предварительно загруженных тестовых данных из ClickHouse, и они доступны для широкой публики. См. stateful тест в непрерывной интеграции.

Каждый тест может быть одного из двух типов: .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 подстрока. Существуют также опции для запуска тестов параллельно или в случайном порядке.

Добавление нового теста

Чтобы добавить новый тест, сначала создайте файл .sql или .sh в директории queries/0_stateless. Затем сгенерируйте соответствующий файл .reference, используя clickhouse-client < 12345_test.sql > 12345_test.reference или ./12345_test.sh > ./12345_test.reference.

Тесты должны создавать, удалять, выбирать из и т. д. таблицы в базе данных test, которая автоматически создается заранее. Использование временных таблиц допускается.

Чтобы настроить такую же среду, как в CI локально, установите конфигурации тестов (они будут использовать имитацию Zookeeper и настроят некоторые параметры)

примечание

Тесты должны:

  • быть минимальными: создавать только минимально необходимые таблицы, колонки и, сложность,
  • быть быстрыми: не занимать более нескольких секунд (лучше: менее секунды),
  • быть корректными и детерминированными: проваливаться, если и только если тестируемая функция не работает,
  • быть изолированными/stateless: не полагаться на среду и время,
  • быть исчерпывающими: охватывать крайние случаи, такие как нули, нулевые значения, пустые наборы, исключения (негативные тесты, используйте синтаксис -- { 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 в правильных конфигурациях, где сервер настроен на поддержку распределенных запросов.

Работа с временными файлами

Иногда в shell тесте вам может понадобиться создать файл на лету, с которым нужно работать. Имейте в виду, что некоторые проверки 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, который просто выполняет токенизацию стандартного ввода и записывает цветной результат на стандартный вывод. Вы можете использовать эти инструменты в качестве примеров кода и для исследования и ручного тестирования.

Разные тесты

Существуют тесты для моделей машинного обучения в 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 (или stop для остановки сервера). Ищите журналы по адресу /etc/clickhouse-server/clickhouse-server.log.

Когда ClickHouse уже установлен в вашей системе, вы можете собрать новый бинарный файл clickhouse и заменить существующий бинарный файл:

Также вы можете остановить системный сервер clickhouse и запустить свой с той же конфигурацией, но с выводом журналов в терминал:

Пример с gdb:

Если системный сервер clickhouse уже запущен и вы не хотите его останавливать, вы можете изменить номера портов в своем 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, см. эту статью.

Фuzzing

Фuzzing ClickHouse реализован как с использованием libFuzzer, так и случайных SQL запросов. Все тестирование на фuzzing должно выполняться с санитайзерами (Address и Undefined).

LibFuzzer используется для изолированного тестирования фuzzing библиотечного кода. Фуззеры реализованы как часть тестового кода и имеют постфикс с именем "_fuzzer". Пример фуззера можно найти в src/Parsers/fuzzers/lexer_fuzzer.cpp. Конфигурации, специфические для LibFuzzer, словари и корпуса хранятся в tests/fuzz. Мы призываем вас писать тесты на фuzzing для каждой функции, которая обрабатывает ввод от пользователей.

Фуззеры по умолчанию не собираются. Чтобы собрать фуззеры, необходимо установить как -DENABLE_FUZZING=1, так и -DENABLE_TESTS=1. Мы рекомендуем отключить Jemalloc во время сборки фуззеров. Конфигурация, использованная для интеграции фuzzing ClickHouse в Google OSS-Fuzz, может быть найдена в docker/fuzz.

Мы также используем простой тест на фuzzing, чтобы создавать случайные SQL запросы и проверять, что сервер не выдает ошибок при их выполнении. Вы можете найти его в 00746_sql_fuzzy.pl. Этот тест следует запускать непрерывно (на ночь и дольше).

Мы также используем сложный фуззер на основе AST, который способен находить огромное количество крайних случаев. Он осуществляет случайные перестановки и подстановки в запросах AST. Он запоминает узлы AST из предыдущих тестов, чтобы использовать их для фuzzing следующих тестов, обрабатывая их в случайном порядке. Вы можете узнать больше об этом фуззере в этой статье в блоге.

Стресс тест

Стресс-тесты — это еще один случай фuzzing. Все функциональные тесты выполняются параллельно в случайном порядке с одним сервером. Результаты тестов не проверяются.

Проверяется, что:

  • сервер не завершается, никаких отладочных или санитарных триггеров не вызывается;
  • нет взаимных блокировок;
  • структура базы данных консистентна;
  • сервер может успешно остановиться после теста и снова запуститься без исключений.

Существует пять вариантов (Debug, ASan, TSan, MSan, UBSan).

Thread Fuzzer

Thread Fuzzer (пожалуйста, не путайте с Thread Sanitizer) — это еще один вид фuzzing, который позволяет рандомизировать порядок выполнения потоков. Это помогает найти еще больше специальных случаев.

Аудит безопасности

Наша команда по безопасности провела базовый обзор возможностей ClickHouse с точки зрения безопасности.

Статические анализаторы

Мы запускаем clang-tidy на основе каждого коммита. Проверки clang-static-analyzer также включены. clang-tidy также используется для некоторых проверок стиля.

Мы оценили clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL. Вы найдете инструкции по использованию в директории tests/instructions/.

Если вы используете CLion в качестве IDE, вы можете использовать некоторые проверки clang-tidy из коробки.

Мы также используем shellcheck для статического анализа shell-скриптов.

Укрепление

В режиме отладки мы используем пользовательский аллокатор, который делает ASLR пользовательских выделений памяти.

Мы также вручную защищаем участки памяти, которые, как ожидается, будут только для чтения после выделения.

В режиме отладки мы также используем настройку libc, которая гарантирует, что никакие "вредные" (устаревшие, небезопасные, не потокобезопасные) функции не будут вызваны.

Отладочные проверки используются широко.

В режиме отладки, если выбрасывается исключение с кодом "логической ошибки" (что подразумевает наличие ошибки), программа завершается преждевременно. Это позволяет использовать исключения в версии для выпуска, но делает это утверждением в версии для отладки.

Отладочная версия jemalloc используется для режимов отладки. Отладочная версия libc++ используется для режимов отладки.

Проверки целостности во время выполнения

Данные, хранящиеся на диске, проверяются на контрольную сумму. Данные в таблицах MergeTree проверяются на контрольную сумму тремя способами одновременно* (сжатые блоки данных, несжатые блоки данных, общая контрольная сумма по блокам). Данные, передаваемые по сети между клиентом и сервером или между серверами, также проверяются на контрольную сумму. Репликация обеспечивает бит-идентичные данные на репликах.

Это необходимо для защиты от неисправного оборудования (битовая порча на носителе, битовые сбои в ОЗУ сервера, битовые сбои в ОЗУ сетевого контроллера, битовые сбои в ОЗУ сетевого коммутатора, битовые сбои в ОЗУ клиента, битовые сбои на проводе). Обратите внимание, что битовые сбои распространены и вероятны даже для ECC ОЗУ и при наличии контрольных сумм 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 и могут быть загружены по прямым ссылкам. Артефакты хранятся в течение нескольких месяцев. Когда вы отправляете pull request на GitHub, мы помечаем его как "можно тестировать", и наша CI система будет собирать пакеты ClickHouse (релиз, отладка, с санитайзером адресов и т. д.) для вас.