Пользовательские функции WebAssembly
Обзор
wasm32-unknown-unknown) без каких-либо зависимостей от операционной системы или стандартной библиотеки. Кроме того, поддерживается только стандартная 32-битная целевая платформа WebAssembly (без расширения wasm64).
Модуль должен соответствовать одному из поддерживаемых протоколов взаимодействия (ABI) для работы с ClickHouse.
После компиляции бинарный код модуля загружается в ClickHouse путём вставки в таблицу system.webassembly_modules.
После этого вы можете создавать UDF, ссылающиеся на функции, экспортируемые модулем, с помощью оператора CREATE FUNCTION ... LANGUAGE WASM.
Предварительные требования
Быстрый старт
wat2wasm из WebAssembly Binary Toolkit (WABT) или команду parse из wasm-tools.
FORMAT RawBlob, чтобы выполнить вставку в таблицу system.webassembly_modules.
Затем мы определяем UDF, которая ссылается на функцию steps, экспортируемую этим модулем:
:: мы указываем имя функции из модуля, поскольку оно отличается от имени UDF.
Теперь мы можем использовать функцию collatz_steps в запросах:
number явно приводится к типу UInt32, поскольку функции WebAssembly ожидают точного соответствия типов сигнатуре, указанной в операторе CREATE FUNCTION.
В результате мы получили последовательность шагов Коллатца для чисел от 1 до 100, соответствующую последовательности A006577 из OEIS.
Управление модулями WASM через системную таблицу
system.webassembly_modules со следующей структурой:
- Столбцы
nameString — Имя модуля. Не пустое, только буквенно-цифровые символы и знак подчёркивания.codeString — Необработанный бинарный код WASM. Только для записи; при чтении возвращается пустая строка.hashUInt256 — SHA256 бинарного файла модуля (ноль, если он есть на диске, но ещё не загружен).
Добавление модуля
Распределение модуля по cluster
system.webassembly_modules — это таблица уровня instance: INSERT выполняется только на той реплике, которая обрабатывает connection. Для оператора INSERT нет формы ON CLUSTER, поэтому последующий CREATE FUNCTION ... ON CLUSTER завершится ошибкой на репликах, где этого модуля нет:
insert на всех узлах, записывайте в табличную функцию cluster, а не в локальную таблицу system.webassembly_modules:
Этот подход опирается на то, что базовый путь распределённой записи проходит через все реплики в каждом сегменте, а это происходит только если для кластера задано
internal_replication=false. При internal_replication=true (значение по умолчанию для кластеров, использующих ReplicatedMergeTree для собственной репликации) вставка отправляется только на одну работоспособную реплику в каждом сегменте, а system.webassembly_modules по этому пути не реплицируется — поэтому на части реплик модуль по-прежнему будет отсутствовать. В такой конфигурации нужно выполнять вставку в каждую реплику отдельно, например перебирая system.clusters и записывая через remote(...) для каждого хоста, либо копируя бинарный файл в user_scripts/wasm/ на каждом хосте.Проверить значение internal_replication для кластера можно с помощью SELECT cluster, shard_num, internal_replication FROM system.clusters.CREATE FUNCTION ... ON CLUSTER выполнится успешно:
clusterAllReplicas:
system.webassembly_modules идемпотентны для одной и той же пары (name, hash), поэтому повторный запуск распределённой вставки безопасен и является разумным способом восстановить состояние после замены реплики. Обратите внимание: вновь добавленные серверы не получают существующие модули задним числом — необходимо повторно выполнить вставку в обновлённый кластер или поместить бинарный файл в каталог 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 InterfaceROW_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 может повысить производительность при использовании движкаwasmtime. Однако для недоверенного или содержащего ошибки гостевого кода это может увеличить риск неконтролируемого выполнения.
Версии 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 и возвращает одно значение i32.
Гостевой код обрабатывает данные и возвращает указатель на буфер результата с сериализованными результирующими данными.
Гостевой код должен предоставлять две функции для создания и освобождения этих буферов.
ABI ASSEMBLYSCRIPT
-
Числовые:
Int8/UInt8,Int16/UInt16(на границе расширяются доi32),Int32/UInt32,Int64/UInt64,Float32,Float64 -
String— сопоставляется со строковым типом AssemblyScriptstring(UTF-16 в памяти WASM). ClickHouse автоматически выполняет преобразование UTF-8 ↔ UTF-16. - Пользовательские классы AssemblyScript не поддерживаются в качестве типов аргументов или возвращаемых значений — их идентификаторы классов среды выполнения нестабильны между компиляциями (см. AssemblyScript#2982).
__new, __pin и __unpin. Стандартный механизм обработки входящих и выходных строк рассчитан на их наличие. Рекомендуемая команда:
env.abort для ловушек среды выполнения (нехватка памяти, проверка границ и т. д.). ClickHouse автоматически предоставляет этот импорт: при срабатывании abort активный запрос завершается с исключением WASM_ERROR, которое включает декодированное сообщение AssemblyScript и местоположение в исходном коде.
Пример:
asc и загрузки полученного файла .wasm в system.webassembly_modules объявите пользовательские функции (UDF) следующим образом:
Примечание о разработке пользовательских функций (UDF) на Rust
clickhouse_create_buffer и clickhouse_destroy_buffer — достаточно добавить этот крейт в зависимости. Также в нём есть макросы #[clickhouse_wasm_udf], которые оборачивают обычные функции Rust в требуемый ABI-формат.
С этим крейтом вы можете писать пользовательские функции (UDF) так:
serde.
API хоста, доступный модулям
clickhouse_server_version() -> i64— возвращает версию сервера ClickHouse в виде целого числа (например, 25011001 для v25.11.1.1).clickhouse_throw(ptr: i32, size: i32)— вызывает ошибку с указанным сообщением. Принимает указатель на область памяти, содержащую строку сообщения об ошибке, и размер этой строки.clickhouse_log(ptr: i32, size: i32)— записывает сообщение в текстовый журнал сервера ClickHouse.clickhouse_random(ptr: i32, size: i32)— заполняет память случайными байтами.env.abort(message: i32, fileName: i32, line: i32, column: i32)— предоставляется для модулей, совместимых с AssemblyScript. Его вызов (или срабатывание ловушки среды выполнения AssemblyScript, которая его вызывает) завершает UDF с исключениемWASM_ERROR, содержащим декодированное сообщение и расположение в исходном коде. На модули, которые не импортируютenv.abort, это не влияет.
Настройки
-
webassembly_udf_max_fuel— Лимит fuel для каждого запуска экземпляра WebAssembly UDF. Каждая инструкция WebAssembly расходует некоторое количество fuel. Значение масштабируется в 1024 раза перед передачей в runtime, поэтому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— Максимальное количество строк, передаваемых в WebAssembly UDF в одном блоке. Установите 0, чтобы обрабатывать все строки сразу. -
webassembly_udf_max_instances— Максимальное количество экземпляров WebAssembly UDF, которые могут выполняться параллельно для одной функции.