fix(cache): harden object data cache coordination (#5004)

* fix(cache): enforce projected entry capacity

Refs: rustfs/backlog#1335

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): fence identity budget eviction by generation

Refs rustfs/backlog#1334.

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): fence clear against concurrent fills

Refs rustfs/backlog#1333

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): linearize memory reservation claims

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): retain allocation memory claims

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): publish memory snapshots by epoch

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(cache): coordinate cold object fills

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ecstore): fence metadata cache transition races

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-07-18 22:37:10 +08:00
committed by GitHub
parent 4faea7fcbc
commit 15f4e75870
39 changed files with 9930 additions and 538 deletions
+29 -9
View File
@@ -13,11 +13,26 @@
// limitations under the License.
use bytes::Bytes;
use std::cmp;
use crate::index::ObjectDataCacheGeneration;
use crate::key::ObjectDataCacheKey;
const ENTRY_OVERHEAD_BYTES: usize = 64;
const ENTRY_OVERHEAD_BYTES: u64 = 64;
/// Estimates the weighted capacity charged for a key and planned body.
pub(crate) fn projected_weight(key: &ObjectDataCacheKey, body_bytes: u64) -> u64 {
let key_bytes = key
.bucket
.len()
.saturating_add(key.object.len())
.saturating_add(key.version_id.len())
.saturating_add(key.etag.len());
u64::try_from(key_bytes)
.unwrap_or(u64::MAX)
.saturating_add(body_bytes)
.saturating_add(ENTRY_OVERHEAD_BYTES)
}
/// Cached object body entry.
///
@@ -29,12 +44,17 @@ const ENTRY_OVERHEAD_BYTES: usize = 64;
#[derive(Debug, Clone)]
pub struct ObjectDataCacheEntry {
bytes: Bytes,
generation: ObjectDataCacheGeneration,
}
impl ObjectDataCacheEntry {
/// Creates a new cached entry.
pub fn new(bytes: Bytes) -> Self {
Self { bytes }
Self { bytes, generation: 0 }
}
pub(crate) fn with_generation(bytes: Bytes, generation: ObjectDataCacheGeneration) -> Self {
Self { bytes, generation }
}
/// Returns a clone of the cached body bytes.
@@ -42,14 +62,14 @@ impl ObjectDataCacheEntry {
self.bytes.clone()
}
pub(crate) const fn generation(&self) -> ObjectDataCacheGeneration {
self.generation
}
/// Returns the estimated weighted size for capacity accounting.
pub fn estimated_weight(&self, key: &ObjectDataCacheKey) -> u32 {
let key_bytes = key.bucket.len() + key.object.len() + key.version_id.len() + key.etag.len();
let body_bytes = self.bytes.len();
let total = key_bytes.saturating_add(body_bytes).saturating_add(ENTRY_OVERHEAD_BYTES);
let clamped = cmp::min(total, u32::MAX as usize);
u32::try_from(clamped).unwrap_or(u32::MAX)
let body_bytes = u64::try_from(self.bytes.len()).unwrap_or(u64::MAX);
u32::try_from(projected_weight(key, body_bytes)).unwrap_or(u32::MAX)
}
}