1use std::sync::Arc;
16
17use common_datasource::compression::CompressionType;
18use common_telemetry::{debug, warn};
19use futures::TryStreamExt;
20use futures::future::try_join_all;
21use object_store::{Buffer, Entry, ErrorKind, Lister, ObjectStore};
22use snafu::{ResultExt, ensure};
23use store_api::ManifestVersion;
24use store_api::storage::RegionId;
25use tokio::sync::Semaphore;
26
27use crate::cache::manifest_cache::ManifestCache;
28use crate::error::{
29 CompressObjectSnafu, DecompressObjectSnafu, InvalidScanIndexSnafu, ManifestDeltaNotFoundSnafu,
30 ManifestVersionConflictSnafu, OpenDalSnafu, Result,
31};
32use crate::manifest::storage::size_tracker::Tracker;
33use crate::manifest::storage::utils::{
34 get_from_cache_decoded, sort_manifests, write_and_put_cache,
35};
36use crate::manifest::storage::{
37 FETCH_MANIFEST_PARALLELISM, delta_file, file_compress_type, file_version, gen_path,
38 is_delta_file, list_start_after,
39};
40
41#[derive(Debug, Clone)]
42pub(crate) struct DeltaStorage<T: Tracker> {
43 object_store: ObjectStore,
44 compress_type: CompressionType,
45 path: String,
46 delta_tracker: Arc<T>,
47 manifest_cache: Option<ManifestCache>,
48}
49
50impl<T: Tracker> DeltaStorage<T> {
51 pub(crate) fn new(
52 path: String,
53 object_store: ObjectStore,
54 compress_type: CompressionType,
55 manifest_cache: Option<ManifestCache>,
56 delta_tracker: Arc<T>,
57 ) -> Self {
58 Self {
59 object_store,
60 compress_type,
61 path,
62 delta_tracker,
63 manifest_cache,
64 }
65 }
66
67 pub(crate) fn path(&self) -> &str {
68 &self.path
69 }
70
71 pub(crate) fn object_store(&self) -> &ObjectStore {
72 &self.object_store
73 }
74
75 fn delta_file_path(&self, version: ManifestVersion) -> String {
76 gen_path(&self.path, &delta_file(version), self.compress_type)
77 }
78
79 pub(crate) async fn manifest_lister(
84 &self,
85 start_after: Option<&str>,
86 ) -> Result<Option<Lister>> {
87 let mut builder = self.object_store.lister_with(&self.path);
88 if let Some(s) = start_after {
89 builder = builder.start_after(s);
90 }
91 match builder.await {
92 Ok(streamer) => Ok(Some(streamer)),
93 Err(e) if e.kind() == ErrorKind::NotFound => {
94 debug!("Manifest directory does not exist: {}", self.path);
95 Ok(None)
96 }
97 Err(e) => Err(e).context(OpenDalSnafu)?,
98 }
99 }
100
101 pub async fn get_paths<F, R>(&self, start_after: Option<&str>, mut filter: F) -> Result<Vec<R>>
108 where
109 F: FnMut(Entry) -> Option<R>,
110 {
111 let Some(streamer) = self.manifest_lister(start_after).await? else {
112 return Ok(vec![]);
113 };
114
115 streamer
116 .try_filter_map(|e| {
117 let result = filter(e);
118 async { Ok(result) }
119 })
120 .try_collect::<Vec<_>>()
121 .await
122 .context(OpenDalSnafu)
123 }
124
125 pub async fn scan(
127 &self,
128 start: ManifestVersion,
129 end: ManifestVersion,
130 ) -> Result<Vec<(ManifestVersion, Entry)>> {
131 ensure!(start <= end, InvalidScanIndexSnafu { start, end });
132
133 let start_after = (start > 0).then(|| list_start_after(&self.path, start));
136 let mut total_paths = 0;
137 let mut entries: Vec<(ManifestVersion, Entry)> = self
138 .get_paths(start_after.as_deref(), |entry| {
139 total_paths += 1;
140 let file_name = entry.name();
141 if is_delta_file(file_name) {
142 let version = file_version(file_name);
143 if start <= version && version < end {
144 return Some((version, entry));
145 }
146 }
147 None
148 })
149 .await?;
150
151 sort_manifests(&mut entries);
152
153 common_telemetry::debug!(
154 "DeltaStorage get paths for {}, start: {}, end: {}, start_after: {:?}, total_paths: {}, entries: {}",
155 self.path,
156 start,
157 end,
158 start_after,
159 total_paths,
160 entries.len()
161 );
162
163 Ok(entries)
164 }
165
166 pub async fn fetch_manifests_strict_from(
170 &self,
171 start_version: ManifestVersion,
172 end_version: ManifestVersion,
173 region_id: RegionId,
174 ) -> Result<Vec<(ManifestVersion, Vec<u8>)>> {
175 let mut manifests = self.fetch_manifests(start_version, end_version).await?;
176 let start_index = manifests.iter().position(|(v, _)| *v == start_version);
177 debug!(
178 "Fetches manifests in range [{},{}), start_index: {:?}, region_id: {}, manifests: {:?}",
179 start_version,
180 end_version,
181 start_index,
182 region_id,
183 manifests.iter().map(|(v, _)| *v).collect::<Vec<_>>()
184 );
185 if let Some(start_index) = start_index {
186 Ok(manifests.split_off(start_index))
187 } else {
188 Ok(vec![])
189 }
190 }
191
192 pub(crate) async fn fetch_manifests_from_entries(
194 &self,
195 entries: Vec<(ManifestVersion, Entry)>,
196 ) -> Result<Vec<(ManifestVersion, Vec<u8>)>> {
197 if entries.is_empty() {
198 return Ok(vec![]);
199 }
200
201 let semaphore = Semaphore::new(FETCH_MANIFEST_PARALLELISM);
203
204 let tasks = entries.iter().map(|(v, entry)| async {
205 let _permit = semaphore.acquire().await.unwrap();
207
208 let cache_key = entry.path();
209 let compress_type = file_compress_type(entry.name());
210 if let Some(data) = get_from_cache_decoded(self.manifest_cache.as_ref(), cache_key, compress_type).await {
212 let size = entry.metadata().content_length();
213 if size > 0 {
214 self.delta_tracker.record(*v, size);
215 } else {
216 match self.object_store.stat(entry.path()).await {
217 Ok(metadata) => self.delta_tracker.record(*v, metadata.content_length()),
218 Err(error) => warn!(error; "Failed to account for cached manifest delta {}", entry.path()),
219 }
220 }
221 return Ok((*v, data));
222 }
223
224 let bytes = match self.object_store.read(entry.path()).await {
226 Ok(bytes) => bytes,
227 Err(error) if error.kind() == ErrorKind::NotFound => {
228 return Err(error).context(ManifestDeltaNotFoundSnafu {
229 version: *v,
230 path: entry.path(),
231 });
232 }
233 Err(error) => return Err(error).context(OpenDalSnafu),
234 };
235 self.delta_tracker.record(*v, bytes.len() as u64);
236 let data = compress_type
237 .decode(bytes.clone())
238 .await
239 .context(DecompressObjectSnafu {
240 compress_type,
241 path: entry.path(),
242 })?;
243
244 if let Some(cache) = &self.manifest_cache {
246 cache.put_file(cache_key.to_string(), bytes.to_vec()).await;
247 }
248
249 Ok((*v, data))
250 });
251
252 try_join_all(tasks).await
253 }
254
255 pub async fn fetch_manifests(
260 &self,
261 start_version: ManifestVersion,
262 end_version: ManifestVersion,
263 ) -> Result<Vec<(ManifestVersion, Vec<u8>)>> {
264 let manifests = self.scan(start_version, end_version).await?;
265 self.fetch_manifests_from_entries(manifests).await
266 }
267
268 pub async fn save(&mut self, version: ManifestVersion, bytes: &[u8]) -> Result<()> {
270 let path = self.delta_file_path(version);
271 debug!("Save log to manifest storage, version: {}", version);
272 let data = self
273 .compress_type
274 .encode(bytes)
275 .await
276 .context(CompressObjectSnafu {
277 compress_type: self.compress_type,
278 path: &path,
279 })?;
280 let delta_size = data.len();
281
282 write_and_put_cache(
283 &self.object_store,
284 self.manifest_cache.as_ref(),
285 &path,
286 data,
287 )
288 .await?;
289 self.delta_tracker.record(version, delta_size as u64);
290
291 Ok(())
292 }
293
294 pub(crate) async fn save_conditional(
300 &mut self,
301 version: ManifestVersion,
302 bytes: &[u8],
303 ) -> Result<()> {
304 let path = gen_path(&self.path, &delta_file(version), CompressionType::Gzip);
305 let data = if self.compress_type.is_compressed() {
308 CompressionType::Gzip.encode(bytes).await
309 } else {
310 CompressionType::encode_gzip_stored(bytes).await
311 }
312 .context(CompressObjectSnafu {
313 compress_type: CompressionType::Gzip,
314 path: &path,
315 })?;
316 let data = Buffer::from(data);
317
318 let stored_size = match self
319 .object_store
320 .write_with(&path, data.clone())
321 .if_not_exists(true)
322 .await
323 {
324 Ok(_) => data.len(),
325 Err(error)
326 if matches!(
327 error.kind(),
328 ErrorKind::AlreadyExists | ErrorKind::ConditionNotMatch
329 ) =>
330 {
331 self.ensure_same_delta(version, &path, bytes).await?
332 }
333 Err(error) => return Err(error).context(OpenDalSnafu),
334 };
335
336 self.delta_tracker.record(version, stored_size as u64);
337 if let Some(cache) = &self.manifest_cache {
338 cache.put_file(path, data.to_vec()).await;
339 }
340
341 Ok(())
342 }
343
344 async fn ensure_same_delta(
347 &self,
348 version: ManifestVersion,
349 path: &str,
350 bytes: &[u8],
351 ) -> Result<usize> {
352 let conflict = |reason: String| {
353 ManifestVersionConflictSnafu {
354 version,
355 manifest_dir: &self.path,
356 reason,
357 }
358 .build()
359 };
360 let existing = self
361 .object_store
362 .read(path)
363 .await
364 .map_err(|e| conflict(format!("cannot read the existing delta: {e}")))?;
365 let stored_size = existing.len();
366 let existing = CompressionType::Gzip
367 .decode(existing)
368 .await
369 .map_err(|e| conflict(format!("cannot decode the existing delta: {e}")))?;
370 if existing != bytes {
371 return Err(conflict(
372 "the existing delta has different content".to_string(),
373 ));
374 }
375 Ok(stored_size)
376 }
377}
378
379#[cfg(test)]
380impl<T: Tracker> DeltaStorage<T> {
381 pub fn set_compress_type(&mut self, compress_type: CompressionType) {
382 self.compress_type = compress_type;
383 }
384}