按需(惰性)输入
如果你可以轻松地预先提供所有输入,则 Salsa 输入工作得最好。然而,有时输入集是事先不知道的。
一个典型的例子是从磁盘读取文件。虽然 Salsa 可以急切地扫描特定目录并把内存中创建的文件树作为输入结构体,但更直接的方法是延迟读取文件。
也就是说,当某个查询第一次请求文件的文本时:
- 从磁盘读取文件并缓存它
- 为此路径设置文件系统监视器 (file-system watcher)
- 在监视器发送更改通知的时候,更新缓存的文件
这可以在 Salsa 中使用数据库中的缓存输入和增加数据库 trait 方法来从缓存中获得。
一个完整的可运行的文件监视示例见 lazy-input。
#[salsa::input]
struct File {
path: PathBuf,
#[return_ref]
contents: String,
}
trait Db: salsa::DbWithJar<Jar> {
fn input(&self, path: PathBuf) -> Result<File>;
}
#[salsa::db(Jar)]
struct Database {
storage: salsa::Storage<Self>,
logs: Mutex<Vec<String>>,
files: DashMap<PathBuf, File>,
file_watcher: Mutex<Debouncer<RecommendedWatcher>>,
}
impl Database {
fn new(tx: Sender<DebounceEventResult>) -> Self {
let storage = Default::default();
Self {
storage,
logs: Default::default(),
files: DashMap::new(),
file_watcher: Mutex::new(new_debouncer(Duration::from_secs(1), None, tx).unwrap()),
}
}
}
impl Db for Database {
fn input(&self, path: PathBuf) -> Result<File> {
let path = path
.canonicalize()
.wrap_err_with(|| format!("Failed to read {}", path.display()))?;
Ok(match self.files.entry(path.clone()) {
// If the file already exists in our cache then just return it.
Entry::Occupied(entry) => *entry.get(),
// If we haven't read this file yet set up the watch, read the
// contents, store it in the cache, and return it.
Entry::Vacant(entry) => {
// Set up the watch before reading the contents to try to avoid
// race conditions.
let watcher = &mut *self.file_watcher.lock().unwrap();
watcher
.watcher()
.watch(&path, RecursiveMode::NonRecursive)
.unwrap();
let contents = std::fs::read_to_string(&path)
.wrap_err_with(|| format!("Failed to read {}", path.display()))?;
*entry.insert(File::new(self, path, contents))
}
})
}
}
- 在
Db
trait 上定义一个按需获取File
的方法(它只需要&dyn Db
而不需要&mut dyn Db
) - 每个文件应该只有一个输入结构体,所以给方法实现缓存(
DashMap
就像RwLock<HashMap>
一样)
然后,执行顶级查询的驱动代码负责在文件更改通知到达时更新文件内容。它更新 Salsa 输入的方式与更新任何其他输入的方式相同。
这里,我们实现了一个简单的驱动循环,每当文件发生变化时,它都会重新编译代码。你可以使用日志来检查是否只重新计算了可能已更改的查询。
{{#include ../../../examples-2022/lazy-input/src/main.rs:main}}