Skip to content

Commit

Permalink
feat(cli): v8 code cache
Browse files Browse the repository at this point in the history
  • Loading branch information
igorzi committed Mar 26, 2024
1 parent d6452b3 commit 244b9d8
Show file tree
Hide file tree
Showing 13 changed files with 452 additions and 14 deletions.
10 changes: 10 additions & 0 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ pub struct Flags {
pub unstable_config: UnstableConfig,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub v8_flags: Vec<String>,
pub no_code_cache: bool,
}

fn join_paths(allowlist: &[String], d: &str) -> String {
Expand Down Expand Up @@ -932,6 +933,8 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
flags.unstable_config.sloppy_imports =
matches.get_flag("unstable-sloppy-imports");

flags.no_code_cache = matches.get_flag("no-code-cache");

if matches.get_flag("quiet") {
flags.log_level = Some(Level::Error);
} else if let Some(log_level) = matches.get_one::<String>("log-level") {
Expand Down Expand Up @@ -1066,6 +1069,13 @@ fn clap_root() -> Command {
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.global(true),
)
.arg(
Arg::new("no-code-cache")
.long("no-code-cache")
.help("Disable V8 code cache feature")
.action(ArgAction::SetTrue)
.global(true),
);

for (flag_name, help, _) in crate::UNSTABLE_GRANULAR_FLAGS {
Expand Down
4 changes: 4 additions & 0 deletions cli/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,10 @@ impl CliOptions {
&self.flags.v8_flags
}

pub fn no_code_cache(&self) -> bool {
self.flags.no_code_cache
}

pub fn watch_paths(&self) -> Vec<PathBuf> {
let mut full_paths = Vec::new();
if let DenoSubcommand::Run(RunFlags {
Expand Down
15 changes: 15 additions & 0 deletions cli/cache/caches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use once_cell::sync::OnceCell;
use super::cache_db::CacheDB;
use super::cache_db::CacheDBConfiguration;
use super::check::TYPE_CHECK_CACHE_DB;
use super::code_cache::CODE_CACHE_DB;
use super::deno_dir::DenoDirProvider;
use super::fast_check::FAST_CHECK_CACHE_DB;
use super::incremental::INCREMENTAL_CACHE_DB;
Expand All @@ -22,6 +23,7 @@ pub struct Caches {
fast_check_db: OnceCell<CacheDB>,
node_analysis_db: OnceCell<CacheDB>,
type_checking_cache_db: OnceCell<CacheDB>,
code_cache_db: OnceCell<CacheDB>,
}

impl Caches {
Expand All @@ -34,6 +36,7 @@ impl Caches {
fast_check_db: Default::default(),
node_analysis_db: Default::default(),
type_checking_cache_db: Default::default(),
code_cache_db: Default::default(),
}
}

Expand Down Expand Up @@ -124,4 +127,16 @@ impl Caches {
.map(|dir| dir.type_checking_cache_db_file_path()),
)
}

pub fn code_cache_db(&self) -> CacheDB {
Self::make_db(
&self.code_cache_db,
&CODE_CACHE_DB,
self
.dir_provider
.get_or_create()
.ok()
.map(|dir| dir.code_cache_db_file_path()),
)
}
}
196 changes: 196 additions & 0 deletions cli/cache/code_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use deno_core::error::AnyError;
use deno_runtime::code_cache;
use deno_runtime::deno_webstorage::rusqlite::params;

use super::cache_db::CacheDB;
use super::cache_db::CacheDBConfiguration;
use super::cache_db::CacheFailure;

pub static CODE_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration {
table_initializer: "CREATE TABLE IF NOT EXISTS codecache (
specifier TEXT NOT NULL,
type TEXT NOT NULL,
data BLOB NOT NULL,
PRIMARY KEY (specifier, type)
);",
on_version_change: "DELETE FROM codecache;",
preheat_queries: &[],
on_failure: CacheFailure::Blackhole,
};

#[derive(Clone)]
pub struct CodeCache {
inner: CodeCacheInner,
}

impl CodeCache {
pub fn new(db: CacheDB) -> Self {
Self {
inner: CodeCacheInner::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 code cache: {err:#}");
} else {
log::debug!("Error using code cache: {:#}", err);
}
T::default()
}
}
}

pub fn get_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
) -> Option<Vec<u8>> {
Self::ensure_ok(self.inner.get_sync(specifier, code_cache_type))
}

pub fn set_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
data: &[u8],
) {
Self::ensure_ok(self.inner.set_sync(specifier, code_cache_type, data));
}
}

impl code_cache::CodeCache for CodeCache {
fn get_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
) -> Option<Vec<u8>> {
self.get_sync(specifier, code_cache_type)
}

fn set_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
data: &[u8],
) {
self.set_sync(specifier, code_cache_type, data);
}
}

#[derive(Clone)]
struct CodeCacheInner {
conn: CacheDB,
}

impl CodeCacheInner {
pub fn new(conn: CacheDB) -> Self {
Self { conn }
}

pub fn get_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
) -> Result<Option<Vec<u8>>, AnyError> {
let query = "
SELECT
data
FROM
codecache
WHERE
specifier=?1 AND type=?2
LIMIT 1";
self.conn.query_row(
query,
params![specifier, code_cache_type.as_str()],
|row| {
let value: Vec<u8> = row.get(0)?;
Ok(value)
},
)
}

pub fn set_sync(
&self,
specifier: &str,
code_cache_type: code_cache::CodeCacheType,
data: &[u8],
) -> Result<(), AnyError> {
let sql = "
INSERT OR REPLACE INTO
codecache (specifier, type, data)
VALUES
(?1, ?2, ?3)";
self
.conn
.execute(sql, params![specifier, code_cache_type.as_str(), data])?;
Ok(())
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
pub fn end_to_end() {
let conn = CacheDB::in_memory(&CODE_CACHE_DB, "1.0.0");
let cache = CodeCacheInner::new(conn);

assert!(cache
.get_sync("file:///foo/bar.js", code_cache::CodeCacheType::EsModule)
.unwrap()
.is_none());
let data_esm = vec![1, 2, 3];
cache
.set_sync(
"file:///foo/bar.js",
code_cache::CodeCacheType::EsModule,
&data_esm,
)
.unwrap();
assert_eq!(
cache
.get_sync("file:///foo/bar.js", code_cache::CodeCacheType::EsModule)
.unwrap()
.unwrap(),
data_esm
);

assert!(cache
.get_sync("file:///foo/bar.js", code_cache::CodeCacheType::Script)
.unwrap()
.is_none());
let data_script = vec![1, 2, 3];
cache
.set_sync(
"file:///foo/bar.js",
code_cache::CodeCacheType::Script,
&data_script,
)
.unwrap();
assert_eq!(
cache
.get_sync("file:///foo/bar.js", code_cache::CodeCacheType::Script)
.unwrap()
.unwrap(),
data_script
);
assert_eq!(
cache
.get_sync("file:///foo/bar.js", code_cache::CodeCacheType::EsModule)
.unwrap()
.unwrap(),
data_esm
);
}
}
6 changes: 6 additions & 0 deletions cli/cache/deno_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ impl DenoDir {
self.root.join("npm")
}

/// Path for the V8 code cache.
pub fn code_cache_db_file_path(&self) -> PathBuf {
// bump this version name to invalidate the entire cache
self.root.join("code_cache_v1")
}

/// Path used for the REPL history file.
/// Can be overridden or disabled by setting `DENO_REPL_HISTORY` environment variable.
pub fn repl_history_file_path(&self) -> Option<PathBuf> {
Expand Down
2 changes: 2 additions & 0 deletions cli/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::time::SystemTime;
mod cache_db;
mod caches;
mod check;
mod code_cache;
mod common;
mod deno_dir;
mod disk_cache;
Expand All @@ -37,6 +38,7 @@ mod parsed_source;

pub use caches::Caches;
pub use check::TypeCheckCache;
pub use code_cache::CodeCache;
pub use common::FastInsecureHasher;
pub use deno_dir::DenoDir;
pub use deno_dir::DenoDirProvider;
Expand Down
21 changes: 21 additions & 0 deletions cli/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::args::PackageJsonDepsProvider;
use crate::args::StorageKeyResolver;
use crate::args::TsConfigType;
use crate::cache::Caches;
use crate::cache::CodeCache;
use crate::cache::DenoDir;
use crate::cache::DenoDirProvider;
use crate::cache::EmitCache;
Expand Down Expand Up @@ -178,6 +179,7 @@ struct CliFactoryServices {
cjs_resolutions: Deferred<Arc<CjsResolutionStore>>,
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
code_cache: Deferred<Arc<CodeCache>>,
}

pub struct CliFactory {
Expand Down Expand Up @@ -226,6 +228,9 @@ impl CliFactory {
_ = caches.fast_check_db();
_ = caches.type_checking_cache_db();
}
if !self.options.no_code_cache() {
_ = caches.code_cache_db();
}
}
_ => {}
}
Expand Down Expand Up @@ -534,6 +539,12 @@ impl CliFactory {
})
}

pub fn code_cache(&self) -> Result<&Arc<CodeCache>, AnyError> {
self.services.code_cache.get_or_try_init(|| {
Ok(Arc::new(CodeCache::new(self.caches()?.code_cache_db())))
})
}

pub fn parsed_source_cache(&self) -> &Arc<ParsedSourceCache> {
self
.services
Expand Down Expand Up @@ -782,6 +793,11 @@ impl CliFactory {
fs.clone(),
cli_node_resolver.clone(),
),
if self.options.no_code_cache() {
None
} else {
Some(self.code_cache()?.clone())
},
)),
self.root_cert_store_provider().clone(),
self.fs().clone(),
Expand All @@ -796,6 +812,11 @@ impl CliFactory {
// self.options.disable_deprecated_api_warning,
true,
self.options.verbose_deprecated_api_warning,
if self.options.no_code_cache() {
None
} else {
Some(self.code_cache()?.clone())
},
))
}

Expand Down
Loading

0 comments on commit 244b9d8

Please sign in to comment.