mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 20:06:37 +00:00
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:
@@ -307,6 +307,17 @@ pub struct GetObjectReader {
|
||||
}
|
||||
|
||||
impl GetObjectReader {
|
||||
/// Builds a fully materialized reader from a cache-coordinated body.
|
||||
pub fn from_cache_body(mut object_info: ObjectInfo, body: Bytes) -> Result<Self> {
|
||||
object_info.size = i64::try_from(body.len()).map_err(|_| Error::other("cached GET body length exceeds i64::MAX"))?;
|
||||
Ok(Self {
|
||||
stream: Box::new(std::io::Cursor::new(body.clone())),
|
||||
object_info,
|
||||
buffered_body: Some(body),
|
||||
body_source: GetObjectBodySource::HookServed,
|
||||
})
|
||||
}
|
||||
|
||||
/// True when `buffered_body` is the body the cache hook served. The app
|
||||
/// layer serves it as the object-data-cache source without a second lookup.
|
||||
pub fn is_cache_hook_served(&self) -> bool {
|
||||
@@ -1674,6 +1685,40 @@ mod tests {
|
||||
bytes
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cache_body_uses_plaintext_length_for_compressed_metadata() {
|
||||
let mut metadata = HashMap::new();
|
||||
rustfs_utils::http::insert_str(
|
||||
&mut metadata,
|
||||
rustfs_utils::http::SUFFIX_COMPRESSION,
|
||||
"klauspost/compress/s2".to_string(),
|
||||
);
|
||||
let object_info = ObjectInfo {
|
||||
size: 3,
|
||||
actual_size: 11,
|
||||
user_defined: Arc::new(metadata),
|
||||
..Default::default()
|
||||
};
|
||||
assert!(object_info.is_compressed());
|
||||
|
||||
let body = Bytes::from_static(b"hello world");
|
||||
let mut reader =
|
||||
GetObjectReader::from_cache_body(object_info, body.clone()).expect("cache body length must fit in object metadata");
|
||||
|
||||
assert_eq!(reader.body_source, GetObjectBodySource::HookServed);
|
||||
assert_eq!(reader.buffered_body.as_ref(), Some(&body));
|
||||
assert_eq!(reader.object_info.size, 11);
|
||||
assert_eq!(reader.object_info.actual_size, 11);
|
||||
assert!(reader.object_info.is_compressed());
|
||||
let mut restored = Vec::new();
|
||||
reader
|
||||
.stream
|
||||
.read_to_end(&mut restored)
|
||||
.await
|
||||
.expect("cache body should stream");
|
||||
assert_eq!(restored, body);
|
||||
}
|
||||
|
||||
/// Regression for the #4576 fallout: the encrypt side persists a random
|
||||
/// SSE-C nonce, and this reader-side resolver must read it back — falling
|
||||
/// back to the deterministic legacy nonce only when no IV was stored.
|
||||
|
||||
Reference in New Issue
Block a user