Fonctions WebAssembly définies par l’utilisateur
Vue d’ensemble
wasm32-unknown-unknown), sans dépendre d’un système d’exploitation ni d’une bibliothèque standard. En outre, seule la cible WebAssembly 32 bits par défaut est prise en charge (pas d’extension wasm64).
Le module doit respecter l’un des protocoles de communication (ABI) pris en charge pour interagir avec ClickHouse.
Une fois compilé, le code binaire du module est chargé dans ClickHouse en l’insérant dans la table system.webassembly_modules.
Après cela, vous pouvez créer des UDF qui référencent les fonctions exportées par le module à l’aide de l’instruction CREATE FUNCTION ... LANGUAGE WASM.
Prérequis
Quick Start
wat2wasm du WebAssembly Binary Toolkit (WABT) ou la commande parse de wasm-tools.
FORMAT RawBlob afin de l’insérer dans la table system.webassembly_modules.
Nous définissons ensuite l’UDF qui fait référence à la fonction steps exportée par le module :
::, car il diffère du nom de l’UDF.
Nous pouvons maintenant utiliser la fonction collatz_steps dans nos requêtes :
number est explicitement convertie en UInt32, car les fonctions WebAssembly attendent une correspondance exacte avec les types spécifiés dans la signature de l’instruction CREATE FUNCTION.
Le résultat obtenu est la séquence des étapes de Collatz pour les nombres de 1 à 100, correspondant à la suite A006577 de l’OEIS.
Gérer les modules WASM via la table système
system.webassembly_modules, qui a la structure suivante :
- Colonnes
nameString — Nom du module. Non vide, caractères de mot uniquement.codeString — Code WASM binaire brut. En écriture seule ; les lectures renvoient une chaîne vide.hashUInt256 — SHA256 du binaire du module (zéro s’il est présent sur le disk mais pas encore chargé).
Insérer un module
Distribuer un module à l’échelle d’un cluster
system.webassembly_modules est une table propre à chaque instance — un INSERT n’atteint que la réplique qui gère la connection. Il n’existe pas de forme ON CLUSTER pour l’instruction INSERT, donc un CREATE FUNCTION ... ON CLUSTER exécuté ensuite échouera sur les répliques qui ne disposent pas du module :
cluster plutôt que dans la table locale system.webassembly_modules :
Cette approche repose sur le fait que le chemin sous-jacent d’écriture distribuée passe par chaque réplique au sein de chaque shard, ce qui ne se produit que lorsque le cluster est configuré avec
internal_replication=false. Avec internal_replication=true (la valeur par défaut pour les clusters qui utilisent ReplicatedMergeTree pour gérer eux-mêmes la réplication), l’insert est envoyé à une seule réplique saine par shard, et system.webassembly_modules n’est pas répliquée par ce chemin ; certaines répliques n’auront donc toujours pas le module. Dans cette configuration, vous devez effectuer un insert sur chaque réplique individuellement, par exemple en parcourant system.clusters et en écrivant via remote(...) pour chaque hôte, ou en copiant le binaire dans user_scripts/wasm/ sur chaque hôte.Vous pouvez vérifier internal_replication pour un cluster avec SELECT cluster, shard_num, internal_replication FROM system.clusters.CREATE FUNCTION ... ON CLUSTER aboutit :
clusterAllReplicas :
system.webassembly_modules sont idempotentes pour une même paire (name, hash) ; il est donc sans risque de réexécuter l’insert distribué, et c’est un moyen raisonnable de rétablir l’état après le remplacement d’une réplique. Notez que les serveurs nouvellement ajoutés ne reçoivent pas rétroactivement les modules existants — vous devez réexécuter l’insert sur le cluster mis à jour, ou placer le binaire dans le répertoire user_scripts/wasm/ sur le nouvel hôte.
Lister les modules
Supprimer un module
DELETE FROM system.webassembly_modules WHERE name = '...'.
Le prédicat doit être soit name = 'literal' pour une correspondance exacte, soit name LIKE 'pattern' pour supprimer tous les modules dont le nom correspond au motif ; aucune autre forme n’est acceptée.
Créer une UDF WebAssembly
function_name: Nom de la fonction dans ClickHouse. Peut être différent du nom de la fonction exportée par le module.FROM 'module_name' :: 'source_function_name': Nom du module WASM chargé et nom de la fonction du module WASM à utiliser (par défaut : function_name)ARGUMENTS: Liste des noms et types d’arguments (les noms sont facultatifs et utilisés pour les formats de sérialisation qui prennent en charge les champs nommés)ABI: Version de l’interface binaire d’applicationROW_DIRECT: Correspondance de types directe, traitement ligne par ligneBUFFERED_V1: Traitement par blocs avec sérialisationASSEMBLYSCRIPT: Traitement ligne par ligne pour les modules produits par le compilateur AssemblyScript. Les types numériques sont mappés vers les primitives d’AssemblyScript ; leStringde ClickHouse est mappé vers lestringd’AssemblyScript.
DETERMINISTIC: Déclare la fonction comme déterministe — elle renvoie toujours le même résultat pour la même entrée. Lorsqu’elle est spécifiée, ClickHouse peut précalculer les appels dont tous les arguments sont des constantes : la fonction est évaluée une seule fois lors de l’analyse de la requête, puis le résultat est réutilisé pour chaque ligne.SHA256_HASH: Hachage attendu du module pour vérification (rempli automatiquement s’il est omis) ; peut être utilisé pour garantir que le bon module WASM est chargé sur différentes répliques.SETTINGS: Paramètres propres à la fonctionserialization_formatString — Format de sérialisation lorsque l’ABI l’exige. Valeurs prises en charge :MsgPack,JSONEachRow,CSV,TSV,TSVRaw,RowBinaryetBuffers. Valeur par défaut :MsgPack. Les formats par blocs tels queBuffersdoivent renvoyer une seule colonne dont le type correspond à la signature de fonction déclarée.webassembly_udf_enable_fuelBool — Active un budget de fuel limité pour la fonction. Valeur par défaut :true. Lorsquefalse, le paramètre de requêtewebassembly_udf_max_fuelest ignoré pour cette fonction. La désactivation des limites de fuel peut améliorer les performances lors de l’utilisation du moteurwasmtime. Cependant, pour du code invité non fiable ou bogué, cela peut augmenter le risque d’exécution incontrôlée.
Versions d’ABI
ROW_DIRECT: correspondance directe des types (types primitifsInt32,UInt32,Int64,UInt64,Float32,Float64uniquement)BUFFERED_V1: types complexes avec sérialisationASSEMBLYSCRIPT: interopération ligne par ligne avec les modules AssemblyScript ; prend en charge les types numériques etString.
ABI ROW_DIRECT
- Les arguments et les types de retour doivent être des types numériques
Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128. - Les chaînes de caractères ne sont pas prises en charge par cette ABI.
- Les signatures doivent correspondre à l’export WASM (
i32/i64/f32/f64/v128). - Le module n’a pas besoin d’exporter de fonctions de support.
ABI BUFFERED_V1
Cette ABI est expérimentale et susceptible d’être modifiée dans de futures versions.
i32 et renvoie une seule valeur i32.
Le code invité traite les données et renvoie un pointeur vers le buffer de résultat contenant les données de résultat sérialisées.
Le code invité doit fournir deux fonctions pour créer et détruire ces buffers.
ABI ASSEMBLYSCRIPT
-
Numériques :
Int8/UInt8,Int16/UInt16(élargis eni32à l’interface),Int32/UInt32,Int64/UInt64,Float32,Float64 -
String— correspond àstringen AssemblyScript (UTF-16 dans la mémoire WASM). ClickHouse gère automatiquement la conversion UTF-8 ↔ UTF-16. - Les classes AssemblyScript personnalisées ne sont pas prises en charge comme types d’argument ou de retour — leurs identifiants de classe à l’exécution ne sont pas stables d’une compilation à l’autre (voir AssemblyScript#2982).
__new, __pin et __unpin soient exportés. La gestion standard des chaînes entrantes/sortantes s’appuie sur eux. Invocation recommandée :
env.abort pour les interruptions d’exécution (mémoire insuffisante, vérification des limites, etc.). ClickHouse fournit automatiquement cet import : lorsqu’un abort est déclenché, la requête en cours échoue avec une exception WASM_ERROR qui inclut le message AssemblyScript décodé et l’emplacement dans le code source.
Exemple:
asc et chargement du fichier .wasm obtenu dans system.webassembly_modules, déclarez les UDFs comme suit :
Remarque sur le développement de UDFs en Rust
clickhouse_create_buffer et clickhouse_destroy_buffer : il vous suffit d’ajouter la crate comme dépendance. Des macros #[clickhouse_wasm_udf] sont également disponibles pour encapsuler vos fonctions Rust habituelles dans le format ABI requis.
Avec cette crate, vous pouvez écrire des UDFs comme ceci :
serde.
API hôte disponible pour les modules
clickhouse_server_version() -> i64— renvoie la version du serveur ClickHouse sous forme d’entier (p. ex. 25011001 pour v25.11.1.1).clickhouse_throw(ptr: i32, size: i32)— lève une erreur avec le message fourni. Accepte un pointeur vers l’emplacement mémoire contenant la chaîne du message d’erreur ainsi que la taille de cette chaîne.clickhouse_log(ptr: i32, size: i32)— écrit un message dans le journal texte du serveur ClickHouse.clickhouse_random(ptr: i32, size: i32)— remplit la mémoire avec des octets aléatoires.env.abort(message: i32, fileName: i32, line: i32, column: i32)— fournie pour les modules compatibles avec AssemblyScript. Son appel (ou le déclenchement d’un trap du runtime AssemblyScript qui l’appelle) met fin à l’UDF avec une exceptionWASM_ERRORcontenant le message décodé et l’emplacement dans le code source. Les modules qui n’importent pasenv.abortne sont pas affectés.
Paramètres
-
webassembly_udf_max_fuel— Limite de fuel par exécution d’une instance d’UDF WebAssembly. Chaque instruction WebAssembly consomme une certaine quantité de fuel. La valeur est multipliée par 1024 avant d’être transmise au runtime, ainsiwebassembly_udf_max_fuel = 1correspond à environ 1024 unités de fuel. Définissez-la sur 0 pour supprimer toute limite finie. S’applique uniquement aux fonctions dont le paramètre par fonctionwebassembly_udf_enable_fuelest true, ce qui est la valeur par défaut. -
webassembly_udf_max_memory— Limite de mémoire, en octets, par instance d’UDF WebAssembly. -
webassembly_udf_max_input_block_size— Nombre maximal de lignes transmises à une UDF WebAssembly dans un seul bloc. Définissez 0 pour traiter toutes les lignes en une seule fois. -
webassembly_udf_max_instances— Nombre maximal d’instances d’UDF WebAssembly pouvant s’exécuter en parallèle par fonction.