feat(metrics): expose deferred usage freshness (#6449)

This commit is contained in:
cxymds
2026-08-23 19:31:01 +08:00
committed by GitHub
parent b2e60be647
commit a8e4b67d99
8 changed files with 331 additions and 25 deletions
+75
View File
@@ -302,10 +302,28 @@ pub struct ScannerUsageFreshnessSnapshot {
pub last_usage_save_result: String,
#[serde(rename = "last_usage_save_result_code", default)]
pub last_usage_save_result_code: u64,
#[serde(rename = "last_durable_success_unix_secs", default)]
pub last_durable_success_unix_secs: u64,
#[serde(rename = "last_publication_unix_secs", default)]
pub last_publication_unix_secs: u64,
#[serde(rename = "last_publication_state", default)]
pub last_publication_state: String,
#[serde(rename = "last_publication_reason", default)]
pub last_publication_reason: String,
#[serde(rename = "deferred_pending", default)]
pub deferred_pending: bool,
#[serde(rename = "deferred_total", default)]
pub deferred_total: u64,
#[serde(rename = "last_deferred_unix_secs", default)]
pub last_deferred_unix_secs: u64,
#[serde(rename = "last_deferred_reason", default)]
pub last_deferred_reason: String,
}
impl ScannerUsageFreshnessSnapshot {
fn merge(&mut self, other: &Self) {
let self_deferred_state_at = self.last_deferred_unix_secs.max(self.last_durable_success_unix_secs);
let other_deferred_state_at = other.last_deferred_unix_secs.max(other.last_durable_success_unix_secs);
self.dirty_pending_buckets = self.dirty_pending_buckets.saturating_add(other.dirty_pending_buckets);
self.last_dirty_mark_unix_secs = self.last_dirty_mark_unix_secs.max(other.last_dirty_mark_unix_secs);
self.last_dirty_clear_unix_secs = self.last_dirty_clear_unix_secs.max(other.last_dirty_clear_unix_secs);
@@ -318,6 +336,22 @@ impl ScannerUsageFreshnessSnapshot {
self.last_usage_save_result = other.last_usage_save_result.clone();
self.last_usage_save_result_code = other.last_usage_save_result_code;
}
self.last_durable_success_unix_secs = self.last_durable_success_unix_secs.max(other.last_durable_success_unix_secs);
if other.last_publication_unix_secs > self.last_publication_unix_secs {
self.last_publication_unix_secs = other.last_publication_unix_secs;
self.last_publication_state = other.last_publication_state.clone();
self.last_publication_reason = other.last_publication_reason.clone();
}
self.deferred_total = self.deferred_total.saturating_add(other.deferred_total);
if other_deferred_state_at > self_deferred_state_at {
self.deferred_pending = other.deferred_pending;
} else if other_deferred_state_at == self_deferred_state_at {
self.deferred_pending |= other.deferred_pending;
}
if other.last_deferred_unix_secs > self.last_deferred_unix_secs {
self.last_deferred_unix_secs = other.last_deferred_unix_secs;
self.last_deferred_reason = other.last_deferred_reason.clone();
}
}
}
@@ -1566,6 +1600,47 @@ mod tests {
assert_eq!(explicit_false.current_cycle_active, Some(false));
}
#[test]
fn usage_freshness_deferred_fields_are_backward_compatible_and_merge() {
let legacy: ScannerUsageFreshnessSnapshot = serde_json::from_value(serde_json::json!({
"dirty_pending_buckets": 2,
"last_usage_save_result": "success"
}))
.expect("legacy usage freshness should decode");
assert!(!legacy.deferred_pending);
assert_eq!(legacy.deferred_total, 0);
let mut merged = ScannerUsageFreshnessSnapshot::default();
merged.merge(&ScannerUsageFreshnessSnapshot {
deferred_pending: true,
deferred_total: 2,
last_deferred_unix_secs: 20,
last_deferred_reason: "data_movement".to_string(),
..Default::default()
});
merged.merge(&ScannerUsageFreshnessSnapshot {
deferred_total: 1,
last_deferred_unix_secs: 10,
last_deferred_reason: "older".to_string(),
..Default::default()
});
assert!(merged.deferred_pending);
assert_eq!(merged.deferred_total, 3);
assert_eq!(merged.last_deferred_unix_secs, 20);
assert_eq!(merged.last_deferred_reason, "data_movement");
merged.merge(&ScannerUsageFreshnessSnapshot {
deferred_pending: false,
last_durable_success_unix_secs: 30,
last_publication_unix_secs: 30,
last_publication_state: "success".to_string(),
..Default::default()
});
assert!(!merged.deferred_pending);
assert_eq!(merged.last_durable_success_unix_secs, 30);
assert_eq!(merged.last_publication_state, "success");
}
#[test]
fn scanner_metrics_merge_prefers_an_active_first_cycle() {
let collected_at = Timestamp::now();