sausage core: modules per topic, journal writer thread with WAL and batches, hash cache by metadata, 49 tests per rule
Blank stdout lines give no warning. core.exit requests the exit in its own handler.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
//! Component versions cached by file metadata, with independent compilation per hash.
|
||||
use crate::{error, runtime::Runtime};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
time::SystemTime,
|
||||
};
|
||||
use tokio::sync::{Mutex as AsyncMutex, OnceCell};
|
||||
use wasmtime::component::Component;
|
||||
|
||||
struct Version {
|
||||
size: u64,
|
||||
modified: SystemTime,
|
||||
hash: String,
|
||||
component: Component,
|
||||
}
|
||||
type PathEntry = Arc<AsyncMutex<Option<Version>>>;
|
||||
type CompiledEntry = Arc<OnceCell<Component>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Components {
|
||||
paths: Mutex<HashMap<PathBuf, PathEntry>>,
|
||||
compiled: Mutex<HashMap<String, CompiledEntry>>,
|
||||
}
|
||||
impl Components {
|
||||
/// Stat each path, but read and hash its bytes only after its metadata changes.
|
||||
pub(crate) async fn load(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
runtime: Arc<Runtime>,
|
||||
) -> Result<(Component, String), serde_json::Value> {
|
||||
let entry = self
|
||||
.paths
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(path.clone())
|
||||
.or_default()
|
||||
.clone();
|
||||
let mut cached = entry.lock().await;
|
||||
let metadata = tokio::fs::metadata(&path)
|
||||
.await
|
||||
.map_err(|_| error(-32001, "no such plugin"))?;
|
||||
let modified = metadata
|
||||
.modified()
|
||||
.map_err(|e| error(-32603, &e.to_string()))?;
|
||||
if let Some(version) = cached.as_ref()
|
||||
&& version.size == metadata.len()
|
||||
&& version.modified == modified
|
||||
{
|
||||
return Ok((version.component.clone(), version.hash.clone()));
|
||||
}
|
||||
let (bytes, hash) = tokio::task::spawn_blocking(move || -> std::io::Result<_> {
|
||||
let bytes = std::fs::read(path)?;
|
||||
let hash = format!("{:x}", Sha256::digest(&bytes));
|
||||
Ok((bytes, hash))
|
||||
})
|
||||
.await
|
||||
.map_err(|e| error(-32603, &e.to_string()))?
|
||||
.map_err(|e| error(-32603, &e.to_string()))?;
|
||||
let compiled = self
|
||||
.compiled
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(hash.clone())
|
||||
.or_default()
|
||||
.clone();
|
||||
let component = compiled
|
||||
.get_or_try_init(|| async move {
|
||||
tokio::task::spawn_blocking(move || runtime.compile(&bytes))
|
||||
.await
|
||||
.map_err(|e| error(-32603, &e.to_string()))?
|
||||
.map_err(|e| error(-32603, &e.to_string()))
|
||||
})
|
||||
.await?
|
||||
.clone();
|
||||
*cached = Some(Version {
|
||||
size: metadata.len(),
|
||||
modified,
|
||||
hash: hash.clone(),
|
||||
component: component.clone(),
|
||||
});
|
||||
Ok((component, hash))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user