Passer au contenu principal

Types de tests

ClickHouse comprend les tests suivants :

Tests fonctionnels

Les tests fonctionnels sont les plus simples et les plus pratiques à utiliser. La plupart des fonctionnalités de ClickHouse peuvent être testées avec des tests fonctionnels, et ils sont obligatoires pour toute modification du code de ClickHouse pouvant être testée de cette façon. Chaque test fonctionnel envoie une ou plusieurs requêtes au serveur ClickHouse en cours d’exécution et compare le résultat à la référence. Les tests se trouvent dans le répertoire ./tests/queries. Chaque test peut être de l’un des deux types suivants : .sql et .sh.
  • Un test .sql est un simple script SQL envoyé à clickhouse-client via un pipe.
  • Un test .sh est un script exécuté directement.
Les tests SQL sont généralement préférables aux tests .sh. N’utilisez les tests .sh que lorsque vous devez tester une fonctionnalité qui ne peut pas l’être en SQL pur, par exemple pour envoyer des données d’entrée à clickhouse-client via un pipe ou pour tester clickhouse-local.
Une erreur fréquente lors des tests des types de données DateTime et DateTime64 consiste à supposer que le serveur utilise un fuseau horaire spécifique (par ex. “UTC”). Ce n’est pas le cas : les fuseaux horaires utilisés dans les exécutions de tests CI sont délibérément choisis de manière aléatoire. La solution de contournement la plus simple consiste à spécifier explicitement le fuseau horaire des valeurs de test, par ex. toDateTime64(val, 3, 'Europe/Amsterdam').

Exécuter un test en local

Démarrez le serveur ClickHouse en local, à l’écoute du port par défaut (9000). Pour exécuter, par exemple, le test 01428_hash_set_nan_key, placez-vous dans le dossier du dépôt et lancez la commande suivante :
PATH=<path to clickhouse-client>:$PATH tests/clickhouse-test 01428_hash_set_nan_key
Les résultats des tests (stderr et stdout) sont enregistrés dans les fichiers 01428_hash_set_nan_key.[stderr|stdout], situés à côté du test correspondant (pour queries/0_stateless/foo.sql, la sortie se trouvera dans queries/0_stateless/foo.stdout). Consultez tests/clickhouse-test --help pour connaître toutes les options de clickhouse-test. Vous pouvez exécuter tous les tests ou un sous-ensemble de tests en fournissant un filtre sur les noms de test : ./clickhouse-test substring. Il existe également des options pour exécuter les tests en parallèle ou dans un ordre aléatoire.

Exécution des tests rapides

Il vous faudra peut-être une machine suffisamment puissante pour exécuter un sous-ensemble de tests (appelé “test rapide”). La configuration suivante fonctionne sur une instance Ubuntu AWS amd64 t3.2xlarge avec 100 Go d’espace de stockage.
  1. Installez les prérequis, puis reconnectez-vous.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
  1. Récupérez le code source.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. Compilez le code et exécutez les “tests rapides”.
python -m ci.praktika run fast
Vous devriez obtenir
Failed: 0, Passed: 7394, Skipped: 1795
Si vous laissez l’exécution sans surveillance, vous pouvez utiliser nohup ou disown pour qu’elle continue à s’exécuter après la perte de la connexion ssh.

Exécution des tests sans état

Il vous faudra peut-être une machine assez puissante pour exécuter les tests sans état. La configuration ci-dessous fonctionne sur une instance Ubuntu AWS amd64 m7i.8xlarge avec 200 Go de stockage.
  1. Installez les prérequis, puis reconnectez-vous.
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. Récupérez le code source.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. Compilez le code.
python -m ci.praktika run build_debug
cp ci/tmp/build/programs/clickhouse ci/tmp
  1. Exécutez des tests sans état, qui peuvent s’exécuter en parallèle.
python -m ci.praktika run functional
Vous devriez obtenir
Failed: 0, Passed: 8497, Skipped: 103
Remarque : les commandes python -m ci.praktika run lancent un job d’intégration continue spécifique. Pour en savoir plus sur la CI de ClickHouse, consultez cette page.

Ajouter un nouveau test

Pour ajouter un nouveau test, créez d’abord un fichier .sql ou .sh dans le répertoire queries/0_stateless. Générez ensuite le fichier .reference correspondant à l’aide de clickhouse-client < 12345_test.sql > 12345_test.reference ou de ./12345_test.sh > ./12345_test.reference. Les tests doivent uniquement créer, supprimer, interroger, etc. des tables dans la base de données test, qui est automatiquement créée au préalable. Il est possible d’utiliser des tables temporaires. Pour retrouver localement le même environnement qu’en CI, installez les configurations de test (elles utiliseront une implémentation factice de ZooKeeper et ajusteront certains paramètres)
cd <repository>/tests/config
sudo ./install.sh
Les tests doivent être
  • minimaux : ne créer que les tables, colonnes et la complexité strictement nécessaires,
  • rapides : ne pas prendre plus de quelques secondes (mieux : moins d’une seconde),
  • corrects et déterministes : échouer si et seulement si la fonctionnalité testée ne fonctionne pas,
  • isolés/sans état : ne pas dépendre de l’environnement ni du moment de l’exécution,
  • exhaustifs : couvrir les cas limites comme les zéros, les valeurs NULL, les ensembles vides et les exceptions (tests négatifs : utilisez pour cela la syntaxe -- { serverError xyz } et -- { clientError xyz }),
  • nettoyer les tables à la fin du test (au cas où il resterait des éléments),
  • s’assurer que les autres tests ne vérifient pas la même chose (autrement dit, faites d’abord un grep).

Tests basés sur des modèles avec Jinja

Un test .sql peut être écrit sous forme de modèle Jinja2 en ajoutant le suffixe .j2 au nom du fichier ; ainsi, foo.sql devient foo.sql.j2. Avant d’exécuter le test, clickhouse-test génère à partir du modèle un script .sql ordinaire, puis exécute le résultat. C’est utile lorsqu’un test répète la même requête avec de légères variations : une boucle génère les requêtes à partir d’un modèle compact au lieu de les écrire une par une à la main. Les constructions les plus couramment utilisées sont :
  • {% for ... %} ... {% endfor %} pour répéter un bloc,
  • {{ expression }} pour insérer une valeur dans la sortie,
  • -%} et {%- pour supprimer les espaces adjacents afin que le script généré reste propre.
Par exemple, ce modèle :
{% for type in ['UInt8', 'UInt16', 'UInt32'] -%}
SELECT toTypeName(0::{{ type }});
{% endfor -%}
s’affiche ainsi :
SELECT toTypeName(0::UInt8);
SELECT toTypeName(0::UInt16);
SELECT toTypeName(0::UInt32);
La sortie attendue peut être fournie soit sous la forme d’un simple fichier <name>.reference contenant les résultats après expansion complète, soit sous la forme d’un modèle <name>.reference.j2, que clickhouse-test génère de la même façon avant la comparaison. Utilisez la forme avec modèle lorsque la sortie attendue suit elle aussi un schéma répétitif. Pour plus d’exemples, consultez les fichiers *.sql.j2 existants dans tests/queries/0_stateless/.

Limiter l’exécution des tests

Un test peut avoir zéro, un ou plusieurs tags indiquant les restrictions sur les contextes dans lesquels il s’exécute dans la CI. Pour les tests .sql, les tags sont placés sur la première ligne sous forme de commentaire SQL :
-- Tags: no-fasttest, no-replicated-database
-- no-fasttest: <provide_a_reason_for_the_tag_here>
-- no-replicated-database: <provide_a_reason_here>

SELECT 1
Pour les tests .sh, les tags sont écrits sous forme de commentaire à la deuxième ligne :
#!/usr/bin/env bash
# Tags: no-fasttest, no-replicated-database
# - no-fasttest: <provide_a_reason_for_the_tag_here>
# - no-replicated-database: <provide_a_reason_here>
Liste des tags disponibles :
Nom du tagDescriptionExemple d’utilisation
disabledLe test n’est pas exécuté
longLe temps d’exécution du test passe de 1 à 10 minutes
deadlockLe test est exécuté en boucle pendant une longue période
raceIdentique à deadlock. Préférez deadlock
shardLe serveur doit écouter sur 127.0.0.*
distributedIdentique à shard. Préférez shard
globalIdentique à shard. Préférez shard
zookeeperLe test nécessite ZooKeeper ou ClickHouse Keeper pour s’exécuterLe test utilise ReplicatedMergeTree
replicaIdentique à zookeeper. Préférez zookeeper
no-fasttestLe test n’est pas exécuté dans test rapideLe test utilise le moteur de table MySQL, qui est désactivé dans test rapide
fasttest-onlyLe test est exécuté uniquement dans test rapide
no-[asan, tsan, msan, ubsan]Désactive les tests dans les builds avec des sanitizersLe test est exécuté sous QEMU, qui ne fonctionne pas avec les sanitizers
no-replicated-databaseDésactive le test lorsque la base de données par défaut utilise ReplicatedDatabaseEngine
no-ordinary-databaseDésactive le test lorsque le moteur de base de données par défaut est Ordinary
no-parallelEmpêche l’exécution d’autres tests en parallèle avec celui-ciLe test lit les tables system et les invariants peuvent être rompus
no-parallel-replicasDésactive le test lorsque les répliques parallèles sont activées
no-debugDésactive les tests dans les builds Debug
no-releaseDésactive les tests dans les builds Release
no-darwinDésactive le test sur macOS (Darwin)Le test repose sur des fonctionnalités propres à Linux, comme les distributed queries, procfs ou le serveur HTTP
Les options suivantes sont également prises en charge : 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. En plus des paramètres ci-dessus, vous pouvez utiliser les flags USE_* de system.build_options pour indiquer l’utilisation de fonctionnalités ClickHouse particulières. Par exemple, si votre test utilise une table MySQL, vous devez ajouter le tag use-mysql.

Définition des limites pour les paramètres aléatoires

Un test peut définir des valeurs minimales et maximales autorisées pour les paramètres pouvant être attribués aléatoirement pendant l’exécution du test. Pour les tests .sh, les limites sont indiquées sous forme de commentaire sur la ligne à côté des tags, ou sur la deuxième ligne si aucun tag n’est spécifié :
#!/usr/bin/env bash
# Tags: no-fasttest
# Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
Pour les tests .sql, les tags sont indiqués sous forme de commentaire SQL sur la ligne suivante ou sur la première ligne :
-- Tags: no-fasttest
-- Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
SELECT 1
Si vous ne devez spécifier qu’une seule limite, vous pouvez utiliser None pour l’autre.

Choisir le nom du test

Le nom du test commence par un préfixe à cinq chiffres, suivi d’un nom descriptif, comme 00422_hash_function_constexpr.sql. Pour choisir le préfixe, repérez le plus grand préfixe déjà présent dans le directory, puis incrémentez-le de un.
ls tests/queries/0_stateless/[0-9]*.reference | tail -n 1
Entre-temps, il est possible que d’autres tests soient ajoutés avec le même préfixe numérique, mais cela ne pose aucun problème et vous n’aurez pas à le modifier par la suite.

Vérifier qu’une erreur doit se produire

Il arrive que vous souhaitiez vérifier qu’une erreur du serveur se produit avec une requête incorrecte. Nous prenons en charge des annotations spéciales à cet effet dans les tests SQL, sous la forme suivante :
SELECT x; -- { serverError 49 }
Ce test vérifie que le serveur renvoie une erreur de code 49 signalant la colonne inconnue x. S’il n’y a pas d’erreur, ou si l’erreur est différente, le test échouera. Si vous voulez vous assurer qu’une erreur se produit côté client, utilisez plutôt l’annotation clientError. Ne vérifiez pas le libellé exact du message d’erreur : il peut changer à l’avenir et faire échouer le test inutilement. Vérifiez uniquement le code d’erreur. Si le code d’erreur existant n’est pas assez précis pour vos besoins, envisagez d’en ajouter un nouveau.

Tester une requête distribuée

Si vous souhaitez utiliser des requêtes distribuées dans des tests fonctionnels, vous pouvez utiliser la fonction de table remote avec les adresses 127.0.0.{1..2} pour que le serveur puisse s’interroger lui-même, ou utiliser des clusters de test prédéfinis dans le fichier de configuration du serveur, comme test_shard_localhost. N’oubliez pas d’ajouter les mots shard ou distributed au nom du test, afin qu’il soit exécuté par la CI dans les configurations appropriées, où le serveur est configuré pour prendre en charge les requêtes distribuées.

Travailler avec les fichiers temporaires

Dans un test shell, il peut parfois être nécessaire de créer un fichier à la volée pour l’utiliser. Gardez à l’esprit que certaines vérifications CI exécutent les tests en parallèle. Si vous créez ou supprimez un fichier temporaire dans votre script sans lui donner un nom unique, cela peut faire échouer certaines vérifications CI, comme Flaky. Pour éviter ce problème, utilisez la variable d’environnement $CLICKHOUSE_TEST_UNIQUE_NAME afin de donner aux fichiers temporaires un nom unique pour le test en cours d’exécution. Vous avez ainsi la garantie que le fichier que vous créez pendant la phase de préparation ou supprimez pendant la phase de nettoyage est uniquement utilisé par ce test, et non par un autre test exécuté en parallèle.

Bogues connus

Lorsque nous connaissons des bogues pouvant être facilement reproduits par des tests fonctionnels, nous plaçons des tests fonctionnels préparés dans le répertoire tests/queries/bugs. Ces tests seront déplacés vers tests/queries/0_stateless une fois les bogues corrigés.

Tests d’intégration

Les tests d’intégration permettent de tester ClickHouse dans une configuration en cluster, ainsi que son interaction avec d’autres serveurs comme MySQL, Postgres ou MongoDB. Ils sont utiles pour émuler des partitions réseau, des pertes de paquets, etc. Ces tests sont exécutés sous Docker et créent plusieurs conteneurs exécutant différents logiciels. Consultez tests/integration/README.md pour savoir comment exécuter ces tests. Notez que l’intégration de ClickHouse avec des drivers tiers n’est pas testée. Par ailleurs, nous ne disposons actuellement pas de tests d’intégration pour nos drivers JDBC et ODBC.

Tests unitaires

Les tests unitaires sont utiles lorsque vous souhaitez tester non pas ClickHouse dans son ensemble, mais une bibliothèque ou une classe isolée. Vous pouvez activer ou désactiver la compilation des tests avec l’option CMake ENABLE_TESTS. Les tests unitaires (et les autres programmes de test) se trouvent dans les sous-répertoires tests du code. Pour exécuter les tests unitaires, tapez ninja test. Certains tests utilisent gtest, mais d’autres sont simplement des programmes qui renvoient un code de sortie non nul en cas d’échec. Il n’est pas nécessaire d’avoir des tests unitaires si le code est déjà couvert par des tests fonctionnels (et les tests fonctionnels sont généralement beaucoup plus simples à utiliser). Vous pouvez exécuter des vérifications gtest individuelles en appelant directement l’exécutable, par exemple :
$ ./src/unit_tests_dbms --gtest_filter=LocalAddress*

Tests de performance

Les tests de performance permettent de mesurer et de comparer les performances de certaines parties isolées de ClickHouse à l’aide de requêtes synthétiques. Les tests de performance se trouvent dans tests/performance/. Chaque test est représenté par un fichier .xml contenant une description du cas de test. Les tests sont exécutés avec l’outil docker/test/performance-comparison. Consultez le fichier README pour savoir comment l’utiliser. Chaque test exécute une ou plusieurs requêtes (éventuellement avec des combinaisons de paramètres) en boucle. Si vous souhaitez améliorer les performances de ClickHouse dans un scénario donné, et si les améliorations peuvent être observées sur des requêtes simples, il est fortement recommandé d’écrire un test de performance. Il est également recommandé d’écrire des tests de performance lorsque vous ajoutez ou modifiez des fonctions SQL relativement isolées et peu obscures. Il est toujours judicieux d’utiliser perf top ou d’autres outils perf pendant vos tests.

Outils et scripts de test

Certains programmes du répertoire tests ne sont pas des tests à proprement parler, mais des outils de test. Par exemple, pour Lexer, il existe un outil src/Parsers/tests/lexer qui se contente d’effectuer la tokenisation de stdin et d’écrire le résultat en couleur sur stdout. Vous pouvez utiliser ce type d’outils comme exemples de code, ainsi que pour l’exploration et les tests manuels.

Tests divers

Il existe des tests pour les modèles d’apprentissage automatique dans tests/external_models. Ces tests ne sont plus mis à jour et doivent être portés dans les tests d’intégration. Il existe un test distinct pour les insertions avec quorum. Ce test exécute un cluster ClickHouse sur des serveurs distincts et émule divers cas de défaillance : partition réseau, perte de paquets (entre les nœuds ClickHouse, entre ClickHouse et ZooKeeper, entre le serveur ClickHouse et le client, etc.), kill -9, kill -STOP et kill -CONT, comme Jepsen. Ensuite, le test vérifie que toutes les insertions validées ont bien été écrites et qu’aucune des insertions rejetées ne l’a été.

Tests manuels

Lorsque vous développez une nouvelle fonctionnalité, il est également logique de la tester manuellement. Vous pouvez procéder comme suit : Compilez ClickHouse. Exécutez ClickHouse depuis le terminal : placez-vous dans le répertoire programs/clickhouse-server et lancez-le avec ./clickhouse-server. Par défaut, il utilisera la configuration (config.xml, users.xml et les fichiers contenus dans les répertoires config.d et users.d) du répertoire courant. Pour vous connecter au serveur ClickHouse, exécutez programs/clickhouse-client/clickhouse-client. Notez que tous les outils ClickHouse (serveur, client, etc.) ne sont en réalité que des liens symboliques vers un unique binaire nommé clickhouse. Vous trouverez ce binaire dans programs/clickhouse. Tous les outils peuvent également être appelés sous la forme clickhouse tool au lieu de clickhouse-tool. Vous pouvez également installer le paquet ClickHouse : soit la release stable depuis le dépôt ClickHouse, soit compiler vous-même le paquet avec ./release à la racine des sources ClickHouse. Démarrez ensuite le serveur avec sudo clickhouse start (ou stop pour arrêter le serveur). Consultez les logs dans /etc/clickhouse-server/clickhouse-server.log. Si ClickHouse est déjà installé sur votre système, vous pouvez compiler un nouveau binaire clickhouse et remplacer le binaire existant :
$ sudo clickhouse stop
$ sudo cp ./clickhouse /usr/bin/
$ sudo clickhouse start
Vous pouvez aussi arrêter le service système clickhouse-server et lancer votre propre instance avec la même configuration, mais avec les logs dans le terminal :
$ sudo clickhouse stop
$ sudo -u clickhouse /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
Exemple avec gdb :
$ sudo -u clickhouse gdb --args /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
Si le service clickhouse-server est déjà en cours d’exécution et que vous ne souhaitez pas l’arrêter, vous pouvez modifier les numéros de port dans votre config.xml (ou les surcharger dans un fichier du répertoire config.d), indiquer un chemin de données approprié, puis le lancer. Le binaire clickhouse n’a pratiquement aucune dépendance et fonctionne sur un large éventail de distributions Linux. Pour tester rapidement vos modifications sur un serveur de manière un peu sommaire, vous pouvez simplement copier votre binaire clickhouse fraîchement compilé sur votre serveur avec scp, puis l’exécuter comme dans les exemples ci-dessus.

Tests de build

Les tests de build permettent de vérifier que le build n’est pas défectueux sur diverses configurations alternatives et sur certains autres systèmes. Ces tests sont également automatisés. Exemples :
  • compilation croisée pour Darwin x86_64 (macOS)
  • compilation croisée pour FreeBSD x86_64
  • compilation croisée pour Linux AArch64
  • build sur Ubuntu avec des bibliothèques issues des paquets système (déconseillé)
  • build avec liaison dynamique des bibliothèques (déconseillé)
Par exemple, un build avec des paquets système est une mauvaise pratique, car nous ne pouvons pas garantir la version exacte des paquets présents sur un système. Mais c’est réellement nécessaire pour les mainteneurs Debian. Pour cette raison, nous devons au moins prendre en charge cette variante de build. Autre exemple : la liaison dynamique est une source fréquente de problèmes, mais elle est nécessaire pour certains passionnés. Même si nous ne pouvons pas exécuter tous les tests sur toutes les variantes de build, nous voulons au moins vérifier que les différentes variantes de build ne sont pas défectueuses. C’est à cela que servent les tests de build. Nous vérifions également qu’il n’existe pas d’unités de traduction trop longues à compiler ou nécessitant trop de RAM. Nous vérifions également qu’il n’existe pas de frames de pile trop volumineuses.

Tester la compatibilité du protocole

Lorsque nous faisons évoluer le protocole réseau de ClickHouse, nous vérifions manuellement que l’ancien clickhouse-client fonctionne avec le nouveau clickhouse-server et que le nouveau clickhouse-client fonctionne avec l’ancien clickhouse-server (simplement en exécutant les binaires des paquets correspondants). Nous testons également automatiquement certains cas à l’aide de tests d’intégration :
  • si les données écrites par une ancienne version de ClickHouse peuvent être lues correctement par la nouvelle version ;
  • si les requêtes distribuées fonctionnent dans un cluster utilisant différentes versions de ClickHouse.

Aide du compilateur

Le code principal de ClickHouse (situé dans le répertoire src) est compilé avec -Wall -Wextra -Werror, ainsi qu’avec quelques avertissements supplémentaires activés. En revanche, ces options ne sont pas activées pour les bibliothèques tierces. Clang propose encore davantage d’avertissements utiles : vous pouvez les passer en revue avec -Weverything et en retenir certains pour la compilation par défaut. Nous utilisons toujours clang pour compiler ClickHouse, aussi bien en développement qu’en production. Vous pouvez compiler sur votre propre machine en mode debug (pour économiser la batterie de votre ordinateur portable), mais notez que le compilateur peut générer davantage d’avertissements avec -O3 grâce à une meilleure analyse du flux de contrôle et à l’analyse interprocédurale. Lors d’une compilation avec clang en mode debug, la version debug de libc++ est utilisée, ce qui permet de détecter davantage d’erreurs à l’exécution.

Sanitizers

Si le processus (serveur ClickHouse ou client) plante au démarrage lors d’une exécution en local, vous devrez peut-être désactiver la randomisation de l’espace d’adressage : sudo sysctl kernel.randomize_va_space=0

Sanitizer d’adresses

Nous exécutons des tests fonctionnels, d’intégration, de stress et unitaires avec ASan à chaque commit.

Thread sanitizer

Nous exécutons des tests fonctionnels, d’intégration, de stress et unitaires avec TSan à chaque commit.

Sanitizer mémoire

Nous exécutons des tests fonctionnels, d’intégration, de stress et unitaires avec MSan à chaque commit.

Sanitizer pour comportements indéfinis

Nous exécutons, à chaque commit, les tests fonctionnels, d’intégration, de stress et unitaires avec UBSan. Le code de certaines bibliothèques tierces n’est pas instrumenté pour détecter les comportements indéfinis.

Valgrind (memcheck)

Nous exécutions autrefois les tests fonctionnels sous Valgrind pendant la nuit, mais ce n’est plus le cas. Cela prend plusieurs heures. Actuellement, il existe un faux positif connu dans la bibliothèque re2, voir cet article.

Fuzzing

Le fuzzing de ClickHouse est mis en œuvre à la fois avec libFuzzer et des requêtes SQL aléatoires. Tous les tests de fuzzing doivent être effectués avec des sanitizers (Address et Undefined). LibFuzzer est utilisé pour tester de manière isolée le code des bibliothèques. Les fuzzers sont implémentés dans le code de test et portent le suffixe “_fuzzer”. Vous trouverez un exemple de fuzzer dans src/Parsers/fuzzers/lexer_fuzzer.cpp. Les configurations, dictionnaires et corpus spécifiques à LibFuzzer sont stockés dans tests/fuzz. Nous vous encourageons à écrire des tests de fuzzing pour chaque fonctionnalité qui traite des entrées utilisateur. Les fuzzers ne sont pas compilés par défaut. Pour compiler les fuzzers, les options -DENABLE_FUZZING=1 et -DENABLE_TESTS=1 doivent toutes deux être définies. Nous recommandons de désactiver Jemalloc lors de la compilation des fuzzers. La configuration utilisée pour intégrer le fuzzing de ClickHouse à Google OSS-Fuzz se trouve dans docker/fuzz. Nous utilisons également un test de fuzzing simple pour générer des requêtes SQL aléatoires et vérifier que le serveur ne s’arrête pas pendant leur exécution. Vous pouvez le trouver dans 00746_sql_fuzzy.pl. Ce test doit être exécuté en continu (pendant la nuit et au-delà). Nous utilisons également un fuzzer de requêtes sophistiqué basé sur l’AST, capable de trouver un très grand nombre de cas limites. Il effectue des permutations et des substitutions aléatoires dans l’AST des requêtes. Il mémorise les nœuds AST des tests précédents pour les réutiliser dans le fuzzing des tests suivants, qu’il traite dans un ordre aléatoire. Vous pouvez en apprendre davantage sur ce fuzzer dans cet article de blog.

Test de stress

Les tests de stress constituent une autre forme de fuzzing. Ils exécutent tous les tests fonctionnels en parallèle, dans un ordre aléatoire, sur un seul serveur. Les résultats des tests ne sont pas vérifiés. Les points suivants sont vérifiés :
  • le serveur ne plante pas et aucun piège de débogage ou de sanitizer ne se déclenche ;
  • il n’y a pas d’interblocages ;
  • la structure de la base de données est cohérente ;
  • le serveur peut s’arrêter correctement après le test puis redémarrer sans exception.
Il existe cinq variantes (Débogage, ASan, TSan, MSan, UBSan).

Thread fuzzer

Le Thread Fuzzer (à ne pas confondre avec le Thread Sanitizer) est un autre type de fuzzing qui permet de rendre aléatoire l’ordre d’exécution des threads. Il aide à détecter encore plus de cas particuliers.

Audit de sécurité

Notre équipe de sécurité a effectué un examen sommaire des fonctionnalités de ClickHouse sous l’angle de la sécurité.

Analyseurs statiques

Nous exécutons clang-tidy à chaque commit. Les vérifications clang-static-analyzer sont également activées. clang-tidy est aussi utilisé pour certaines vérifications de style. Nous avons évalué clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL. Vous trouverez les instructions d’utilisation dans le répertoire tests/instructions/. Si vous utilisez CLion comme IDE, vous pouvez profiter immédiatement de certaines vérifications clang-tidy. Nous utilisons également shellcheck pour l’analyse statique des scripts shell.

Durcissement

Dans la compilation de débogage, nous utilisons un allocator personnalisé qui applique l’ASLR aux allocations au niveau utilisateur. Nous protégeons également manuellement les régions mémoire qui sont censées être en lecture seule après l’allocation. Dans la compilation de débogage, nous utilisons également une personnalisation de libc qui garantit qu’aucune fonction « nocive » (Obsolete, non sécurisée, non thread-safe) n’est appelée. Les assertions de débogage sont largement utilisées. Dans la compilation de débogage, si une exception avec le code « logical error » (ce qui indique un bug) est levée, le programme est arrêté prématurément. Cela permet d’utiliser des exceptions dans la compilation de release, tout en les transformant en assertions dans la compilation de débogage. La version de débogage de jemalloc est utilisée pour les compilations de débogage. La version de débogage de libc++ est utilisée pour les compilations de débogage.

Contrôles d’intégrité à l’exécution

Les données stockées sur disque font l’objet de sommes de contrôle. Les données des tables MergeTree sont protégées simultanément par des sommes de contrôle de trois façons* (blocs de données compressés, blocs de données non compressés, somme de contrôle globale sur l’ensemble des blocs). Les données transférées sur le réseau entre le client et le serveur, ou entre serveurs, font également l’objet de sommes de contrôle. La réplication garantit des données identiques bit à bit sur les répliques. Cela est nécessaire pour se prémunir contre les défaillances matérielles (altération de bits sur les supports de stockage, inversions de bits dans la RAM du serveur, inversions de bits dans la RAM du contrôleur réseau, inversions de bits dans la RAM du commutateur réseau, inversions de bits dans la RAM du client, inversions de bits sur le réseau). Notez que les inversions de bits sont fréquentes et susceptibles de se produire même avec de la RAM ECC et en présence de sommes de contrôle TCP (si vous exploitez des milliers de serveurs traitant chaque jour des pétaoctets de données). Voir la vidéo (russe). ClickHouse fournit des outils de diagnostic qui aideront les ingénieurs d’exploitation à détecter les défaillances matérielles.
  • et ce n’est pas lent.

Style de code

Les règles de style de code sont décrites ici. Pour détecter certaines violations courantes des règles de style, vous pouvez utiliser le script utils/check-style. Pour imposer un style correct à votre code, vous pouvez utiliser clang-format. Le fichier .clang-format se trouve à la racine du code source. Il correspond en grande partie à notre style de code actuel. Mais il n’est pas recommandé d’appliquer clang-format à des fichiers existants, car cela dégrade le formatage. Vous pouvez utiliser l’outil clang-format-diff, que vous trouverez dans le dépôt source de clang. Vous pouvez aussi essayer l’outil uncrustify pour reformater votre code. Le fichier de configuration uncrustify.cfg se trouve à la racine du code source. Il est moins éprouvé que clang-format. CLion possède son propre formateur de code, qui doit être ajusté à notre style de code.

Couverture des tests

Nous suivons également la couverture des tests, mais uniquement pour les tests fonctionnels et seulement pour clickhouse-server. Elle est mesurée quotidiennement.

Tests des tests

Il existe une vérification automatisée pour détecter les tests instables. Elle exécute tous les nouveaux tests 100 fois (pour les tests fonctionnels) ou 10 fois (pour les tests d’intégration). Si un test échoue ne serait-ce qu’une seule fois, il est considéré comme instable.

Automatisation des tests

Nous exécutons les tests avec GitHub Actions. Les tâches de build et les tests sont exécutés dans Sandbox à chaque commit. Les paquets générés et les résultats des tests sont publiés sur GitHub et peuvent être téléchargés via des liens directs. Les artefacts sont conservés pendant plusieurs mois. Lorsque vous envoyez une pull request sur GitHub, nous lui attribuons l’étiquette “can be tested” et notre système de CI construira pour vous des paquets ClickHouse (release, debug, avec address sanitizer, etc.).
Dernière modification le 29 juin 2026