Skip to content

refactor(fuse): single cache with Arc fields and moka atomic upsert#700

Merged
szbr9486 merged 1 commit intomainfrom
open-cache
Mar 6, 2026
Merged

refactor(fuse): single cache with Arc fields and moka atomic upsert#700
szbr9486 merged 1 commit intomainfrom
open-cache

Conversation

@bigbigxu
Copy link
Contributor

@bigbigxu bigbigxu commented Mar 6, 2026

  • meta_cache: use single FastSyncCache<String, Arc>; wrap list/status/blocks in Arc so merge only does Arc::clone; use entry().and_upsert_with() for atomic read-modify-write (moka key-level lock), remove custom stripe lock
  • node_state: pass path to put_open in new_reader, use if let for Cv branch

- meta_cache: use single FastSyncCache<String, Arc<CacheValue>>; wrap list/status/blocks in Arc
  so merge only does Arc::clone; use entry().and_upsert_with() for atomic read-modify-write
  (moka key-level lock), remove custom stripe lock
- node_state: pass path to put_open in new_reader, use if let for Cv branch
Copilot AI review requested due to automatic review settings March 6, 2026 03:31
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors metadata caching and FUSE reader creation to reduce lock contention and avoid repeated deep clones by consolidating cache entries and caching CV file block metadata for faster opens.

Changes:

  • Refactor MetaCache to a single FastSyncCache<String, Arc<CacheValue>> and use moka’s atomic entry().and_upsert_with() for updates.
  • Update FUSE NodeState::new_reader to reuse cached FileBlocks to construct a CV FsReader without calling UnifiedFileSystem::open.
  • Extend FsReader to retain FileBlocks (and expose file_blocks()), and increase the default master_conn_pool_size to 3.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
curvine-fuse/src/fs/state/node_state.rs Uses cached FileBlocks to fast-path CV reader creation and populates the cache on open.
curvine-common/src/fs/meta_cache.rs Consolidates list/status/blocks caches into a single moka-backed cache value with atomic upserts.
curvine-common/src/conf/client_conf.rs Raises default master connection pool size from 1 to 3.
curvine-client/src/file/fs_reader.rs Stores FileBlocks in FsReader and exposes it for downstream caching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +276 to +294
let reader = match self.get_cached_blocks(path) {
Some(blocks) => {
let reader = FsReader::new(path.clone(), self.fs.fs_context().clone(), blocks)?;
UnifiedReader::Cv(reader)
}

None => {
let reader = self.fs.open(path).await?;

if self.conf.enable_meta_cache {
if let UnifiedReader::Cv(cv_reader) = &reader {
self.meta_cache
.put_open(path, cv_reader.file_blocks().clone());
}
}

reader
}
};
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When cached blocks exist, new_reader builds UnifiedReader::Cv(FsReader) directly instead of calling UnifiedFileSystem::open. This bypasses unified mount/cache behavior (notably cache-mode validity checks like read_verify_ufs / check_cache_validity) and can serve stale CV data when the underlying UFS has changed. Consider only using cached blocks when the path is not mounted (or mount is fs-mode / read_verify_ufs is false), otherwise fall back to self.fs.open(path).await (or re-run an equivalent validity check before trusting cached blocks).

Suggested change
let reader = match self.get_cached_blocks(path) {
Some(blocks) => {
let reader = FsReader::new(path.clone(), self.fs.fs_context().clone(), blocks)?;
UnifiedReader::Cv(reader)
}
None => {
let reader = self.fs.open(path).await?;
if self.conf.enable_meta_cache {
if let UnifiedReader::Cv(cv_reader) = &reader {
self.meta_cache
.put_open(path, cv_reader.file_blocks().clone());
}
}
reader
}
};
// Always go through UnifiedFileSystem::open to respect mount and cache validity checks.
let reader = self.fs.open(path).await?;
if self.conf.enable_meta_cache {
if let UnifiedReader::Cv(cv_reader) = &reader {
self.meta_cache
.put_open(path, cv_reader.file_blocks().clone());
}
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache itself carries the risk of reading dirty data; opening it first defeats the purpose of caching.

Comment on lines +108 to +112
.entry(path.clone_uri())
.and_upsert_with(|maybe_entry| {
let prev = maybe_entry.map(|e| e.into_value());
Arc::new(CacheValue {
list: None,
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalidate_list uses entry(...).and_upsert_with(...), which will insert a new empty CacheValue if the key is missing. That means an invalidate call can add entries (and refresh TTL) instead of being a no-op. Consider only modifying existing entries (e.g., get then update) or using an entry API that avoids insertion/removes the key when appropriate (same applies to invalidate_status/invalidate_open).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the GET method first can lead to issues with concurrent updates.

@szbr9486 szbr9486 merged commit e6f32d5 into main Mar 6, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants