Skip to content
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

feat: use sha in cache lock file, needed for source builds. #900

Merged
merged 4 commits into from
Oct 14, 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
9 changes: 4 additions & 5 deletions crates/rattler_cache/src/package_cache/cache_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ use std::fmt::{Display, Formatter};

/// Provides a unique identifier for packages in the cache.
/// TODO: This could not be unique over multiple subdir. How to handle?
/// TODO: Wouldn't it be better to cache based on hashes?
#[derive(Debug, Hash, Clone, Eq, PartialEq)]
pub struct CacheKey {
name: String,
version: String,
build_string: String,
sha256: Option<Sha256Hash>,
pub(crate) name: String,
pub(crate) version: String,
pub(crate) build_string: String,
pub(crate) sha256: Option<Sha256Hash>,
}

impl CacheKey {
Expand Down
97 changes: 93 additions & 4 deletions crates/rattler_cache/src/package_cache/cache_lock.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::{
fmt::{Debug, Formatter},
io::{Read, Seek, Write},
io::{Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
sync::Arc,
time::Duration,
};

use digest::generic_array::GenericArray;
use fs4::fs_std::FileExt;
use parking_lot::Mutex;
use rattler_digest::Sha256Hash;

use crate::package_cache::PackageCacheError;

Expand All @@ -19,6 +21,7 @@ use crate::package_cache::PackageCacheError;
pub struct CacheLock {
pub(super) _lock: CacheRwLock,
pub(super) revision: u64,
pub(super) sha256: Option<Sha256Hash>,
pub(super) path: PathBuf,
}

Expand All @@ -27,6 +30,7 @@ impl Debug for CacheLock {
f.debug_struct("CacheLock")
.field("path", &self.path)
.field("revision", &self.revision)
.field("sha256", &self.sha256)
.finish()
}
}
Expand Down Expand Up @@ -151,8 +155,13 @@ impl CacheRwLock {
}

impl CacheRwLock {
pub async fn write_revision(&mut self, revision: u64) -> Result<(), PackageCacheError> {
pub async fn write_revision_and_sha(
&mut self,
revision: u64,
sha256: Option<&Sha256Hash>,
) -> Result<(), PackageCacheError> {
let file = self.file.clone();
let sha256 = sha256.cloned();
simple_spawn_blocking::tokio::run_blocking_task(move || {
let mut file = file.lock();

Expand All @@ -173,6 +182,21 @@ impl CacheRwLock {
)
})?;

// Write the bytes of the sha256 hash
let sha_bytes = if let Some(sha) = sha256 {
let len = sha.len();
let sha = &sha[..];
file.write_all(sha).map_err(|e| {
PackageCacheError::LockError(
"failed to write sha256 from cache lock".to_string(),
e,
)
})?;
len
} else {
0
};

// Ensure all bytes are written to disk
file.flush().map_err(|e| {
PackageCacheError::LockError(
Expand All @@ -182,7 +206,8 @@ impl CacheRwLock {
})?;

// Update the length of the file
file.set_len(revision_bytes.len() as u64).map_err(|e| {
let file_length = revision_bytes.len() + sha_bytes;
file.set_len(file_length as u64).map_err(|e| {
PackageCacheError::LockError(
"failed to truncate cache lock after writing revision".to_string(),
e,
Expand All @@ -196,9 +221,17 @@ impl CacheRwLock {
}

impl CacheRwLock {
/// Reads the revision from the cache lock file.
pub fn read_revision(&mut self) -> Result<u64, PackageCacheError> {
let mut file = self.file.lock();
file.rewind().map_err(|e| {
PackageCacheError::LockError(
"failed to rewind cache lock for reading revision".to_string(),
e,
)
})?;
let mut buf = [0; 8];
match self.file.lock().read_exact(&mut buf) {
match file.read_exact(&mut buf) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
return Ok(0);
Expand All @@ -212,6 +245,36 @@ impl CacheRwLock {
}
Ok(u64::from_be_bytes(buf))
}

/// Reads the sha256 hash from the cache lock file.
pub fn read_sha256(&mut self) -> Result<Option<Sha256Hash>, PackageCacheError> {
const SHA256_LEN: usize = 32;
const REVISION_LEN: u64 = 8;
let mut file = self.file.lock();
file.rewind().map_err(|e| {
PackageCacheError::LockError(
"failed to rewind cache lock for reading sha256".to_string(),
e,
)
})?;
let mut buf = [0; SHA256_LEN];
let _ = file.seek(SeekFrom::Start(REVISION_LEN)).map_err(|e| {
PackageCacheError::LockError("failed to seek to sha256 in cache lock".to_string(), e)
})?;
match file.read_exact(&mut buf) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
return Ok(None);
}
Err(e) => {
return Err(PackageCacheError::LockError(
"failed to read sha256 from cache lock".to_string(),
e,
));
}
}
Ok(Some(GenericArray::clone_from_slice(&buf)))
}
}

async fn warn_timeout_future(message: String) {
Expand All @@ -220,3 +283,29 @@ async fn warn_timeout_future(message: String) {
tracing::warn!("{}", &message);
}
}

#[cfg(test)]
mod tests {
use rattler_digest::{parse_digest_from_hex, Sha256};

use super::CacheRwLock;

#[tokio::test]
async fn cache_lock_serialize_deserialize() {
// Temporarily create a lock file and write a revision and sha to it
let temp_dir = tempfile::tempdir().unwrap();
let lock_file = temp_dir.path().join("foo.lock");
// Acquire a write lock on the file
let mut lock = CacheRwLock::acquire_write(&lock_file).await.unwrap();
// Write a revision and sha to the lock file
let sha = parse_digest_from_hex::<Sha256>(
"4dd9893f1eee45e1579d1a4f5533ef67a84b5e4b7515de7ed0db1dd47adc6bc8",
);
lock.write_revision_and_sha(1, sha.as_ref()).await.unwrap();
// Read back the revision and sha from the lock file
let revision = lock.read_revision().unwrap();
assert_eq!(revision, 1);
let read_sha = lock.read_sha256().unwrap();
assert_eq!(sha, read_sha);
}
}
Loading
Loading