@@ -14,14 +14,12 @@ use wasmtime_wasi::{
1414} ;
1515
1616use crate :: {
17- bindings:: exports:: datafusion_udf_wasm:: udf:: types as wit_types,
18- compilation_cache:: CompilationCache , error:: DataFusionResultExt ,
17+ bindings:: exports:: datafusion_udf_wasm:: udf:: types as wit_types, error:: DataFusionResultExt ,
1918 tokio_helpers:: async_in_sync_context,
2019} ;
2120use crate :: { error:: WasmToDataFusionResultExt , tokio_helpers:: blocking_io} ;
2221
2322mod bindings;
24- mod compilation_cache;
2523mod conversion;
2624mod error;
2725mod tokio_helpers;
@@ -59,6 +57,54 @@ impl WasiView for WasmStateImpl {
5957 }
6058}
6159
60+ /// Pre-compiled WASM component.
61+ ///
62+ /// The pre-compilation is stateless and can be used to [create](WasmScalarUdf::new) multiple instances that do not share
63+ /// any state.
64+ pub struct WasmComponentPrecompiled {
65+ engine : Engine ,
66+ component : Component ,
67+ }
68+
69+ impl WasmComponentPrecompiled {
70+ pub async fn new ( wasm_binary : Arc < [ u8 ] > ) -> DataFusionResult < Self > {
71+ tokio:: task:: spawn_blocking ( move || {
72+ let engine = Engine :: new (
73+ wasmtime:: Config :: new ( )
74+ . async_support ( true )
75+ . memory_init_cow ( true ) ,
76+ )
77+ . context ( "create WASM engine" , None ) ?;
78+
79+ let compiled_component = engine
80+ . precompile_component ( & wasm_binary)
81+ . context ( "pre-compile component" , None ) ?;
82+
83+ // SAFETY: the compiled version was produced by us with the same engine. This is NOT external/untrusted input.
84+ let component_res = unsafe { Component :: deserialize ( & engine, compiled_component) } ;
85+ let component = component_res. context ( "create WASM component" , None ) ?;
86+
87+ Ok ( Self { engine, component } )
88+ } )
89+ . await
90+ . map_err ( |e| DataFusionError :: External ( Box :: new ( e) ) ) ?
91+ }
92+ }
93+
94+ impl std:: fmt:: Debug for WasmComponentPrecompiled {
95+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
96+ let Self {
97+ engine,
98+ component : _,
99+ } = self ;
100+
101+ f. debug_struct ( "WasmComponentPrecompiled" )
102+ . field ( "engine" , engine)
103+ . field ( "component" , & "<COMPONENT>" )
104+ . finish ( )
105+ }
106+ }
107+
62108pub struct WasmScalarUdf {
63109 store : Arc < Mutex < Store < WasmStateImpl > > > ,
64110 bindings : Arc < bindings:: Datafusion > ,
@@ -68,7 +114,15 @@ pub struct WasmScalarUdf {
68114}
69115
70116impl WasmScalarUdf {
71- pub async fn new ( wasm_binary : & [ u8 ] , source : String ) -> DataFusionResult < Vec < Self > > {
117+ /// Create multiple UDFs from a single WASM VM.
118+ ///
119+ /// UDFs bound to the same VM share state, however calling this method multiple times will yield independent WASM VMs.
120+ pub async fn new (
121+ component : & WasmComponentPrecompiled ,
122+ source : String ,
123+ ) -> DataFusionResult < Vec < Self > > {
124+ let WasmComponentPrecompiled { engine, component } = component;
125+
72126 // TODO: we need an in-mem file system for this, see
73127 // - https://github.com/bytecodealliance/wasmtime/issues/8963
74128 // - https://github.com/Timmmm/wasmtime_fs_demo
@@ -88,24 +142,13 @@ impl WasmScalarUdf {
88142 wasi_ctx,
89143 resource_table : ResourceTable :: new ( ) ,
90144 } ;
145+ let mut store = Store :: new ( engine, state) ;
91146
92- let engine = Engine :: new (
93- wasmtime:: Config :: new ( )
94- . async_support ( true )
95- . enable_incremental_compilation ( Arc :: new ( CompilationCache :: default ( ) ) )
96- . context ( "enable incremental compilation" , None ) ?,
97- )
98- . context ( "create WASM engine" , None ) ?;
99- let mut store = Store :: new ( & engine, state) ;
100-
101- let component =
102- Component :: from_binary ( & engine, wasm_binary) . context ( "create WASM component" , None ) ?;
103-
104- let mut linker = Linker :: new ( & engine) ;
147+ let mut linker = Linker :: new ( engine) ;
105148 wasmtime_wasi:: p2:: add_to_linker_async ( & mut linker) . context ( "link WASI p2" , None ) ?;
106149
107150 let bindings = Arc :: new (
108- bindings:: Datafusion :: instantiate_async ( & mut store, & component, & linker)
151+ bindings:: Datafusion :: instantiate_async ( & mut store, component, & linker)
109152 . await
110153 . context ( "initialize bindings" , Some ( & store. data ( ) . stderr . contents ( ) ) ) ?,
111154 ) ;
0 commit comments