extism / elixir-sdk

Extism Elixir Host SDK
BSD 3-Clause "New" or "Revised" License
27 stars 1 forks source link

Re-write to use Wasmex #3

Open bhelx opened 11 months ago

bhelx commented 11 months ago

Wasmex is making good progress https://github.com/tessi/wasmex

They have some limited support for host functions, and we do not. We could, in theory, write a pure elixir library that uses wasmex instead of our custom rust nif. Some complications with this would be that we'd need to implement a lot of the Extism layers over again in this library. It would probably look similar to my initial implementation of our browser runtime which re-implements Extism in typescript: https://github.com/extism/extism/tree/v0.5.0/browser

The other challenge would be that the host functions would need to be this async thing and spin up threads, etc. I think if we do go down this path, we should help wasmex get async support first.

Related to #2

tessi commented 11 months ago

👋happy to help you adopt wasmex if that’s what you choose to do.

We can also talk about adding maintainers to wasmex so you (and the community) are not „stuck“ waiting on me for important updates.

bhelx commented 11 months ago

Thanks for your support @tessi! Going to take a look at your async PR next week regardless. On Sep 15, 2023 at 6:02 AM -0500, Philipp Tessenow @.***>, wrote:

👋happy to help you adopt wasmex if that’s what you choose to do. We can also talk about adding maintainers to wasmex so you (and the community) are not „stuck“ waiting on me for important updates. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

bhelx commented 1 month ago

Writing up my thoughts on how to do this. I'll start by outlining my Plan A. But I have a Plan B if we need. \cc @munjalpatel

How it works today

First it's important to understand how all this works today. Here is a graphic overview of the library:

Screenshot 2024-06-28 at 9 43 15 AM

The Extism "runtime" is written in rust. It's primarily delivered as a whole bundle of code along with the wasmtime runtime. We have bundled all this up into a rust crate. So when I created this SDK naturally I just made a NIF which uses this crate. This is nice because most of the heavy work is handled by the rust code and we are really just wrapping the rust API with rustler.

This could effectively be said to be "targeting" the "Extism runtime" (because the wasmtime runtime is hidden on purpose). So in order to use wasmex, we are effectively targeting a new runtime. Ignore the fact that they are the same underlying runtime, we're now exposing a different API to the elixir layer.

How we often do this is we basically port the "extism runtime" to the native language and expose extism as imports to the plug-in. Take javascript for example, we implement Extism as javascript: https://github.com/extism/js-sdk/blob/7f3c9a2ca113aff49d2fe8288e12a53bc8467601/src/call-context.ts and these get imported to the plugin.

However, I think it's going to be an issue with the way that wasmex implements host functions. Extism is in this category of needing host functions to be synchronous and very fast as there can be a lot of cross chatter. It should "work" but will be very slow and noisy. I roughly outline the problem in this blog post here. It's a limitation of NIFs unfortunately and I don't think writing a native erlang runtime is in the cards for me at this moment in time.

All hope is not lost, I may have a solution that is not only easier, but I believe should be more performant assuming my light reading of the wasmex source code is correct. (Edit: seems we will need to add support but that should be fine). The solution borrows on a strategy I used to bootstrap extism on our new JVM native runtime. This involves compiling extism itself to wasm and then using wasmtime's dynamic linker to link to the plugin when we instantiate it. So, there will effectively be two instances for every plugin, but the extism.wasm runtime is quite tiny (just under 4kb). And because they will be linked in the rust world, it should be fast (i believe).

Screenshot 2024-06-28 at 9 57 34 AM
bhelx commented 1 month ago

Oh jeez okay: https://github.com/tessi/wasmex/issues/437 perhaps i read it wrong? We may need to implement dynamic linking in wasmex. We could possibly in the meantime fake it in this library by creating two instances and manually linking them.

munjalpatel commented 1 month ago

@bhelx linking extism.wasm to plugin.wasm makes sense. Implementing https://github.com/tessi/wasmex/issues/437 would also enable a lot of other use cases.

Alternatively, I wonder if we can use wasm-merge now that it's back! https://github.com/WebAssembly/binaryen/pull/5709

bhelx commented 1 month ago

Wasm-merge might work as a stop gap and help us get started. It would probably emit a multi memory module but I think wasmtime could run it. That would also mean that we can't run out of the box extism plug-ins but that might be okay during the beta period until we have a long term fix.

munjalpatel commented 1 month ago

Wasm-merge might work as a stop gap and help us get started. It would probably emit a multi memory module but I think wasmtime could run it. That would also mean that we can't run out of the box extism plug-ins but that might be okay during the beta period until we have a long term fix.

Wasmtime does support multi-memory as of v15 --> https://webassembly.org/features

bhelx commented 1 month ago

Having discussed with @munjalpatel offline. Our plan is to add support to dynamically link modules in wasmex. It should hopefully be a small change. After we do this we can include the wasmex dep, and the extism kernel, and then it should just be a matter of replacing all the Extism.Native.* calls with some wasmex flavored functions. Once that works we can delete the nif.

munjalpatel commented 1 month ago

@bhelx @tessi https://github.com/tessi/wasmex/pull/596 will enable us to dynamically link wasm modules