perf(filemeta): phase-1~3 rename_data metadata optimization (#3011)

* chore(perf): harden amd64 profiling benchmark flow

* fix(profiling): isolate bench buckets and map protobuf conflict

* perf: avoid blocking owned local writes

* style: format profile admin handler

* docs: clarify observability trace validation

* perf: reduce mkdir overhead on local writes

* perf: add rename_data meta microbenchmark

* perf(filemeta): fast-path data_dir decode in version meta

* perf(filemeta): collapse data-dir lookup into one scan

* perf(filemeta): reduce scan allocs and refresh meta bench

* perf(ecstore): skip mkdir path on read-only open

* perf(filemeta): single-pass unshared data-dir scan

* perf(filemeta): add two-key inline remove fast path

* perf(filemeta): compare remove-two keys by bytes first

* bench(ecstore): add remove_two-only micro benchmark

* bench(ecstore): stabilize rename_data meta benchmark timing

* bench(ecstore): align rename_data path with remove_two

* perf(filemeta): avoid uuid string alloc in remove_two

* perf(filemeta): add fast-path for empty inline data

* perf(filemeta): streamline add_version match branch

* perf(filemeta): fast-return remove_key on miss

* perf(filemeta): speed up add_version insertion lookup

* style(ecstore): normalize formatting in perf-tuning files

* refactor(filemeta): unify inline data removal paths
This commit is contained in:
houseme
2026-05-19 18:20:24 +08:00
committed by GitHub
parent f695870626
commit 25c6bdf490
16 changed files with 1059 additions and 177 deletions
+107
View File
@@ -13,8 +13,48 @@
// limitations under the License.
use super::*;
use std::collections::HashSet;
impl FileMeta {
pub fn find_unshared_data_dir_for_version(&self, version_id: Option<Uuid>) -> Option<Uuid> {
let vid = version_id.unwrap_or_default();
let mut target_data_dir = None;
let mut target_selected = false;
let mut other_data_dirs = HashSet::new();
for version in self
.versions
.iter()
.filter(|v| v.header.version_type == VersionType::Object && v.header.uses_data_dir())
{
let is_target_version = version.header.version_id.unwrap_or_default() == vid;
if is_target_version {
if target_selected {
continue;
}
target_selected = true;
target_data_dir = FileMetaVersion::decode_data_dir_from_meta(&version.meta).unwrap_or_default();
if let Some(dir) = target_data_dir
&& other_data_dirs.contains(&dir)
{
return None;
}
continue;
}
let dir = FileMetaVersion::decode_data_dir_from_meta(&version.meta).unwrap_or_default();
if let Some(dir) = dir {
if target_data_dir == Some(dir) {
return None;
}
other_data_dirs.insert(dir);
}
}
target_data_dir
}
pub fn shard_data_dir_count(&self, vid: &Option<Uuid>, data_dir: &Option<Uuid>) -> usize {
let vid = vid.unwrap_or_default();
self.versions
@@ -58,3 +98,70 @@ impl FileMeta {
.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use s3s::header::X_AMZ_RESTORE;
use time::format_description::well_known::Rfc3339;
use time::{Duration, OffsetDateTime};
fn make_file_info(version_id: Uuid, data_dir: Uuid) -> FileInfo {
let restore_header = format!(
"ongoing-request=\"false\", expiry-date=\"{}\"",
(OffsetDateTime::now_utc() + Duration::days(1))
.format(&Rfc3339)
.expect("format restore expiry"),
);
FileInfo {
version_id: Some(version_id),
data_dir: Some(data_dir),
size: 64 * 1024,
mod_time: Some(OffsetDateTime::now_utc()),
metadata: [
("etag".to_string(), format!("etag-{version_id}")),
(X_AMZ_RESTORE.as_str().to_string(), restore_header),
]
.into_iter()
.collect(),
erasure: ErasureInfo {
algorithm: ErasureAlgo::ReedSolomon.to_string(),
data_blocks: 4,
parity_blocks: 2,
block_size: 1024 * 1024,
index: 1,
distribution: vec![1, 2, 3, 4, 5, 6],
..Default::default()
},
..Default::default()
}
}
#[test]
fn find_unshared_data_dir_for_version_returns_data_dir_when_unique() {
let target_version = Uuid::new_v4();
let target_data_dir = Uuid::new_v4();
let mut meta = FileMeta::new();
meta.add_version(make_file_info(target_version, target_data_dir))
.expect("seed target version");
meta.add_version(make_file_info(Uuid::new_v4(), Uuid::new_v4()))
.expect("seed non-shared version");
let got = meta.find_unshared_data_dir_for_version(Some(target_version));
assert_eq!(got, Some(target_data_dir));
}
#[test]
fn find_unshared_data_dir_for_version_returns_none_when_shared() {
let target_version = Uuid::new_v4();
let shared_data_dir = Uuid::new_v4();
let mut meta = FileMeta::new();
meta.add_version(make_file_info(target_version, shared_data_dir))
.expect("seed target version");
meta.add_version(make_file_info(Uuid::new_v4(), shared_data_dir))
.expect("seed shared version");
let got = meta.find_unshared_data_dir_for_version(Some(target_version));
assert_eq!(got, None);
}
}
+101
View File
@@ -312,6 +312,77 @@ pub struct FileMetaVersion {
}
impl FileMetaVersion {
fn decode_data_dir_from_v2_object(buf: &[u8]) -> Result<Option<Uuid>> {
let mut cur = std::io::Cursor::new(buf);
let mut fields = rmp::decode::read_map_len(&mut cur)?;
let mut version_type = VersionType::Invalid;
while fields > 0 {
fields -= 1;
let key_len = rmp::decode::read_str_len(&mut cur)? as usize;
let mut key_buf = vec![0u8; key_len];
cur.read_exact(&mut key_buf)?;
let key = String::from_utf8(key_buf)?;
match key.as_str() {
"Type" => {
let v: i64 = rmp::decode::read_int(&mut cur)?;
version_type = VersionType::from_u8(v as u8);
}
"V2Obj" => {
if version_type != VersionType::Object {
skip_msgp_value(&mut cur)?;
continue;
}
let mut first = [0u8; 1];
cur.read_exact(&mut first)?;
if first[0] == 0xc0 {
return Ok(None);
}
let mut prepend = PrependByteReader {
byte: Some(first[0]),
inner: &mut cur,
};
let mut obj_fields = rmp::decode::read_map_len(&mut prepend)?;
let mut data_dir: Option<Uuid> = None;
while obj_fields > 0 {
obj_fields -= 1;
let obj_key_len = rmp::decode::read_str_len(&mut prepend)? as usize;
let mut obj_key_buf = vec![0u8; obj_key_len];
prepend.read_exact(&mut obj_key_buf)?;
let obj_key = String::from_utf8(obj_key_buf)?;
if obj_key == "DDir" {
let bin_len = rmp::decode::read_bin_len(&mut prepend)? as usize;
if bin_len != 16 {
return Err(Error::other(format!("DDir must be 16 bytes, got {bin_len}")));
}
let mut raw = [0u8; 16];
prepend.read_exact(&mut raw)?;
let id = Uuid::from_bytes(raw);
data_dir = if id.is_nil() { None } else { Some(id) };
break;
}
skip_msgp_value(&mut prepend)?;
}
return Ok(data_dir);
}
_ => {
skip_msgp_value(&mut cur)?;
}
}
}
Ok(None)
}
pub fn valid(&self) -> bool {
if !self.version_type.valid() {
return false;
@@ -367,6 +438,9 @@ impl FileMetaVersion {
// decode_data_dir_from_meta reads data_dir from meta TODO: directly parse only data_dir from meta buf, msg.skip
pub fn decode_data_dir_from_meta(buf: &[u8]) -> Result<Option<Uuid>> {
if let Ok(data_dir) = Self::decode_data_dir_from_v2_object(buf) {
return Ok(data_dir);
}
Ok(Self::try_from(buf)?.get_data_dir())
}
@@ -3213,4 +3287,31 @@ mod tests {
assert_eq!(fi.mod_time, Some(sample_mod_time()));
assert_eq!(fi.metadata.get("x-rustfs-test").map(String::as_str), Some("gone"));
}
#[test]
fn decode_data_dir_from_meta_extracts_v2_object_fast_path() {
let data_dir = Uuid::new_v4();
let version = FileMetaVersion {
version_type: VersionType::Object,
object: Some(MetaObject {
version_id: Some(Uuid::new_v4()),
data_dir: Some(data_dir),
erasure_algorithm: ErasureAlgo::ReedSolomon,
erasure_m: 2,
erasure_n: 4,
erasure_block_size: 1024 * 1024,
erasure_index: 1,
erasure_dist: vec![1, 2, 3, 4, 5, 6],
bitrot_checksum_algo: ChecksumAlgo::HighwayHash,
size: 64 * 1024,
mod_time: Some(OffsetDateTime::now_utc()),
..Default::default()
}),
..Default::default()
};
let encoded = version.marshal_msg().expect("marshal");
let decoded = FileMetaVersion::decode_data_dir_from_meta(&encoded).expect("decode data_dir");
assert_eq!(decoded, Some(data_dir));
}
}