WebAssembly ユーザー定義関数
概要
wasm32-unknown-unknown) としてコンパイルする必要があります。また、サポートされるのはデフォルトの 32 ビット WebAssembly ターゲットのみです (wasm64 拡張はサポートされません) 。
モジュールは、ClickHouse と連携するために、サポートされている通信プロトコル (ABI) のいずれかに従う必要があります。
コンパイル後、モジュールのバイナリコードは system.webassembly_modules テーブルに insert することで ClickHouse に読み込まれます。
その後、CREATE FUNCTION ... LANGUAGE WASM ステートメントを使用して、モジュールがエクスポートした関数を参照する UDF を作成できます。
前提条件
クイックスタート
wat2wasm、または wasm-tools の parse コマンドを使用できます。
FORMAT RawBlob を使ってバイナリの WASM コードを ClickHouse client に直接パイプし、system.webassembly_modules テーブルに挿入しています。
次に、モジュールからエクスポートされた steps 関数を参照する UDF を定義します。
:: の後にはモジュール内の関数名を指定している点に注意してください。
これで、クエリで collatz_steps 関数を使用できます。
number カラムは、WebAssembly 関数では CREATE FUNCTION ステートメントで指定されたシグネチャどおりに型が完全に一致している必要があるため、明示的に UInt32 にキャストされています。
その結果、1 から 100 までの数に対する Collatz のステップ数列が得られ、これは OEIS の数列 A006577 に対応します。
system table 経由での WASM モジュール管理
system.webassembly_modules テーブルに、次の構造で格納されます。
- カラム
nameString — モジュール名。空は不可で、使用できるのは単語文字のみです。codeString — 生のバイナリ WASM コード。書き込み専用で、読み出し時には空文字列が返されます。hashUInt256 — モジュールバイナリの SHA256 (ディスク上には存在するが、まだ読み込まれていない場合は 0) 。
モジュールを追加する
クラスター全体にモジュールを配布する
system.webassembly_modules はインスタンスごとのテーブルであり、INSERT は接続を処理しているレプリカにしか反映されません。INSERT ステートメントには ON CLUSTER 形式がないため、その後の CREATE FUNCTION ... ON CLUSTER は、モジュールがないレプリカでは失敗します。
insert をすべてのノードに分散するには、ローカルの system.webassembly_modules テーブルではなく、cluster テーブル関数に書き込みます。
この方法は、基盤となる分散書き込み経路が各分片内のすべてのレプリカを巡回することを前提としていますが、これはクラスターが
internal_replication=false に設定されている場合にのみ起こります。internal_replication=true (ReplicatedMergeTree を使ってレプリケーションを自前で処理するクラスターのデフォルト) では、insert は分片ごとに正常な 1 つのレプリカにのみ送られ、system.webassembly_modules はこの経路ではレプリケーションされません。そのため、一部のレプリカには引き続きモジュールが存在しないままになります。この構成では、各レプリカに対して個別に insert する必要があります。たとえば、system.clusters を順に処理してホストごとに remote(...) 経由で書き込むか、すべてのホストの user_scripts/wasm/ に binary をコピーします。クラスターの internal_replication は、SELECT cluster, shard_num, internal_replication FROM system.clusters で確認できます。CREATE FUNCTION ... ON CLUSTER が成功します:
clusterAllReplicas を使うと、すべてのレプリカでモジュールが読み込まれていることを確認できます。
system.webassembly_modules への挿入は、同じ (name, hash) の組み合わせに対しては冪等です。そのため、分散された insert を再実行しても安全であり、レプリカの置き換え後に状態を修復する現実的な方法でもあります。なお、新たに追加されたサーバーには既存のモジュールは自動では配布されません。更新後のクラスターに対して insert を再実行するか、新しいホストの user_scripts/wasm/ ディレクトリにバイナリを配置する必要があります。
モジュールを一覧表示
モジュールを削除する
DELETE FROM system.webassembly_modules WHERE name = '...' ステートメントを使用します。
条件式には、完全一致の name = 'literal' または、名前がパターンに一致するすべてのモジュールを削除する name LIKE 'pattern' のいずれかを指定する必要があります。これ以外の形式は受け付けられません。
WebAssembly UDFを作成する
function_name: ClickHouse 内の関数名。モジュール内でエクスポートされた関数名と異なる場合があります。FROM 'module_name' :: 'source_function_name': 読み込まれた WASM モジュール名と、使用する WASM モジュール内の関数名 (デフォルトは function_name)ARGUMENTS: 引数名と型の一覧 (名前は省略可能で、名前付きフィールドをサポートするシリアライゼーションフォーマットで使用されます)ABI: Application Binary Interface のバージョンROW_DIRECT: 直接的な型マッピング、行単位の処理BUFFERED_V1: シリアライゼーションを伴うブロックベースの処理ASSEMBLYSCRIPT: AssemblyScript コンパイラで生成されたモジュール向けの行単位処理。数値型は AssemblyScript のプリミティブにマップされ、ClickHouseStringは AssemblyScriptstringにマップされます。
DETERMINISTIC: 関数を決定論的として宣言します。つまり、同じ入力に対して常に同じ出力を返します。指定すると、ClickHouse はすべての引数が定数である呼び出しを定数畳み込みすることがあります。関数はクエリ解析時に一度評価され、その結果がすべての行で再利用されます。SHA256_HASH: 検証用の想定モジュールハッシュ (省略した場合は自動入力) 。異なるレプリカ間で正しい WASM モジュールが読み込まれていることを保証するために使用できます。SETTINGS: 関数ごとの設定serialization_formatString — ABI で必要なシリアライゼーションフォーマット。サポートされる値:MsgPack,JSONEachRow,CSV,TSV,TSVRaw,RowBinary,Buffers。デフォルト:MsgPack。Buffersなどのブロックベースのフォーマットでは、宣言された関数シグネチャに一致する型の単一カラムを返す必要があります。webassembly_udf_enable_fuelBool — 関数の有限 fuel バジェットを有効にします。デフォルト:true。falseの場合、この関数ではクエリレベルの設定webassembly_udf_max_fuelは無視されます。fuel 制限を無効にすると、wasmtimeengine の使用時にパフォーマンスが向上することがあります。ただし、信頼できない ゲストコード や不具合のある ゲストコード では、暴走実行のリスクが高まる可能性があります。
ABI バージョン
ROW_DIRECT: 直接型マッピング (プリミティブ型Int32、UInt32、Int64、UInt64、Float32、Float64のみ)BUFFERED_V1: シリアライゼーションを伴う複合型ASSEMBLYSCRIPT: AssemblyScript モジュールとの行ごとの相互運用。数値型とStringをサポートします。
ABI ROW_DIRECT
- 引数と戻り値の型には、数値型
Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128を使用します。 - この ABI では文字列はサポートされていません。
- シグネチャは WASM のエクスポート (
i32/i64/f32/f64/v128) と一致している必要があります。 - モジュール側でエクスポートが必要なサポート関数はありません。
ABI BUFFERED_V1
この ABI は実験的機能であり、今後のリリースで変更される可能性があります。
i32 引数を受け取り、1 つの i32 値を返します。
ゲストコードはデータを処理し、シリアライズされた結果データを含む結果バッファへのポインタを返します。
ゲストコードは、これらのバッファを作成および破棄するための 2 つの関数を提供する必要があります。
ABI ASSEMBLYSCRIPT
-
数値:
Int8/UInt8、Int16/UInt16(境界ではi32に拡張) 、Int32/UInt32、Int64/UInt64、Float32、Float64 -
String— AssemblyScript のstringにマッピングされます (WASM メモリ内では UTF-16) 。ClickHouse が UTF-8 ↔ UTF-16 の変換を自動的に処理します。 - カスタム AssemblyScript クラスは、引数型または戻り値の型としてはサポートされていません。実行時クラス ID がコンパイルごとに安定しないためです (AssemblyScript#2982 を参照) 。
__new、__pin、__unpin がエクスポートされるよう、AssemblyScript の managed runtime でコンパイルする必要があります。標準の入力/出力 string 処理はこれらを前提としています。推奨される呼び出しは次のとおりです:
env.abort もインポートします。ClickHouse はこのインポートを自動的に提供します。abort がトリガーされると、実行中のクエリは、デコードされた AssemblyScript のメッセージとソース位置を含む WASM_ERROR 例外で失敗します。
例:
asc でコンパイルし、生成された .wasm を system.webassembly_modules にロードした後、UDFs を次のように宣言します。
RustでUDFを開発する際の注意
clickhouse_create_buffer と clickhouse_destroy_buffer を手動で実装する必要はなく、依存関係として追加するだけで済みます。さらに、通常のRust関数を必要なABI形式でラップするマクロ #[clickhouse_wasm_udf] も用意されています。
このcrateを使うと、次のようにUDFを書けます。
serde を使用してシリアライゼーション/デシリアライゼーションを自動的に処理します。
モジュールから利用できるホスト API
clickhouse_server_version() -> i64— ClickHouse server のバージョンを整数で返します (例: v25.11.1.1 の場合は 25011001) 。clickhouse_throw(ptr: i32, size: i32)— 指定されたメッセージでエラーをスローします。エラーメッセージ文字列を含むメモリ位置へのポインタと、その文字列のサイズを受け取ります。clickhouse_log(ptr: i32, size: i32)— ClickHouse server のテキストログにメッセージを記録します。clickhouse_random(ptr: i32, size: i32)— メモリをランダムなバイト列で埋めます。env.abort(message: i32, fileName: i32, line: i32, column: i32)— AssemblyScript 互換モジュール向けに提供されています。これを呼び出すと (またはそれを呼び出す AssemblyScript runtime トラップがトリガーされると) 、デコードされたメッセージとソース位置を含むWASM_ERROR例外によって UDF が終了します。env.abortをインポートしないモジュールには影響ありません。
設定
-
webassembly_udf_max_fuel— WebAssembly UDF インスタンスの実行ごとの fuel 上限です。各 WebAssembly 命令は一定量の fuel を消費します。この値はランタイムに渡される前に 1024 倍されるため、webassembly_udf_max_fuel = 1はおよそ 1024 fuel 単位に相当します。有限の制限を設けない場合は 0 に設定します。これは、関数ごとの設定webassembly_udf_enable_fuelが true の関数にのみ適用され、これがデフォルトです。 -
webassembly_udf_max_memory— WebAssembly UDF インスタンスごとのメモリ上限です (バイト単位) 。 -
webassembly_udf_max_input_block_size— 1 つのブロックで WebAssembly UDF に渡される最大行数です。すべての行を一度に処理する場合は 0 に設定します。 -
webassembly_udf_max_instances— 関数ごとに並列実行できる WebAssembly UDF インスタンスの最大数です。