use std::collections::HashMap;
use std::sync::Mutex;
use wasmtime::{Engine, Instance, Module, Store};
type PoolKey = (u64, String);
pub struct WasmInstancePool {
engine: Engine,
pools: Mutex<HashMap<PoolKey, Vec<PooledInstance>>>,
pool_size: usize,
}
pub struct PooledInstance {
pub store: Store<()>,
pub instance: Instance,
}
impl WasmInstancePool {
pub fn new(engine: Engine, pool_size: usize) -> Self {
Self {
engine,
pools: Mutex::new(HashMap::new()),
pool_size,
}
}
pub fn acquire(
&self,
tenant_id: u64,
func_name: &str,
module: &Module,
fuel: u64,
memory_bytes: usize,
) -> crate::Result<PooledInstance> {
let key = (tenant_id, func_name.to_string());
{
let mut pools = self.pools.lock().unwrap_or_else(|p| p.into_inner());
if let Some(pool) = pools.get_mut(&key)
&& let Some(mut inst) = pool.pop()
{
let _ = inst.store.set_fuel(fuel);
return Ok(inst);
}
}
self.create_instance(module, fuel, memory_bytes)
}
pub fn release(&self, tenant_id: u64, func_name: &str, instance: PooledInstance) {
let key = (tenant_id, func_name.to_string());
let mut pools = self.pools.lock().unwrap_or_else(|p| p.into_inner());
let pool = pools.entry(key).or_default();
if pool.len() < self.pool_size {
pool.push(instance);
}
}
pub fn warm(
&self,
tenant_id: u64,
func_name: &str,
module: &Module,
fuel: u64,
memory_bytes: usize,
count: usize,
) -> crate::Result<()> {
let mut instances = Vec::with_capacity(count);
for _ in 0..count {
instances.push(self.create_instance(module, fuel, memory_bytes)?);
}
let key = (tenant_id, func_name.to_string());
let mut pools = self.pools.lock().unwrap_or_else(|p| p.into_inner());
let pool = pools.entry(key).or_default();
pool.extend(instances);
Ok(())
}
pub fn evict(&self, tenant_id: u64, func_name: &str) {
let key = (tenant_id, func_name.to_string());
let mut pools = self.pools.lock().unwrap_or_else(|p| p.into_inner());
pools.remove(&key);
}
fn create_instance(
&self,
module: &Module,
fuel: u64,
_memory_bytes: usize,
) -> crate::Result<PooledInstance> {
let mut store = Store::new(&self.engine, ());
store.set_fuel(fuel).map_err(|e| crate::Error::Internal {
detail: format!("failed to set WASM fuel: {e}"),
})?;
let instance =
Instance::new(&mut store, module, &[]).map_err(|e| crate::Error::BadRequest {
detail: format!("WASM instantiation failed: {e}"),
})?;
Ok(PooledInstance { store, instance })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pool_creates() {
let mut config = wasmtime::Config::new();
config.consume_fuel(true);
let engine = Engine::new(&config).unwrap();
let pool = WasmInstancePool::new(engine, 4);
assert_eq!(pool.pool_size, 4);
}
}