メインコンテンツまでスキップ
メインコンテンツまでスキップ

Testing ClickHouse

Functional Tests

Functional testsは最もシンプルで使いやすいテストです。 ほとんどのClickHouseの機能はfunctional testsを使用してテストでき、テスト可能なClickHouseコードの変更に対しては必須です。

各functional testは、実行中のClickHouseサーバーに1つ以上のクエリを送り、結果をリファレンスと比較します。

テストはqueriesディレクトリにあります。 サブディレクトリは2つあり、statelessstatefulです。

  • Stateless testsは事前にロードされたテストデータなしでクエリを実行します - テスト自体内で小さな合成データセットをその場で作成することがよくあります。
  • Stateful testsはClickHouseからの事前にロードされたテストデータを必要とし、一般公開されています。stateful test in continuous integrationを参照してください。

各テストは、.sql.shの2つのタイプのいずれかです。

  • .sqlテストは、clickhouse-clientにパイプされるシンプルなSQLスクリプトです。
  • .shテストは、自身で実行されるスクリプトです。

SQLテストは一般的に.shテストよりも望まれます。 純粋なSQLからは動作しない機能をテストする必要がある場合のみ、.shテストを使用するべきです。例えば、clickhouse-clientに入力データをパイプする場合や、clickhouse-localをテストする場合です。

注記

DateTime型とDateTime64型をテストする際の一般的な間違いは、サーバーが特定のタイムゾーン(例:"UTC")を使用していると仮定することです。これは事実ではなく、CIテストの実行中のタイムゾーンは故意にランダム化されています。テスト値に対してタイムゾーンを明示的に指定するのが最も簡単な回避策です。例:toDateTime64(val, 3, 'Europe/Amsterdam')

Running a Test Locally

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。 テストを並行して実行するためのオプションや、ランダム順序で実行するオプションもあります。

Adding a New Test

新しいテストを追加するには、まずqueries/0_statelessディレクトリに.sqlまたは.shファイルを作成します。 次に、clickhouse-client < 12345_test.sql > 12345_test.referenceまたは./12345_test.sh > ./12345_test.referenceを使用して、対応する.referenceファイルを生成します。

テストは、事前に自動で作成されるデータベースtest内で、テーブルを作成、削除、選択するのみとしてください。 一時テーブルを使用することは問題ありません。

CIと同じ環境をローカルにセットアップするには、テスト設定をインストールします(これによりZookeeperのモック実装が使用され、一部の設定が調整されます)。

注記

テストは次の条件を満たさなければなりません:

  • 最小限であること:必要最小限のテーブル、カラム、および複雑さを作成するのみ
  • 迅速であること:数秒を超えないこと(できればサブセカンドで)
  • 正確かつ決定論的であること:テスト機能が正しく動作しない場合にのみ失敗する
  • 隔離されている/ステートレスであること:環境やタイミングに依存しない
  • 包括的であること:ゼロ、null、空のセット、例外(負のテスト、構文-- { serverError xyz }-- { clientError xyz }を使用)などのコーナーケースをカバーする
  • テストの最後にテーブルをクリーンアップすること(残り物があれば)
  • 他のテストが同じものをテストしないことを確認すること(つまり、最初にgrepすること)。

Restricting test runs

テストには、CIでの実行コンテキストを制限するための0個以上の tags を持たせることができます。

.sqlテストの場合、タグは最初の行にSQLコメントとして置かれます。

.shテストの場合、タグは2行目のコメントとして書かれます。

利用可能なタグのリスト:

タグ名説明使用例
disabledテストは実行されません
longテストの実行時間は1分から10分に延長されます
deadlockテストは長時間ループで実行されます
racedeadlockと同じ。deadlockを優先してください
shardサーバーは127.0.0.*をリッスンする必要があります
distributedshardと同じ。shardを優先してください
globalshardと同じ。shardを優先してください
zookeeperテストを実行するためにZookeeperまたはClickHouse Keeperが必要ですテストはReplicatedMergeTreeを使用します
replicazookeeperと同じ。zookeeperを優先してください
no-fasttestFast testの下でテストは実行されませんテストはFast testで無効にされているMySQLテーブルエンジンを使用します
no-[asan, tsan, msan, ubsan]sanitizersを使用したビルドでテストを無効にしますテストはQEMUで実行されますが、sanitizersでは動作しません
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

上記の設定に加えて、特定のClickHouse機能の使用を定義するために、system.build_optionsからUSE_*フラグを使用できます。 例えば、テストがMySQLテーブルを使用する場合、use-mysqlタグを追加するべきです。

Specifying limits for random settings

テストは、テスト実行中にランダム化可能な設定の最小および最大許容値を指定できます。

.shテストの場合、制限はタグの隣の行や、タグが指定されていない場合の2行目にコメントとして書かれます。

.sqlテストの場合、タグはタグの隣の行や最初の行にSQLコメントとして置かれます。

制限が一つのみを指定する必要がある場合、もう一つにはNoneを使用できます。

Choosing the Test Name

テスト名は、5桁のプレフィックスで始まり、続いて説明的な名前が付きます。例えば00422_hash_function_constexpr.sqlのように。 プレフィックスを選択するには、ディレクトリに既に存在する最大のプレフィックスを見つけ、それを1増やします。

その間に、同じ数値のプレフィックスを持つ他のテストが追加される場合がありますが、これは問題なく、後で変更する必要はありません。

Checking for an Error that Must Occur

時には、誤ったクエリに対してサーバーエラーが発生することをテストしたいことがあります。このための特別な注釈をSQLテストでサポートしています。次の形式です。

このテストは、サーバーが未知のカラムxについてエラーコード49を返すことを保証します。 エラーがない場合や、エラーが異なる場合、テストは失敗します。 クライアント側でエラーが発生することを確認するには、clientError注釈を使用してください。

エラーメッセージの特定の表現を確認しないでください。それは将来的に変更される可能性があり、テストが不必要に壊れることになります。 エラーコードのみを確認します。 既存のエラーコードがあなたのニーズに合わない場合は、新しいものを追加することを検討してください。

Testing a Distributed Query

functional testsで分散クエリを使用する場合は、サーバーが自身をクエリするために127.0.0.{1..2}アドレスを持つremoteテーブル関数を活用できます。または、test_shard_localhostのようなサーバー構成ファイル内の事前定義されたテストクラスタを使用することもできます。 テスト名にshardまたはdistributedという言葉を追加して、CIで正しい構成で実行されるようにしてください。サーバーは分散クエリをサポートするように構成されています。

Working with Temporary Files

時にはシェルテストで作業するためにその場でファイルを作成する必要があります。 いくつかのCIチェックがテストを並行して実行するため、スクリプト内でユニークな名前なしで一時ファイルを作成または削除すると、FlakyなどのCIチェックが失敗する可能性があります。 これを回避するために、環境変数$CLICKHOUSE_TEST_UNIQUE_NAMEを使用して、一時ファイルにそのテストにユニークな名前を付けるべきです。 これにより、セットアップ中に作成したりクリーンアップ中に削除するファイルが、そのテストでのみ使用されているものであり、並行して実行されている他のテストによるものではないことが保証されます。

Known Bugs

再現可能なバグが知られている場合、準備されたfunctional testsをtests/queries/bugsディレクトリに配置します。 これらのテストは、バグが修正されたときにtests/queries/0_statelessに移動されます。

Integration Tests

Integration testsは、クラスタ構成でClickHouseをテストし、MySQL、Postgres、MongoDBなどの他のサーバーとの相互作用をテストすることを可能にします。 ネットワーク分割、パケット破損などをエミュレートするのに便利です。 これらのテストはDockerの下で実行され、さまざまなソフトウェアを持つ複数のコンテナを作成します。

これらのテストを実行する方法については、tests/integration/README.mdを参照してください。

ClickHouseとサードパーティのドライバとの統合はテストされていないことに注意してください。 また、現在のところ、JDBCおよびODBCドライバとの統合テストもありません。

Unit Tests

Unit testsは、ClickHouse全体をテストしたいのではなく、単一の孤立したライブラリまたはクラスをテストしたいときに便利です。 テストのビルドを有効または無効にするには、ENABLE_TESTS CMakeオプションを使用します。 Unit tests(および他のテストプログラム)は、コード全体のtestsサブディレクトリにあります。 Unit testsを実行するには、ninja testと入力します。 一部のテストはgtestを使用しますが、テスト失敗時に非ゼロの終了コードを返す単なるプログラムもあります。

コードがすでにfunctional testsでカバーされている場合、unit testsを持つ必要はありません(functional testsは通常、はるかにシンプルに使用できます)。

個々のgtestチェックを直接実行可能ファイルを呼び出して実行できます。例えば:

Performance Tests

Performance testsは、合成クエリに対してClickHouseのいくつかの孤立した部分のパフォーマンスを測定および比較することを可能にします。 Performance testsはtests/performance/にあります。 各テストは、テストケースの説明を含む.xmlファイルによって表されます。 テストはdocker/test/performance-comparisonツールを使用して実行されます。呼び出しについてはREADMEファイルを参照してください。

各テストは、一度に1つ以上のクエリ(パラメータの組み合わせを含む可能性があります)をループ内で実行します。

特定のシナリオでClickHouseのパフォーマンスを向上させることを望んでおり、改善が単純なクエリで観察可能な場合は、パフォーマンステストを書くことが強く推奨されます。 また、比較的孤立していてあまり obscure でないSQL関数を追加および変更するときも、パフォーマンステストを書くことが推奨されます。 テスト中にperf topや他のperfツールを使用することが常に意味を持ちます。

Test Tools and Scripts

testsディレクトリ内の一部のプログラムは準備されたテストではなく、テストツールです。 例えば、Lexerのためのツールsrc/Parsers/tests/lexerは、標準入力のトークン化を行い、色付きの結果を標準出力に書き込みます。 これらの種のツールをコードの例や探求、手動テストのために使用できます。

Miscellaneous Tests

tests/external_modelsには機械学習モデルのテストがあります。 これらのテストは更新されず、統合テストに移動する必要があります。

クオラム挿入に対する別のテストがあります。 このテストは、Separate serversでClickHouseクラスターを実行し、ネットワーク分割、パケット破損(ClickHouseノード間、ClickHouseとZookeeper間、ClickHouseサーバーとクライアント間など)、kill -9kill -STOPkill -CONTなどのさまざまな障害ケースをエミュレートします。この後、すべての確認された挿入が書き込まれ、拒否された挿入がされなかったことをチェックします。

クオラムテストは、ClickHouseがオープンソースとされる前に、別のチームによって書かれました。 このチームはもはやClickHouseのメンテナンスを行っていません。 テストは偶然にもJavaで書かれました。 これらの理由から、クオラムテストは再記述され、統合テストに移動する必要があります。

Manual Testing

新しい機能を開発しているときは、手動でテストすることも理にかなっています。 以下の手順で行うことができます:

ClickHouseをビルドします。ターミナルからClickHouseを実行します:ディレクトリをprograms/clickhouse-serverに変更し、./clickhouse-serverを実行します。これにより、デフォルトで現在のディレクトリから設定(config.xmlusers.xmlおよびconfig.dおよびusers.dディレクトリ内のファイル)が使用されます。ClickHouseサーバーに接続するには、programs/clickhouse-client/clickhouse-clientを実行します。

すべてのclickhouseツール(サーバー、クライアントなど)は、clickhouseという単一のバイナリへのシンボリックリンクに過ぎません。 このバイナリはprograms/clickhouseにあります。 すべてのツールも、clickhouse toolのように呼び出すことができます。

もしくは、ClickHouseパッケージをインストールすることもできます:ClickHouseリポジトリからの安定版リリース、もしくはClickHouseソースのルートで./releaseを使って自身用のパッケージをビルドできます。 その後、sudo clickhouse start(またはサーバーを停止するにはsudo clickhouse stop)でサーバーを開始します。 ログは/etc/clickhouse-server/clickhouse-server.logにあります。

システムにClickHouseがすでにインストールされている場合、新しいclickhouseバイナリをビルドし、既存のバイナリを置き換えることができます。

また、システムのclickhouse-serverを停止し、同じ設定でログがターミナルに出力されるように独自のClickHouseサーバーを実行できます。

gdbを使った例:

システムのclickhouse-serverがすでに実行中で停止したくない場合は、config.xml内のポート番号を変更するか(またはconfig.dディレクトリ内のファイルで上書きし)、適切なデータパスを提供して実行できます。

clickhouseバイナリはほとんど依存関係がなく、さまざまなLinuxディストリビューションで動作します。 サーバーで変更を迅速かつ簡単にテストするには、単に新しくビルドされたclickhouseバイナリをサーバーにscpし、上記の例のように実行できます。

Build Tests

Build testsは、さまざまな代替構成といくつかの外国システムでビルドが壊れていないことを確認するために使用されます。 これらのテストは自動化されています。

例:

  • Darwin x86_64のためのクロスコンパイル(macOS)
  • FreeBSD x86_64のためのクロスコンパイル
  • Linux AArch64のためのクロスコンパイル
  • システムパッケージからのライブラリを使用してUbuntuでビルド(推奨されません)
  • ライブラリの共有リンクを使用してビルド(推奨されません)

例えば、システムパッケージを使用したビルドは悪いプラクティスです。なぜなら、システムが持っているパッケージの正確なバージョンを保証できないからです。 しかし、これはDebianのメンテナンスにとって非常に必要です。 このため、少なくともこのビルドバリアントをサポートする必要があります。 別の例:共有リンクは、一般的に問題の源ですが、一部の愛好家には必要です。

すべてのビルドバリアントですべてのテストを実行できるわけではありませんが、さまざまなビルドバリアントが壊れていないことを少なくとも確認したいと考えています。 この目的で、ビルドテストを使用します。

また、コンパイルに時間がかかりすぎるか、RAMを過剰に必要とする翻訳単位がないこともテストしています。

さらに、大きすぎるスタックフレームがないこともテストしています。

Testing for Protocol Compatibility

ClickHouseのネットワークプロトコルを拡張する際に、古いclickhouse-clientが新しいclickhouse-serverと動作すること、新しいclickhouse-clientが古いclickhouse-serverと動作することを手動でテストします(対応するパッケージのバイナリを実行することで)。

私たちはまた、統合テストで自動的にいくつかのケースをテストします:

  • 古いバージョンのClickHouseによって書き込まれたデータが新しいバージョンによって正常に読み込めるかどうか。
  • 異なるClickHouseバージョンでのクラスター内で分散クエリが正常に動作するかどうか。

Help from the Compiler

主要なClickHouseコード(srcディレクトリにあります)は、-Wall -Wextra -Werrorでビルドされ、いくつかの追加の警告が有効化されています。 ただし、これらのオプションはサードパーティのライブラリには有効化されていません。

Clangにはさらに役立つ警告が多数あり、これらを-Weverythingで検索し、デフォルトビルド用に選択できます。

私たちは常にClangを使用してClickHouseをビルドしており、開発や生産のために使用します。 あなた自身のマシンでデバッグモードでビルドができるが(ノートパソコンのバッテリーを節約するため)、コンパイラは-O3でのビルドにおいてより多くの警告を生成できることに注意してください。理由は、制御フローと手続き間解析がより良く行われるからです。 デバッグモードでClangでビルドする際には、デバッグバージョンのlibc++が使用され、実行時のエラーをより多くキャッチできるようになります。

Sanitizers

注記

ローカルで実行する際に、ClickHouseサーバーまたはクライアントが起動時にクラッシュする場合、アドレス空間配置のランダマイズを無効にする必要があるかもしれません:sudo sysctl kernel.randomize_va_space=0

Address sanitizer

私たちは、ASan下で機能テスト、統合テスト、ストレステスト、ユニットテストをコミットごとに実行しています。

Thread sanitizer

私たちは、TSan下で機能テスト、統合テスト、ストレステスト、ユニットテストをコミットごとに実行しています。

Memory sanitizer

私たちは、MSan下で機能テスト、統合テスト、ストレステスト、ユニットテストをコミットごとに実行しています。

Undefined behaviour sanitizer

私たちは、UBSan下で機能テスト、統合テスト、ストレステスト、ユニットテストをコミットごとに実行しています。 いくつかのサードパーティライブラリのコードはUBに対してsanitizeされていません。

Valgrind (Memcheck)

以前はValgrind下で夜間に機能テストを実行していましたが、現在はこれを行っていません。 複数の時間がかかります。 現在、re2ライブラリに1つの既知の偽陽性があります。詳細はこの記事を参照してください。

Fuzzing

ClickHouseのファジングは、libFuzzerとランダムSQLクエリの両方を使用して実装されています。 すべてのファジングテストはサニタイザー(AddressとUndefined)で実行する必要があります。

LibFuzzerはライブラリコードの孤立したファジングテストに使用されます。 ファジングプログラムはテストの一部として実装され、"_fuzzer"という名前の接尾辞が付けられます。 ファジングの例はsrc/Parsers/fuzzers/lexer_fuzzer.cppにあります。 LibFuzzer固有の構成、辞書、およびコーパスはtests/fuzzに保存されています。 ユーザー入力を処理するすべての機能に対してファジングテストを書くことを推奨します。

ファジングプログラムはデフォルトではビルドされません。 ファジングプログラムをビルドするには、-DENABLE_FUZZING=1および-DENABLE_TESTS=1の両方のオプションを設定する必要があります。 ファジングプログラムをビルド中にJemallocを無効にすることを推奨します。 ClickHouseファジングをGoogle OSS-Fuzzに統合するために使用される構成は、docker/fuzzにあります。

また、ランダムなSQLクエリを生成し、サーバーがそれを実行中にクラッシュしないことを確認するための単純なファジングテストも使用します。 このテストは00746_sql_fuzzy.plにあります。 このテストは継続的に(夜間およびそれ以降)実行するべきです。

さらに、ASTに基づく高度なクエリファジングプログラムを使用して、大量のコーナーケースを発見できるようにしています。 それは、クエリAST内でのランダムな順列と置換を行います。 それは、前のテストからのASTノードを覚えて次のテストのファジングに使用します。処理中のランダム順序で。 このファジングプログラムについての詳細は、このブログ記事で学ぶことができます。

Stress test

ストレステストは、ファジングの別のケースです。 各functional testを単一のサーバーでランダムな順序で並行実行します。 テストの結果はチェックされません。

次のことが確認されます:

  • サーバーがクラッシュせず、デバッグまたはサニタイザーのトラップがトリガーされないこと;
  • デッドロックがないこと;
  • データベース構造が一貫していること;
  • テスト後、サーバーは正常に停止し、例外なしで再起動できること。

5つの異なるバリエーションがあります(Debug、ASan、TSan、MSan、UBSan)。

Thread Fuzzer

Thread Fuzzer(Thread Sanitizerと混同しないでください)は、スレッドの実行順序をランダム化する別の種類のファジングで、さらに特殊なケースを見つけるのに役立ちます。

Security Audit

私たちのセキュリティチームは、セキュリティの観点からClickHouseの能力を基本的にレビューしました。

Static Analyzers

私たちは、コミットごとにclang-tidyを実行しています。 clang-static-analyzerのチェックも有効です。 clang-tidyは、一部のスタイルチェックにも使用されます。

私たちはclang-tidyCoveritycppcheckPVS-StudiotscancodeCodeQLを評価しました。 使用のための指示はtests/instructions/ディレクトリにあります。

CLionをIDEとして使用する場合、すぐに利用できるclang-tidyのチェックを活用できます。

また、シェルスクリプトの静的分析にはshellcheckを使用しています。

Hardening

デバッグビルドでは、ユーザーレベルの割り当てのASLRを行うカスタムアロケータを使用しています。

さらに、割り当て後に読み取り専用であることが期待されるメモリ領域も手動で保護しています。

デバッグビルドでは、呼び出される危険な(時代遅れ、不安全、スレッドセーフでない)関数が呼び出されないように、libcのカスタマイズも含めています。

デバッグアサーションは広範に使用されています。

デバッグビルドでは、「論理エラー」コードの例外がスローされると、プログラムが早期に終了します。 これにより、リリースビルドで例外を使用できますが、デバッグビルドではアサーションとして扱われます。

デバッグビルドにはjemallocのデバッグバージョンが使用されます。 デバッグビルドにはlibc++のデバッグバージョンが使用されます。

Runtime Integrity Checks

ディスク上に保存されるデータはチェックサムが付与されています。 MergeTreeテーブルのデータは、三つの方法で同時にチェックサムが付与されています(圧縮データブロック、非圧縮データブロック、ブロック全体の合計チェックサム)。 クライアントとサーバー間またはサーバー間でネットワークを通じて転送されるデータにもチェックサムが付与されています。 レプリケーションはレプリカ上のビット同一のデータを保証します。

これはハードウェアの故障(ストレージ媒体のビット劣化、サーバーのRAMのビット反転、ネットワークコントローラのRAMのビット反転、ネットワークスイッチのRAMのビット反転、クライアントのRAMのビット反転、回線上のビット反転)から保護するために必要です。 ビット反転は一般的であり、ECC RAMやTCPチェックサムがある場合でも発生する可能性が高いことに注意してください(ペタバイトのデータを処理している何千ものサーバーを実行している場合)。 このビデオ(ロシア語)

ClickHouseは、運用エンジニアが故障したハードウェアを見つけるのに役立つ診断を提供します。

* そしてそれは遅くありません。

Code Style

コードスタイルルールはこちらに記載されています。

一般的なスタイル違反をチェックするために、utils/check-styleスクリプトを使用できます。

コードのスタイルを強制するために、clang-formatを使用できます。 ファイル.clang-formatはソースのルートにあります。 それはほとんど私たちの実際のコードスタイルに対応しています。 しかし、既存のファイルに対してclang-formatを適用することは推奨されません。なぜならフォーマットが悪化するからです。 代わりに、clangのソースリポジトリ内にあるclang-format-diffツールを使用できます。

また、コードを再フォーマットするためにuncrustifyツールを試すこともできます。 設定はソースのルートにあるuncrustify.cfgにあります。 これはclang-formatよりもテストされていません。

CLionには独自のコードフォーマッタがあり、私たちのコードスタイルのために調整する必要があります。

私たちはまた、コード内のタイプミスを見つけるためにcodespellを使用しています。 これも自動化されています。

Test Coverage

私たちはテストカバレッジを追跡していますが、functional testsのみに対して、かつclickhouse-serverのみに対して行います。 これは日次で実行されます。

Tests for Tests

フレークテストのチェックが自動化されています。 すべての新しいテストを100回(functional testsの場合)または10回(integration testsの場合)実行します。 少なくとも1回でもテストが失敗した場合、それはフレークと見なされます。

Test Automation

私たちはGitHub Actionsを使用してテストを実行します。

ビルドジョブとテストは、コミットごとにSandboxで実行されます。 結果として得られるパッケージとテスト結果はGitHubに公開され、直接リンクでダウンロードできます。 アーティファクトは数ヶ月保存されます。 GitHubでプルリクエストを送信すると、「テスト可能」とタグ付けされ、私たちのCIシステムがClickHouseパッケージ(リリース、デバッグ、アドレスサニタイザー付きなど)をあなたのためにビルドします。