Skip to content

Commit

Permalink
Add a layer of indirection to the local path-based wheel cache
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Apr 8, 2024
1 parent 134810c commit a3c2d63
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 25 deletions.
6 changes: 6 additions & 0 deletions crates/uv-cache/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,12 @@ impl ArchiveTimestamp {
}
}

/// Return the modification timestamp for a file.
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, io::Error> {
let metadata = fs_err::metadata(path.as_ref())?;
Ok(Self::Exact(Timestamp::from_metadata(&metadata)))
}

/// Return the modification timestamp for an archive.
pub fn timestamp(&self) -> Timestamp {
match self {
Expand Down
85 changes: 61 additions & 24 deletions crates/uv-distribution/src/distribution_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use distribution_types::{
};
use platform_tags::Tags;
use pypi_types::Metadata23;
use uv_cache::{ArchiveTarget, ArchiveTimestamp, CacheBucket, CacheEntry, WheelCache};
use uv_cache::{ArchiveTimestamp, CacheBucket, CacheEntry, CachedByTimestamp, WheelCache};
use uv_client::{CacheControl, CachedClientError, Connectivity, RegistryClient};
use uv_fs::write_atomic;
use uv_types::{BuildContext, NoBinary, NoBuild};

use crate::locks::Locks;
Expand Down Expand Up @@ -471,30 +472,31 @@ impl<'a, Context: BuildContext + Send + Sync> DistributionDatabase<'a, Context>
wheel_entry: CacheEntry,
dist: &BuiltDist,
) -> Result<LocalWheel, Error> {
// If the file is already unzipped, and the unzipped directory is fresh,
// return it.
match wheel_entry.path().canonicalize() {
Ok(archive) => {
if ArchiveTimestamp::up_to_date_with(path, ArchiveTarget::Cache(&archive))
.map_err(Error::CacheRead)?
{
return Ok(LocalWheel {
dist: Dist::Built(dist.clone()),
archive,
filename: filename.clone(),
});
}
}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
Err(err) => return Err(Error::CacheRead(err)),
// Determine the last-modified time of the wheel.
let modified = ArchiveTimestamp::from_file(path).map_err(Error::CacheRead)?;

// Attempt to read the archive pointer from the cache.
let archive_entry = wheel_entry.with_file(format!("{}.rev", filename.stem()));
let archive = read_timestamped_archive(&archive_entry, modified)?;

// If the file is already unzipped, and the cache is up-to-date, return it.
if let Some(archive) = archive {
Ok(LocalWheel {
dist: Dist::Built(dist.clone()),
archive,
filename: filename.clone(),
})
} else {
// Otherwise, unzip the wheel.
let archive = self.unzip_wheel(path, wheel_entry.path()).await?;
write_timestamped_archive(&archive_entry, archive.clone(), modified).await?;

Ok(LocalWheel {
dist: Dist::Built(dist.clone()),
archive,
filename: filename.clone(),
})
}

// Otherwise, unzip the wheel.
Ok(LocalWheel {
dist: Dist::Built(dist.clone()),
archive: self.unzip_wheel(path, wheel_entry.path()).await?,
filename: filename.clone(),
})
}

/// Unzip a wheel into the cache, returning the path to the unzipped directory.
Expand Down Expand Up @@ -542,3 +544,38 @@ impl<'a, Context: BuildContext + Send + Sync> DistributionDatabase<'a, Context>
self.build_context.index_locations()
}
}

/// Write a timestamped archive path to the cache.
async fn write_timestamped_archive(
cache_entry: &CacheEntry,
data: PathBuf,
modified: ArchiveTimestamp,
) -> Result<(), Error> {
write_atomic(
cache_entry.path(),
rmp_serde::to_vec(&CachedByTimestamp {
timestamp: modified.timestamp(),
data,
})?,
)
.await
.map_err(Error::CacheWrite)
}

/// Read an existing timestamped archive path, if it exists and is up-to-date.
fn read_timestamped_archive(
cache_entry: &CacheEntry,
modified: ArchiveTimestamp,
) -> Result<Option<PathBuf>, Error> {
match fs_err::read(cache_entry.path()) {
Ok(cached) => {
let cached = rmp_serde::from_slice::<CachedByTimestamp<PathBuf>>(&cached)?;
if cached.timestamp == modified.timestamp() {
return Ok(Some(cached.data));
}
}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
Err(err) => return Err(Error::CacheRead(err)),
}
Ok(None)
}
2 changes: 1 addition & 1 deletion crates/uv/tests/pip_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1708,7 +1708,7 @@ fn install_path_built_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 1 file for tomli ([SIZE])
Removed 2 files for tomli ([SIZE])
"###
);

Expand Down

0 comments on commit a3c2d63

Please sign in to comment.