1use common_telemetry::{debug, error, info};
16use snafu::OptionExt;
17use store_api::region_engine::MitoCopyRegionFromResponse;
18use store_api::storage::{FileId, RegionId};
19
20use crate::error::{InvalidRequestSnafu, MissingManifestSnafu, Result};
21use crate::manifest::action::{RegionEdit, RegionMetaAction, RegionMetaActionList};
22use crate::region::{
23 FileDescriptor, MitoRegionRef, RegionFileCopier, RegionLeaderState, RegionMetadataLoader,
24};
25use crate::request::{
26 BackgroundNotify, CopyRegionFromFinished, CopyRegionFromRequest, WorkerRequest,
27};
28use crate::sst::file::FileMeta;
29use crate::sst::location::region_dir_from_table_dir;
30use crate::worker::{RegionWorkerLoop, WorkerRequestWithTime};
31
32impl<S> RegionWorkerLoop<S> {
33 pub(crate) fn handle_copy_region_from_request(&mut self, request: CopyRegionFromRequest) {
34 let region_id = request.region_id;
35 let source_region_id = request.source_region_id;
36 let sender = request.sender;
37 let region = match self.regions.writable_non_staging_region(region_id) {
38 Ok(region) => region,
39 Err(e) => {
40 let _ = sender.send(Err(e));
41 return;
42 }
43 };
44
45 let same_table = source_region_id.table_id() == region_id.table_id();
46 if !same_table {
47 let _ = sender.send(
48 InvalidRequestSnafu {
49 region_id,
50 reason: format!("Source and target regions must be from the same table, source_region_id: {source_region_id}, target_region_id: {region_id}"),
51 }
52 .fail(),
53 );
54 return;
55 }
56 if source_region_id == region_id {
57 let _ = sender.send(
58 InvalidRequestSnafu {
59 region_id,
60 reason: format!("Source and target regions must be different, source_region_id: {source_region_id}, target_region_id: {region_id}"),
61 }
62 .fail(),
63 );
64 return;
65 }
66
67 let region_metadata_loader = RegionMetadataLoader::new(
68 self.config.clone(),
69 self.object_store_manager.clone(),
70 self.node_lease.is_enabled(),
71 );
72 let worker_sender = self.sender.clone();
73
74 common_runtime::spawn_global(async move {
75 let (region_edit, source_file_ids) = match Self::copy_region_from(
76 ®ion,
77 region_metadata_loader,
78 source_region_id,
79 region_id,
80 request.parallelism.max(1),
81 )
82 .await
83 {
84 Ok(region_files) => region_files,
85 Err(e) => {
86 let _ = sender.send(Err(e));
87 return;
88 }
89 };
90
91 match region_edit {
92 Some(region_edit) => {
93 if let Err(e) = worker_sender
94 .send(WorkerRequestWithTime::new(WorkerRequest::Background {
95 region_id,
96 notify: BackgroundNotify::CopyRegionFromFinished(
97 CopyRegionFromFinished {
98 region_id,
99 edit: region_edit,
100 sender,
101 },
102 ),
103 }))
104 .await
105 {
106 error!(e; "Failed to send copy region from finished notification to worker, region_id: {}", region_id);
107 }
108 }
109 None => {
110 let _ = sender.send(Ok(MitoCopyRegionFromResponse {
111 copied_file_ids: source_file_ids,
112 }));
113 }
114 }
115 });
116 }
117
118 pub(crate) fn handle_copy_region_from_finished(&mut self, request: CopyRegionFromFinished) {
119 let region_id = request.region_id;
120 let sender = request.sender;
121 let region = match self.regions.writable_region(region_id) {
122 Ok(region) => region,
123 Err(e) => {
124 let _ = sender.send(Err(e));
125 return;
126 }
127 };
128
129 let copied_file_ids = request
130 .edit
131 .files_to_add
132 .iter()
133 .map(|file_meta| file_meta.file_id)
134 .collect();
135
136 region
137 .version_control
138 .apply_edit(Some(request.edit), &[], region.file_purger.clone());
139
140 let _ = sender.send(Ok(MitoCopyRegionFromResponse { copied_file_ids }));
141 }
142
143 async fn copy_region_from(
147 region: &MitoRegionRef,
148 region_metadata_loader: RegionMetadataLoader,
149 source_region_id: RegionId,
150 target_region_id: RegionId,
151 parallelism: usize,
152 ) -> Result<(Option<RegionEdit>, Vec<FileId>)> {
153 let table_dir = region.table_dir();
154 let path_type = region.path_type();
155 let region_dir = region_dir_from_table_dir(table_dir, source_region_id, path_type);
156 info!(
157 "Loading source region manifest from region dir: {region_dir}, target region: {target_region_id}"
158 );
159 let source_region_manifest = region_metadata_loader
160 .load_manifest(®ion_dir, ®ion.version().options)
161 .await?
162 .context(MissingManifestSnafu {
163 region_id: source_region_id,
164 })?;
165 let mut new_file_metas = vec![];
166 let target_region_manifest = region.manifest_ctx.manifest().await;
167 let source_file_ids = source_region_manifest
168 .files
169 .keys()
170 .cloned()
171 .collect::<Vec<_>>();
172 debug!(
173 "source region files: {:?}, source region id: {}",
174 source_region_manifest.files, source_region_id
175 );
176 for (file_id, file_meta) in &source_region_manifest.files {
177 if !target_region_manifest.files.contains_key(file_id) {
178 new_file_metas.push(remap_copied_file_meta(file_meta, target_region_id));
179 }
180 }
181 if new_file_metas.is_empty() {
182 return Ok((None, source_file_ids));
183 }
184
185 let file_descriptors = new_file_metas
186 .iter()
187 .flat_map(file_descriptors_for_meta)
188 .collect();
189 debug!("File descriptors to copy: {:?}", file_descriptors);
190 let copier = RegionFileCopier::new(region.access_layer());
191 copier
193 .copy_files(
194 source_region_id,
195 target_region_id,
196 file_descriptors,
197 parallelism,
198 )
199 .await?;
200 let edit = RegionEdit {
201 files_to_add: new_file_metas,
202 files_to_remove: vec![],
203 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
204 compaction_time_window: None,
205 flushed_entry_id: None,
206 flushed_sequence: None,
207 committed_sequence: None,
208 };
209 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit.clone()));
210 info!(
211 "Applying {:?} to region {target_region_id}, reason: CopyRegionFrom",
212 edit
213 );
214 let version = region
215 .manifest_ctx
216 .update_manifest(RegionLeaderState::Writable, action_list, false)
217 .await?;
218 info!(
219 "Successfully update manifest version to {version}, region: {target_region_id}, reason: CopyRegionFrom"
220 );
221
222 Ok((Some(edit), source_file_ids))
223 }
224}
225
226fn remap_copied_file_meta(file_meta: &FileMeta, target_region_id: RegionId) -> FileMeta {
227 let mut new_file_meta = file_meta.clone();
228 new_file_meta.region_id = target_region_id;
229 new_file_meta.preserve_row_sequence = false;
239 new_file_meta.sequence = None;
240 new_file_meta
241}
242
243fn file_descriptors_for_meta(file_meta: &FileMeta) -> Vec<FileDescriptor> {
244 let data = FileDescriptor::Data {
245 file_id: file_meta.file_id,
246 size: file_meta.file_size,
247 };
248 if file_meta.exists_index() {
249 let region_index_id = file_meta.index_id();
250 vec![
251 data,
252 FileDescriptor::Index {
253 file_id: region_index_id.file_id.file_id(),
254 version: region_index_id.version,
255 size: file_meta.index_file_size(),
256 },
257 ]
258 } else {
259 vec![data]
260 }
261}