WebAssembly 사용자 정의 함수

WebAssembly 사용자 정의 함수 (WebAssembly UDF)

ClickHouse는 WebAssembly로 작성된 사용자 정의 함수(UDF)를 지원해요. Rust, C, C++ 등으로 작성한 커스텀 로직을 WebAssembly 모듈로 컴파일해 실행할 수 있습니다.

출처: 문서

본문

ClickHouse는 WebAssembly로 작성된 사용자 정의 함수(UDF) 생성을 지원합니다. Rust, C, C++ 등으로 작성된 커스텀 로직을 WebAssembly 모듈로 컴파일해 실행할 수 있습니다.

개요 (Overview)

WebAssembly 모듈은 ClickHouse에서 호출할 수 있는 하나 이상의 함수를 담은 컴파일된 바이너리 파일입니다. 모듈을 한 번 로드해 여러 번 재사용하는 라이브러리 또는 공유 객체로 생각하면 됩니다.

UDF를 담은 WebAssembly 모듈은 Rust, C, C++처럼 WebAssembly로 컴파일할 수 있는 모든 언어로 작성할 수 있습니다. WebAssembly로 컴파일된 코드("guest" 코드)와 이를 실행하는 ClickHouse("host")는 전용 메모리 공간에만 접근할 수 있는 샌드박스 환경에서 실행됩니다.

Guest 코드는 ClickHouse가 호출할 수 있는 함수를 내보냅니다. 여기에는 커스텀 로직을 구현하는 함수(UDF를 정의하는 데 사용)와, 메모리 관리 및 ClickHouse와 WebAssembly 코드 간 데이터 교환에 필요한 지원 함수가 포함됩니다.

코드는 운영체제나 표준 라이브러리에 의존하지 않는 "독립형(freestanding)" WebAssembly(일명 wasm32-unknown-unknown)로 컴파일해야 합니다. 기본 32비트 WebAssembly 대상만 지원됩니다(wasm64 확장 없음). 모듈은 ClickHouse와 상호작용하기 위해 지원되는 통신 프로토콜(ABI) 중 하나를 따라야 합니다.

컴파일 후 모듈의 바이너리 코드는 system.webassembly_modules 테이블에 삽입해 ClickHouse에 로드됩니다. 그런 다음 CREATE FUNCTION ... LANGUAGE WASM 문으로 모듈이 내보낸 함수를 참조하는 UDF를 만들 수 있습니다.

사전 요구 사항 (Prerequisites)

ClickHouse 구성에서 WebAssembly 지원을 활성화합니다:

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

사용 가능한 엔진 구현:

  • wasmtime (기본값이며 현재 유일) — Wasmtime 사용.

빠른 시작 (Quick Start)

이 예제는 Collatz 추측 계산기를 구현해 WebAssembly UDF를 만드는 전체 워크플로를 보여줍니다. 코드를 WebAssembly의 사람이 읽을 수 있는 표현인 WebAssembly Text 형식(WAT)으로 작성합니다. ClickHouse는 바이너리 형식의 모듈을 요구하므로 트랜스파일러로 WAT를 WASM으로 변환합니다. 변환에는 WebAssembly Binary Toolkit (WABT)wat2wasm 또는 wasm-toolsparse 명령을 사용할 수 있습니다.

cat << 'EOF' | wasm-tools parse | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
(module
  (func $next (param $n i32) (result i32)
    local.get $n i32.const 1 i32.and
    (if (result i32)
      (then local.get $n i32.const 3 i32.mul i32.const 1 i32.add)
      (else local.get $n i32.const 2 i32.div_u)))
  (func $steps (export "steps") (param $n i32) (result i32)
    (local $count i32)
    local.get $n i32.const 1 i32.lt_u
    (if (then i32.const 0 return))
    (block $done (loop $loop
      local.get $n i32.const 1 i32.eq br_if $done
      local.get $n call $next local.set $n
      local.get $count i32.const 1 i32.add local.set $count
      br $loop))
    local.get $count)
)
EOF

위 스니펫에서 FORMAT RawBlob을 사용해 바이너리 WASM 코드를 ClickHouse 클라이언트로 직접 파이프해 system.webassembly_modules 테이블에 삽입합니다.

그런 다음 모듈이 내보낸 steps 함수를 참조하는 UDF를 정의합니다:

CREATE FUNCTION collatz_steps LANGUAGE WASM ARGUMENTS (n UInt32) RETURNS UInt32 FROM 'collatz' :: 'steps';

모듈 함수 이름이 UDF 이름과 다르므로 :: 뒤에 모듈의 함수 이름을 지정합니다.

이제 쿼리에서 collatz_steps 함수를 사용할 수 있습니다:

SELECT groupArray(collatz_steps(number :: UInt32))
FROM numbers(1, 100)
FORMAT TSV

number 컬럼은 WebAssembly 함수가 CREATE FUNCTION 문에 지정된 시그니처의 정확한 타입 일치를 기대하므로 UInt32로 명시적으로 캐스팅합니다. 결과는 1부터 100까지 숫자의 Collatz 단계 수열로, OEIS의 A006577 수열에 해당합니다.

[0,1,7,2,5,8,16,3,19,6,14,9,9,17,17,4,12,20,20,7,7,15,15,10,23,10,111,18,18,18,106,5,26,13,13,21,21,21,34,8,109,8,29,16,16,16,104,11,24,24,24,11,11,112,112,19,32,19,32,19,19,107,107,6,27,27,27,14,14,14,102,22,115,22,14,22,22,35,35,9,22,110,110,9,9,30,30,17,30,17,92,17,17,105,105,12,118,25,25,25]

시스템 테이블로 WASM 모듈 관리하기

WebAssembly 모듈은 다음 구조의 system.webassembly_modules 테이블에 저장됩니다:

  • 컬럼
    • name String — 모듈 이름. 비어 있지 않고 단어 문자만 허용.
    • code String — 원시 바이너리 WASM 코드. 쓰기 전용이며 읽기는 빈 문자열을 반환.
    • hash UInt256 — 모듈 바이너리의 SHA256(디스크에 있지만 아직 로드되지 않았으면 0).

모듈 관리는 이 테이블에 대한 표준 SQL 연산으로 이뤄집니다.

모듈 삽입

INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');

선택적으로 무결성 해시를 제공할 수 있습니다.

모듈 나열

SELECT * FROM system.webassembly_modules;

모듈 삭제

DELETE FROM system.webassembly_modules WHERE name = 'my_module';

모듈 대체

INSERT INTO system.webassembly_modules (name, code, hash)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...'), toUInt256('a1b2...');

WebAssembly UDF 만들기

로드된 모듈이 내보낸 함수를 참조하는 WebAssembly UDF를 만듭니다:

CREATE FUNCTION name LANGUAGE WASM ARGUMENTS (arg1 Type1, ...) RETURNS ReturnType FROM 'module_name' :: 'exported_function';

FROM 절은 모듈 이름과, 모듈이 내보낸 함수 이름을 지정합니다. UDF 이름과 다른 경우 :: 뒤에 내보낸 함수 이름을 씁니다.

ABI 버전

WebAssembly UDF는 ClickHouse와 데이터를 교환하기 위해 지원되는 몇 가지 ABI(응용 프로그램 이진 인터페이스) 중 하나를 따릅니다. 사용 가능한 ABI와 그 세부 사항은 ClickHouse 문서에서 정의되며, 각 ABI는 모듈과 host 간의 인자 전달 및 반환 값 교환 방식을 결정합니다.

모듈에 사용 가능한 Host API

WebAssembly 모듈(guest)은 ClickHouse(host)가 제공하는 함수를 호출할 수 있습니다. 이는 모듈이 접근할 수 있는 제한된 host API 집합으로 제한되며, 메모리 관리와 데이터 교환을 위한 지원 함수를 포함합니다.

설정 (Settings)

  • allow_experimental_webassembly_udf — WebAssembly UDF 실험 기능을 활성화합니다.
  • webassembly_udf_engine — 사용할 WebAssembly 런타임 엔진(현재 wasmtime).

더 알아보기 (Learn more)