Пользовательские функции на WebAssembly
ClickHouse поддерживает создание пользовательских функций (UDF), реализованных на WebAssembly. Это позволяет выполнять пользовательскую логику, написанную на таких языках, как Rust, C, C++ и другие, компилируя их в модули WebAssembly.
Обзор
Модуль WebAssembly — это скомпилированный двоичный файл, который содержит одну или несколько функций, вызываемых из ClickHouse. Модуль можно рассматривать как библиотеку или разделяемый объект, который загружается один раз и многократно переиспользуется.
Модуль WebAssembly, содержащий UDF, может быть написан на любом языке, компилируемом в WebAssembly, например Rust, C или C++.
Код, скомпилированный в WebAssembly («гостевой» код) и выполняемый ClickHouse («хост»), запускается в изолированном окружении (sandbox), имеющем доступ только к выделенному участку памяти.
Гостевой код экспортирует функции, которые ClickHouse может вызывать; сюда входят функции, реализующие вашу прикладную логику (используемые для определения UDF), а также вспомогательные функции, необходимые для управления памятью и обмена данными между ClickHouse и кодом WebAssembly.
Ваш код должен быть скомпилирован в «автономный» WebAssembly (также известный как wasm32-unknown-unknown) без каких-либо зависимостей от операционной системы или стандартной библиотеки. Также поддерживается только стандартная 32-битная цель WebAssembly (расширение wasm64 не поддерживается).
Модуль должен следовать одному из поддерживаемых протоколов взаимодействия (ABI) для интеграции с ClickHouse.
После компиляции двоичный код модуля загружается в ClickHouse путём вставки его в таблицу system.webassembly_modules.
После этого вы можете создавать UDF, которые ссылаются на функции, экспортируемые модулем, с помощью выражения CREATE FUNCTION ... LANGUAGE WASM.
Предварительные требования
Включите поддержку WebAssembly в конфигурации ClickHouse:
Доступные реализации движка:
Быстрый старт
В этом примере демонстрируется полный рабочий процесс создания WebAssembly UDF путём реализации калькулятора гипотезы Коллатца.
Мы напишем код в текстовом формате WebAssembly (WAT), который является человекочитаемым представлением WebAssembly, поэтому на этом этапе знание какого-либо языка программирования не требуется.
ClickHouse требует, чтобы модуль был в двоичном формате, поэтому мы воспользуемся транспайлером для преобразования WAT в WASM.
Для выполнения этого преобразования вы можете использовать wat2wasm из WebAssembly Binary Toolkit (WABT) или команду parse из wasm-tools.
В приведённом выше фрагменте мы передаём бинарный код WASM непосредственно в клиент ClickHouse, используя FORMAT RawBlob, чтобы вставить его в таблицу system.webassembly_modules.
Затем мы определяем UDF, который ссылается на функцию steps, экспортируемую из модуля:
Обратите внимание, что мы указываем имя функции из модуля после ::, так как оно отличается от имени UDF.
Теперь мы можем использовать функцию collatz_steps в наших запросах:
Столбец number явно приводится к типу UInt32, потому что функции WebAssembly ожидают точного соответствия типов сигнатуре, указанной в операторе CREATE FUNCTION.
В результате мы получили последовательность шагов Коллатца для чисел от 1 до 100, соответствующую последовательности A006577 из OEIS.
Управление модулями WASM через системную таблицу
Модули WebAssembly хранятся в таблице system.webassembly_modules, имеющей следующую структуру:
- Столбцы
nameString — Имя модуля. Непустое, допускаются только буквенно-цифровые символы и подчёркивания.codeString — Сырые двоичные данные WASM. Только для записи, при чтении возвращается пустая строка.hashUInt256 — SHA256 бинарного файла модуля (ноль, если модуль присутствует на диске, но ещё не загружен).
Управление модулями осуществляется с помощью стандартных SQL-операций над этой таблицей:
Добавить модуль
При необходимости укажите контрольную сумму:
Если указанный хэш не совпадает с вычисленным SHA256‑хэшем кода модуля, вставка завершается с ошибкой. Это может быть полезно при загрузке модулей из внешних источников, таких как S3 или HTTP.
Список модулей
Удаление модуля
Удаление выполняется запросом DELETE FROM system.webassembly_modules WHERE name = '...'.
Поддерживается только удаление одного модуля за один запрос по его точному имени.
Если какие-либо существующие UDF ссылаются на модуль, удаление завершится с ошибкой, поэтому сначала нужно удалить эти UDF.
Создайте UDF на WebAssembly
Синтаксис:
Параметры:
function_name: Имя функции в ClickHouse. Может отличаться от имени экспортируемой функции в модуле.FROM 'module_name' :: 'source_function_name': Имя загруженного модуля WASM и имя функции в модуле WASM, которое следует использовать (по умолчанию —function_name).ARGUMENTS: Список имён и типов аргументов (имена необязательны и используются для форматов сериализации, которые поддерживают именованные поля).ABI: Версия Application Binary InterfaceROW_DIRECT: Прямое сопоставление типов, построчная обработкаBUFFERED_V1: Блочная обработка с сериализацией
SHA256_HASH: Ожидаемый хэш модуля для проверки (автоматически заполняется, если опущен); может использоваться для обеспечения загрузки корректного модуля WASM на разных репликах.SETTINGS: Настройки для отдельной функцииmax_fuelUInt64 — Количество инструкций (fuel) на экземпляр. Значение по умолчанию:100000.max_memoryUInt64 — Максимальное использование памяти на экземпляр, в байтах. Диапазон: 64 KiB … 4 GiB. Значение по умолчанию:104857600(100 MiB).serialization_formatString — Формат сериализации для ABI, где это требуется. Значение по умолчанию:MsgPack.max_input_block_sizeUInt64 — Если указано, ограничивает максимальный размер входного блока в строках для ABI, использующего блочную обработку. Значение по умолчанию:0(без ограничений).max_instancesUInt64 — Максимальное количество параллельных экземпляров функции в одном запросе. Значение по умолчанию:128.
Версии ABI
Для взаимодействия с ClickHouse модули WebAssembly должны соответствовать одному из поддерживаемых ABI (Application Binary Interface).
ROW_DIRECT: Прямое отображение типов (только примитивные типыInt32,UInt32,Int64,UInt64,Float32,Float64)BUFFERED_V1: Сложные типы с сериализацией
ABI ROW_DIRECT
Вызывает экспортируемую функцию WASM напрямую для каждой строки.
- Аргументы и возвращаемые значения должны иметь числовые типы
Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128. - Строки не поддерживаются в этом ABI.
- Сигнатуры должны соответствовать экспорту WASM (
i32/i64/f32/f64/v128). - Модуль не обязан экспортировать какие-либо вспомогательные функции.
Например, функция с сигнатурой:
Его можно создать следующим образом:
WebAssembly не различает знаковые и беззнаковые аргументы, а использует разные инструкции для интерпретации значений. Поэтому размер аргумента должен строго совпадать, тогда как его знаковость определяется операциями внутри функции.
ABI BUFFERED_V1
Этот ABI является экспериментальным и может измениться в будущих релизах.
Обрабатывает целые блоки целиком, используя (де)сериализацию через память WASM. Поддерживает любые типы аргументов и возвращаемых значений.
Сериализованные данные копируются в память WASM, передаваемую как указатель на буфер (состоящий из указателя на данные и размера этих данных) в функцию UDF вместе с числом строк во входных данных. Таким образом, функция, определяемая пользователем, на стороне WASM всегда принимает два аргумента i32 и возвращает одно значение i32. Гостевой код обрабатывает данные и возвращает указатель на буфер результата с сериализованными данными результата.
Гостевой код должен реализовать две функции для создания и уничтожения этих буферов.
Примеры определений на языке C:
Host API, доступный модулям
Следующие функции хоста могут быть импортированы и использованы в модулях:
clickhouse_server_version() -> i64— возвращает версию сервера ClickHouse в виде целого числа (например, 25011001 для v25.11.1.1).clickhouse_terminate(ptr: i32, size: i32)— вызывает ошибку с переданным сообщением. Принимает указатель на область памяти, содержащую строку сообщения об ошибке, и размер строки.clickhouse_log(ptr: i32, size: i32)— записывает сообщение в текстовый лог сервера ClickHouse.clickhouse_random(ptr: i32, size: i32)— заполняет память случайными байтами.