1use std::collections::{HashMap, HashSet};
16use std::time::Instant;
17
18use common_telemetry::info;
19use store_api::storage::RegionId;
20
21use crate::error::Result;
22use crate::gc::scheduler::GcScheduler;
23
24#[derive(Debug, Clone)]
26pub(crate) struct RegionGcInfo {
27 pub(crate) last_gc_time: Instant,
29 pub(crate) last_full_listing_time: Option<Instant>,
31}
32
33pub(crate) type RegionGcTracker = HashMap<RegionId, RegionGcInfo>;
35
36impl GcScheduler {
37 pub(crate) async fn cleanup_tracker_if_needed(&self) -> Result<()> {
40 let last_cleanup = *self.last_tracker_cleanup.lock().await;
41 let now = Instant::now();
42
43 if now.saturating_duration_since(last_cleanup) < self.config.tracker_cleanup_interval {
45 return Ok(());
46 }
47
48 info!("Starting region GC tracker cleanup");
49 let cleanup_start = Instant::now();
50
51 let table_to_region_stats = self.ctx.get_table_to_region_stats().await?;
53 let mut current_regions = HashSet::new();
54 for region_stats in table_to_region_stats.values() {
55 for region_stat in region_stats {
56 current_regions.insert(region_stat.id);
57 }
58 }
59
60 let table_reparts = self.ctx.get_table_reparts().await?;
61 for (_, repart) in table_reparts {
62 for src_region in repart.src_to_dst.keys() {
63 current_regions.insert(*src_region);
64 }
65 }
66
67 let mut tracker = self.region_gc_tracker.lock().await;
69 let initial_count = tracker.len();
70 tracker.retain(|region_id, _| current_regions.contains(region_id));
71 let removed_count = initial_count - tracker.len();
72
73 *self.last_tracker_cleanup.lock().await = now;
74
75 info!(
76 "Completed region GC tracker cleanup: removed {} stale entries out of {} total (retained {}). Duration: {:?}",
77 removed_count,
78 initial_count,
79 tracker.len(),
80 cleanup_start.elapsed()
81 );
82
83 Ok(())
84 }
85
86 pub(crate) async fn update_full_listing_time(
87 &self,
88 region_id: RegionId,
89 did_full_listing: bool,
90 ) {
91 let mut gc_tracker = self.region_gc_tracker.lock().await;
92 let now = Instant::now();
93
94 gc_tracker
95 .entry(region_id)
96 .and_modify(|info| {
97 if did_full_listing {
98 info.last_full_listing_time = Some(now);
99 }
100 info.last_gc_time = now;
101 })
102 .or_insert_with(|| RegionGcInfo {
103 last_gc_time: now,
104 last_full_listing_time: Some(now),
106 });
107 }
108}