Skip to content

Commit d106ab1

Browse files
Make metadata deserialization failures non-fatal in the cache (#11105)
## Summary If we fail to deserialize cached metadata in the cache, we should just ignore it, rather than failing. Ideally, this never happens. If it does, it means we missed a cache version bump. But if it does happen, it should still be non-fatal. Closes #11043. Closes #11101. ## Test Plan Prior to this PR, the following would fail: - `uvx [email protected] venv --python 3.12 --cache-dir foo` - `uvx [email protected] pip install ./scripts/packages/hatchling_dynamic --no-deps --python 3.12 --cache-dir foo` - `uvx [email protected] venv --python 3.12 --cache-dir foo` - `uvx [email protected] pip install ./scripts/packages/hatchling_dynamic --no-deps --python 3.12 --cache-dir foo` We can't go back and fix 0.5.18, but this will prevent such regressions in the future.
1 parent 1dfa650 commit d106ab1

File tree

3 files changed

+92
-65
lines changed

3 files changed

+92
-65
lines changed

crates/uv-cache/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ impl CacheBucket {
793793
match self {
794794
// Note that when bumping this, you'll also need to bump it
795795
// in crates/uv/tests/cache_prune.rs.
796-
Self::SourceDistributions => "sdists-v6",
796+
Self::SourceDistributions => "sdists-v7",
797797
Self::FlatIndex => "flat-index-v2",
798798
Self::Git => "git-v0",
799799
Self::Interpreter => "interpreter-v4",

crates/uv-distribution/src/source/mod.rs

Lines changed: 90 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -551,15 +551,21 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
551551

552552
// If the cache contains compatible metadata, return it.
553553
let metadata_entry = cache_shard.entry(METADATA);
554-
if let Some(metadata) = CachedMetadata::read(&metadata_entry)
555-
.await?
556-
.filter(|metadata| metadata.matches(source.name(), source.version()))
557-
{
558-
debug!("Using cached metadata for: {source}");
559-
return Ok(ArchiveMetadata {
560-
metadata: Metadata::from_metadata23(metadata.into()),
561-
hashes: revision.into_hashes(),
562-
});
554+
match CachedMetadata::read(&metadata_entry).await {
555+
Ok(Some(metadata)) => {
556+
if metadata.matches(source.name(), source.version()) {
557+
debug!("Using cached metadata for: {source}");
558+
return Ok(ArchiveMetadata {
559+
metadata: Metadata::from_metadata23(metadata.into()),
560+
hashes: revision.into_hashes(),
561+
});
562+
}
563+
debug!("Cached metadata does not match expected name and version for: {source}");
564+
}
565+
Ok(None) => {}
566+
Err(err) => {
567+
debug!("Failed to deserialize cached metadata for: {source} ({err})");
568+
}
563569
}
564570

565571
// Otherwise, we need a wheel.
@@ -882,15 +888,21 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
882888

883889
// If the cache contains compatible metadata, return it.
884890
let metadata_entry = cache_shard.entry(METADATA);
885-
if let Some(metadata) = CachedMetadata::read(&metadata_entry)
886-
.await?
887-
.filter(|metadata| metadata.matches(source.name(), source.version()))
888-
{
889-
debug!("Using cached metadata for: {source}");
890-
return Ok(ArchiveMetadata {
891-
metadata: Metadata::from_metadata23(metadata.into()),
892-
hashes: revision.into_hashes(),
893-
});
891+
match CachedMetadata::read(&metadata_entry).await {
892+
Ok(Some(metadata)) => {
893+
if metadata.matches(source.name(), source.version()) {
894+
debug!("Using cached metadata for: {source}");
895+
return Ok(ArchiveMetadata {
896+
metadata: Metadata::from_metadata23(metadata.into()),
897+
hashes: revision.into_hashes(),
898+
});
899+
}
900+
debug!("Cached metadata does not match expected name and version for: {source}");
901+
}
902+
Ok(None) => {}
903+
Err(err) => {
904+
debug!("Failed to deserialize cached metadata for: {source} ({err})");
905+
}
894906
}
895907

896908
// Otherwise, we need a source distribution.
@@ -1183,31 +1195,38 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
11831195

11841196
// If the cache contains compatible metadata, return it.
11851197
let metadata_entry = cache_shard.entry(METADATA);
1186-
if let Some(metadata) = CachedMetadata::read(&metadata_entry)
1187-
.await?
1188-
.filter(|metadata| metadata.matches(source.name(), source.version()))
1189-
{
1190-
// If necessary, mark the metadata as dynamic.
1191-
let metadata = if dynamic {
1192-
ResolutionMetadata {
1193-
dynamic: true,
1194-
..metadata.into()
1198+
match CachedMetadata::read(&metadata_entry).await {
1199+
Ok(Some(metadata)) => {
1200+
if metadata.matches(source.name(), source.version()) {
1201+
debug!("Using cached metadata for: {source}");
1202+
1203+
// If necessary, mark the metadata as dynamic.
1204+
let metadata = if dynamic {
1205+
ResolutionMetadata {
1206+
dynamic: true,
1207+
..metadata.into()
1208+
}
1209+
} else {
1210+
metadata.into()
1211+
};
1212+
return Ok(ArchiveMetadata::from(
1213+
Metadata::from_workspace(
1214+
metadata,
1215+
resource.install_path.as_ref(),
1216+
None,
1217+
self.build_context.locations(),
1218+
self.build_context.sources(),
1219+
self.build_context.bounds(),
1220+
)
1221+
.await?,
1222+
));
11951223
}
1196-
} else {
1197-
metadata.into()
1198-
};
1199-
1200-
return Ok(ArchiveMetadata::from(
1201-
Metadata::from_workspace(
1202-
metadata,
1203-
resource.install_path.as_ref(),
1204-
None,
1205-
self.build_context.locations(),
1206-
self.build_context.sources(),
1207-
self.build_context.bounds(),
1208-
)
1209-
.await?,
1210-
));
1224+
debug!("Cached metadata does not match expected name and version for: {source}");
1225+
}
1226+
Ok(None) => {}
1227+
Err(err) => {
1228+
debug!("Failed to deserialize cached metadata for: {source} ({err})");
1229+
}
12111230
}
12121231

12131232
// If the backend supports `prepare_metadata_for_build_wheel`, use it.
@@ -1635,27 +1654,35 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
16351654
.map_err(Error::CacheRead)?
16361655
.is_fresh()
16371656
{
1638-
if let Some(metadata) = CachedMetadata::read(&metadata_entry)
1639-
.await?
1640-
.filter(|metadata| metadata.matches(source.name(), source.version()))
1641-
{
1642-
let git_member = GitWorkspaceMember {
1643-
fetch_root: fetch.path(),
1644-
git_source: resource,
1645-
};
1646-
1647-
debug!("Using cached metadata for: {source}");
1648-
return Ok(ArchiveMetadata::from(
1649-
Metadata::from_workspace(
1650-
metadata.into(),
1651-
&path,
1652-
Some(&git_member),
1653-
self.build_context.locations(),
1654-
self.build_context.sources(),
1655-
self.build_context.bounds(),
1656-
)
1657-
.await?,
1658-
));
1657+
match CachedMetadata::read(&metadata_entry).await {
1658+
Ok(Some(metadata)) => {
1659+
if metadata.matches(source.name(), source.version()) {
1660+
debug!("Using cached metadata for: {source}");
1661+
1662+
let git_member = GitWorkspaceMember {
1663+
fetch_root: fetch.path(),
1664+
git_source: resource,
1665+
};
1666+
return Ok(ArchiveMetadata::from(
1667+
Metadata::from_workspace(
1668+
metadata.into(),
1669+
&path,
1670+
Some(&git_member),
1671+
self.build_context.locations(),
1672+
self.build_context.sources(),
1673+
self.build_context.bounds(),
1674+
)
1675+
.await?,
1676+
));
1677+
}
1678+
debug!(
1679+
"Cached metadata does not match expected name and version for: {source}"
1680+
);
1681+
}
1682+
Ok(None) => {}
1683+
Err(err) => {
1684+
debug!("Failed to deserialize cached metadata for: {source} ({err})");
1685+
}
16591686
}
16601687
}
16611688

crates/uv/tests/it/cache_prune.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ fn prune_stale_revision() -> Result<()> {
348348
----- stderr -----
349349
DEBUG uv [VERSION] ([COMMIT] DATE)
350350
Pruning cache at: [CACHE_DIR]/
351-
DEBUG Removing dangling source revision: [CACHE_DIR]/sdists-v6/[ENTRY]
351+
DEBUG Removing dangling source revision: [CACHE_DIR]/sdists-v7/[ENTRY]
352352
DEBUG Removing dangling cache archive: [CACHE_DIR]/archive-v0/[ENTRY]
353353
Removed [N] files ([SIZE])
354354
"###);

0 commit comments

Comments
 (0)