From 77fcd3b6e228bbb413657bb02f9f83dd75b5eb73 Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 1 May 2026 07:40:20 +0800 Subject: [PATCH] perf(pm): dedup version-manifest disk writes via OnceMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream `inflight_version<(name, original_spec)>` gate keys on the *original* spec while `store_version_manifest` keys on the *resolved* version. Two sibling specs (`^4.17.0`, `~4.17.20`) resolving to the same `4.17.21` each pass through different gates and would each `tokio::spawn(write_json)` for the same path — duplicate serialize, duplicate disk write, and a brief truncate-race window where a concurrent reader sees partial JSON. Add an `OnceMap<(name, version), ()>` to `DiskManifestStore` and use `register` as a sync atomic "first wins" check. Subsequent callers for the same `(name, resolved_version)` skip the spawn entirely. For ant-design preload (~600 specs → ~250 unique versions), this drops ~350 redundant spawn+serialize+write cycles (~1.75 MB redundant disk write). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/pm/src/util/manifest_store.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/pm/src/util/manifest_store.rs b/crates/pm/src/util/manifest_store.rs index 7f9c61bb17..abe8c1cd65 100644 --- a/crates/pm/src/util/manifest_store.rs +++ b/crates/pm/src/util/manifest_store.rs @@ -16,16 +16,27 @@ use async_trait::async_trait; use serde::Serialize; use utoo_ruborist::model::manifest::CoreVersionManifest; use utoo_ruborist::service::{ManifestStore, VersionsInfo}; +use utoo_ruborist::util::OnceMap; use crate::util::json::read_json_file; pub struct DiskManifestStore { cache_dir: PathBuf, + /// Dedup `(name, resolved_version)` disk writes. The upstream + /// `inflight_version<(name, original_spec)>` gate keys on the original + /// spec, so two specs (`^4.17.0`, `~4.17.20`) resolving to the same + /// `4.17.21` each call `store_version_manifest` and would otherwise + /// spawn duplicate `write_json` tasks racing on truncate of the same + /// path. `OnceMap::register` is a sync, atomic "first wins" check. + inflight_version_writes: Arc>, } impl DiskManifestStore { pub fn new(cache_dir: PathBuf) -> Self { - Self { cache_dir } + Self { + cache_dir, + inflight_version_writes: Arc::new(OnceMap::new()), + } } fn versions_path(&self, name: &str) -> PathBuf { @@ -67,8 +78,21 @@ impl ManifestStore for DiskManifestStore { version: &str, manifest: Arc, ) { + let key = (name.to_string(), version.to_string()); + let Some(notify) = self.inflight_version_writes.register(key.clone()) else { + // Another caller already enqueued this write — skip the + // duplicate serialize + spawn + truncate-race. + return; + }; let path = self.manifest_path(name, version); - tokio::spawn(async move { write_json(&path, &*manifest).await }); + // Move an `Arc` clone of the OnceMap into the task so we can + // transition Waiting → Done after the write completes; this also + // notifies any future `wait_if_pending` callers. + let inflight = Arc::clone(&self.inflight_version_writes); + tokio::spawn(async move { + write_json(&path, &*manifest).await; + inflight.complete(key, Some(()), notify); + }); } }