The single global repo lock becomes one lock per marketplace, and refresh_pins takes each marketplace's lock only while setting its pins, so a slow fetch no longer queues installs and refreshes of other marketplaces. refresh_marketplace now takes the lock first and reads the settings store under it (a closure, not a copy captured earlier); a marketplace removed meanwhile gets no cache and no snapshot. Removing a marketplace deletes its snapshot and cache under the same lock (remove_marketplace_cache). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -322,7 +322,8 @@ pub(crate) mod ops {
|
||||
branch: None,
|
||||
account_id: None,
|
||||
});
|
||||
let snap = crate::marketplace::refresh_marketplace(&mgr, &settings, "m1").await;
|
||||
let snap =
|
||||
crate::marketplace::refresh_marketplace(&mgr, &|| settings.clone(), "m1").await;
|
||||
|
||||
let repo = crate::marketplace::git::cache_path(data.path(), "m1");
|
||||
let tree = GitTree::open(&repo, &head).unwrap();
|
||||
@@ -568,21 +569,23 @@ async fn snapshot_blocking(
|
||||
}
|
||||
|
||||
/// Make each cache's pin refs exactly the commits installs reference, so a
|
||||
/// pinned version can never be garbage-collected away. Under the repo lock
|
||||
/// (pre-flight F11): a concurrent fetch writes refs in the same repos.
|
||||
/// pinned version can never be garbage-collected away. Each marketplace's
|
||||
/// pins are set under that marketplace's repo lock only (pre-flight F11, PR
|
||||
/// review #5): a concurrent fetch writes refs in the same repo.
|
||||
pub(crate) async fn refresh_pins(state: &AppState) {
|
||||
let settings = state.settings_store.get();
|
||||
let pins = mk::pins_by_marketplace(&settings, &state.projects_store.list());
|
||||
let root = state.marketplace.data_root().to_path_buf();
|
||||
let ids: Vec<String> = settings.marketplaces.iter().map(|m| m.id.clone()).collect();
|
||||
let _repo_guard = state.marketplace.repo_lock().lock().await;
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
for id in ids {
|
||||
let repo = git::cache_path(&root, &id);
|
||||
for m in &settings.marketplaces {
|
||||
let lock = state.marketplace.repo_lock(&m.id);
|
||||
let _repo_guard = lock.lock().await;
|
||||
let repo = git::cache_path(&root, &m.id);
|
||||
let commits = pins.get(&m.id).cloned().unwrap_or_default();
|
||||
let id = m.id.clone();
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
if !repo.exists() {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
let commits = pins.get(&id).cloned().unwrap_or_default();
|
||||
if let Err(e) = git::set_pins(&repo, &commits) {
|
||||
log::warn!(
|
||||
"Could not update the pinned commits of marketplace {}: {}",
|
||||
@@ -590,28 +593,14 @@ pub(crate) async fn refresh_pins(state: &AppState) {
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Forget a marketplace's snapshot and delete its cache, under the repo lock.
|
||||
/// Forget a marketplace's snapshot and delete its cache, under its repo lock.
|
||||
pub(crate) async fn remove_cache(state: &AppState, marketplace_id: &str) {
|
||||
state.marketplace.remove_snapshot(marketplace_id);
|
||||
let path = git::cache_path(state.marketplace.data_root(), marketplace_id);
|
||||
let _repo_guard = state.marketplace.repo_lock().lock().await;
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
if path.exists() {
|
||||
if let Err(e) = std::fs::remove_dir_all(&path) {
|
||||
log::warn!(
|
||||
"Could not delete the marketplace cache {}: {}",
|
||||
path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
mk::remove_marketplace_cache(&state.marketplace, marketplace_id).await;
|
||||
}
|
||||
|
||||
fn save_new_account(
|
||||
@@ -665,7 +654,7 @@ pub async fn refresh_marketplaces(
|
||||
.iter()
|
||||
.filter(|m| marketplace_id.as_deref().is_none_or(|id| id == m.id))
|
||||
{
|
||||
mk::refresh_marketplace(&state.marketplace, &settings, &m.id).await;
|
||||
mk::refresh_marketplace(&state.marketplace, &|| state.settings_store.get(), &m.id).await;
|
||||
}
|
||||
refresh_pins(&state).await;
|
||||
list_marketplace_snapshots(state).await
|
||||
@@ -696,7 +685,8 @@ pub async fn add_marketplace(
|
||||
|
||||
let mut trial = settings.clone();
|
||||
trial.marketplaces.push(m.clone());
|
||||
let snap = mk::refresh_marketplace(&state.marketplace, &trial, &m.id).await;
|
||||
// Not yet in the store: the trial settings stand in for it.
|
||||
let snap = mk::refresh_marketplace(&state.marketplace, &|| trial.clone(), &m.id).await;
|
||||
let failure = snap.fetch_error.clone().or_else(|| {
|
||||
snap.head_commit
|
||||
.is_none()
|
||||
|
||||
Reference in New Issue
Block a user