mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-29 08:27:06 +00:00
perf(ecstore): use AHashMap for FileInfo metadata fields (#6738)
This commit is contained in:
@@ -51,6 +51,9 @@ s3s = { workspace = true, features = ["minio"] }
|
||||
regex.workspace = true
|
||||
arc-swap.workspace = true
|
||||
|
||||
# High-performance hashing
|
||||
ahash = { workspace = true, features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true, features = ["html_reports"] }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{Error, ReplicationState, ReplicationStatusType, Result, TRANSITION_COMPLETE, VersionPurgeStatusType};
|
||||
use ahash::AHashMap;
|
||||
use bytes::Bytes;
|
||||
use rmp_serde::Serializer;
|
||||
use rustfs_utils::HashAlgorithm;
|
||||
@@ -67,7 +68,7 @@ pub struct ObjectPartInfo {
|
||||
// Index holds the index of the part in the erasure coding
|
||||
pub index: Option<Bytes>,
|
||||
// Checksums holds checksums of the part
|
||||
pub checksums: Option<HashMap<String, String>>,
|
||||
pub checksums: Option<AHashMap<String, String>>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
@@ -268,7 +269,7 @@ pub struct FileInfo {
|
||||
pub mode: Option<u32>,
|
||||
// WrittenByVersion is the unix time stamp of the version that created this version of the object
|
||||
pub written_by_version: Option<u64>,
|
||||
pub metadata: HashMap<String, String>,
|
||||
pub metadata: AHashMap<String, String>,
|
||||
pub parts: Vec<ObjectPartInfo>,
|
||||
pub erasure: ErasureInfo,
|
||||
// MarkDeleted marks this version as deleted
|
||||
@@ -301,7 +302,7 @@ fn is_sensitive_metadata_key(key: &str) -> bool {
|
||||
.any(|prefix| starts_with_ignore_ascii_case(key, prefix))
|
||||
}
|
||||
|
||||
struct RedactedMetadata<'a>(&'a HashMap<String, String>);
|
||||
struct RedactedMetadata<'a>(&'a AHashMap<String, String>);
|
||||
|
||||
impl std::fmt::Debug for RedactedMetadata<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@@ -425,7 +426,7 @@ struct FileInfoMapDef {
|
||||
size: i64,
|
||||
mode: Option<u32>,
|
||||
written_by_version: Option<u64>,
|
||||
metadata: HashMap<String, String>,
|
||||
metadata: AHashMap<String, String>,
|
||||
parts: Vec<ObjectPartInfo>,
|
||||
erasure: ErasureInfo,
|
||||
mark_deleted: bool,
|
||||
@@ -1079,7 +1080,7 @@ impl FileInfo {
|
||||
mod_time: Option<OffsetDateTime>,
|
||||
actual_size: i64,
|
||||
index: Option<Bytes>,
|
||||
checksums: Option<HashMap<String, String>>,
|
||||
checksums: Option<AHashMap<String, String>>,
|
||||
) {
|
||||
let part = ObjectPartInfo {
|
||||
etag,
|
||||
@@ -1457,7 +1458,7 @@ pub fn parse_restore_obj_status(restore_hdr: &str) -> Result<RestoreStatus> {
|
||||
Err(Error::other(ERR_RESTORE_HDR_MALFORMED))
|
||||
}
|
||||
|
||||
pub fn is_restored_object_on_disk(meta: &HashMap<String, String>) -> bool {
|
||||
pub fn is_restored_object_on_disk<S: std::hash::BuildHasher>(meta: &HashMap<String, String, S>) -> bool {
|
||||
if let Some(restore_hdr) = meta.get(X_AMZ_RESTORE.as_str())
|
||||
&& let Ok(restore_status) = parse_restore_obj_status(restore_hdr)
|
||||
{
|
||||
@@ -2135,7 +2136,10 @@ mod tests {
|
||||
-1_000_000i64..=1_000_000i64,
|
||||
optional_timestamp_strategy(),
|
||||
proptest::option::of(bytes_strategy(16)),
|
||||
proptest::option::of(hash_map(small_string_strategy(), small_string_strategy(), 0..=3)),
|
||||
proptest::option::of(
|
||||
hash_map(small_string_strategy(), small_string_strategy(), 0..=3)
|
||||
.prop_map(|m| m.into_iter().collect::<AHashMap<String, String>>()),
|
||||
),
|
||||
proptest::option::of(small_string_strategy()),
|
||||
)
|
||||
.prop_map(|(etag, number, size, actual_size, mod_time, index, checksums, error)| ObjectPartInfo {
|
||||
@@ -2170,7 +2174,8 @@ mod tests {
|
||||
-1_000_000i64..=1_000_000i64,
|
||||
proptest::option::of(any::<u32>()),
|
||||
proptest::option::of(any::<u64>()),
|
||||
hash_map(small_string_strategy(), small_string_strategy(), 0..=4),
|
||||
hash_map(small_string_strategy(), small_string_strategy(), 0..=4)
|
||||
.prop_map(|m| m.into_iter().collect::<AHashMap<String, String>>()),
|
||||
vec(object_part_info_strategy(), 0..=3),
|
||||
erasure_info_strategy(),
|
||||
any::<bool>(),
|
||||
|
||||
@@ -174,10 +174,10 @@ fn valid_target_delete_marker_version(arn: &str, version_id: &str) -> bool {
|
||||
/// included in the quorum hash, so such a divergence does surface — but as a
|
||||
/// quorum failure on an otherwise healthy object, which is not a state worth
|
||||
/// reaching. Merge the RPC metadata carrier instead, and only ever insert.
|
||||
fn persist_target_delete_marker_versions(
|
||||
fn persist_target_delete_marker_versions<S: std::hash::BuildHasher>(
|
||||
meta_sys: &mut HashMap<String, Vec<u8>>,
|
||||
versions: &HashMap<String, String>,
|
||||
transport_metadata: &HashMap<String, String>,
|
||||
transport_metadata: &HashMap<String, String, S>,
|
||||
) {
|
||||
let mut bounded = BTreeMap::new();
|
||||
// A corrupt carrier means the dual internal prefixes disagreed. Do not merge
|
||||
|
||||
@@ -181,7 +181,7 @@ mod tests {
|
||||
data_dir: Some(data_dir),
|
||||
size: 64 * 1024,
|
||||
mod_time: Some(OffsetDateTime::now_utc()),
|
||||
metadata,
|
||||
metadata: metadata.into_iter().collect(),
|
||||
erasure: ErasureInfo {
|
||||
algorithm: ErasureAlgo::ReedSolomon.to_string(),
|
||||
data_blocks: 4,
|
||||
|
||||
@@ -26,7 +26,7 @@ use super::msgp_decode::{
|
||||
PrependByteReader, prealloc_hint, read_exact_vec, read_nil_or_array_len, read_nil_or_map_len, skip_msgp_value,
|
||||
};
|
||||
use super::*;
|
||||
use crate::{ChecksumInfo, TransitionVersionState};
|
||||
use crate::{AHashMap, ChecksumInfo, TransitionVersionState};
|
||||
use rustfs_utils::HashAlgorithm;
|
||||
use rustfs_utils::http::{
|
||||
RUSTFS_INTERNAL_PREFIX, SUFFIX_CRC, SUFFIX_FREE_VERSION, SUFFIX_INLINE_DATA, SUFFIX_PART_CHECKSUMS, SUFFIX_PURGESTATUS,
|
||||
@@ -377,7 +377,7 @@ impl<'a> DerivedInternalMetadata<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
struct UniquePartChecksums(HashMap<String, String>);
|
||||
struct UniquePartChecksums(AHashMap<String, String>);
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for UniquePartChecksums {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
@@ -397,7 +397,7 @@ impl<'de> serde::Deserialize<'de> for UniquePartChecksums {
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut checksums = HashMap::with_capacity(seq.size_hint().unwrap_or_default());
|
||||
let mut checksums = AHashMap::with_capacity(seq.size_hint().unwrap_or_default());
|
||||
while let Some((key, value)) = seq.next_element::<(String, String)>()? {
|
||||
if checksums.insert(key, value).is_some() {
|
||||
return Err(serde::de::Error::custom("duplicate part checksum name"));
|
||||
@@ -1477,7 +1477,7 @@ pub struct MetaObjectV1 {
|
||||
#[serde(rename = "Erasure")]
|
||||
pub erasure: MetaObjectV1Erasure,
|
||||
#[serde(rename = "Meta")]
|
||||
pub meta: HashMap<String, String>,
|
||||
pub meta: AHashMap<String, String>,
|
||||
#[serde(rename = "Parts")]
|
||||
pub parts: Vec<MetaObjectV1Part>,
|
||||
#[serde(rename = "VersionID")]
|
||||
@@ -1543,7 +1543,7 @@ pub struct MetaObjectV1Part {
|
||||
#[serde(rename = "i")]
|
||||
pub index: Option<Bytes>,
|
||||
#[serde(rename = "crc")]
|
||||
pub checksums: Option<HashMap<String, String>>,
|
||||
pub checksums: Option<AHashMap<String, String>>,
|
||||
#[serde(rename = "err")]
|
||||
pub error: Option<String>,
|
||||
}
|
||||
@@ -1887,7 +1887,7 @@ impl MetaObjectV1Part {
|
||||
"i" => self.index = Some(Bytes::from(read_msgp_bin(rd)?)),
|
||||
"crc" => {
|
||||
let len = rmp::decode::read_map_len(rd)? as usize;
|
||||
let mut checksums = HashMap::with_capacity(prealloc_hint(len));
|
||||
let mut checksums = AHashMap::with_capacity(prealloc_hint(len));
|
||||
for _ in 0..len {
|
||||
checksums.insert(read_msgp_string(rd)?, read_msgp_string(rd)?);
|
||||
}
|
||||
@@ -2570,7 +2570,7 @@ impl MetaObject {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let mut metadata = HashMap::with_capacity(self.meta_user.len() + self.meta_sys.len());
|
||||
let mut metadata = AHashMap::with_capacity(self.meta_user.len() + self.meta_sys.len());
|
||||
for (k, v) in &self.meta_user {
|
||||
if k == AMZ_META_UNENCRYPTED_CONTENT_LENGTH || k == AMZ_META_UNENCRYPTED_CONTENT_MD5 {
|
||||
continue;
|
||||
@@ -2861,7 +2861,7 @@ impl From<FileInfo> for MetaObject {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_internal_replication_state(metadata: &HashMap<String, String>) -> Option<ReplicationState> {
|
||||
fn get_internal_replication_state<S: std::hash::BuildHasher>(metadata: &HashMap<String, String, S>) -> Option<ReplicationState> {
|
||||
let mut rs = ReplicationState::default();
|
||||
let mut has = false;
|
||||
|
||||
@@ -2942,7 +2942,7 @@ impl MetaDeleteMarker {
|
||||
}
|
||||
|
||||
pub fn into_fileinfo(&self, volume: &str, path: &str, _all_parts: bool) -> Result<FileInfo> {
|
||||
let metadata = self
|
||||
let metadata: AHashMap<String, String> = self
|
||||
.meta_sys
|
||||
.clone()
|
||||
.into_iter()
|
||||
@@ -5500,10 +5500,10 @@ mod tests {
|
||||
/// entirely, silently dropping the whole legacy body on re-marshal.
|
||||
#[test]
|
||||
fn legacy_version_body_round_trips_through_encode() {
|
||||
let mut meta = HashMap::new();
|
||||
let mut meta = AHashMap::new();
|
||||
meta.insert("content-type".to_string(), "application/octet-stream".to_string());
|
||||
|
||||
let mut crc = HashMap::new();
|
||||
let mut crc = AHashMap::new();
|
||||
crc.insert("crc32c".to_string(), "deadbeef".to_string());
|
||||
|
||||
let legacy = MetaObjectV1 {
|
||||
|
||||
@@ -22,6 +22,12 @@ mod replication;
|
||||
|
||||
pub mod test_data;
|
||||
|
||||
/// High-performance HashMap type alias using ahash instead of SipHash.
|
||||
pub type AHashMap<K, V> = ahash::AHashMap<K, V>;
|
||||
|
||||
/// High-performance HashSet type alias using ahash.
|
||||
pub type AHashSet<K> = ahash::AHashSet<K>;
|
||||
|
||||
pub use error::*;
|
||||
pub use fileinfo::*;
|
||||
pub use filemeta::*;
|
||||
|
||||
@@ -1676,7 +1676,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn metacache_entry_with_mod_time(mod_time: OffsetDateTime, etag: &str) -> MetaCacheEntry {
|
||||
let mut metadata = HashMap::new();
|
||||
let mut metadata = ahash::AHashMap::new();
|
||||
metadata.insert("etag".to_string(), etag.to_string());
|
||||
|
||||
let mut meta = FileMeta::new();
|
||||
@@ -1705,7 +1705,7 @@ mod tests {
|
||||
data_blocks: usize,
|
||||
parity_blocks: usize,
|
||||
) -> MetaCacheEntry {
|
||||
let mut metadata = HashMap::new();
|
||||
let mut metadata = ahash::AHashMap::new();
|
||||
metadata.insert("etag".to_string(), etag.to_string());
|
||||
|
||||
let mut fi = FileInfo::new("object", data_blocks, parity_blocks);
|
||||
@@ -1730,7 +1730,7 @@ mod tests {
|
||||
fn metacache_entry_with_erasure_versions(versions: &[(OffsetDateTime, &str, usize, usize)]) -> MetaCacheEntry {
|
||||
let mut meta = FileMeta::new();
|
||||
for (idx, (mod_time, etag, data_blocks, parity_blocks)) in versions.iter().enumerate() {
|
||||
let mut metadata = HashMap::new();
|
||||
let mut metadata = ahash::AHashMap::new();
|
||||
metadata.insert("etag".to_string(), (*etag).to_string());
|
||||
|
||||
let mut fi = FileInfo::new("object", *data_blocks, *parity_blocks);
|
||||
@@ -1776,7 +1776,7 @@ mod tests {
|
||||
/// Build an entry holding a single object version with an explicit version id
|
||||
/// and mod_time, so a set of these can model DISJOINT per-disk version sets.
|
||||
fn metacache_entry_single_version(version_u128: u128, mod_time: OffsetDateTime, etag: &str) -> MetaCacheEntry {
|
||||
let mut metadata = HashMap::new();
|
||||
let mut metadata = ahash::AHashMap::new();
|
||||
metadata.insert("etag".to_string(), etag.to_string());
|
||||
|
||||
let mut fi = FileInfo::new("object", 4, 2);
|
||||
|
||||
Reference in New Issue
Block a user