deno/cli/cache/fast_check.rs

163 lines
4.3 KiB
Rust

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_graph::FastCheckCacheItem;
use deno_graph::FastCheckCacheKey;
use deno_runtime::deno_webstorage::rusqlite::params;
use super::cache_db::CacheDB;
use super::cache_db::CacheDBConfiguration;
use super::cache_db::CacheFailure;
pub static FAST_CHECK_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration {
table_initializer: "CREATE TABLE IF NOT EXISTS fastcheckcache (
hash TEXT PRIMARY KEY,
data TEXT NOT NULL
);",
on_version_change: "DELETE FROM fastcheckcache;",
preheat_queries: &[],
on_failure: CacheFailure::Blackhole,
};
#[derive(Clone)]
pub struct FastCheckCache {
inner: FastCheckCacheInner,
}
impl FastCheckCache {
pub fn new(db: CacheDB) -> Self {
Self {
inner: FastCheckCacheInner::new(db),
}
}
fn ensure_ok<T: Default>(res: Result<T, AnyError>) -> T {
match res {
Ok(x) => x,
Err(err) => {
// TODO(mmastrac): This behavior was inherited from before the refactoring but it probably makes sense to move it into the cache
// at some point.
// should never error here, but if it ever does don't fail
if cfg!(debug_assertions) {
panic!("Error using fast check cache: {err:#}");
} else {
log::debug!("Error using fast check cache: {:#}", err);
}
T::default()
}
}
}
}
impl deno_graph::FastCheckCache for FastCheckCache {
fn get(&self, key: FastCheckCacheKey) -> Option<FastCheckCacheItem> {
Self::ensure_ok(self.inner.get(key))
}
fn set(&self, key: FastCheckCacheKey, value: FastCheckCacheItem) {
Self::ensure_ok(self.inner.set(key, &value));
}
}
#[derive(Clone)]
struct FastCheckCacheInner {
conn: CacheDB,
}
impl FastCheckCacheInner {
pub fn new(conn: CacheDB) -> Self {
Self { conn }
}
pub fn get(
&self,
key: FastCheckCacheKey,
) -> Result<Option<FastCheckCacheItem>, AnyError> {
let query = "
SELECT
data
FROM
fastcheckcache
WHERE
hash=?1
LIMIT 1";
let res = self
.conn
// key is a string because SQLite can't handle u64
.query_row(query, params![key.as_u64().to_string()], |row| {
let value: Vec<u8> = row.get(0)?;
Ok(bincode::deserialize::<FastCheckCacheItem>(&value)?)
})?;
Ok(res)
}
pub fn set(
&self,
key: FastCheckCacheKey,
data: &FastCheckCacheItem,
) -> Result<(), AnyError> {
let sql = "
INSERT OR REPLACE INTO
fastcheckcache (hash, data)
VALUES
(?1, ?2)";
self.conn.execute(
sql,
params![key.as_u64().to_string(), &bincode::serialize(data)?],
)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use std::collections::BTreeSet;
use deno_ast::ModuleSpecifier;
use deno_graph::FastCheckCacheModuleItem;
use deno_graph::FastCheckCacheModuleItemDiagnostic;
use deno_semver::package::PackageNv;
use super::*;
#[test]
pub fn cache_general_use() {
let conn = CacheDB::in_memory(&FAST_CHECK_CACHE_DB, "1.0.0");
let cache = FastCheckCacheInner::new(conn);
let key = FastCheckCacheKey::build(
&PackageNv::from_str("@scope/a@1.0.0").unwrap(),
&Default::default(),
);
assert!(cache.get(key).unwrap().is_none());
let value = FastCheckCacheItem {
dependencies: BTreeSet::from([
PackageNv::from_str("@scope/b@1.0.0").unwrap()
]),
modules: vec![(
ModuleSpecifier::parse("https://jsr.io/test.ts").unwrap(),
FastCheckCacheModuleItem::Diagnostic(
FastCheckCacheModuleItemDiagnostic { source_hash: 123 },
),
)],
};
cache.set(key, &value).unwrap();
let stored_value = cache.get(key).unwrap().unwrap();
assert_eq!(stored_value, value);
// adding when already exists should not cause issue
cache.set(key, &value).unwrap();
// recreating with same cli version should still have it
let conn = cache.conn.recreate_with_version("1.0.0");
let cache = FastCheckCacheInner::new(conn);
let stored_value = cache.get(key).unwrap().unwrap();
assert_eq!(stored_value, value);
// now changing the cli version should clear it
let conn = cache.conn.recreate_with_version("2.0.0");
let cache = FastCheckCacheInner::new(conn);
assert!(cache.get(key).unwrap().is_none());
}
}