fix(scanner): harden lifecycle and tiering backlog (#3469)

* fix(scanner): expose lifecycle transition backlog

* fix(scanner): expose lifecycle expiry backlog

* fix(scanner): preserve lifecycle backlog states

---------

Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
This commit is contained in:
Henry Guo
2026-06-15 15:01:21 +08:00
committed by GitHub
parent 57260c8314
commit dd6b4c35ad
7 changed files with 578 additions and 49 deletions
+64
View File
@@ -357,6 +357,36 @@ impl ScannerPacingPressureSnapshot {
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScannerLifecycleExpirySnapshot {
#[serde(rename = "current_queue_capacity", default)]
pub current_queue_capacity: u64,
#[serde(rename = "current_queued", default)]
pub current_queued: u64,
#[serde(rename = "current_active", default)]
pub current_active: u64,
#[serde(rename = "current_workers", default)]
pub current_workers: u64,
#[serde(rename = "queue_missed", default)]
pub queue_missed: u64,
#[serde(rename = "scanner_queued", default)]
pub scanner_queued: u64,
#[serde(rename = "scanner_missed", default)]
pub scanner_missed: u64,
}
impl ScannerLifecycleExpirySnapshot {
fn merge(&mut self, other: &Self) {
self.current_queue_capacity = self.current_queue_capacity.saturating_add(other.current_queue_capacity);
self.current_queued = self.current_queued.saturating_add(other.current_queued);
self.current_active = self.current_active.saturating_add(other.current_active);
self.current_workers = self.current_workers.saturating_add(other.current_workers);
self.queue_missed = self.queue_missed.saturating_add(other.queue_missed);
self.scanner_queued = self.scanner_queued.saturating_add(other.scanner_queued);
self.scanner_missed = self.scanner_missed.saturating_add(other.scanner_missed);
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScannerLifecycleTransitionSnapshot {
#[serde(rename = "current_queue_capacity", default)]
@@ -373,6 +403,8 @@ pub struct ScannerLifecycleTransitionSnapshot {
pub queue_send_timeout: u64,
#[serde(rename = "compensation_scheduled", default)]
pub compensation_scheduled: u64,
#[serde(rename = "compensation_pending", default)]
pub compensation_pending: u64,
#[serde(rename = "compensation_running", default)]
pub compensation_running: u64,
#[serde(rename = "scanner_queued", default)]
@@ -394,6 +426,7 @@ impl ScannerLifecycleTransitionSnapshot {
self.queue_full = self.queue_full.saturating_add(other.queue_full);
self.queue_send_timeout = self.queue_send_timeout.saturating_add(other.queue_send_timeout);
self.compensation_scheduled = self.compensation_scheduled.saturating_add(other.compensation_scheduled);
self.compensation_pending = self.compensation_pending.saturating_add(other.compensation_pending);
self.compensation_running = self.compensation_running.saturating_add(other.compensation_running);
self.scanner_queued = self.scanner_queued.saturating_add(other.scanner_queued);
self.scanner_missed = self.scanner_missed.saturating_add(other.scanner_missed);
@@ -524,6 +557,8 @@ pub struct ScannerMetrics {
pub partial_cycles_by_source: Vec<ScannerSourceCycleSnapshot>,
#[serde(rename = "pacing_pressure", default)]
pub pacing_pressure: ScannerPacingPressureSnapshot,
#[serde(rename = "lifecycle_expiry", default)]
pub lifecycle_expiry: ScannerLifecycleExpirySnapshot,
#[serde(rename = "lifecycle_transition", default)]
pub lifecycle_transition: ScannerLifecycleTransitionSnapshot,
#[serde(rename = "maintenance_control", default)]
@@ -599,6 +634,7 @@ impl ScannerMetrics {
self.active_scan_paths = self.active_scan_paths.saturating_add(other.active_scan_paths);
self.oldest_active_path_age_seconds = self.oldest_active_path_age_seconds.max(other.oldest_active_path_age_seconds);
self.pacing_pressure.merge(&other.pacing_pressure);
self.lifecycle_expiry.merge(&other.lifecycle_expiry);
self.lifecycle_transition.merge(&other.lifecycle_transition);
self.maintenance_control.merge(&other.maintenance_control);
self.current_set_scan_concurrency_limit = self
@@ -1354,6 +1390,15 @@ mod tests {
current_cycle_lifecycle_transition_actions: 3,
last_cycle_lifecycle_expiry_actions: 5,
last_cycle_lifecycle_transition_actions: 7,
lifecycle_expiry: ScannerLifecycleExpirySnapshot {
current_queue_capacity: 8,
current_queued: 2,
current_active: 1,
current_workers: 2,
queue_missed: 3,
scanner_queued: 5,
scanner_missed: 2,
},
lifecycle_transition: ScannerLifecycleTransitionSnapshot {
current_queue_capacity: 8,
current_queued: 2,
@@ -1362,6 +1407,7 @@ mod tests {
queue_full: 3,
queue_send_timeout: 1,
compensation_scheduled: 1,
compensation_pending: 2,
compensation_running: 1,
scanner_queued: 5,
scanner_missed: 2,
@@ -1377,6 +1423,15 @@ mod tests {
current_cycle_lifecycle_transition_actions: 13,
last_cycle_lifecycle_expiry_actions: 17,
last_cycle_lifecycle_transition_actions: 19,
lifecycle_expiry: ScannerLifecycleExpirySnapshot {
current_queue_capacity: 4,
current_queued: 3,
current_active: 2,
current_workers: 1,
queue_missed: 2,
scanner_queued: 6,
scanner_missed: 4,
},
lifecycle_transition: ScannerLifecycleTransitionSnapshot {
current_queue_capacity: 4,
current_queued: 3,
@@ -1385,6 +1440,7 @@ mod tests {
queue_full: 2,
queue_send_timeout: 3,
compensation_scheduled: 4,
compensation_pending: 3,
compensation_running: 0,
scanner_queued: 6,
scanner_missed: 4,
@@ -1394,6 +1450,13 @@ mod tests {
..Default::default()
});
assert_eq!(scanner.lifecycle_expiry.current_queue_capacity, 12);
assert_eq!(scanner.lifecycle_expiry.current_queued, 5);
assert_eq!(scanner.lifecycle_expiry.current_active, 3);
assert_eq!(scanner.lifecycle_expiry.current_workers, 3);
assert_eq!(scanner.lifecycle_expiry.queue_missed, 5);
assert_eq!(scanner.lifecycle_expiry.scanner_queued, 11);
assert_eq!(scanner.lifecycle_expiry.scanner_missed, 6);
assert_eq!(scanner.lifecycle_transition.current_queue_capacity, 12);
assert_eq!(scanner.lifecycle_transition.current_queued, 5);
assert_eq!(scanner.lifecycle_transition.current_active, 3);
@@ -1401,6 +1464,7 @@ mod tests {
assert_eq!(scanner.lifecycle_transition.queue_full, 5);
assert_eq!(scanner.lifecycle_transition.queue_send_timeout, 4);
assert_eq!(scanner.lifecycle_transition.compensation_scheduled, 5);
assert_eq!(scanner.lifecycle_transition.compensation_pending, 5);
assert_eq!(scanner.lifecycle_transition.compensation_running, 1);
assert_eq!(scanner.lifecycle_transition.scanner_queued, 11);
assert_eq!(scanner.lifecycle_transition.scanner_missed, 6);