Skip to content

Commit 685a798

Browse files
Avoid holding flat index lock across indexes (#18659)
## Summary Closes #18621.
1 parent 9d45e7f commit 685a798

1 file changed

Lines changed: 22 additions & 21 deletions

File tree

crates/uv-client/src/registry_client.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub struct RegistryClient {
250250
connectivity: Connectivity,
251251
/// Client HTTP read timeout.
252252
read_timeout: Duration,
253-
/// The flat index entries for each `--find-links`-style index URL.
253+
/// The flat index entries for each `--find-links`-style index URL, with one slot per index.
254254
flat_indexes: Arc<Mutex<FlatIndexCache>>,
255255
/// The pyx token store to use for persistent credentials.
256256
// TODO(charlie): The token store is only needed for `is_known_url`; can we avoid storing it here?
@@ -459,11 +459,15 @@ impl RegistryClient {
459459
package_name: &PackageName,
460460
index: &IndexUrl,
461461
) -> Result<Vec<FlatIndexEntry>, Error> {
462-
// Store the flat index entries in a cache, to avoid redundant fetches. A flat index will
463-
// typically contain entries for multiple packages; as such, it's more efficient to cache
464-
// the entire index rather than re-fetching it for each package.
465-
let mut cache = self.flat_indexes.lock().await;
466-
if let Some(entries) = cache.get(index) {
462+
// Each flat index gets its own slot, so lookups for the same index share a fetch while
463+
// unrelated indexes can proceed concurrently.
464+
let flat_index_slot = {
465+
let mut cache = self.flat_indexes.lock().await;
466+
cache.get_or_insert(index)
467+
};
468+
let mut flat_index = flat_index_slot.lock().await;
469+
470+
if let Some(entries) = flat_index.as_ref() {
467471
return Ok(entries.get(package_name).cloned().unwrap_or_default());
468472
}
469473

@@ -488,7 +492,7 @@ impl RegistryClient {
488492
.unwrap_or_default();
489493

490494
// Write to the cache.
491-
cache.insert(index.clone(), entries_by_package);
495+
*flat_index = Some(entries_by_package);
492496

493497
Ok(package_entries)
494498
}
@@ -1274,25 +1278,22 @@ impl From<IndexStatusCodeDecision> for SimpleMetadataSearchOutcome {
12741278

12751279
/// A map from [`IndexUrl`] to [`FlatIndexEntry`] entries found at the given URL, indexed by
12761280
/// [`PackageName`].
1277-
#[derive(Default, Debug, Clone)]
1278-
struct FlatIndexCache(FxHashMap<IndexUrl, FxHashMap<PackageName, Vec<FlatIndexEntry>>>);
1281+
#[derive(Default, Debug)]
1282+
struct FlatIndexCache(FxHashMap<IndexUrl, FlatIndexSlot>);
12791283

12801284
impl FlatIndexCache {
1281-
/// Get the entries for a given index URL.
1282-
fn get(&self, index: &IndexUrl) -> Option<&FxHashMap<PackageName, Vec<FlatIndexEntry>>> {
1283-
self.0.get(index)
1284-
}
1285-
1286-
/// Insert the entries for a given index URL.
1287-
fn insert(
1288-
&mut self,
1289-
index: IndexUrl,
1290-
entries: FxHashMap<PackageName, Vec<FlatIndexEntry>>,
1291-
) -> Option<FxHashMap<PackageName, Vec<FlatIndexEntry>>> {
1292-
self.0.insert(index, entries)
1285+
/// Return the per-index slot for this flat index, creating it on first access.
1286+
fn get_or_insert(&mut self, index: &IndexUrl) -> FlatIndexSlot {
1287+
self.0
1288+
.entry(index.clone())
1289+
.or_insert_with(|| Arc::new(Mutex::new(None)))
1290+
.clone()
12931291
}
12941292
}
12951293

1294+
type FlatIndexEntriesByPackage = FxHashMap<PackageName, Vec<FlatIndexEntry>>;
1295+
type FlatIndexSlot = Arc<Mutex<Option<FlatIndexEntriesByPackage>>>;
1296+
12961297
#[derive(Default, Debug, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
12971298
#[rkyv(derive(Debug))]
12981299
pub struct VersionFiles {

0 commit comments

Comments
 (0)