Skip to content

perf: move jsr manifest checksum to locker #488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub enum JsrLoadError {
ContentLoadExternalSpecifier,
#[error(transparent)]
ContentLoad(Arc<anyhow::Error>),
#[error("JSR package manifest for '{}' failed to load: {:#}", .0, .1)]
#[error("JSR package manifest for '{}' failed to load. {:#}", .0, .1)]
PackageManifestLoad(String, Arc<anyhow::Error>),
#[error("JSR package not found: {}", .0)]
PackageNotFound(String),
Expand Down Expand Up @@ -3057,7 +3057,7 @@ struct PendingModuleLoadItem {

struct LoadedJsrPackageViaHttpsUrl {
nv: PackageNv,
manifest_checksum: String,
manifest_checksum_for_locker: Option<LoaderChecksum>,
}

type PendingInfoFuture<'a> = LocalBoxFuture<'a, PendingInfo>;
Expand Down Expand Up @@ -3282,10 +3282,12 @@ impl<'a, 'graph> Builder<'a, 'graph> {
loaded_package_via_https_url,
}) => {
if let Some(pkg) = loaded_package_via_https_url {
self
.graph
.packages
.ensure_package(pkg.nv, pkg.manifest_checksum);
if let Some(locker) = &mut self.locker {
if let Some(checksum) = pkg.manifest_checksum_for_locker {
locker.set_pkg_manifest_checksum(&pkg.nv, checksum);
}
}
self.graph.packages.ensure_package(pkg.nv);
}

match result {
Expand Down Expand Up @@ -3459,10 +3461,12 @@ impl<'a, 'graph> Builder<'a, 'graph> {
match version_info_result {
Ok(version_info_load_item) => {
let version_info = version_info_load_item.info;
self
.graph
.packages
.ensure_package(nv.clone(), version_info_load_item.checksum);
self.graph.packages.ensure_package(nv.clone());
if let Some(locker) = &mut self.locker {
if let Some(checksum) = version_info_load_item.checksum_for_locker {
locker.set_pkg_manifest_checksum(nv, checksum);
}
}
let base_url = self.jsr_url_provider.package_url(nv);
let export_name =
normalize_export_name(resolution_item.nv_ref.sub_path());
Expand Down Expand Up @@ -4277,7 +4281,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
maybe_checksum = self
.locker
.as_ref()
.and_then(|l| l.get_checksum(&requested_specifier));
.and_then(|l| l.get_remote_checksum(&requested_specifier));
}
let fut = async move {
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -4328,7 +4332,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
maybe_version_info.replace(info);
loaded_package_via_https_url.replace(LoadedJsrPackageViaHttpsUrl {
nv: package_nv,
manifest_checksum: inner.checksum,
manifest_checksum_for_locker: inner.checksum_for_locker,
});
}

Expand Down Expand Up @@ -4417,7 +4421,11 @@ impl<'a, 'graph> Builder<'a, 'graph> {
Ok(err) => Err(ModuleError::LoadingErr(
load_specifier.clone(),
maybe_range.cloned(),
ModuleLoadError::HttpsChecksumIntegrity(err),
if maybe_version_info.is_some() {
JsrLoadError::ContentChecksumIntegrity(err).into()
} else {
ModuleLoadError::HttpsChecksumIntegrity(err)
},
)),
Err(err) => Err(ModuleError::LoadingErr(
load_specifier.clone(),
Expand Down Expand Up @@ -4474,7 +4482,7 @@ impl<'a, 'graph> Builder<'a, 'graph> {
self.state.jsr.metadata.queue_load_package_version_info(
package_nv,
self.fill_pass_mode.to_cache_setting(),
self.graph.packages.get_manifest_checksum(package_nv),
self.locker.as_deref(),
JsrMetadataStoreServices {
executor: self.executor,
jsr_url_provider: self.jsr_url_provider,
Expand Down Expand Up @@ -4551,8 +4559,8 @@ impl<'a, 'graph> Builder<'a, 'graph> {
&& matches!(specifier.scheme(), "https" | "http")
{
if let Some(locker) = &mut self.locker {
if !locker.has_checksum(&specifier) {
locker.set_checksum(
if !locker.has_remote_checksum(&specifier) {
locker.set_remote_checksum(
&specifier,
LoaderChecksum::new(LoaderChecksum::gen(
module_source_and_info.source().as_bytes(),
Expand Down
30 changes: 17 additions & 13 deletions src/jsr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ use crate::source::LoadOptions;
use crate::source::LoadResponse;
use crate::source::Loader;
use crate::source::LoaderChecksum;
use crate::source::Locker;
use crate::Executor;

#[derive(Clone)]
pub struct PendingJsrPackageVersionInfoLoadItem {
pub checksum: String,
pub checksum_for_locker: Option<LoaderChecksum>,
pub info: Arc<JsrPackageVersionInfo>,
}

Expand Down Expand Up @@ -103,7 +104,7 @@ impl JsrMetadataStore {
&mut self,
package_nv: &PackageNv,
cache_setting: CacheSetting,
maybe_expected_checksum: Option<&str>,
maybe_locker: Option<&dyn Locker>,
services: JsrMetadataStoreServices,
) {
if self
Expand All @@ -121,24 +122,27 @@ impl JsrMetadataStore {
package_nv.name, package_nv.version
))
.unwrap();
let maybe_expected_checksum =
maybe_expected_checksum.map(|c| LoaderChecksum::new(c.to_string()));
let maybe_expected_checksum = maybe_locker
.as_ref()
.and_then(|locker| locker.get_pkg_manifest_checksum(package_nv));
let should_compute_checksum =
maybe_expected_checksum.is_none() && maybe_locker.is_some();
let fut = self.load_data(
specifier,
services,
cache_setting,
// we won't have a checksum when loading this the
// first time or when not using a lockfile
maybe_expected_checksum.clone(),
|content| {
// if we have the expected checksum, then we can re-use that here
let checksum = maybe_expected_checksum
.map(|c| c.into_string())
.unwrap_or_else(|| LoaderChecksum::gen(content));
// we won't have a checksum when not using a lockfile
maybe_expected_checksum,
move |content| {
let checksum_for_locker = if should_compute_checksum {
Some(LoaderChecksum::new(LoaderChecksum::gen(content)))
} else {
None
};
let version_info: JsrPackageVersionInfo =
serde_json::from_slice(content)?;
Ok(PendingJsrPackageVersionInfoLoadItem {
checksum,
checksum_for_locker,
info: Arc::new(version_info),
})
},
Expand Down
67 changes: 15 additions & 52 deletions src/packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,11 @@ impl JsrPackageVersionInfo {

#[derive(Debug, Clone)]
struct PackageNvInfo {
manifest_checksum: String,
/// Collection of exports used.
exports: BTreeMap<String, String>,
found_dependencies: HashSet<JsrDepPackageReq>,
}

#[derive(Debug, Clone)]
pub struct PackageManifestIntegrityError {
pub nv: PackageNv,
pub actual: String,
pub expected: String,
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct PackageSpecifiers {
#[serde(flatten)]
Expand All @@ -138,10 +130,20 @@ impl PackageSpecifiers {
self.package_reqs.is_empty()
}

/// The total number of JSR packages found in the graph.
pub fn packages_len(&self) -> usize {
self.packages.len()
}

/// The total number of dependencies of jsr packages found in the graph.
pub fn package_deps_sum(&self) -> usize {
self
.packages
.iter()
.map(|p| p.1.found_dependencies.len())
.sum()
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using these two methods to tell whether the lockfile needs updating after building the graph in the CLI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable


pub fn add_nv(&mut self, package_req: PackageReq, nv: PackageNv) {
let nvs = self
.packages_by_name
Expand All @@ -153,60 +155,21 @@ impl PackageSpecifiers {
self.package_reqs.insert(package_req, nv.clone());
}

pub(crate) fn ensure_package(
&mut self,
nv: PackageNv,
manifest_checksum: String,
) {
pub(crate) fn ensure_package(&mut self, nv: PackageNv) {
self.packages.entry(nv).or_insert_with(|| PackageNvInfo {
manifest_checksum,
exports: Default::default(),
found_dependencies: Default::default(),
});
}

pub(crate) fn get_manifest_checksum(&self, nv: &PackageNv) -> Option<&str> {
self.packages.get(nv).map(|p| p.manifest_checksum.as_str())
}

pub fn add_manifest_checksum(
&mut self,
nv: PackageNv,
checksum: String,
) -> Result<(), Box<PackageManifestIntegrityError>> {
let package = self.packages.get(&nv);
if let Some(package) = package {
if package.manifest_checksum != checksum {
Err(Box::new(PackageManifestIntegrityError {
nv,
actual: checksum,
expected: package.manifest_checksum.clone(),
}))
} else {
Ok(())
}
} else {
self.packages.insert(
nv,
PackageNvInfo {
manifest_checksum: checksum,
exports: Default::default(),
found_dependencies: Default::default(),
},
);
Ok(())
}
}

/// Gets the dependencies (package constraints) of JSR packages found in the graph.
pub fn packages_with_checksum_and_deps(
pub fn packages_with_deps(
&self,
) -> impl Iterator<
Item = (&PackageNv, &String, impl Iterator<Item = &JsrDepPackageReq>),
> {
) -> impl Iterator<Item = (&PackageNv, impl Iterator<Item = &JsrDepPackageReq>)>
{
self.packages.iter().map(|(nv, info)| {
let deps = info.found_dependencies.iter();
(nv, &info.manifest_checksum, deps)
(nv, deps)
})
}

Expand Down
60 changes: 47 additions & 13 deletions src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,43 +175,77 @@ impl fmt::Display for LoaderChecksum {
}

pub trait Locker {
fn get_checksum(&self, specifier: &ModuleSpecifier)
-> Option<LoaderChecksum>;
fn has_checksum(&self, specifier: &ModuleSpecifier) -> bool;
fn set_checksum(
fn get_remote_checksum(
&self,
specifier: &ModuleSpecifier,
) -> Option<LoaderChecksum>;
fn has_remote_checksum(&self, specifier: &ModuleSpecifier) -> bool;
fn set_remote_checksum(
&mut self,
specifier: &ModuleSpecifier,
checksum: LoaderChecksum,
);

fn get_pkg_manifest_checksum(
&self,
package_nv: &PackageNv,
) -> Option<LoaderChecksum>;
fn set_pkg_manifest_checksum(
&mut self,
package_nv: &PackageNv,
checksum: LoaderChecksum,
);
}

#[derive(Debug, Default, Clone)]
pub struct HashMapLocker(HashMap<ModuleSpecifier, LoaderChecksum>);
pub struct HashMapLocker {
remote: HashMap<ModuleSpecifier, LoaderChecksum>,
pkg_manifests: HashMap<PackageNv, LoaderChecksum>,
}

impl HashMapLocker {
pub fn inner(&self) -> &HashMap<ModuleSpecifier, LoaderChecksum> {
&self.0
pub fn remote(&self) -> &HashMap<ModuleSpecifier, LoaderChecksum> {
&self.remote
}

pub fn pkg_manifests(&self) -> &HashMap<PackageNv, LoaderChecksum> {
&self.pkg_manifests
}
}

impl Locker for HashMapLocker {
fn get_checksum(
fn get_remote_checksum(
&self,
specifier: &ModuleSpecifier,
) -> Option<LoaderChecksum> {
self.0.get(specifier).cloned()
self.remote.get(specifier).cloned()
}

fn has_checksum(&self, specifier: &ModuleSpecifier) -> bool {
self.0.contains_key(specifier)
fn has_remote_checksum(&self, specifier: &ModuleSpecifier) -> bool {
self.remote.contains_key(specifier)
}

fn set_checksum(
fn set_remote_checksum(
&mut self,
specifier: &ModuleSpecifier,
checksum: LoaderChecksum,
) {
self.0.insert(specifier.clone(), checksum);
self.remote.insert(specifier.clone(), checksum);
}

fn get_pkg_manifest_checksum(
&self,
package_nv: &PackageNv,
) -> Option<LoaderChecksum> {
self.pkg_manifests.get(package_nv).cloned()
}

fn set_pkg_manifest_checksum(
&mut self,
package_nv: &PackageNv,
checksum: LoaderChecksum,
) {
self.pkg_manifests.insert(package_nv.clone(), checksum);
}
}

Expand Down
Loading