mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 20:06:37 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d33e2fdcf1 |
@@ -585,12 +585,9 @@ impl VersionsHistogram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replication statistics for a single target.
|
/// Replication statistics for a single target
|
||||||
///
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
/// Renamed from `ReplicationStats`; serde field names are preserved
|
pub struct ReplicationStats {
|
||||||
/// byte-identically to maintain wire compatibility with existing snapshots.
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct ReplicationTargetUsage {
|
|
||||||
pub pending_size: u64,
|
pub pending_size: u64,
|
||||||
pub replicated_size: u64,
|
pub replicated_size: u64,
|
||||||
pub failed_size: u64,
|
pub failed_size: u64,
|
||||||
@@ -603,7 +600,7 @@ pub struct ReplicationTargetUsage {
|
|||||||
pub replicated_count: u64,
|
pub replicated_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReplicationTargetUsage {
|
impl ReplicationStats {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
let Self {
|
let Self {
|
||||||
pending_size,
|
pending_size,
|
||||||
@@ -639,7 +636,7 @@ impl ReplicationTargetUsage {
|
|||||||
/// Replication statistics for all targets
|
/// Replication statistics for all targets
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct ReplicationAllStats {
|
pub struct ReplicationAllStats {
|
||||||
pub targets: HashMap<String, ReplicationTargetUsage>,
|
pub targets: HashMap<String, ReplicationStats>,
|
||||||
pub replica_size: u64,
|
pub replica_size: u64,
|
||||||
pub replica_count: u64,
|
pub replica_count: u64,
|
||||||
}
|
}
|
||||||
@@ -652,7 +649,7 @@ impl ReplicationAllStats {
|
|||||||
targets,
|
targets,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
*replica_size == 0 && *replica_count == 0 && targets.values().all(ReplicationTargetUsage::is_empty)
|
*replica_size == 0 && *replica_count == 0 && targets.values().all(ReplicationStats::is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated(note = "use is_empty instead")]
|
#[deprecated(note = "use is_empty instead")]
|
||||||
@@ -2469,7 +2466,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replication_stats_empty_checks_every_field() {
|
fn replication_stats_empty_checks_every_field() {
|
||||||
type SetField = fn(&mut ReplicationTargetUsage);
|
type SetField = fn(&mut ReplicationStats);
|
||||||
|
|
||||||
let cases: [(&str, SetField); 10] = [
|
let cases: [(&str, SetField); 10] = [
|
||||||
("pending_size", |stats| stats.pending_size = 1),
|
("pending_size", |stats| stats.pending_size = 1),
|
||||||
@@ -2484,9 +2481,9 @@ mod tests {
|
|||||||
("replicated_count", |stats| stats.replicated_count = 1),
|
("replicated_count", |stats| stats.replicated_count = 1),
|
||||||
];
|
];
|
||||||
|
|
||||||
assert!(ReplicationTargetUsage::default().is_empty());
|
assert!(ReplicationStats::default().is_empty());
|
||||||
for (field, set_nonzero) in cases {
|
for (field, set_nonzero) in cases {
|
||||||
let mut stats = ReplicationTargetUsage::default();
|
let mut stats = ReplicationStats::default();
|
||||||
set_nonzero(&mut stats);
|
set_nonzero(&mut stats);
|
||||||
assert!(!stats.is_empty(), "{field} must make replication stats non-empty");
|
assert!(!stats.is_empty(), "{field} must make replication stats non-empty");
|
||||||
}
|
}
|
||||||
@@ -2517,17 +2514,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let empty_targets = ReplicationAllStats {
|
let empty_targets = ReplicationAllStats {
|
||||||
targets: HashMap::from([("arn:test:empty".to_string(), ReplicationTargetUsage::default())]),
|
targets: HashMap::from([("arn:test:empty".to_string(), ReplicationStats::default())]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert!(empty_targets.is_empty(), "all-empty targets must keep aggregate stats empty");
|
assert!(empty_targets.is_empty(), "all-empty targets must keep aggregate stats empty");
|
||||||
|
|
||||||
let stats = ReplicationAllStats {
|
let stats = ReplicationAllStats {
|
||||||
targets: HashMap::from([
|
targets: HashMap::from([
|
||||||
("arn:test:empty".to_string(), ReplicationTargetUsage::default()),
|
("arn:test:empty".to_string(), ReplicationStats::default()),
|
||||||
(
|
(
|
||||||
"arn:test:non-empty".to_string(),
|
"arn:test:non-empty".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
pending_count: 1,
|
pending_count: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -2568,7 +2565,7 @@ mod tests {
|
|||||||
replication_stats: Some(ReplicationAllStats {
|
replication_stats: Some(ReplicationAllStats {
|
||||||
targets: HashMap::from([(
|
targets: HashMap::from([(
|
||||||
"arn:test:pending".to_string(),
|
"arn:test:pending".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
pending_count: 1,
|
pending_count: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -2717,7 +2714,7 @@ mod tests {
|
|||||||
targets: HashMap::from([
|
targets: HashMap::from([
|
||||||
(
|
(
|
||||||
"arn:self-only".to_string(),
|
"arn:self-only".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
pending_size: 7,
|
pending_size: 7,
|
||||||
pending_count: 1,
|
pending_count: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -2725,7 +2722,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"arn:shared".to_string(),
|
"arn:shared".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
failed_size: 3,
|
failed_size: 3,
|
||||||
failed_count: 1,
|
failed_count: 1,
|
||||||
missed_threshold_size: 2,
|
missed_threshold_size: 2,
|
||||||
@@ -2744,7 +2741,7 @@ mod tests {
|
|||||||
targets: HashMap::from([
|
targets: HashMap::from([
|
||||||
(
|
(
|
||||||
"arn:shared".to_string(),
|
"arn:shared".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
failed_size: 5,
|
failed_size: 5,
|
||||||
failed_count: 2,
|
failed_count: 2,
|
||||||
after_threshold_size: 4,
|
after_threshold_size: 4,
|
||||||
@@ -2754,7 +2751,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"arn:other-only".to_string(),
|
"arn:other-only".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
replicated_size: 11,
|
replicated_size: 11,
|
||||||
replicated_count: 3,
|
replicated_count: 3,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -2996,7 +2993,7 @@ mod tests {
|
|||||||
fn replication_target_deserialization_preserves_large_historical_maps() {
|
fn replication_target_deserialization_preserves_large_historical_maps() {
|
||||||
let mut stats = ReplicationAllStats::default();
|
let mut stats = ReplicationAllStats::default();
|
||||||
for index in 0..=1024 {
|
for index in 0..=1024 {
|
||||||
stats.targets.insert(format!("target-{index}"), ReplicationTargetUsage::default());
|
stats.targets.insert(format!("target-{index}"), ReplicationStats::default());
|
||||||
}
|
}
|
||||||
let encoded = rmp_serde::to_vec_named(&stats).expect("large replication target fixture should encode");
|
let encoded = rmp_serde::to_vec_named(&stats).expect("large replication target fixture should encode");
|
||||||
let decoded = rmp_serde::from_slice::<ReplicationAllStats>(&encoded)
|
let decoded = rmp_serde::from_slice::<ReplicationAllStats>(&encoded)
|
||||||
@@ -3005,42 +3002,6 @@ mod tests {
|
|||||||
assert_eq!(decoded.targets.len(), stats.targets.len());
|
assert_eq!(decoded.targets.len(), stats.targets.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Round-trip test: encoding a [`ReplicationTargetUsage`] and decoding it back
|
|
||||||
/// must produce the exact same value. This guards against accidental serde
|
|
||||||
/// field-name drift during the `ReplicationStats` -> `ReplicationTargetUsage`
|
|
||||||
/// rename. Wire-level field names are the serialized Rust field identifiers,
|
|
||||||
/// which must remain byte-identical.
|
|
||||||
#[test]
|
|
||||||
fn replication_target_usage_rmp_round_trip() {
|
|
||||||
let original = ReplicationTargetUsage {
|
|
||||||
pending_size: 100,
|
|
||||||
replicated_size: 2_000,
|
|
||||||
failed_size: 50,
|
|
||||||
failed_count: 3,
|
|
||||||
pending_count: 7,
|
|
||||||
missed_threshold_size: 11,
|
|
||||||
after_threshold_size: 22,
|
|
||||||
missed_threshold_count: 1,
|
|
||||||
after_threshold_count: 2,
|
|
||||||
replicated_count: 99,
|
|
||||||
};
|
|
||||||
|
|
||||||
let buf = rmp_serde::to_vec_named(&original).expect("encode ReplicationTargetUsage to msgpack");
|
|
||||||
let decoded: ReplicationTargetUsage =
|
|
||||||
rmp_serde::from_slice(&buf).expect("decode ReplicationTargetUsage from msgpack");
|
|
||||||
assert_eq!(original, decoded, "round-trip through rmp must preserve every field");
|
|
||||||
|
|
||||||
// Also verify that encoding as an unnamed sequence and then decoding
|
|
||||||
// with named fields produces the correct mapping (this catches reordering).
|
|
||||||
let named_buf = rmp_serde::to_vec_named(&original).expect("re-encode for field-name pinning");
|
|
||||||
// Spot-check that known field names appear in the named encoding.
|
|
||||||
let named_str = String::from_utf8_lossy(&named_buf);
|
|
||||||
assert!(named_str.contains("pending_size"), "field 'pending_size' must survive the rename");
|
|
||||||
assert!(named_str.contains("replicated_size"), "field 'replicated_size' must survive the rename");
|
|
||||||
assert!(named_str.contains("missed_threshold_size"), "field 'missed_threshold_size' must survive the rename");
|
|
||||||
assert!(named_str.contains("after_threshold_count"), "field 'after_threshold_count' must survive the rename");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_merge_rejects_noncanonical_histograms_without_mutation() {
|
fn checked_merge_rejects_noncanonical_histograms_without_mutation() {
|
||||||
let mut entry = DataUsageEntry {
|
let mut entry = DataUsageEntry {
|
||||||
|
|||||||
+32
-138
@@ -12,18 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{collections::HashMap, time::Duration};
|
|
||||||
|
|
||||||
use jiff::Timestamp;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::heal_commands::HealResultItem;
|
/// Bitflag helper for service trace categories.
|
||||||
|
///
|
||||||
|
/// Each variant occupies a single bit so that a `TraceType` value can represent
|
||||||
|
/// an arbitrary combination of categories via bitwise OR.
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
|
||||||
pub struct TraceType(u64);
|
pub struct TraceType(u64);
|
||||||
|
|
||||||
impl TraceType {
|
impl TraceType {
|
||||||
// Define some constants
|
|
||||||
pub const OS: TraceType = TraceType(1 << 0);
|
pub const OS: TraceType = TraceType(1 << 0);
|
||||||
pub const STORAGE: TraceType = TraceType(1 << 1);
|
pub const STORAGE: TraceType = TraceType(1 << 1);
|
||||||
pub const S3: TraceType = TraceType(1 << 2);
|
pub const S3: TraceType = TraceType(1 << 2);
|
||||||
@@ -40,15 +38,13 @@ impl TraceType {
|
|||||||
pub const FTP: TraceType = TraceType(1 << 13);
|
pub const FTP: TraceType = TraceType(1 << 13);
|
||||||
pub const ILM: TraceType = TraceType(1 << 14);
|
pub const ILM: TraceType = TraceType(1 << 14);
|
||||||
|
|
||||||
// MetricsAll must be last.
|
/// All trace categories combined. Must be updated when adding new variants.
|
||||||
pub const ALL: TraceType = TraceType((1 << 15) - 1);
|
pub const ALL: TraceType = TraceType((1 << 15) - 1);
|
||||||
|
|
||||||
pub fn new(t: u64) -> Self {
|
pub fn new(t: u64) -> Self {
|
||||||
Self(t)
|
Self(t)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TraceType {
|
|
||||||
pub fn contains(&self, x: &TraceType) -> bool {
|
pub fn contains(&self, x: &TraceType) -> bool {
|
||||||
(self.0 & x.0) == x.0
|
(self.0 & x.0) == x.0
|
||||||
}
|
}
|
||||||
@@ -76,140 +72,38 @@ impl TraceType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceInfo {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
trace_type: u64,
|
|
||||||
#[serde(rename = "nodename")]
|
|
||||||
node_name: String,
|
|
||||||
#[serde(rename = "funcname")]
|
|
||||||
func_name: String,
|
|
||||||
#[serde(rename = "time")]
|
|
||||||
time: Timestamp,
|
|
||||||
#[serde(rename = "path")]
|
|
||||||
path: String,
|
|
||||||
#[serde(rename = "dur")]
|
|
||||||
duration: Duration,
|
|
||||||
#[serde(rename = "bytes", skip_serializing_if = "Option::is_none")]
|
|
||||||
bytes: Option<i64>,
|
|
||||||
#[serde(rename = "msg", skip_serializing_if = "Option::is_none")]
|
|
||||||
message: Option<String>,
|
|
||||||
#[serde(rename = "error", skip_serializing_if = "Option::is_none")]
|
|
||||||
error: Option<String>,
|
|
||||||
#[serde(rename = "custom", skip_serializing_if = "Option::is_none")]
|
|
||||||
custom: Option<HashMap<String, String>>,
|
|
||||||
#[serde(rename = "http", skip_serializing_if = "Option::is_none")]
|
|
||||||
http: Option<TraceHTTPStats>,
|
|
||||||
#[serde(rename = "healResult", skip_serializing_if = "Option::is_none")]
|
|
||||||
heal_result: Option<HealResultItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TraceInfo {
|
|
||||||
pub fn mask(&self) -> u64 {
|
|
||||||
TraceType::new(self.trace_type).mask()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceInfoLegacy {
|
|
||||||
trace_info: TraceInfo,
|
|
||||||
#[serde(rename = "request")]
|
|
||||||
req_info: Option<TraceRequestInfo>,
|
|
||||||
#[serde(rename = "response")]
|
|
||||||
resp_info: Option<TraceResponseInfo>,
|
|
||||||
#[serde(rename = "stats")]
|
|
||||||
call_stats: Option<TraceCallStats>,
|
|
||||||
#[serde(rename = "storageStats")]
|
|
||||||
storage_stats: Option<StorageStats>,
|
|
||||||
#[serde(rename = "osStats")]
|
|
||||||
os_stats: Option<OSStats>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct StorageStats {
|
|
||||||
path: String,
|
|
||||||
duration: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct OSStats {
|
|
||||||
path: String,
|
|
||||||
duration: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceHTTPStats {
|
|
||||||
req_info: TraceRequestInfo,
|
|
||||||
resp_info: TraceResponseInfo,
|
|
||||||
call_stats: TraceCallStats,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceCallStats {
|
|
||||||
input_bytes: i32,
|
|
||||||
output_bytes: i32,
|
|
||||||
latency: Duration,
|
|
||||||
time_to_first_byte: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceRequestInfo {
|
|
||||||
time: Timestamp,
|
|
||||||
proto: String,
|
|
||||||
method: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
path: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
raw_query: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
headers: Option<HashMap<String, String>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
body: Option<Vec<u8>>,
|
|
||||||
client: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct TraceResponseInfo {
|
|
||||||
time: Timestamp,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
headers: Option<HashMap<String, String>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
body: Option<Vec<u8>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
status_code: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trace_timestamps_serialize_as_rfc3339_utc() {
|
fn trace_type_contains_and_overlaps() {
|
||||||
let timestamp = Timestamp::constant(1_700_000_000, 123_456_000);
|
let mut combined = TraceType::default();
|
||||||
let trace = TraceInfo {
|
combined.merge(&TraceType::S3);
|
||||||
time: timestamp,
|
combined.merge(&TraceType::HEALING);
|
||||||
http: Some(TraceHTTPStats {
|
|
||||||
req_info: TraceRequestInfo {
|
|
||||||
time: timestamp,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
resp_info: TraceResponseInfo {
|
|
||||||
time: timestamp,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = serde_json::to_value(trace).expect("trace should serialize");
|
assert!(combined.contains(&TraceType::S3));
|
||||||
assert_eq!(value["time"], "2023-11-14T22:13:20.123456Z");
|
assert!(combined.contains(&TraceType::HEALING));
|
||||||
assert_eq!(value["http"]["req_info"]["time"], "2023-11-14T22:13:20.123456Z");
|
assert!(!combined.contains(&TraceType::SCANNER));
|
||||||
assert_eq!(value["http"]["resp_info"]["time"], "2023-11-14T22:13:20.123456Z");
|
assert!(combined.overlaps(&TraceType::S3));
|
||||||
let trace: TraceInfo = serde_json::from_value(value).expect("trace should deserialize");
|
assert!(combined.overlaps(&TraceType::HEALING));
|
||||||
assert_eq!(trace.time, timestamp);
|
assert!(!combined.overlaps(&TraceType::SCANNER));
|
||||||
let http = trace.http.expect("http trace should deserialize");
|
}
|
||||||
assert_eq!(http.req_info.time, timestamp);
|
|
||||||
assert_eq!(http.resp_info.time, timestamp);
|
#[test]
|
||||||
|
fn trace_type_set_if() {
|
||||||
|
let mut tt = TraceType::default();
|
||||||
|
tt.set_if(true, &TraceType::OS);
|
||||||
|
tt.set_if(false, &TraceType::S3);
|
||||||
|
assert!(tt.contains(&TraceType::OS));
|
||||||
|
assert!(!tt.contains(&TraceType::S3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trace_type_single_type() {
|
||||||
|
assert!(TraceType::S3.single_type());
|
||||||
|
let mut combined = TraceType::S3;
|
||||||
|
combined.merge(&TraceType::HEALING);
|
||||||
|
assert!(!combined.single_type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use super::persistence::DataUsageCacheLoadAttempt;
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::storage_api::scanner_io::{HTTPRangeSpec, ObjectIO};
|
use crate::storage_api::scanner_io::{HTTPRangeSpec, ObjectIO};
|
||||||
use crate::{ScannerGetObjectReader, ScannerPutObjReader};
|
use crate::{ScannerGetObjectReader, ScannerPutObjReader};
|
||||||
use rustfs_data_usage::{ReplicationAllStats, ReplicationTargetUsage};
|
use rustfs_data_usage::{ReplicationAllStats, ReplicationStats};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
@@ -1636,7 +1636,7 @@ fn size_recursive_prunes_empty_and_preserves_threshold_replication_stats() {
|
|||||||
replication_stats: Some(ReplicationAllStats {
|
replication_stats: Some(ReplicationAllStats {
|
||||||
targets: HashMap::from([(
|
targets: HashMap::from([(
|
||||||
"arn:test:threshold".to_string(),
|
"arn:test:threshold".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
after_threshold_count: 1,
|
after_threshold_count: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use rustfs_data_usage::{ReplicationAllStats, ReplicationTargetUsage};
|
use rustfs_data_usage::{ReplicationAllStats, ReplicationStats};
|
||||||
|
|
||||||
const TEST_PLAN_DIGEST: DataUsageScanPlanDigest = DataUsageScanPlanDigest([7; 32]);
|
const TEST_PLAN_DIGEST: DataUsageScanPlanDigest = DataUsageScanPlanDigest([7; 32]);
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ fn completed_data_usage_info_flattens_nested_bucket_entries() {
|
|||||||
replication_stats: Some(ReplicationAllStats {
|
replication_stats: Some(ReplicationAllStats {
|
||||||
targets: HashMap::from([(
|
targets: HashMap::from([(
|
||||||
"arn:target".to_string(),
|
"arn:target".to_string(),
|
||||||
ReplicationTargetUsage {
|
ReplicationStats {
|
||||||
replicated_size: 2048,
|
replicated_size: 2048,
|
||||||
replicated_count: 2,
|
replicated_count: 2,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
Reference in New Issue
Block a user