mirror of
https://github.com/extism/extism.git
synced 2026-01-08 05:23:55 -05:00
feat: enable wasmtime caching (#605)
Alternate to: #596 without support for manually compiling/loading native code - Enables wasmtime caching: https://docs.wasmtime.dev/cli-cache.html - Adds `EXTISM_CACHE_CONFIG` and `PluginBuilder::with_cache_config` to determine where to load a custom cache configuration from - Adds `PluginBuilder::with_cache_disabled` to disable the cache when initializing a plugin - Setting `EXTISM_CACHE_CONFIG=""` will also disable caching ## Performance With caching: ``` create/create_plugin time: [2.9079 ms 2.9139 ms 2.9200 ms] change: [+3.2677% +3.6399% +3.9766%] (p = 0.00 < 0.20) Change within noise threshold. ``` Compared to `main`: ``` create/create_plugin time: [26.089 ms 26.498 ms 26.923 ms] change: [+0.1729% +2.0868% +4.1337%] (p = 0.04 < 0.20) Change within noise threshold. ```
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -46,4 +46,6 @@ java/.DS_Store
|
||||
extism-maturin/src/extism.h
|
||||
runtime/*.log
|
||||
libextism/example
|
||||
libextism/extism*.pc
|
||||
libextism/extism*.pc
|
||||
*.cwasm
|
||||
test-cache
|
||||
|
||||
@@ -181,6 +181,18 @@ impl Wasm {
|
||||
Wasm::Url { req: _, meta } => meta,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update Wasm module name
|
||||
pub fn with_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.meta_mut().name = Some(name.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Update Wasm module hash
|
||||
pub fn with_hash(mut self, hash: impl Into<String>) -> Self {
|
||||
self.meta_mut().hash = Some(hash.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json_schema")]
|
||||
@@ -237,6 +249,11 @@ impl Manifest {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_wasm(mut self, wasm: impl Into<Wasm>) -> Self {
|
||||
self.wasm.push(wasm.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Disallow HTTP requests to all hosts
|
||||
pub fn disallow_all_hosts(mut self) -> Self {
|
||||
self.allowed_hosts = Some(vec![]);
|
||||
@@ -329,7 +346,7 @@ mod base64 {
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
|
||||
let base64 = general_purpose::STANDARD.encode(v);
|
||||
let base64 = general_purpose::STANDARD.encode(v.as_slice());
|
||||
String::serialize(&base64, s)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ There are a few environment variables that can be used for debugging purposes:
|
||||
- `EXTISM_COREDUMP=extism.core`: write [coredump](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md) to a file when a WebAssembly function traps
|
||||
- `EXTISM_DEBUG=1`: generate debug information
|
||||
- `EXTISM_PROFILE=perf|jitdump|vtune`: enable Wasmtime profiling
|
||||
- `EXTISM_CACHE_CONFIG=path/to/config.toml`: enable Wasmtime cache, see [the docs](https://docs.wasmtime.dev/cli-cache.html) for details about configuration. Setting this to an empty string will disable caching.
|
||||
|
||||
> *Note*: The debug and coredump info will only be written if the plug-in has an error.
|
||||
|
||||
|
||||
@@ -23,11 +23,10 @@ pub use current_plugin::CurrentPlugin;
|
||||
pub use extism_convert::{FromBytes, FromBytesOwned, ToBytes};
|
||||
pub use extism_manifest::{Manifest, Wasm, WasmMetadata};
|
||||
pub use function::{Function, UserData, Val, ValType, PTR};
|
||||
pub use plugin::{CancelHandle, Plugin, EXTISM_ENV_MODULE, EXTISM_USER_MODULE};
|
||||
pub use plugin_builder::PluginBuilder;
|
||||
pub use plugin::{CancelHandle, Plugin, WasmInput, EXTISM_ENV_MODULE, EXTISM_USER_MODULE};
|
||||
pub use plugin_builder::{DebugOptions, PluginBuilder};
|
||||
|
||||
pub(crate) use internal::{Internal, Wasi};
|
||||
pub(crate) use plugin_builder::DebugOptions;
|
||||
pub(crate) use timer::{Timer, TimerAction};
|
||||
pub(crate) use tracing::{debug, error, trace, warn};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::io::Read;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::plugin::WasmInput;
|
||||
use crate::*;
|
||||
|
||||
fn hex(data: &[u8]) -> String {
|
||||
@@ -14,32 +15,9 @@ fn hex(data: &[u8]) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn cache_add_file(hash: &str, data: &[u8]) -> Result<(), Error> {
|
||||
let cache_dir = std::env::temp_dir().join("extism-cache");
|
||||
let _ = std::fs::create_dir(&cache_dir);
|
||||
let file = cache_dir.join(hash);
|
||||
if file.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
std::fs::write(file, data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cache_get_file(hash: &str) -> Result<Option<Vec<u8>>, Error> {
|
||||
let cache_dir = std::env::temp_dir().join("extism-cache");
|
||||
let file = cache_dir.join(hash);
|
||||
if file.exists() {
|
||||
let r = std::fs::read(file)?;
|
||||
return Ok(Some(r));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn check_hash(hash: &Option<String>, data: &[u8]) -> Result<(), Error> {
|
||||
fn check_hash(hash: &Option<String>, data: &[u8]) -> Result<Option<String>, Error> {
|
||||
match hash {
|
||||
None => Ok(()),
|
||||
None => Ok(None),
|
||||
Some(hash) => {
|
||||
let digest = sha2::Sha256::digest(data);
|
||||
let hex = hex(&digest);
|
||||
@@ -50,7 +28,7 @@ fn check_hash(hash: &Option<String>, data: &[u8]) -> Result<(), Error> {
|
||||
hash
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
Ok(Some(hex))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +58,6 @@ fn to_module(engine: &Engine, wasm: &extism_manifest::Wasm) -> Result<(String, M
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
check_hash(&meta.hash, &buf)?;
|
||||
|
||||
Ok((name, Module::new(engine, buf)?))
|
||||
}
|
||||
extism_manifest::Wasm::Data { meta, data } => {
|
||||
@@ -117,17 +94,9 @@ fn to_module(engine: &Engine, wasm: &extism_manifest::Wasm) -> Result<(String, M
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(h) = &meta.hash {
|
||||
if let Ok(Some(data)) = cache_get_file(h) {
|
||||
check_hash(&meta.hash, &data)?;
|
||||
let module = Module::new(engine, data)?;
|
||||
return Ok((name.to_string(), module));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "register-http"))]
|
||||
{
|
||||
return Err(anyhow::format_err!("HTTP registration is disabled"));
|
||||
return anyhow::bail!("HTTP registration is disabled");
|
||||
}
|
||||
|
||||
#[cfg(feature = "register-http")]
|
||||
@@ -144,15 +113,12 @@ fn to_module(engine: &Engine, wasm: &extism_manifest::Wasm) -> Result<(String, M
|
||||
let mut data = Vec::new();
|
||||
r.read_to_end(&mut data)?;
|
||||
|
||||
// Try to cache file
|
||||
if let Some(hash) = &meta.hash {
|
||||
cache_add_file(hash, &data);
|
||||
}
|
||||
|
||||
// Check hash against manifest
|
||||
check_hash(&meta.hash, &data)?;
|
||||
|
||||
// Convert fetched data to module
|
||||
let module = Module::new(engine, data)?;
|
||||
|
||||
Ok((name.to_string(), module))
|
||||
}
|
||||
}
|
||||
@@ -163,52 +129,64 @@ const WASM_MAGIC: [u8; 4] = [0x00, 0x61, 0x73, 0x6d];
|
||||
|
||||
pub(crate) fn load(
|
||||
engine: &Engine,
|
||||
data: &[u8],
|
||||
input: WasmInput<'_>,
|
||||
) -> Result<(extism_manifest::Manifest, BTreeMap<String, Module>), Error> {
|
||||
let extism_module = Module::new(engine, WASM)?;
|
||||
let has_magic = data.len() >= 4 && data[0..4] == WASM_MAGIC;
|
||||
let is_wast = data.starts_with(b"(module") || data.starts_with(b";;");
|
||||
if !has_magic && !is_wast {
|
||||
trace!("Loading manifest");
|
||||
if let Ok(s) = std::str::from_utf8(data) {
|
||||
if let Ok(t) = toml::from_str::<extism_manifest::Manifest>(s) {
|
||||
trace!("Manifest is TOML");
|
||||
let mut m = modules(&t, engine)?;
|
||||
m.insert(EXTISM_ENV_MODULE.to_string(), extism_module);
|
||||
return Ok((t, m));
|
||||
let mut mods = BTreeMap::new();
|
||||
mods.insert(EXTISM_ENV_MODULE.to_string(), Module::new(engine, WASM)?);
|
||||
|
||||
match input {
|
||||
WasmInput::Data(data) => {
|
||||
let has_magic = data.len() >= 4 && data[0..4] == WASM_MAGIC;
|
||||
let is_wat = data.starts_with(b"(module") || data.starts_with(b";;");
|
||||
if !has_magic && !is_wat {
|
||||
trace!("Loading manifest");
|
||||
if let Ok(s) = std::str::from_utf8(&data) {
|
||||
let t = if let Ok(t) = toml::from_str::<extism_manifest::Manifest>(s) {
|
||||
trace!("Manifest is TOML");
|
||||
modules(engine, &t, &mut mods)?;
|
||||
t
|
||||
} else if let Ok(t) = serde_json::from_str::<extism_manifest::Manifest>(s) {
|
||||
trace!("Manifest is JSON");
|
||||
modules(engine, &t, &mut mods)?;
|
||||
t
|
||||
} else {
|
||||
anyhow::bail!("Unknown manifest format");
|
||||
};
|
||||
return Ok((t, mods));
|
||||
}
|
||||
}
|
||||
|
||||
let m = Module::new(engine, data)?;
|
||||
mods.insert("main".to_string(), m);
|
||||
Ok((Default::default(), mods))
|
||||
}
|
||||
WasmInput::Manifest(m) => {
|
||||
trace!("Loading from existing manifest");
|
||||
modules(engine, &m, &mut mods)?;
|
||||
Ok((m, mods))
|
||||
}
|
||||
WasmInput::ManifestRef(m) => {
|
||||
trace!("Loading from existing manifest");
|
||||
modules(engine, m, &mut mods)?;
|
||||
Ok((m.clone(), mods))
|
||||
}
|
||||
|
||||
let t = serde_json::from_slice::<extism_manifest::Manifest>(data)?;
|
||||
trace!("Manifest is JSON");
|
||||
let mut m = modules(&t, engine)?;
|
||||
m.insert(EXTISM_ENV_MODULE.to_string(), extism_module);
|
||||
return Ok((t, m));
|
||||
}
|
||||
|
||||
trace!("Loading WASM module bytes");
|
||||
let m = Module::new(engine, data)?;
|
||||
let mut modules = BTreeMap::new();
|
||||
modules.insert(EXTISM_ENV_MODULE.to_string(), extism_module);
|
||||
modules.insert("main".to_string(), m);
|
||||
Ok((Default::default(), modules))
|
||||
}
|
||||
|
||||
pub(crate) fn modules(
|
||||
manifest: &extism_manifest::Manifest,
|
||||
engine: &Engine,
|
||||
) -> Result<BTreeMap<String, Module>, Error> {
|
||||
manifest: &extism_manifest::Manifest,
|
||||
modules: &mut BTreeMap<String, Module>,
|
||||
) -> Result<(), Error> {
|
||||
if manifest.wasm.is_empty() {
|
||||
return Err(anyhow::format_err!("No wasm files specified"));
|
||||
}
|
||||
|
||||
let mut modules = BTreeMap::new();
|
||||
|
||||
// If there's only one module, it should be called `main`
|
||||
if manifest.wasm.len() == 1 {
|
||||
let (_, m) = to_module(engine, &manifest.wasm[0])?;
|
||||
modules.insert("main".to_string(), m);
|
||||
return Ok(modules);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for f in &manifest.wasm {
|
||||
@@ -217,5 +195,5 @@ pub(crate) fn modules(
|
||||
modules.insert(name, m);
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -109,7 +109,7 @@ impl Internal for Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
fn profiling_strategy() -> ProfilingStrategy {
|
||||
pub(crate) fn profiling_strategy() -> ProfilingStrategy {
|
||||
match std::env::var("EXTISM_PROFILE").as_deref() {
|
||||
Ok("perf") => ProfilingStrategy::PerfMap,
|
||||
Ok("jitdump") => ProfilingStrategy::JitDump,
|
||||
@@ -122,55 +122,106 @@ fn profiling_strategy() -> ProfilingStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WasmInput<'a>: Into<std::borrow::Cow<'a, [u8]>> {}
|
||||
/// Defines an input type for Wasm data.
|
||||
///
|
||||
/// Types that implement `Into<WasmInput>` can be passed directly into `Plugin::new`
|
||||
pub enum WasmInput<'a> {
|
||||
/// Raw Wasm module
|
||||
Data(std::borrow::Cow<'a, [u8]>),
|
||||
/// Owned manifest
|
||||
Manifest(Manifest),
|
||||
/// Borrowed manifest
|
||||
ManifestRef(&'a Manifest),
|
||||
}
|
||||
|
||||
impl<'a> WasmInput<'a> for Manifest {}
|
||||
impl<'a> WasmInput<'a> for &Manifest {}
|
||||
impl<'a> WasmInput<'a> for &'a [u8] {}
|
||||
impl<'a> WasmInput<'a> for Vec<u8> {}
|
||||
impl<'a> From<Manifest> for WasmInput<'a> {
|
||||
fn from(value: Manifest) -> Self {
|
||||
WasmInput::Manifest(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Manifest> for WasmInput<'a> {
|
||||
fn from(value: &'a Manifest) -> Self {
|
||||
WasmInput::ManifestRef(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut Manifest> for WasmInput<'a> {
|
||||
fn from(value: &'a mut Manifest) -> Self {
|
||||
WasmInput::ManifestRef(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for WasmInput<'a> {
|
||||
fn from(value: &'a [u8]) -> Self {
|
||||
WasmInput::Data(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for WasmInput<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
WasmInput::Data(value.as_bytes().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Vec<u8>> for WasmInput<'a> {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
WasmInput::Data(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Vec<u8>> for WasmInput<'a> {
|
||||
fn from(value: &'a Vec<u8>) -> Self {
|
||||
WasmInput::Data(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
/// Create a new plugin from a Manifest or WebAssembly module, and host functions. The `with_wasi`
|
||||
/// parameter determines whether or not the module should be executed with WASI enabled.
|
||||
pub fn new<'a>(
|
||||
wasm: impl WasmInput<'a>,
|
||||
wasm: impl Into<WasmInput<'a>>,
|
||||
imports: impl IntoIterator<Item = Function>,
|
||||
with_wasi: bool,
|
||||
) -> Result<Plugin, Error> {
|
||||
Self::build_new(wasm.into(), imports, with_wasi, Default::default())
|
||||
Self::build_new(wasm.into(), imports, with_wasi, Default::default(), None)
|
||||
}
|
||||
|
||||
pub(crate) fn build_new(
|
||||
wasm: impl AsRef<[u8]>,
|
||||
wasm: WasmInput<'_>,
|
||||
imports: impl IntoIterator<Item = Function>,
|
||||
with_wasi: bool,
|
||||
mut debug_options: DebugOptions,
|
||||
debug_options: DebugOptions,
|
||||
cache_dir: Option<Option<PathBuf>>,
|
||||
) -> Result<Plugin, Error> {
|
||||
// Configure debug options
|
||||
debug_options.debug_info =
|
||||
debug_options.debug_info || std::env::var("EXTISM_DEBUG").is_ok();
|
||||
if let Ok(x) = std::env::var("EXTISM_COREDUMP") {
|
||||
debug_options.coredump = Some(std::path::PathBuf::from(x));
|
||||
};
|
||||
if let Ok(x) = std::env::var("EXTISM_MEMDUMP") {
|
||||
debug_options.memdump = Some(std::path::PathBuf::from(x));
|
||||
};
|
||||
let profiling_strategy = debug_options
|
||||
.profiling_strategy
|
||||
.map_or(ProfilingStrategy::None, |_| profiling_strategy());
|
||||
debug_options.profiling_strategy = Some(profiling_strategy);
|
||||
|
||||
// Setup wasmtime types
|
||||
let engine = Engine::new(
|
||||
Config::new()
|
||||
.epoch_interruption(true)
|
||||
.debug_info(debug_options.debug_info)
|
||||
.coredump_on_trap(debug_options.coredump.is_some())
|
||||
.profiler(profiling_strategy)
|
||||
.wasm_tail_call(true)
|
||||
.wasm_function_references(true),
|
||||
)?;
|
||||
let (manifest, modules) = manifest::load(&engine, wasm.as_ref())?;
|
||||
let mut config = Config::new();
|
||||
config
|
||||
.epoch_interruption(true)
|
||||
.debug_info(debug_options.debug_info)
|
||||
.coredump_on_trap(debug_options.coredump.is_some())
|
||||
.profiler(debug_options.profiling_strategy)
|
||||
.wasm_tail_call(true)
|
||||
.wasm_function_references(true);
|
||||
|
||||
match cache_dir {
|
||||
Some(None) => (),
|
||||
Some(Some(path)) => {
|
||||
config.cache_config_load(path)?;
|
||||
}
|
||||
None => {
|
||||
if let Ok(env) = std::env::var("EXTISM_CACHE_CONFIG") {
|
||||
if !env.is_empty() {
|
||||
config.cache_config_load(&env)?;
|
||||
}
|
||||
} else {
|
||||
config.cache_config_load_default()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let engine = Engine::new(&config)?;
|
||||
let (manifest, modules) = manifest::load(&engine, wasm)?;
|
||||
|
||||
let available_pages = manifest.memory.max_pages;
|
||||
debug!("Available pages: {available_pages:?}");
|
||||
|
||||
@@ -1,29 +1,55 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{plugin::WasmInput, *};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct DebugOptions {
|
||||
pub(crate) profiling_strategy: Option<wasmtime::ProfilingStrategy>,
|
||||
pub(crate) coredump: Option<std::path::PathBuf>,
|
||||
pub(crate) memdump: Option<std::path::PathBuf>,
|
||||
pub(crate) debug_info: bool,
|
||||
#[derive(Clone)]
|
||||
pub struct DebugOptions {
|
||||
pub profiling_strategy: wasmtime::ProfilingStrategy,
|
||||
pub coredump: Option<std::path::PathBuf>,
|
||||
pub memdump: Option<std::path::PathBuf>,
|
||||
pub debug_info: bool,
|
||||
}
|
||||
|
||||
impl Default for DebugOptions {
|
||||
fn default() -> Self {
|
||||
let debug_info = std::env::var("EXTISM_DEBUG").is_ok();
|
||||
let coredump = if let Ok(x) = std::env::var("EXTISM_COREDUMP") {
|
||||
Some(std::path::PathBuf::from(x))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let memdump = if let Ok(x) = std::env::var("EXTISM_MEMDUMP") {
|
||||
Some(std::path::PathBuf::from(x))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
DebugOptions {
|
||||
profiling_strategy: plugin::profiling_strategy(),
|
||||
coredump,
|
||||
memdump,
|
||||
debug_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PluginBuilder is used to configure and create `Plugin` instances
|
||||
pub struct PluginBuilder<'a> {
|
||||
source: std::borrow::Cow<'a, [u8]>,
|
||||
source: WasmInput<'a>,
|
||||
wasi: bool,
|
||||
functions: Vec<Function>,
|
||||
debug_options: DebugOptions,
|
||||
cache_config: Option<Option<PathBuf>>,
|
||||
}
|
||||
|
||||
impl<'a> PluginBuilder<'a> {
|
||||
/// Create a new `PluginBuilder` from a `Manifest` or raw Wasm bytes
|
||||
pub fn new(plugin: impl WasmInput<'a>) -> Self {
|
||||
pub fn new(plugin: impl Into<WasmInput<'a>>) -> Self {
|
||||
PluginBuilder {
|
||||
source: plugin.into(),
|
||||
wasi: false,
|
||||
functions: vec![],
|
||||
debug_options: DebugOptions::default(),
|
||||
cache_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,28 +106,56 @@ impl<'a> PluginBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set profiling strategy
|
||||
pub fn with_profiling_strategy(mut self, p: wasmtime::ProfilingStrategy) -> Self {
|
||||
self.debug_options.profiling_strategy = Some(p);
|
||||
self.debug_options.profiling_strategy = p;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_coredump(mut self, path: impl AsRef<std::path::Path>) -> Self {
|
||||
self.debug_options.coredump = Some(path.as_ref().to_path_buf());
|
||||
/// Enable Wasmtime coredump on trap
|
||||
pub fn with_coredump(mut self, path: impl Into<std::path::PathBuf>) -> Self {
|
||||
self.debug_options.coredump = Some(path.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_memdump(mut self, path: impl AsRef<std::path::Path>) -> Self {
|
||||
self.debug_options.memdump = Some(path.as_ref().to_path_buf());
|
||||
/// Enable Extism memory dump when plugin calls return an error
|
||||
pub fn with_memdump(mut self, path: impl Into<std::path::PathBuf>) -> Self {
|
||||
self.debug_options.memdump = Some(path.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Compile with debug info
|
||||
pub fn with_debug_info(mut self) -> Self {
|
||||
self.debug_options.debug_info = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure debug options
|
||||
pub fn with_debug_options(mut self, options: DebugOptions) -> Self {
|
||||
self.debug_options = options;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set wasmtime compilation cache config path
|
||||
pub fn with_cache_config(mut self, dir: impl Into<PathBuf>) -> Self {
|
||||
self.cache_config = Some(Some(dir.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Turn wasmtime compilation caching off
|
||||
pub fn with_cache_disabled(mut self) -> Self {
|
||||
self.cache_config = Some(None);
|
||||
self
|
||||
}
|
||||
|
||||
/// Generate a new plugin with the configured settings
|
||||
pub fn build(self) -> Result<Plugin, Error> {
|
||||
Plugin::build_new(self.source, self.functions, self.wasi, self.debug_options)
|
||||
Plugin::build_new(
|
||||
self.source,
|
||||
self.functions,
|
||||
self.wasi,
|
||||
self.debug_options,
|
||||
self.cache_config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,3 +539,36 @@ fn test_http_post() {
|
||||
assert!(!res.is_empty());
|
||||
assert!(res.contains(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disable_cache() {
|
||||
// Warmup cache
|
||||
let _plugin: CountVowelsPlugin = PluginBuilder::new(WASM_NO_FUNCTIONS)
|
||||
.build()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// This should be fast
|
||||
let start = std::time::Instant::now();
|
||||
let mut plugin: CountVowelsPlugin = PluginBuilder::new(WASM_NO_FUNCTIONS)
|
||||
.build()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let t = std::time::Instant::now() - start;
|
||||
let _output: Json<Count> = plugin.count_vowels("abc123").unwrap();
|
||||
|
||||
// This should take longer than the first run
|
||||
let start = std::time::Instant::now();
|
||||
let mut plugin: CountVowelsPlugin = PluginBuilder::new(WASM_NO_FUNCTIONS)
|
||||
.with_cache_disabled()
|
||||
.build()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let t1 = std::time::Instant::now() - start;
|
||||
let _output: Json<Count> = plugin.count_vowels("abc123").unwrap();
|
||||
|
||||
assert!(t < t1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user