Skip to content

Commit 83396aa

Browse files
committed
perf(pm): remove resolver memory cache layer
1 parent 2d30f46 commit 83396aa

5 files changed

Lines changed: 13 additions & 223 deletions

File tree

crates/ruborist/src/model/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@
7171
//! cloning at every cache read and graph insertion:
7272
//!
7373
//! ```text
74-
//! MemoryCache ──┐
75-
//! ├── Arc<CoreVersionManifest> ── (ref-count clone)
76-
//! PackageNode ──┘
74+
//! ManifestState ──┐
75+
//! ├── Arc<CoreVersionManifest> ── (ref-count clone)
76+
//! PackageNode ────
7777
//!
7878
//! Cold paths (disk I/O, serde) still use owned CoreVersionManifest,
7979
//! wrapping in Arc::new() at the boundary.
Lines changed: 5 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,14 @@
1-
//! In-memory manifest cache for dependency resolution.
1+
//! Manifest cache data structures for dependency resolution.
22
//!
3-
//! ruborist itself only owns the memory tier; persistent storage (disk, remote
4-
//! KV, …) is delegated to a [`super::store::ManifestStore`] supplied by the
5-
//! host. The project-level cache ([`ProjectCacheData`]) is also pure data —
6-
//! callers load/save it themselves and pass it through `BuildDepsOptions` /
7-
//! `BuildDepsOutput`.
8-
//!
9-
//! # Memory Layout
10-
//!
11-
//! ```text
12-
//! MemoryCache ─ Clone ─► (cheap: Arc ref-count)
13-
//! │
14-
//! └──► Arc<MemoryCacheInner> single allocation
15-
//! ├── DashMap<Arc<FullManifest>> sharded, lock-free reads
16-
//! ├── DashMap<Arc<VersionsInfo>>
17-
//! └── DashMap<Arc<CoreVersionManifest>>
18-
//! │
19-
//! ▼
20-
//! All values Arc-wrapped → get/set is O(1) ref-count,
21-
//! no full clone of the (large) manifest payload.
22-
//!
23-
//! Global singleton: GLOBAL_MEMORY_CACHE (LazyLock)
24-
//! └── all UnifiedRegistry instances share the same cache
25-
//! ```
26-
//!
27-
//! # Lookup Flow
28-
//!
29-
//! ```text
30-
//! resolve(name, spec)
31-
//! │
32-
//! ├─ 1. Memory hit? ──yes──► Arc<CoreVersionManifest> clone → done
33-
//! ├─ 2. ManifestStore hit? ──yes──► populate memory → done
34-
//! └─ 3. Network ──────► fetch JSON → store memory + fire-and-forget
35-
//! ManifestStore::store_*
36-
//! ```
3+
//! The demand BFS loop owns the in-memory manifest maps for one resolution run.
4+
//! This module only carries serializable data shared between the loop,
5+
//! provider jobs, and host persistence.
376
387
use std::collections::HashMap;
39-
use std::sync::{Arc, LazyLock};
408

41-
use dashmap::DashMap;
429
use serde::{Deserialize, Serialize};
4310

44-
use crate::model::manifest::{CoreVersionManifest, FullManifest, VersionsRef};
11+
use crate::model::manifest::{CoreVersionManifest, VersionsRef};
4512

4613
/// Lightweight versions info, persisted by `ManifestStore` for ETag validation.
4714
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -74,121 +41,6 @@ impl<'a> From<&'a Versions> for VersionsRef<'a> {
7441
}
7542
}
7643

77-
// ============================================================================
78-
// Memory cache (lock-free reads via DashMap)
79-
// ============================================================================
80-
81-
/// Thread-safe in-memory manifest cache. Uses sharded `DashMap`s so concurrent
82-
/// reads are lock-free across shards and writes only contend within a single
83-
/// shard; values are stored as `Arc<…>` so reads return cheap ref-count clones
84-
/// instead of cloning the full (potentially large) manifest payload.
85-
#[derive(Clone)]
86-
pub struct MemoryCache(Arc<MemoryCacheInner>);
87-
88-
struct MemoryCacheInner {
89-
full_manifests: DashMap<String, Arc<FullManifest>>,
90-
versions_info: DashMap<String, Arc<VersionsInfo>>,
91-
version_manifests: DashMap<String, Arc<CoreVersionManifest>>,
92-
}
93-
94-
/// Global singleton. All `UnifiedRegistry` instances share the same cache.
95-
static GLOBAL_MEMORY_CACHE: LazyLock<MemoryCache> = LazyLock::new(|| {
96-
MemoryCache(Arc::new(MemoryCacheInner {
97-
full_manifests: DashMap::new(),
98-
versions_info: DashMap::new(),
99-
version_manifests: DashMap::new(),
100-
}))
101-
});
102-
103-
impl Default for MemoryCache {
104-
fn default() -> Self {
105-
GLOBAL_MEMORY_CACHE.clone()
106-
}
107-
}
108-
109-
impl MemoryCache {
110-
pub fn get_full_manifest(&self, name: &str) -> Option<Arc<FullManifest>> {
111-
self.0.full_manifests.get(name).map(|v| v.clone())
112-
}
113-
114-
pub fn set_full_manifest(&self, name: String, manifest: Arc<FullManifest>) {
115-
self.0.full_manifests.insert(name, manifest);
116-
}
117-
118-
pub fn get_versions(&self, name: &str) -> Option<Arc<VersionsInfo>> {
119-
self.0.versions_info.get(name).map(|v| v.clone())
120-
}
121-
122-
pub fn set_versions(&self, name: String, info: Arc<VersionsInfo>) {
123-
self.0.versions_info.insert(name, info);
124-
}
125-
126-
pub fn get_version_manifest(
127-
&self,
128-
name: &str,
129-
version: &str,
130-
) -> Option<Arc<CoreVersionManifest>> {
131-
let key = format!("{name}@{version}");
132-
self.0.version_manifests.get(&key).map(|v| v.clone())
133-
}
134-
135-
pub fn set_version_manifest(
136-
&self,
137-
name: String,
138-
version: String,
139-
manifest: Arc<CoreVersionManifest>,
140-
) {
141-
let key = format!("{name}@{version}");
142-
self.0.version_manifests.insert(key, manifest);
143-
}
144-
145-
pub fn full_manifest_count(&self) -> usize {
146-
self.0.full_manifests.len()
147-
}
148-
149-
pub fn versions_count(&self) -> usize {
150-
self.0.versions_info.len()
151-
}
152-
153-
pub fn version_manifest_count(&self) -> usize {
154-
self.0.version_manifests.len()
155-
}
156-
157-
/// Export all version manifests for persistence into a project cache.
158-
pub fn export_version_manifests(&self) -> Vec<(String, Arc<CoreVersionManifest>)> {
159-
self.0
160-
.version_manifests
161-
.iter()
162-
.map(|kv| (kv.key().clone(), kv.value().clone()))
163-
.collect()
164-
}
165-
166-
/// Get cache statistics.
167-
pub fn stats(&self) -> CacheStats {
168-
CacheStats {
169-
full_manifest_count: self.full_manifest_count(),
170-
versions_count: self.versions_count(),
171-
version_manifest_count: self.version_manifest_count(),
172-
}
173-
}
174-
}
175-
176-
/// Cache statistics.
177-
#[derive(Debug, Clone)]
178-
pub struct CacheStats {
179-
pub full_manifest_count: usize,
180-
pub versions_count: usize,
181-
pub version_manifest_count: usize,
182-
}
183-
184-
/// Alias kept so call sites that pre-date the disk-cache split can continue
185-
/// to spell the in-memory cache as `PackageCache` without churn.
186-
pub type PackageCache = MemoryCache;
187-
188-
// ============================================================================
189-
// Project-level cache (per-project resolved packages)
190-
// ============================================================================
191-
19244
/// Project-level cache data.
19345
///
19446
/// Stores resolved package information for a specific project. Hosts persist
@@ -211,62 +63,3 @@ pub struct ProjectPackageCache {
21163
#[serde(default)]
21264
pub manifests: HashMap<String, CoreVersionManifest>,
21365
}
214-
215-
#[cfg(test)]
216-
mod tests {
217-
use super::*;
218-
219-
#[test]
220-
fn test_memory_cache_full_manifest() {
221-
let cache = MemoryCache::default();
222-
223-
let manifest = FullManifest {
224-
name: "test".to_string(),
225-
..Default::default()
226-
};
227-
228-
cache.set_full_manifest("test".to_string(), Arc::new(manifest));
229-
230-
let retrieved = cache.get_full_manifest("test").unwrap();
231-
assert_eq!(retrieved.name, "test");
232-
assert!(cache.full_manifest_count() >= 1);
233-
}
234-
235-
#[test]
236-
fn test_memory_cache_versions() {
237-
let cache = MemoryCache::default();
238-
239-
let info = VersionsInfo {
240-
versions: Versions {
241-
version_list: vec!["1.0.0".to_string()],
242-
dist_tags: HashMap::new(),
243-
},
244-
etag: Some("abc".to_string()),
245-
last_updated: 12345,
246-
};
247-
248-
cache.set_versions("test".to_string(), Arc::new(info));
249-
250-
let retrieved = cache.get_versions("test").unwrap();
251-
assert_eq!(retrieved.versions.version_list, vec!["1.0.0"]);
252-
assert!(cache.versions_count() >= 1);
253-
}
254-
255-
#[test]
256-
fn test_memory_cache_version_manifest() {
257-
let cache = MemoryCache::default();
258-
259-
let manifest = CoreVersionManifest {
260-
name: "test".to_string(),
261-
version: "1.0.0".to_string(),
262-
..Default::default()
263-
};
264-
265-
cache.set_version_manifest("test".to_string(), "1.0.0".to_string(), Arc::new(manifest));
266-
267-
let retrieved = cache.get_version_manifest("test", "1.0.0").unwrap();
268-
assert_eq!(retrieved.name, "test");
269-
assert_eq!(retrieved.version, "1.0.0");
270-
assert!(cache.version_manifest_count() >= 1);
271-
}
272-
}

crates/ruborist/src/service/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ mod registry;
5454
mod store;
5555

5656
pub use api::{BuildDepsOptions, BuildDepsOutput, build_deps};
57-
pub use cache::{
58-
CacheStats, MemoryCache, PackageCache, ProjectCacheData, ProjectPackageCache, Versions,
59-
VersionsInfo,
60-
};
57+
pub use cache::{ProjectCacheData, ProjectPackageCache, Versions, VersionsInfo};
6158
pub use fs::{Glob, NoopGlob, exists, read_to_string};
6259
pub use http::client_builder;
6360
pub use manifest::{

crates/ruborist/src/service/provider.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! The demand BFS loop owns per-run cache, waiters, and inflight de-duplication.
44
//! A provider only executes one manifest job and hides whether it satisfied the
5-
//! job from memory, persistent storage, or the network.
5+
//! job from memory, disk/OPFS, or the network.
66
77
use std::sync::Arc;
88

crates/ruborist/src/service/store.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Persistence backend for the registry's manifest cache.
22
//!
3-
//! ruborist itself owns only the in-memory tier ([`super::cache::MemoryCache`]);
4-
//! any persistent storage (disk, remote KV, …) is supplied by the host through
5-
//! a [`ManifestStore`] implementation. This keeps the resolver free of file I/O
3+
//! The resolver main loop owns the per-run in-memory manifest maps. Persistent
4+
//! storage (disk, remote KV, …) is supplied by the host through a
5+
//! [`ManifestStore`] implementation. This keeps the resolver free of file I/O
66
//! and lets hosts pick their own format, layout, and write strategy.
77
//!
88
//! Contract:

0 commit comments

Comments
 (0)