mito2/worker/
handle_catchup.rs1use std::sync::Arc;
18
19use common_telemetry::tracing::warn;
20use common_telemetry::{debug, error, info};
21use store_api::logstore::LogStore;
22use store_api::region_engine::{RegionRole, SettableRegionRoleState};
23use store_api::region_request::RegionCatchupRequest;
24use store_api::storage::RegionId;
25
26use crate::error::{self, Result};
27use crate::region::MitoRegion;
28use crate::region::catchup::RegionCatchupTask;
29use crate::region::opener::RegionOpener;
30use crate::request::OptionOutputTx;
31#[cfg(feature = "hdfs-object-store")]
32use crate::temp_file_sweeper;
33use crate::wal::entry_distributor::WalEntryReceiver;
34use crate::worker::RegionWorkerLoop;
35
36impl<S: LogStore> RegionWorkerLoop<S> {
37 pub(crate) async fn handle_catchup_request(
38 &mut self,
39 region_id: RegionId,
40 request: RegionCatchupRequest,
41 entry_receiver: Option<WalEntryReceiver>,
42 sender: OptionOutputTx,
43 ) {
44 let Some(region) = self.regions.get_region(region_id) else {
45 sender.send(Err(error::RegionNotFoundSnafu { region_id }.build()));
46 return;
47 };
48
49 if region.is_writable() {
50 debug!("Region {region_id} is writable, skip catchup");
51 sender.send(Ok(0));
52 return;
53 }
54
55 if self.catchup_regions.is_region_exists(region_id) {
56 warn!("Region {region_id} under catching up");
57 sender.send(Err(error::RegionBusySnafu { region_id }.build()));
58 return;
59 }
60
61 let region = match self.reopen_region_if_needed(region).await {
63 Ok(region) => region,
64 Err(e) => {
65 sender.send(Err(e));
66 return;
67 }
68 };
69
70 self.catchup_regions.insert_region(region_id);
71 let catchup_regions = self.catchup_regions.clone();
72 let wal = self.wal.clone();
73 let allow_stale_entries = self.config.allow_stale_entries;
74 #[cfg(feature = "hdfs-object-store")]
75 let sweep_older_than = self.config.gc.unknown_file_lingering_time;
76 #[cfg(feature = "hdfs-object-store")]
77 let sweep_limiter = self.sweep_limiter.clone();
78 let node_lease = self.node_lease.clone();
79 common_runtime::spawn_global(async move {
80 let mut task = RegionCatchupTask::new(region.clone(), wal, allow_stale_entries)
81 .with_entry_receiver(entry_receiver)
82 .with_expected_last_entry_id(request.entry_id)
83 .with_replay_checkpoint_entry_id(request.checkpoint.map(|c| c.entry_id));
84
85 match task.run().await {
86 Ok(_) => {
87 if request.set_writable {
88 node_lease.seed(®ion.manifest_ctx.leader_deadline);
91 region.set_role(RegionRole::Leader);
92 match region
94 .set_role_state_gracefully(SettableRegionRoleState::Leader)
95 .await
96 {
97 Ok(()) => {
98 #[cfg(feature = "hdfs-object-store")]
99 temp_file_sweeper::spawn_region_sweep(
100 region.access_layer(),
101 region_id,
102 sweep_older_than,
103 sweep_limiter,
104 );
105 }
106 Err(err) => {
107 error!(err; "Failed to set region {region_id} to leader");
108 }
109 }
110 }
111 catchup_regions.remove_region(region_id);
112 sender.send(Ok(0));
113 }
114 Err(err) => {
115 error!(err; "Failed to catchup region {region_id}");
116 catchup_regions.remove_region(region_id);
117 sender.send(Err(err));
118 }
119 }
120 });
121 }
122
123 pub(crate) async fn reopen_region(
125 &mut self,
126 region: &Arc<MitoRegion>,
127 ) -> Result<Arc<MitoRegion>> {
128 let region_id = region.region_id;
129 let manifest_version = region.manifest_ctx.manifest_version().await;
130 let flushed_entry_id = region.version_control.current().last_entry_id;
131 info!(
132 "Reopening the region: {region_id}, manifest version: {manifest_version}, flushed entry id: {flushed_entry_id}"
133 );
134 let reopened_region = RegionOpener::new(
135 region_id,
136 region.table_dir(),
137 region.access_layer.path_type(),
138 self.node_lease.clone(),
139 self.memtable_builder_provider.clone(),
140 self.object_store_manager.clone(),
141 self.purge_scheduler.clone(),
142 self.puffin_manager_factory.clone(),
143 self.intermediate_manager.clone(),
144 self.time_provider.clone(),
145 self.file_ref_manager.clone(),
146 self.partition_expr_fetcher.clone(),
147 )
148 .cache(Some(self.cache_manager.clone()))
149 .series_index_store(self.series_index_store.clone())
150 .series_index_purger(self.series_index_purger.clone())
151 .hook(self.plugins.get())
152 .options(region.version().options.clone())?
153 .skip_wal_replay(true)
154 .open(&self.config, &self.wal)
155 .await?;
156 debug_assert!(!reopened_region.is_writable());
157 reopened_region
159 .version_control
160 .update_last_sequence(region.version_control.current().last_sequence);
161 self.register_region(reopened_region.clone());
162 if let Some(state) = &self.series_index_task_state {
163 state.wake();
164 }
165
166 Ok(reopened_region)
167 }
168
169 async fn reopen_region_if_needed(
170 &mut self,
171 region: Arc<MitoRegion>,
172 ) -> Result<Arc<MitoRegion>> {
173 let version = region.version();
176 let is_empty_memtable = version.memtables.is_empty();
177
178 let region = if !is_empty_memtable || region.manifest_ctx.has_update().await? {
180 if !is_empty_memtable {
181 warn!(
182 "Region {} memtables is not empty, which should not happen, manifest version: {}, last entry id: {}",
183 region.region_id,
184 region.manifest_ctx.manifest_version().await,
185 region.version_control.current().last_entry_id
186 );
187 }
188 self.reopen_region(®ion).await?
189 } else {
190 region
191 };
192
193 Ok(region)
194 }
195}