mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-31 01:09:23 +00:00
feat(scanner): expose pacing pressure status (#3319)
* feat(scanner): expose pacing pressure status * fix(scanner): preserve merged pause pressure * fix(scanner): default missing primary pressure --------- Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
This commit is contained in:
@@ -128,6 +128,81 @@ pub struct ScannerSourceCycleSnapshot {
|
||||
pub cycles: u64,
|
||||
}
|
||||
|
||||
const SCANNER_PRIMARY_PRESSURE_NONE: &str = "none";
|
||||
|
||||
fn default_scanner_primary_pressure() -> String {
|
||||
SCANNER_PRIMARY_PRESSURE_NONE.to_string()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ScannerPacingPressureSnapshot {
|
||||
#[serde(rename = "primary_pressure", default = "default_scanner_primary_pressure")]
|
||||
pub primary_pressure: String,
|
||||
#[serde(rename = "current_queued_scans", default)]
|
||||
pub current_queued_scans: u64,
|
||||
#[serde(rename = "current_active_scans", default)]
|
||||
pub current_active_scans: u64,
|
||||
#[serde(rename = "last_cycle_budget_limited", default)]
|
||||
pub last_cycle_budget_limited: bool,
|
||||
#[serde(rename = "last_cycle_pause_observed", default)]
|
||||
pub last_cycle_pause_observed: bool,
|
||||
#[serde(rename = "last_cycle_throttle_sleep_ratio", default)]
|
||||
pub last_cycle_throttle_sleep_ratio: f64,
|
||||
#[serde(rename = "last_cycle_yield_ratio", default)]
|
||||
pub last_cycle_yield_ratio: f64,
|
||||
#[serde(rename = "last_cycle_total_pause_ratio", default)]
|
||||
pub last_cycle_total_pause_ratio: f64,
|
||||
}
|
||||
|
||||
impl Default for ScannerPacingPressureSnapshot {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
primary_pressure: default_scanner_primary_pressure(),
|
||||
current_queued_scans: 0,
|
||||
current_active_scans: 0,
|
||||
last_cycle_budget_limited: false,
|
||||
last_cycle_pause_observed: false,
|
||||
last_cycle_throttle_sleep_ratio: 0.0,
|
||||
last_cycle_yield_ratio: 0.0,
|
||||
last_cycle_total_pause_ratio: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScannerPacingPressureSnapshot {
|
||||
fn refresh_primary_pressure(&mut self) {
|
||||
self.primary_pressure = if self.current_queued_scans > 0 {
|
||||
"queued_scans"
|
||||
} else if self.last_cycle_budget_limited {
|
||||
"cycle_budget"
|
||||
} else if self.last_cycle_pause_observed {
|
||||
"throttle_pause"
|
||||
} else if self.current_active_scans > 0 {
|
||||
"active_scans"
|
||||
} else {
|
||||
SCANNER_PRIMARY_PRESSURE_NONE
|
||||
}
|
||||
.to_string();
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &Self) {
|
||||
let pause_observed = self.last_cycle_pause_observed
|
||||
|| self.primary_pressure == "throttle_pause"
|
||||
|| other.last_cycle_pause_observed
|
||||
|| other.primary_pressure == "throttle_pause";
|
||||
self.current_queued_scans = self.current_queued_scans.saturating_add(other.current_queued_scans);
|
||||
self.current_active_scans = self.current_active_scans.saturating_add(other.current_active_scans);
|
||||
self.last_cycle_budget_limited |= other.last_cycle_budget_limited;
|
||||
self.last_cycle_pause_observed = pause_observed;
|
||||
self.last_cycle_throttle_sleep_ratio = self
|
||||
.last_cycle_throttle_sleep_ratio
|
||||
.max(other.last_cycle_throttle_sleep_ratio);
|
||||
self.last_cycle_yield_ratio = self.last_cycle_yield_ratio.max(other.last_cycle_yield_ratio);
|
||||
self.last_cycle_total_pause_ratio = self.last_cycle_total_pause_ratio.max(other.last_cycle_total_pause_ratio);
|
||||
self.refresh_primary_pressure();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ScannerMetrics {
|
||||
#[serde(rename = "collected")]
|
||||
@@ -154,6 +229,8 @@ pub struct ScannerMetrics {
|
||||
pub last_cycle_partial_source_code: u64,
|
||||
#[serde(rename = "partial_cycles_by_source", default)]
|
||||
pub partial_cycles_by_source: Vec<ScannerSourceCycleSnapshot>,
|
||||
#[serde(rename = "pacing_pressure", default)]
|
||||
pub pacing_pressure: ScannerPacingPressureSnapshot,
|
||||
}
|
||||
|
||||
impl ScannerMetrics {
|
||||
@@ -165,6 +242,8 @@ impl ScannerMetrics {
|
||||
self.last_cycle_partial_source_code = other.last_cycle_partial_source_code;
|
||||
}
|
||||
|
||||
self.pacing_pressure.merge(&other.pacing_pressure);
|
||||
|
||||
if self.ongoing_buckets < other.ongoing_buckets {
|
||||
self.ongoing_buckets = other.ongoing_buckets;
|
||||
}
|
||||
@@ -711,6 +790,16 @@ mod tests {
|
||||
collected_at,
|
||||
last_cycle_partial_source: "usage".to_string(),
|
||||
last_cycle_partial_source_code: 1,
|
||||
pacing_pressure: ScannerPacingPressureSnapshot {
|
||||
primary_pressure: "cycle_budget".to_string(),
|
||||
current_active_scans: 1,
|
||||
last_cycle_budget_limited: true,
|
||||
last_cycle_pause_observed: true,
|
||||
last_cycle_throttle_sleep_ratio: 0.1,
|
||||
last_cycle_yield_ratio: 0.2,
|
||||
last_cycle_total_pause_ratio: 0.3,
|
||||
..Default::default()
|
||||
},
|
||||
partial_cycles_by_source: vec![ScannerSourceCycleSnapshot {
|
||||
source: "usage".to_string(),
|
||||
cycles: 1,
|
||||
@@ -722,6 +811,15 @@ mod tests {
|
||||
collected_at: collected_at + chrono::Duration::seconds(1),
|
||||
last_cycle_partial_source: "lifecycle".to_string(),
|
||||
last_cycle_partial_source_code: 2,
|
||||
pacing_pressure: ScannerPacingPressureSnapshot {
|
||||
primary_pressure: "queued_scans".to_string(),
|
||||
current_queued_scans: 4,
|
||||
current_active_scans: 2,
|
||||
last_cycle_throttle_sleep_ratio: 0.4,
|
||||
last_cycle_yield_ratio: 0.1,
|
||||
last_cycle_total_pause_ratio: 0.5,
|
||||
..Default::default()
|
||||
},
|
||||
partial_cycles_by_source: vec![
|
||||
ScannerSourceCycleSnapshot {
|
||||
source: "usage".to_string(),
|
||||
@@ -737,6 +835,14 @@ mod tests {
|
||||
|
||||
assert_eq!(scanner.last_cycle_partial_source, "lifecycle");
|
||||
assert_eq!(scanner.last_cycle_partial_source_code, 2);
|
||||
assert_eq!(scanner.pacing_pressure.primary_pressure, "queued_scans");
|
||||
assert_eq!(scanner.pacing_pressure.current_queued_scans, 4);
|
||||
assert_eq!(scanner.pacing_pressure.current_active_scans, 3);
|
||||
assert!(scanner.pacing_pressure.last_cycle_budget_limited);
|
||||
assert!(scanner.pacing_pressure.last_cycle_pause_observed);
|
||||
assert_eq!(scanner.pacing_pressure.last_cycle_throttle_sleep_ratio, 0.4);
|
||||
assert_eq!(scanner.pacing_pressure.last_cycle_yield_ratio, 0.2);
|
||||
assert_eq!(scanner.pacing_pressure.last_cycle_total_pause_ratio, 0.5);
|
||||
assert_eq!(
|
||||
scanner.partial_cycles_by_source,
|
||||
vec![
|
||||
@@ -751,4 +857,40 @@ mod tests {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scanner_metrics_merge_preserves_pause_pressure_without_duration() {
|
||||
let collected_at = Utc::now();
|
||||
let mut scanner = ScannerMetrics {
|
||||
collected_at,
|
||||
pacing_pressure: ScannerPacingPressureSnapshot {
|
||||
primary_pressure: "throttle_pause".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
scanner.merge(&ScannerMetrics {
|
||||
collected_at: collected_at + chrono::Duration::seconds(1),
|
||||
pacing_pressure: ScannerPacingPressureSnapshot::default(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
assert_eq!(scanner.pacing_pressure.primary_pressure, "throttle_pause");
|
||||
assert!(scanner.pacing_pressure.last_cycle_pause_observed);
|
||||
assert_eq!(scanner.pacing_pressure.last_cycle_total_pause_ratio, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scanner_metrics_deserializes_missing_primary_pressure_as_none() {
|
||||
let pacing_pressure: ScannerPacingPressureSnapshot = serde_json::from_str(
|
||||
r#"{
|
||||
"current_active_scans": 1
|
||||
}"#,
|
||||
)
|
||||
.expect("deserialize partial scanner pacing pressure");
|
||||
|
||||
assert_eq!(pacing_pressure.primary_pressure, "none");
|
||||
assert_eq!(pacing_pressure.current_active_scans, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user