diff options
Diffstat (limited to 'cli/cache/node.rs')
-rw-r--r-- | cli/cache/node.rs | 312 |
1 files changed, 117 insertions, 195 deletions
diff --git a/cli/cache/node.rs b/cli/cache/node.rs index 19ac45d6b..f42f132fd 100644 --- a/cli/cache/node.rs +++ b/cli/cache/node.rs @@ -1,40 +1,58 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::path::Path; - use deno_ast::CjsAnalysis; use deno_core::error::AnyError; -use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_runtime::deno_webstorage::rusqlite::params; -use deno_runtime::deno_webstorage::rusqlite::Connection; -use serde::Deserialize; -use serde::Serialize; -use std::path::PathBuf; -use std::sync::Arc; -use super::common::INITIAL_PRAGMAS; +use super::cache_db::CacheDB; +use super::cache_db::CacheDBConfiguration; +use super::cache_db::CacheFailure; use super::FastInsecureHasher; -// todo(dsherret): use deno_ast::CjsAnalysisData directly when upgrading deno_ast -// See https://github.com/denoland/deno_ast/pull/117 -#[derive(Serialize, Deserialize)] -struct CjsAnalysisData { - pub exports: Vec<String>, - pub reexports: Vec<String>, -} +pub static NODE_ANALYSIS_CACHE_DB: CacheDBConfiguration = + CacheDBConfiguration { + table_initializer: concat!( + "CREATE TABLE IF NOT EXISTS cjsanalysiscache ( + specifier TEXT PRIMARY KEY, + source_hash TEXT NOT NULL, + data TEXT NOT NULL + );", + "CREATE UNIQUE INDEX IF NOT EXISTS cjsanalysiscacheidx + ON cjsanalysiscache(specifier);", + "CREATE TABLE IF NOT EXISTS esmglobalscache ( + specifier TEXT PRIMARY KEY, + source_hash TEXT NOT NULL, + data TEXT NOT NULL + );", + "CREATE UNIQUE INDEX IF NOT EXISTS esmglobalscacheidx + ON esmglobalscache(specifier);", + ), + on_version_change: concat!( + "DELETE FROM cjsanalysiscache;", + "DELETE FROM esmglobalscache;", + ), + preheat_queries: &[], + on_failure: CacheFailure::InMemory, + }; #[derive(Clone)] pub struct NodeAnalysisCache { - db_file_path: Option<PathBuf>, - inner: Arc<Mutex<Option<Option<NodeAnalysisCacheInner>>>>, + inner: NodeAnalysisCacheInner, } impl NodeAnalysisCache { - pub fn new(db_file_path: Option<PathBuf>) -> Self { + #[cfg(test)] + pub fn new_in_memory() -> Self { + Self::new(CacheDB::in_memory( + &NODE_ANALYSIS_CACHE_DB, + crate::version::deno(), + )) + } + + pub fn new(db: CacheDB) -> Self { Self { - db_file_path, - inner: Default::default(), + inner: NodeAnalysisCacheInner::new(db), } } @@ -45,16 +63,31 @@ impl NodeAnalysisCache { .to_string() } + 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 esm analysis: {err:#}"); + } else { + log::debug!("Error using esm analysis: {:#}", err); + } + T::default() + } + } + } + pub fn get_cjs_analysis( &self, specifier: &str, expected_source_hash: &str, ) -> Option<CjsAnalysis> { - self - .with_inner(|inner| { - inner.get_cjs_analysis(specifier, expected_source_hash) - }) - .flatten() + Self::ensure_ok( + self.inner.get_cjs_analysis(specifier, expected_source_hash), + ) } pub fn set_cjs_analysis( @@ -63,9 +96,11 @@ impl NodeAnalysisCache { source_hash: &str, cjs_analysis: &CjsAnalysis, ) { - self.with_inner(|inner| { - inner.set_cjs_analysis(specifier, source_hash, cjs_analysis) - }); + Self::ensure_ok(self.inner.set_cjs_analysis( + specifier, + source_hash, + cjs_analysis, + )); } pub fn get_esm_analysis( @@ -73,11 +108,9 @@ impl NodeAnalysisCache { specifier: &str, expected_source_hash: &str, ) -> Option<Vec<String>> { - self - .with_inner(|inner| { - inner.get_esm_analysis(specifier, expected_source_hash) - }) - .flatten() + Self::ensure_ok( + self.inner.get_esm_analysis(specifier, expected_source_hash), + ) } pub fn set_esm_analysis( @@ -86,78 +119,22 @@ impl NodeAnalysisCache { source_hash: &str, top_level_decls: &Vec<String>, ) { - self.with_inner(|inner| { - inner.set_esm_analysis(specifier, source_hash, top_level_decls) - }); - } - - fn with_inner<TResult>( - &self, - action: impl FnOnce(&NodeAnalysisCacheInner) -> Result<TResult, AnyError>, - ) -> Option<TResult> { - // lazily create the cache in order to not - let mut maybe_created = self.inner.lock(); - let inner = match maybe_created.as_ref() { - Some(maybe_inner) => maybe_inner.as_ref(), - None => { - let maybe_inner = match NodeAnalysisCacheInner::new( - self.db_file_path.as_deref(), - crate::version::deno().to_string(), - ) { - Ok(cache) => Some(cache), - Err(err) => { - // should never error here, but if it ever does don't fail - if cfg!(debug_assertions) { - panic!("Error creating node analysis cache: {err:#}"); - } else { - log::debug!("Error creating node analysis cache: {:#}", err); - None - } - } - }; - *maybe_created = Some(maybe_inner); - maybe_created.as_ref().and_then(|p| p.as_ref()) - } - }?; - match action(inner) { - Ok(result) => Some(result), - Err(err) => { - // should never error here, but if it ever does don't fail - if cfg!(debug_assertions) { - panic!("Error using esm analysis: {err:#}"); - } else { - log::debug!("Error using esm analysis: {:#}", err); - } - None - } - } + Self::ensure_ok(self.inner.set_esm_analysis( + specifier, + source_hash, + top_level_decls, + )) } } +#[derive(Clone)] struct NodeAnalysisCacheInner { - conn: Connection, + conn: CacheDB, } impl NodeAnalysisCacheInner { - pub fn new( - db_file_path: Option<&Path>, - version: String, - ) -> Result<Self, AnyError> { - log::debug!("Opening node analysis cache."); - let conn = match db_file_path { - Some(path) => Connection::open(path)?, - None => Connection::open_in_memory()?, - }; - Self::from_connection(conn, version) - } - - fn from_connection( - conn: Connection, - version: String, - ) -> Result<Self, AnyError> { - initialize(&conn, &version)?; - - Ok(Self { conn }) + pub fn new(conn: CacheDB) -> Self { + Self { conn } } pub fn get_cjs_analysis( @@ -174,19 +151,15 @@ impl NodeAnalysisCacheInner { specifier=?1 AND source_hash=?2 LIMIT 1"; - let mut stmt = self.conn.prepare_cached(query)?; - let mut rows = stmt.query(params![specifier, &expected_source_hash])?; - if let Some(row) = rows.next()? { - let analysis_info: String = row.get(0)?; - let analysis_info: CjsAnalysisData = - serde_json::from_str(&analysis_info)?; - Ok(Some(CjsAnalysis { - exports: analysis_info.exports, - reexports: analysis_info.reexports, - })) - } else { - Ok(None) - } + let res = self.conn.query_row( + query, + params![specifier, &expected_source_hash], + |row| { + let analysis_info: String = row.get(0)?; + Ok(serde_json::from_str(&analysis_info)?) + }, + )?; + Ok(res) } pub fn set_cjs_analysis( @@ -200,16 +173,14 @@ impl NodeAnalysisCacheInner { cjsanalysiscache (specifier, source_hash, data) VALUES (?1, ?2, ?3)"; - let mut stmt = self.conn.prepare_cached(sql)?; - stmt.execute(params![ - specifier, - &source_hash.to_string(), - &serde_json::to_string(&CjsAnalysisData { - // temporary clones until upgrading deno_ast - exports: cjs_analysis.exports.clone(), - reexports: cjs_analysis.reexports.clone(), - })?, - ])?; + self.conn.execute( + sql, + params![ + specifier, + &source_hash.to_string(), + &serde_json::to_string(&cjs_analysis)?, + ], + )?; Ok(()) } @@ -227,15 +198,16 @@ impl NodeAnalysisCacheInner { specifier=?1 AND source_hash=?2 LIMIT 1"; - let mut stmt = self.conn.prepare_cached(query)?; - let mut rows = stmt.query(params![specifier, &expected_source_hash])?; - if let Some(row) = rows.next()? { - let top_level_decls: String = row.get(0)?; - let decls: Vec<String> = serde_json::from_str(&top_level_decls)?; - Ok(Some(decls)) - } else { - Ok(None) - } + let res = self.conn.query_row( + query, + params![specifier, &expected_source_hash], + |row| { + let top_level_decls: String = row.get(0)?; + let decls: Vec<String> = serde_json::from_str(&top_level_decls)?; + Ok(decls) + }, + )?; + Ok(res) } pub fn set_esm_analysis( @@ -249,72 +221,26 @@ impl NodeAnalysisCacheInner { esmglobalscache (specifier, source_hash, data) VALUES (?1, ?2, ?3)"; - let mut stmt = self.conn.prepare_cached(sql)?; - stmt.execute(params![ - specifier, - &source_hash, - &serde_json::to_string(top_level_decls)?, - ])?; + self.conn.execute( + sql, + params![ + specifier, + &source_hash, + &serde_json::to_string(top_level_decls)?, + ], + )?; Ok(()) } } -fn initialize(conn: &Connection, cli_version: &str) -> Result<(), AnyError> { - // INT doesn't store up to u64, so use TEXT for source_hash - let query = format!( - "{INITIAL_PRAGMAS} - CREATE TABLE IF NOT EXISTS cjsanalysiscache ( - specifier TEXT PRIMARY KEY, - source_hash TEXT NOT NULL, - data TEXT NOT NULL - ); - CREATE UNIQUE INDEX IF NOT EXISTS cjsanalysiscacheidx - ON cjsanalysiscache(specifier); - CREATE TABLE IF NOT EXISTS esmglobalscache ( - specifier TEXT PRIMARY KEY, - source_hash TEXT NOT NULL, - data TEXT NOT NULL - ); - CREATE UNIQUE INDEX IF NOT EXISTS esmglobalscacheidx - ON esmglobalscache(specifier); - CREATE TABLE IF NOT EXISTS info ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL - ); - " - ); - - conn.execute_batch(&query)?; - - // delete the cache when the CLI version changes - let data_cli_version: Option<String> = conn - .query_row( - "SELECT value FROM info WHERE key='CLI_VERSION' LIMIT 1", - [], - |row| row.get(0), - ) - .ok(); - if data_cli_version.as_deref() != Some(cli_version) { - conn.execute("DELETE FROM cjsanalysiscache", params![])?; - conn.execute("DELETE FROM esmglobalscache", params![])?; - let mut stmt = conn - .prepare("INSERT OR REPLACE INTO info (key, value) VALUES (?1, ?2)")?; - stmt.execute(params!["CLI_VERSION", &cli_version])?; - } - - Ok(()) -} - #[cfg(test)] mod test { use super::*; #[test] pub fn node_analysis_cache_general_use() { - let conn = Connection::open_in_memory().unwrap(); - let cache = - NodeAnalysisCacheInner::from_connection(conn, "1.0.0".to_string()) - .unwrap(); + let conn = CacheDB::in_memory(&NODE_ANALYSIS_CACHE_DB, "1.0.0"); + let cache = NodeAnalysisCacheInner::new(conn); assert!(cache.get_cjs_analysis("file.js", "2").unwrap().is_none()); let cjs_analysis = CjsAnalysis { @@ -349,10 +275,8 @@ mod test { .unwrap(); // recreating with same cli version should still have it - let conn = cache.conn; - let cache = - NodeAnalysisCacheInner::from_connection(conn, "1.0.0".to_string()) - .unwrap(); + let conn = cache.conn.recreate_with_version("1.0.0"); + let cache = NodeAnalysisCacheInner::new(conn); let actual_analysis = cache.get_cjs_analysis("file.js", "2").unwrap().unwrap(); assert_eq!(actual_analysis.exports, cjs_analysis.exports); @@ -362,10 +286,8 @@ mod test { assert_eq!(actual_esm_analysis, esm_analysis); // now changing the cli version should clear it - let conn = cache.conn; - let cache = - NodeAnalysisCacheInner::from_connection(conn, "2.0.0".to_string()) - .unwrap(); + let conn = cache.conn.recreate_with_version("2.0.0"); + let cache = NodeAnalysisCacheInner::new(conn); assert!(cache.get_cjs_analysis("file.js", "2").unwrap().is_none()); assert!(cache.get_esm_analysis("file.js", "2").unwrap().is_none()); } |