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
33impl RegionGcInfo {
34 pub(crate) fn new(last_gc_time: Instant) -> Self {
35 Self {
36 last_gc_time,
37 last_full_listing_time: None,
38 }
39 }
40}
41
42pub(crate) type RegionGcTracker = HashMap<RegionId, RegionGcInfo>;
44
45impl GcScheduler {
46 pub(crate) async fn cleanup_tracker_if_needed(&self) -> Result<()> {
49 let mut last_cleanup = *self.last_tracker_cleanup.lock().await;
50 let now = Instant::now();
51
52 if now.duration_since(last_cleanup) < self.config.tracker_cleanup_interval {
54 return Ok(());
55 }
56
57 info!("Starting region GC tracker cleanup");
58 let cleanup_start = Instant::now();
59
60 let table_to_region_stats = self.ctx.get_table_to_region_stats().await?;
62 let mut current_regions = HashSet::new();
63 for region_stats in table_to_region_stats.values() {
64 for region_stat in region_stats {
65 current_regions.insert(region_stat.id);
66 }
67 }
68
69 let mut tracker = self.region_gc_tracker.lock().await;
71 let initial_count = tracker.len();
72 tracker.retain(|region_id, _| current_regions.contains(region_id));
73 let removed_count = initial_count - tracker.len();
74
75 *self.last_tracker_cleanup.lock().await = now;
76
77 info!(
78 "Completed region GC tracker cleanup: removed {} stale entries out of {} total (retained {}). Duration: {:?}",
79 removed_count,
80 initial_count,
81 tracker.len(),
82 cleanup_start.elapsed()
83 );
84
85 Ok(())
86 }
87
88 pub(crate) async fn should_use_full_listing(&self, region_id: RegionId) -> bool {
90 let gc_tracker = self.region_gc_tracker.lock().await;
91 let now = Instant::now();
92
93 if let Some(gc_info) = gc_tracker.get(®ion_id) {
94 if let Some(last_full_listing) = gc_info.last_full_listing_time {
95 let elapsed = now.duration_since(last_full_listing);
96 elapsed >= self.config.full_file_listing_interval
97 } else {
98 true
100 }
101 } else {
102 true
104 }
105 }
106
107 pub(crate) async fn update_full_listing_time(
108 &self,
109 region_id: RegionId,
110 did_full_listing: bool,
111 ) {
112 let mut gc_tracker = self.region_gc_tracker.lock().await;
113 let now = Instant::now();
114
115 gc_tracker
116 .entry(region_id)
117 .and_modify(|info| {
118 if did_full_listing {
119 info.last_full_listing_time = Some(now);
120 }
121 info.last_gc_time = now;
122 })
123 .or_insert_with(|| RegionGcInfo {
124 last_gc_time: now,
125 last_full_listing_time: Some(now),
127 });
128 }
129}