Skip to content

Instantly share code, notes, and snippets.

@lesandie
Last active June 26, 2026 16:07
Show Gist options
  • Select an option

  • Save lesandie/f9af42c7af7ba97ce6cc07cd992bb98f to your computer and use it in GitHub Desktop.

Select an option

Save lesandie/f9af42c7af7ba97ce6cc07cd992bb98f to your computer and use it in GitHub Desktop.
ClickHouse WebAssembly UDF — State of the Art (June 2026)

ClickHouse WASM UDF — State of the Art (June 2026)

ClickHouse shipped native WASM UDF support in v26.3 LTS (2026-03-26), marked experimental. It is actively developed — ~20 PRs merged in the four months since release — but carries material limitations: the feature requires a server-level opt-in flag, is not available on ClickHouse Cloud, and the sandboxing/ABI surface is still evolving. For self-managed clusters on v26.3+, WASM UDFs are usable today. Cloud deployments must wait for a GA announcement or use the existing executable-UDF approach.

See also: How to write a ClickHouse WebAssembly UDF — practical howto covering syntax, compilation, and loading


1. Availability and Versions

Version Release Date Key WASM Changes
v26.3 LTS 2026-03-26 Initial release. ROW_DIRECT + BUFFERED_V1 ABIs, Wasmtime + WasmEdge runtimes, system.webassembly_modules
v26.4 2026-04-30 No new features; Wasmtime bumped to v43.0.1
v26.5 2026-05-21 DETERMINISTIC keyword, numeric widening coercions, system.functions integration, segfault fix, LIKE support in DELETE
v26.6 2026-06-25 AssemblyScript ABI, Buffers serialization, per-UDF fuel setting, Materialized View bug fix, WasmEdge startup overhead fix (−8.3% wall time), Wasmtime v45

Enabling the feature (server config):

<allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
<webassembly_udf_engine>wasmtime</webassembly_udf_engine>

2. Architecture Overview

2.1 WASM Runtimes

Two bundled runtimes are available:

  • Wasmtime (default, recommended) — Bytecode Alliance runtime, v45.0.1 in v26.6. Used for production workloads.
  • WasmEdge — Alternative runtime. Had a severe startup overhead bug (added ~15% wall time to every clickhouse invocation even without WASM UDFs) fixed in v26.6 (PR #107869).

2.2 ABI Modes (as of v26.6)

Three calling conventions are available today; a fourth is in review:

ABI Scope Types Supported Overhead Use Case
ROW_DIRECT Row-by-row Primitive numerics only (Int32/64, UInt32/64, Float32/64, Int128/UInt128) Minimal — direct WASM call Math-heavy scalar functions
BUFFERED_V1 Block-based All ClickHouse types including String, Array, etc. Serialization round-trip per block (MsgPack default; also JSONEachRow, CSV, TSV, RowBinary, Buffers) String processing, complex types
ASSEMBLYSCRIPT Row-by-row Numerics + String (UTF-8↔UTF-16 conversion) Per-row AssemblyScript-compiled modules
COLUMNAR_V1 Block-based TBD Lowest — bypasses RowBinary High-throughput analytics (PR #104424, not yet merged)

2.3 DDL Syntax

-- Step 1: Load the .wasm binary into the server
INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', file('/path/to/module.wasm');

-- Step 2: Create a function referencing the module
CREATE [OR REPLACE] FUNCTION function_name
LANGUAGE WASM
FROM 'module_name' [:: 'source_function_name']
ARGUMENTS (arg1 Int32, arg2 Float64)
RETURNS Float64
[ABI ROW_DIRECT | BUFFERED_V1 | ASSEMBLYSCRIPT]
[DETERMINISTIC]
[SETTINGS webassembly_udf_max_fuel = 1000000];

-- Inspect loaded modules
SELECT name, hash FROM system.webassembly_modules;

-- Remove a module (v26.5+: supports LIKE pattern)
DELETE FROM system.webassembly_modules WHERE name LIKE 'my_%';

2.4 Host API (available inside WASM modules)

Functions the WASM guest can call back into ClickHouse:

Function Description
clickhouse_server_version() Returns version as integer
clickhouse_throw(ptr, size) Raise an error
clickhouse_log(ptr, size) Write to ClickHouse log (added v26.3, log-level aware)
clickhouse_random(ptr, size) Cryptographic random bytes
env.abort(...) AssemblyScript trap handler

Not yet available: shared cache API (clickhouse_cache_get/set) — feature request #99645 is open.

2.5 Resource Controls

Per-UDF and per-query settings:

Setting Description Default
webassembly_udf_max_fuel Instruction budget (count × 1024); 0 = unlimited Enabled
webassembly_udf_max_memory Memory limit in bytes
webassembly_udf_max_input_block_size Max rows per block (BUFFERED_V1)
webassembly_udf_max_instances Parallel WASM instances
webassembly_udf_enable_fuel Toggle fuel accounting (persisted per-UDF since v26.6) true

Note: Disabling webassembly_udf_enable_fuel is recommended for trusted modules — fuel accounting incurs measurable Wasmtime overhead.

2.6 Observability

ProfileEvents tracked per query:

  • WasmSerializationMicroseconds
  • WasmDeserializationMicroseconds
  • WasmGuestExecuteMicroseconds
  • WasmTotalExecuteMicroseconds

Since v26.5, WASM UDFs appear in system.functions with correct origin, syntax, arguments, and returned_value fields — enabling standard introspection and query analyzer integration.


3. Complete PR History

Foundational Implementation

PR Title State Merged Author
#80291 Wasm udf (initial draft) MERGED 2026-02-26 lioshik
#88747 Support WebAssembly UDFs (main implementation) MERGED 2026-02-26 vdimir
#98582 Followup for wasm udfs — cancellation, settings to query level MERGED 2026-03-05 vdimir
#98859 Update docs/wasm_udf.md MERGED 2026-03-05 vdimir
#99373 WASM UDF improvements — numeric coercion, C++ exceptions, clickhouse_log MERGED 2026-03-18 bacek

v26.5 Improvements

PR Title Merged Author
#100005 Add DETERMINISTIC keyword v26.5 bacek
#100435 Widening coercions: int→float, i32→i64, f32→f64 v26.5 bacek
#101053 Show WASM UDFs in system.functions with arguments and return type v26.5 bacek
#104397 Support LIKE in DELETE FROM system.webassembly_modules v26.5 vdimir
#103101 Fix segfault during WASM module delete v26.5 JosephRedfern

v26.6 Improvements

PR Title Merged Author
#104606 Implement AssemblyScript WASM UDF ABI v26.6 vdimir
#105296 Add note about distributed cluster to WASM UDF docs v26.6 vdimir
#105574 Support Buffers serialization format + per-UDF fuel setting v26.6 antonio2368
#106161 Fix WASM UDFs in Materialized Views v26.6 niyue
#107869 Bump WasmEdge contrib — removes costly per-process startup hash v26.6 Algunenano
#106836 Use Wasmtime v45.0.1 v26.6 thevar1able

Open / In-Flight PRs

PR Title Status
#104424 COLUMNAR_V1 wire format — bypass RowBinary for high-throughput Open (active review; Nullable handling + bounds issues flagged)
#103487 Fix Cranelift stack overflow on aarch64-apple-darwin Open (unfixed)
#105131 Drivers for executable UDFs (Alexey Milovidov) Open — related roadmap item, not WASM-specific

4. Known Limitations and Open Issues

Hard Limitations

  1. Not available on ClickHouse CloudCloudNotSupportedBadge on docs page. Private preview available via support contact; no GA timeline announced.
  2. Requires server-level config flagallow_experimental_webassembly_udf = 1; cannot be enabled per-query.
  3. wasm32-unknown-unknown only — freestanding WASM only; no OS syscalls, no standard library, no wasm64.
  4. No ON CLUSTER INSERT — loading a module to a cluster requires per-node fanout; there is no single INSERT INTO system.webassembly_modules ON CLUSTER statement. Documented workaround: set internal_replication = false on the Distributed table, or script manual uploads.
  5. aarch64-apple-darwin Cranelift stack overflow — PR #103487 open, unfixed in v26.5/v26.6.
  6. WASM UDFs in Materialized Views require v26.6+ (or enable_analyzer = true) — On clusters running < v26.6 with enable_analyzer = false (the old query analyzer), CREATE MATERIALIZED VIEW validation routes through the old interpreter path, which rejects WASM UDFs as non-SQL UDFs with:
    DB::Exception: The function 'fn(...)' is not a SQL defined function and is not supported
    when 'enable_analyzer' is set to false. (UNSUPPORTED_METHOD)
    
    Root cause: the old CREATE MATERIALIZED VIEW validation path classified WASM functions as unrecognized non-SQL UDFs. Fixed in v26.6.1.463 (PR #106161, issue #99789). Workaround on older versions: set enable_analyzer = 1 at query or session level before creating the MV.

ABI-Specific Limitations

  • ROW_DIRECT: no String, no Array, no complex types.
  • ASSEMBLYSCRIPT: no custom classes.
  • BUFFERED_V1: serialization overhead on every block; COLUMNAR_V1 aims to fix this but is not merged.

Missing Host API

  • No shared cache — Issue #99645: clickhouse_cache_get/set/get_or_set keyed by (UDF name, module hash, user). No PR yet.
  • No ZooKeeper/Keeper interaction — Issue #106841: cannot implement shared counters or auto-increment generators. No PR.

Bug History

Issue Status Fix Version Description
#99789 Fixed v26.6.1.463 WASM UDFs failed in CREATE MATERIALIZED VIEW when enable_analyzer = false; old interpreter path classified them as unsupported non-SQL UDFs
#103100 OPEN Partial (v26.5) Segfault when unloading WASM modules via DELETE FROM system.webassembly_modules. The WHERE 1=1 case was patched in v26.5 (PR #103101), but the issue remains open — the server still crashes on macOS/aarch64 (Apple Silicon) with other DELETE patterns. Root cause: null pointer dereference in std::operator== during module cleanup. Reported on v26.4.
#100049 Fixed v26.3+ BUFFERED_V1 + RowBinary + type coercion produced wrong results
Fixed v26.6 WasmEdge startup hash computation added ~15% overhead to all clickhouse invocations

5. Tooling and Ecosystem

Official Rust SDK

Minimal Rust example:

use clickhouse_wasm_udf::clickhouse_udf;

#[clickhouse_udf]
pub fn add_two(x: i32) -> i32 {
    x + 2
}

Community Modules

  • chgeos (Vasily Chekalkin, main WASM UDF contributor): PostGIS-compatible spatial functions (ST_Intersects, ST_Buffer, ST_Within, etc.) as a WASM module backed by GEOS 3.12+. This is the most prominent real-world use case and serves as the reference implementation for BUFFERED_V1.

AssemblyScript (v26.6+)

AssemblyScript users can write typed TypeScript-like code and compile to WASM targeting the new ASSEMBLYSCRIPT ABI. A browser playground is bundled in the v26.6 docs.


6. Comparison with Other Databases

Database WASM UDF Approach Status
ClickHouse WASM modules loaded server-side; runs inside the CH process in a sandboxed WASM runtime (Wasmtime or WasmEdge); three ABIs; resource budgets Experimental since v26.3 (2026-03); self-managed only
DuckDB DuckDB-Wasm compiles the database engine itself to WASM for browser-side analytics — a different concept. DuckDB supports C++ native UDFs via API but has no WASM-as-UDF model inside the server Mature for browser; no server-side WASM UDF
PostgreSQL Extension ecosystem only: wasmer-postgres, pg_extism (Extism PDK), pl/wasm. Not part of core Postgres Mature ecosystem, not core
SingleStore WASM UDF with rich host API; cited by the ClickHouse community in 2024 as the bar ClickHouse should match GA, more mature API surface

Key architectural insight: ClickHouse's model is the closest to SingleStore's — WASM modules run inside the server process in a sandboxed environment with well-defined host ↔ guest ABIs. DuckDB's WASM story is orthogonal (runs the engine in a browser). PostgreSQL's ecosystem is extension-only and not core.


7. Roadmap Signals

  • 2026 roadmap (issue #93288): WASM UDFs were explicitly listed and are now checked off as delivered.
  • COLUMNAR_V1 ABI (PR #104424): The next major performance milestone — direct columnar wire format bypassing RowBinary for high-throughput analytical workloads like spatial analytics. In active review with correctness issues to resolve.
  • Shared cache host API (issue #99645): Needed for stateful patterns; no PR yet.
  • ZooKeeper/Keeper interaction (issue #106841): Needed for cross-instance coordination; no PR yet.
  • Cloud GA: No public timeline. Current signal: "private preview" available by contacting ClickHouse Support.

8. Recommendations

If evaluating for self-managed ClickHouse (v26.3+)

WASM UDFs are viable today for:

  • Scalar math-heavy functions with primitive types → use ROW_DIRECT (zero serialization overhead)
  • String processing, complex types → use BUFFERED_V1 with Buffers format (v26.6+)
  • Spatial analytics → chgeos project demonstrates production-grade patterns

Key caveats to test:

  1. Fuel budget overhead — benchmark webassembly_udf_enable_fuel = true vs false for your workload
  2. Module distribution across cluster nodes — must be scripted; no ON CLUSTER support
  3. COLUMNAR_V1 performance improvements are not yet available; if throughput is critical, wait for that PR to merge

If evaluating for ClickHouse Cloud

Not GA. Request private preview access via ClickHouse Support. Plan for executable UDFs as the fallback — they are Cloud-compatible and support any language.

Language choices

Language ABI Notes
Rust BUFFERED_V1 or ROW_DIRECT Official SDK, best ecosystem
AssemblyScript ASSEMBLYSCRIPT Easiest for TypeScript developers; browser playground available
C/C++ ROW_DIRECT or BUFFERED_V1 Possible but no official helper; must compile with emcc or clang --target wasm32
Go BUFFERED_V1 Possible via TinyGo; not officially tested

9. Key References

Resource URL
Official WASM UDF docs https://clickhouse.com/docs/sql-reference/functions/wasm_udf
v26.3 release blog https://clickhouse.com/blog/clickhouse-release-26-03
Rust SDK (GitHub) https://github.com/ClickHouse/clickhouse-wasm-udf-rs
Rust crate https://crates.io/crates/clickhouse-wasm-udf
chgeos spatial WASM module https://github.com/bacek/chgeos
Main implementation PR ClickHouse/ClickHouse#88747
COLUMNAR_V1 ABI PR (open) ClickHouse/ClickHouse#104424
Shared cache feature request ClickHouse/ClickHouse#99645
Original feature request (closed) ClickHouse/ClickHouse#36892
2026 Roadmap ClickHouse/ClickHouse#93288
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment