Skip to content

Commit 8585e9c

Browse files
committed
perf: use mutex to lock package tarball extraction
1 parent 24a60c9 commit 8585e9c

File tree

3 files changed

+18
-16
lines changed

3 files changed

+18
-16
lines changed

crates/registry/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,10 @@ impl RegistryManager {
9898
let package_node_modules_path =
9999
self.store_path.join(dependency_store_folder_name).join("node_modules");
100100

101-
// TODO: We shouldn't call this function for multiple same packages.
102-
// There needs to be some sort of thread safety.
101+
// Make sure to lock the package's mutex so we don't install the same package's tarball
102+
// in different threads.
103+
let mutex_guard = package.mutex.lock().await;
104+
103105
self.tarball_manager
104106
.download_dependency(
105107
name,
@@ -109,6 +111,8 @@ impl RegistryManager {
109111
)
110112
.await?;
111113

114+
drop(mutex_guard);
115+
112116
if let Some(dependencies) = package_version.dependencies.as_ref() {
113117
join_all(
114118
dependencies

crates/registry/src/package.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use std::collections::HashMap;
1+
use std::{collections::HashMap, sync::Arc};
22

33
use serde::{Deserialize, Serialize};
4+
use tokio::sync::Mutex;
45

56
use crate::error::RegistryError;
67

@@ -35,6 +36,9 @@ pub struct Package {
3536
#[serde(alias = "dist-tags")]
3637
dist_tags: HashMap<String, String>,
3738
pub versions: HashMap<String, PackageVersion>,
39+
40+
#[serde(skip_serializing, skip_deserializing)]
41+
pub mutex: Arc<Mutex<u8>>,
3842
}
3943

4044
impl Package {

crates/tarball/src/lib.rs

+7-13
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,7 @@ impl TarballManager {
6262
archive.unpack(&unpack_path)?;
6363
fs::remove_file(tarball_path)?;
6464
fs::create_dir_all(extract_path)?;
65-
// Check if extract_path is empty or not.
66-
// This is due by a race condition causing 2 different threads to download and
67-
// extract to the same folder. Likely be fixed with mutex locks.
68-
// TODO: Remove this when we have some sort of thread safety
69-
if extract_path.read_dir()?.next().is_none() {
70-
fs::rename(unpack_path.join("package"), extract_path)?;
71-
}
65+
fs::rename(unpack_path.join("package"), extract_path)?;
7266
fs::remove_dir_all(&unpack_path)?;
7367
Ok(())
7468
}
@@ -81,15 +75,17 @@ impl TarballManager {
8175
save_path: &Path,
8276
symlink_to: &Path,
8377
) -> Result<(), TarballError> {
78+
let symlink_exists = symlink_to.is_symlink();
79+
8480
// If name contains `/` such as @fastify/error, we need to make sure that @fastify folder
8581
// exists before we symlink to that directory.
86-
if name.contains('/') {
82+
if name.contains('/') && !symlink_exists {
8783
fs::create_dir_all(symlink_to.parent().unwrap())?;
8884
}
8985

9086
// Do not try to install dependency if this version already exists in package.json
91-
if save_path.exists() {
92-
if !symlink_to.is_symlink() {
87+
if save_path.exists() || symlink_exists {
88+
if !symlink_exists {
9389
symlink_dir(&save_path.to_path_buf(), &symlink_to.to_path_buf())?;
9490
}
9591
return Ok(());
@@ -102,9 +98,7 @@ impl TarballManager {
10298

10399
// TODO: Currently symlink paths are absolute paths.
104100
// If you move the root folder to a different path, all symlinks will be broken.
105-
if !symlink_to.is_symlink() {
106-
symlink_dir(&save_path.to_path_buf(), &symlink_to.to_path_buf())?;
107-
}
101+
symlink_dir(&save_path.to_path_buf(), &symlink_to.to_path_buf())?;
108102

109103
Ok(())
110104
}

0 commit comments

Comments
 (0)