diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-02-20 16:29:57 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-20 21:29:57 +0000 |
commit | f90889e5ee19e0ddcd9c1dbcce98720e417dd83e (patch) | |
tree | e44392e9506ba8cddc4c142d304f43879a418152 /cli/cache | |
parent | dbc4a4d6327062918b3bc41dc3f60c84ae3c620b (diff) |
perf(jsr): fast check cache and lazy fast check graph (#22485)
Diffstat (limited to 'cli/cache')
-rw-r--r-- | cli/cache/caches.rs | 15 | ||||
-rw-r--r-- | cli/cache/deno_dir.rs | 6 | ||||
-rw-r--r-- | cli/cache/fast_check.rs | 162 | ||||
-rw-r--r-- | cli/cache/mod.rs | 8 |
4 files changed, 185 insertions, 6 deletions
diff --git a/cli/cache/caches.rs b/cli/cache/caches.rs index 7220a2f9d..dc97f02d5 100644 --- a/cli/cache/caches.rs +++ b/cli/cache/caches.rs @@ -9,6 +9,7 @@ use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::check::TYPE_CHECK_CACHE_DB; use super::deno_dir::DenoDirProvider; +use super::fast_check::FAST_CHECK_CACHE_DB; use super::incremental::INCREMENTAL_CACHE_DB; use super::module_info::MODULE_INFO_CACHE_DB; use super::node::NODE_ANALYSIS_CACHE_DB; @@ -18,6 +19,7 @@ pub struct Caches { fmt_incremental_cache_db: OnceCell<CacheDB>, lint_incremental_cache_db: OnceCell<CacheDB>, dep_analysis_db: OnceCell<CacheDB>, + fast_check_db: OnceCell<CacheDB>, node_analysis_db: OnceCell<CacheDB>, type_checking_cache_db: OnceCell<CacheDB>, } @@ -29,6 +31,7 @@ impl Caches { fmt_incremental_cache_db: Default::default(), lint_incremental_cache_db: Default::default(), dep_analysis_db: Default::default(), + fast_check_db: Default::default(), node_analysis_db: Default::default(), type_checking_cache_db: Default::default(), } @@ -86,6 +89,18 @@ impl Caches { ) } + pub fn fast_check_db(&self) -> CacheDB { + Self::make_db( + &self.fast_check_db, + &FAST_CHECK_CACHE_DB, + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.fast_check_cache_db_file_path()), + ) + } + pub fn node_analysis_db(&self) -> CacheDB { Self::make_db( &self.node_analysis_db, diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 72f8987bd..ee8c35684 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -98,6 +98,12 @@ impl DenoDir { self.root.join("dep_analysis_cache_v1") } + /// Path for the cache used for fast check. + pub fn fast_check_cache_db_file_path(&self) -> PathBuf { + // bump this version name to invalidate the entire cache + self.root.join("fast_check_cache_v1") + } + /// Path for caching node analysis. pub fn node_analysis_db_file_path(&self) -> PathBuf { // bump this version name to invalidate the entire cache diff --git a/cli/cache/fast_check.rs b/cli/cache/fast_check.rs new file mode 100644 index 000000000..f8335d5ea --- /dev/null +++ b/cli/cache/fast_check.rs @@ -0,0 +1,162 @@ +// 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()); + } +} diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index e9d225146..7d95a6423 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::jsr_url; use crate::args::CacheSetting; use crate::errors::get_error_class_name; use crate::file_fetcher::FetchOptions; @@ -11,7 +10,6 @@ use crate::util::fs::atomic_write_file; use deno_ast::MediaType; use deno_core::futures; use deno_core::futures::FutureExt; -use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; @@ -31,6 +29,7 @@ mod common; mod deno_dir; mod disk_cache; mod emit; +mod fast_check; mod incremental; mod module_info; mod node; @@ -43,6 +42,7 @@ pub use deno_dir::DenoDir; pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; pub use emit::EmitCache; +pub use fast_check::FastCheckCache; pub use incremental::IncrementalCache; pub use module_info::ModuleInfoCache; pub use node::NodeAnalysisCache; @@ -167,10 +167,6 @@ impl FetchCacher { } impl Loader for FetchCacher { - fn registry_url(&self) -> &Url { - jsr_url() - } - fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> { if !self.cache_info_enabled { return None; |