1pub(crate) mod cache_size;
18
19pub(crate) mod file_cache;
20pub(crate) mod index;
21pub(crate) mod manifest_cache;
22pub(crate) mod parquet_page;
23pub(crate) mod range_cache;
24#[cfg(test)]
25pub(crate) mod test_util;
26pub(crate) mod write_cache;
27
28use std::sync::{Arc, Weak};
29use std::{io, mem};
30
31use bytes::Bytes;
32use common_base::readable_size::ReadableSize;
33use common_cache::metrics::{CacheSizeMetric, CacheSizeRegistration};
34use common_datasource::compression::CompressionType;
35use common_runtime::Runtime;
36use common_runtime::runtime::RuntimeTrait;
37use common_telemetry::warn;
38use datatypes::arrow::buffer::BooleanBuffer;
39use datatypes::arrow::record_batch::RecordBatch;
40use datatypes::types::json_type::JsonNativeType;
41use datatypes::value::Value;
42use datatypes::vectors::VectorRef;
43use index::bloom_filter_index::{BloomFilterIndexCache, BloomFilterIndexCacheRef};
44use index::result_cache::IndexResultCache;
45use moka::notification::RemovalCause;
46use moka::sync::Cache;
47use object_store::ObjectStore;
48use parquet::arrow::arrow_reader::{RowSelection, RowSelector};
49use parquet::file::metadata::{
50 FileMetaData, PageIndexPolicy, ParquetMetaData, ParquetMetaDataReader, ParquetMetaDataWriter,
51};
52use puffin::puffin_manager::cache::{PuffinMetadataCache, PuffinMetadataCacheRef};
53use smallvec::SmallVec;
54use snafu::{OptionExt, ResultExt};
55use store_api::metadata::{RegionMetadata, RegionMetadataRef};
56use store_api::storage::{ColumnId, ConcreteDataType, FileId, RegionId, TimeSeriesRowSelector};
57pub use write_cache::{WriteCacheUploadStoreWrapper, WriteCacheUploadStoreWrapperRef};
58
59use crate::cache::cache_size::parquet_meta_size;
60use crate::cache::file_cache::{FileType, IndexKey};
61use crate::cache::index::inverted_index::{InvertedIndexCache, InvertedIndexCacheRef};
62use crate::cache::parquet_page::ParquetPageCache;
63use crate::cache::range_cache::RangeScanCacheValue;
64use crate::cache::write_cache::WriteCacheRef;
65use crate::error::{
66 CompressObjectSnafu, DecompressObjectSnafu, InvalidMetadataSnafu, InvalidParquetSnafu,
67 JoinSnafu, ReadParquetSnafu, Result, UnexpectedSnafu, WriteParquetSnafu,
68};
69use crate::memtable::record_batch_estimated_size;
70use crate::metrics::{CACHE_BYTES, CACHE_EVICTION, CACHE_HIT, CACHE_MISS};
71use crate::read::Batch;
72use crate::read::range_cache::RangeScanCacheKey;
73use crate::read::read_columns::JsonTargetTypes;
74use crate::sst::file::{RegionFileId, RegionIndexId};
75use crate::sst::parquet::PARQUET_METADATA_KEY;
76use crate::sst::parquet::read_columns::ParquetReadColumns;
77use crate::sst::parquet::reader::MetadataCacheMetrics;
78
79pub(crate) fn find_shared_page_source<E: std::error::Error + Send + Sync + 'static>(
81 mut error: &io::Error,
82) -> Option<Arc<E>> {
83 loop {
84 let source = error.get_ref()?;
85 if let Some(source) = source.downcast_ref::<Arc<E>>() {
86 return Some(source.clone());
87 }
88 if let Some(nested) = source.downcast_ref::<Arc<io::Error>>() {
89 error = nested;
90 } else {
91 error = source.downcast_ref::<io::Error>()?;
92 }
93 }
94}
95
96const SST_META_TYPE: &str = "sst_meta";
98const SST_META_DECODED_TYPE: &str = "sst_meta_decoded";
100const VECTOR_TYPE: &str = "vector";
102const FILE_TYPE: &str = "file";
104const INDEX_TYPE: &str = "index";
106const SELECTOR_RESULT_TYPE: &str = "selector_result";
108const RANGE_RESULT_TYPE: &str = "range_result";
110const PREFILTER_RESULT_TYPE: &str = "prefilter_result";
112const RANGE_RESULT_CONCAT_MEMORY_LIMIT: ReadableSize = ReadableSize::mb(512);
113const RANGE_RESULT_CONCAT_MEMORY_PERMIT: ReadableSize = ReadableSize::kb(1);
114
115#[derive(Debug)]
116pub(crate) struct RangeResultMemoryLimiter {
117 semaphore: Arc<tokio::sync::Semaphore>,
118 permit_bytes: usize,
119 total_permits: usize,
120 #[cfg(test)]
123 waited_acquires: std::sync::atomic::AtomicUsize,
124}
125
126impl Default for RangeResultMemoryLimiter {
127 fn default() -> Self {
128 Self::new(
129 RANGE_RESULT_CONCAT_MEMORY_LIMIT.as_bytes() as usize,
130 RANGE_RESULT_CONCAT_MEMORY_PERMIT.as_bytes() as usize,
131 )
132 }
133}
134
135impl RangeResultMemoryLimiter {
136 pub(crate) fn new(limit_bytes: usize, permit_bytes: usize) -> Self {
137 let permit_bytes = permit_bytes.max(1);
138 let total_permits = limit_bytes
139 .div_ceil(permit_bytes)
140 .clamp(1, tokio::sync::Semaphore::MAX_PERMITS);
141 Self {
142 semaphore: Arc::new(tokio::sync::Semaphore::new(total_permits)),
143 permit_bytes,
144 total_permits,
145 #[cfg(test)]
146 waited_acquires: std::sync::atomic::AtomicUsize::new(0),
147 }
148 }
149
150 #[cfg(test)]
151 pub(crate) fn permit_bytes(&self) -> usize {
152 self.permit_bytes
153 }
154
155 #[cfg(test)]
156 pub(crate) fn waited_acquires(&self) -> usize {
157 self.waited_acquires
158 .load(std::sync::atomic::Ordering::Acquire)
159 }
160
161 #[cfg(test)]
162 pub(crate) fn available_permits(&self) -> usize {
163 self.semaphore.available_permits()
164 }
165
166 pub(crate) fn try_acquire(&self, bytes: usize) -> Option<tokio::sync::OwnedSemaphorePermit> {
169 let permits = bytes.div_ceil(self.permit_bytes).max(1);
170 if permits > self.total_permits {
171 return None;
172 }
173 self.semaphore
174 .clone()
175 .try_acquire_many_owned(permits.try_into().ok()?)
176 .ok()
177 }
178
179 pub(crate) async fn acquire(&self, bytes: usize) -> Result<tokio::sync::SemaphorePermit<'_>> {
180 let permits = bytes.div_ceil(self.permit_bytes).max(1);
181 if permits > self.total_permits {
182 return UnexpectedSnafu {
183 reason: format!(
184 "range result memory request of {bytes} bytes exceeds limiter capacity of {} bytes",
185 self.total_permits.saturating_mul(self.permit_bytes)
186 ),
187 }
188 .fail();
189 }
190 #[cfg(test)]
193 if self.semaphore.available_permits() < permits {
194 self.waited_acquires
195 .fetch_add(1, std::sync::atomic::Ordering::Release);
196 }
197
198 self.semaphore
199 .acquire_many(permits as u32)
200 .await
201 .map_err(|_| {
202 UnexpectedSnafu {
203 reason: "range result memory limiter is unexpectedly closed",
204 }
205 .build()
206 })
207 }
208}
209
210#[derive(Debug)]
215pub(crate) struct CachedSstMeta {
216 parquet_metadata: Arc<ParquetMetaData>,
217 parquet_metadata_size: usize,
218 region_metadata: RegionMetadataRef,
219 region_metadata_weight: usize,
220 page_index_policy: PageIndexPolicy,
221}
222
223#[derive(Debug)]
229pub(crate) struct CompactSstMeta {
230 encoded_metadata: Bytes,
231 decoded_size: usize,
232 region_metadata: Weak<RegionMetadata>,
233 page_index_policy: PageIndexPolicy,
234 decoded: tokio::sync::Mutex<Weak<CachedSstMeta>>,
235}
236
237#[derive(Debug)]
239pub(crate) struct PreparedSstMeta {
240 pub(crate) compact: Arc<CompactSstMeta>,
241 pub(crate) decoded: Arc<CachedSstMeta>,
242}
243
244impl PreparedSstMeta {
245 pub(crate) fn decoded(&self) -> Arc<CachedSstMeta> {
246 self.decoded.clone()
247 }
248}
249
250#[derive(Debug)]
252pub(crate) enum SstMetaPreparation {
253 Prepared(PreparedSstMeta),
255 DecodedOnly {
257 decoded: Arc<CachedSstMeta>,
258 encoding_error: crate::error::Error,
259 },
260}
261
262impl SstMetaPreparation {
263 pub(crate) fn decoded(&self) -> Arc<CachedSstMeta> {
264 match self {
265 Self::Prepared(metadata) => metadata.decoded(),
266 Self::DecodedOnly { decoded, .. } => decoded.clone(),
267 }
268 }
269}
270
271impl CompactSstMeta {
272 async fn decode(&self, runtime: &Runtime) -> Result<Arc<CachedSstMeta>> {
273 let mut decoded_guard = self.decoded.lock().await;
274 if let Some(decoded) = decoded_guard.upgrade() {
275 return Ok(decoded);
276 }
277
278 let encoded_metadata = self.encoded_metadata.clone();
279 let decoded_size = self.decoded_size;
280 let region_metadata = self.region_metadata.upgrade();
281 let page_index_policy = self.page_index_policy;
282 let decoded = runtime
283 .spawn_blocking(move || {
284 let bytes = zstd::bulk::decompress(&encoded_metadata, decoded_size).context(
285 DecompressObjectSnafu {
286 compress_type: CompressionType::Zstd,
287 path: "cached SST metadata",
288 },
289 )?;
290 let bytes = Bytes::from(bytes);
291 let mut reader = ParquetMetaDataReader::new()
292 .with_column_index_policy(PageIndexPolicy::Skip)
293 .with_offset_index_policy(page_index_policy);
294 reader.try_parse(&bytes).context(ReadParquetSnafu {
295 path: "cached SST metadata",
296 })?;
297 let metadata = reader.finish().context(ReadParquetSnafu {
298 path: "cached SST metadata",
299 })?;
300 CachedSstMeta::try_new_with_page_index_policy(
301 "cached SST metadata",
302 metadata,
303 region_metadata,
304 page_index_policy,
305 )
306 .map(Arc::new)
307 })
308 .await
309 .context(JoinSnafu)??;
310
311 *decoded_guard = Arc::downgrade(&decoded);
312 Ok(decoded)
313 }
314
315 fn satisfies_page_index_policy(&self, requested: PageIndexPolicy) -> bool {
316 satisfies_page_index_policy(self.page_index_policy, requested)
317 }
318}
319
320pub(crate) async fn decode_sst_meta(
322 file_path: &str,
323 parquet_metadata: ParquetMetaData,
324 region_metadata: Option<RegionMetadataRef>,
325 page_index_policy: PageIndexPolicy,
326 runtime: &Runtime,
327) -> Result<Arc<CachedSstMeta>> {
328 let file_path = file_path.to_string();
329 runtime
330 .spawn_blocking(move || {
331 let parquet_metadata = strip_column_indexes(parquet_metadata);
332 CachedSstMeta::try_new_with_page_index_policy(
333 &file_path,
334 parquet_metadata,
335 region_metadata,
336 page_index_policy,
337 )
338 .map(Arc::new)
339 })
340 .await
341 .context(JoinSnafu)?
342}
343
344pub(crate) async fn prepare_sst_meta(
346 file_path: &str,
347 parquet_metadata: ParquetMetaData,
348 region_metadata: Option<RegionMetadataRef>,
349 page_index_policy: PageIndexPolicy,
350 runtime: &Runtime,
351) -> Result<SstMetaPreparation> {
352 let file_path = file_path.to_string();
353 runtime
354 .spawn_blocking(move || {
355 prepare_sst_meta_sync(
356 &file_path,
357 parquet_metadata,
358 region_metadata,
359 page_index_policy,
360 )
361 })
362 .await
363 .context(JoinSnafu)?
364}
365
366pub(crate) fn prepare_sst_meta_sync(
368 file_path: &str,
369 parquet_metadata: ParquetMetaData,
370 region_metadata: Option<RegionMetadataRef>,
371 page_index_policy: PageIndexPolicy,
372) -> Result<SstMetaPreparation> {
373 let parquet_metadata = strip_column_indexes(parquet_metadata);
374 let cache_encoding = encode_compact_sst_meta(file_path, &parquet_metadata);
375 finish_sst_meta_preparation(
376 file_path,
377 parquet_metadata,
378 region_metadata,
379 page_index_policy,
380 cache_encoding,
381 )
382}
383
384fn strip_column_indexes(parquet_metadata: ParquetMetaData) -> ParquetMetaData {
385 let mut builder = parquet_metadata.into_builder();
388 builder.take_column_index();
389 builder.build()
390}
391
392fn encode_compact_sst_meta(
393 file_path: &str,
394 parquet_metadata: &ParquetMetaData,
395) -> Result<(Bytes, usize)> {
396 let mut encoded = Vec::new();
397 ParquetMetaDataWriter::new(&mut encoded, parquet_metadata)
398 .finish()
399 .context(WriteParquetSnafu)?;
400 let decoded_size = encoded.len();
401 let encoded_metadata = zstd::bulk::compress(&encoded, 3).context(CompressObjectSnafu {
402 compress_type: CompressionType::Zstd,
403 path: file_path,
404 })?;
405
406 Ok((
410 Bytes::from(encoded_metadata.into_boxed_slice()),
411 decoded_size,
412 ))
413}
414
415fn finish_sst_meta_preparation(
416 file_path: &str,
417 parquet_metadata: ParquetMetaData,
418 region_metadata: Option<RegionMetadataRef>,
419 page_index_policy: PageIndexPolicy,
420 cache_encoding: Result<(Bytes, usize)>,
421) -> Result<SstMetaPreparation> {
422 let decoded = Arc::new(CachedSstMeta::try_new_with_page_index_policy(
423 file_path,
424 parquet_metadata,
425 region_metadata,
426 page_index_policy,
427 )?);
428 let (encoded_metadata, decoded_size) = match cache_encoding {
429 Ok(encoded) => encoded,
430 Err(encoding_error) => {
431 return Ok(SstMetaPreparation::DecodedOnly {
432 decoded,
433 encoding_error,
434 });
435 }
436 };
437 let compact = Arc::new(CompactSstMeta {
438 encoded_metadata,
439 decoded_size,
440 region_metadata: Arc::downgrade(&decoded.region_metadata),
441 page_index_policy,
442 decoded: tokio::sync::Mutex::new(Arc::downgrade(&decoded)),
443 });
444
445 Ok(SstMetaPreparation::Prepared(PreparedSstMeta {
446 compact,
447 decoded,
448 }))
449}
450
451impl CachedSstMeta {
452 #[cfg(test)]
453 pub(crate) fn try_new(file_path: &str, parquet_metadata: ParquetMetaData) -> Result<Self> {
454 let page_index_policy = infer_loaded_page_index_policy(&parquet_metadata);
455 Self::try_new_with_page_index_policy(file_path, parquet_metadata, None, page_index_policy)
456 }
457
458 pub(crate) fn try_new_with_region_metadata(
459 file_path: &str,
460 parquet_metadata: ParquetMetaData,
461 region_metadata: Option<RegionMetadataRef>,
462 ) -> Result<Self> {
463 let page_index_policy = infer_loaded_page_index_policy(&parquet_metadata);
464 Self::try_new_with_page_index_policy(
465 file_path,
466 parquet_metadata,
467 region_metadata,
468 page_index_policy,
469 )
470 }
471
472 pub(crate) fn try_new_with_page_index_policy(
473 file_path: &str,
474 parquet_metadata: ParquetMetaData,
475 region_metadata: Option<RegionMetadataRef>,
476 page_index_policy: PageIndexPolicy,
477 ) -> Result<Self> {
478 let file_metadata = parquet_metadata.file_metadata();
479 let key_values = file_metadata
480 .key_value_metadata()
481 .context(InvalidParquetSnafu {
482 file: file_path,
483 reason: "missing key value meta",
484 })?;
485 let meta_value = key_values
486 .iter()
487 .find(|kv| kv.key == PARQUET_METADATA_KEY)
488 .with_context(|| InvalidParquetSnafu {
489 file: file_path,
490 reason: format!("key {} not found", PARQUET_METADATA_KEY),
491 })?;
492 let json = meta_value
493 .value
494 .as_ref()
495 .with_context(|| InvalidParquetSnafu {
496 file: file_path,
497 reason: format!("No value for key {}", PARQUET_METADATA_KEY),
498 })?;
499 let region_metadata = match region_metadata {
500 Some(region_metadata) => region_metadata,
501 None => Arc::new(
502 store_api::metadata::RegionMetadata::from_json(json)
503 .context(InvalidMetadataSnafu)?,
504 ),
505 };
506 let region_metadata_weight = region_metadata.estimated_size().max(json.len());
508 let parquet_metadata = Arc::new(strip_region_metadata_from_parquet(parquet_metadata));
509 let parquet_metadata_size = parquet_meta_size(&parquet_metadata);
510
511 Ok(Self {
512 parquet_metadata,
513 parquet_metadata_size,
514 region_metadata,
515 region_metadata_weight,
516 page_index_policy,
517 })
518 }
519
520 pub(crate) fn parquet_metadata(&self) -> Arc<ParquetMetaData> {
521 self.parquet_metadata.clone()
522 }
523
524 pub(crate) fn parquet_metadata_size(&self) -> usize {
526 self.parquet_metadata_size
527 }
528
529 pub(crate) fn region_metadata(&self) -> RegionMetadataRef {
530 self.region_metadata.clone()
531 }
532
533 fn satisfies_page_index_policy(&self, requested: PageIndexPolicy) -> bool {
534 satisfies_page_index_policy(self.page_index_policy, requested)
535 }
536}
537
538fn satisfies_page_index_policy(cached: PageIndexPolicy, requested: PageIndexPolicy) -> bool {
539 match requested {
540 PageIndexPolicy::Skip => true,
541 PageIndexPolicy::Optional => cached != PageIndexPolicy::Skip,
542 PageIndexPolicy::Required => cached == PageIndexPolicy::Required,
543 }
544}
545
546fn infer_loaded_page_index_policy(parquet_metadata: &ParquetMetaData) -> PageIndexPolicy {
547 if parquet_metadata.offset_index().is_some() {
548 PageIndexPolicy::Optional
549 } else {
550 PageIndexPolicy::Skip
551 }
552}
553
554fn strip_region_metadata_from_parquet(parquet_metadata: ParquetMetaData) -> ParquetMetaData {
555 let file_metadata = parquet_metadata.file_metadata();
556 let filtered_key_values = file_metadata.key_value_metadata().and_then(|key_values| {
557 let filtered = key_values
558 .iter()
559 .filter(|kv| kv.key != PARQUET_METADATA_KEY)
560 .cloned()
561 .collect::<Vec<_>>();
562 (!filtered.is_empty()).then_some(filtered)
563 });
564 let stripped_file_metadata = FileMetaData::new(
565 file_metadata.version(),
566 file_metadata.num_rows(),
567 file_metadata.created_by().map(ToString::to_string),
568 filtered_key_values,
569 file_metadata.schema_descr_ptr(),
570 file_metadata.column_orders().cloned(),
571 );
572
573 let mut builder = parquet_metadata.into_builder();
574 let row_groups = builder.take_row_groups();
575 let offset_index = builder.take_offset_index();
576
577 parquet::file::metadata::ParquetMetaDataBuilder::new(stripped_file_metadata)
578 .set_row_groups(row_groups)
579 .set_offset_index(offset_index)
580 .build()
581}
582
583fn removal_cause_str(cause: RemovalCause) -> &'static str {
584 match cause {
585 RemovalCause::Expired => "expired",
586 RemovalCause::Explicit => "explicit",
587 RemovalCause::Replaced => "replaced",
588 RemovalCause::Size => "size",
589 }
590}
591
592#[derive(Debug, Clone, PartialEq, Eq, Hash)]
593pub(crate) struct PrefilterRowSelector {
594 row_count: usize,
595 skip: bool,
596}
597
598impl From<&RowSelector> for PrefilterRowSelector {
603 fn from(selector: &RowSelector) -> Self {
604 Self {
605 row_count: selector.row_count,
606 skip: selector.skip,
607 }
608 }
609}
610
611#[derive(Debug, Clone, PartialEq, Eq, Hash)]
613pub(crate) struct PrefilterKey {
614 file_id: FileId,
615 row_group_idx: u32,
616 row_selection: Option<Arc<Vec<PrefilterRowSelector>>>,
617 schema_version: u64,
618 filter_exprs: SmallVec<[String; 1]>,
619 mem_usage: usize,
620}
621
622impl PrefilterKey {
623 pub(crate) fn row_selection_snapshot(
624 row_selection: Option<&RowSelection>,
625 ) -> Option<Arc<Vec<PrefilterRowSelector>>> {
626 row_selection.map(|selection| {
627 Arc::new(
628 selection
629 .iter()
630 .map(PrefilterRowSelector::from)
631 .collect::<Vec<_>>(),
632 )
633 })
634 }
635
636 pub(crate) fn new(
637 file_id: FileId,
638 row_group_idx: u32,
639 row_selection: Option<Arc<Vec<PrefilterRowSelector>>>,
640 schema_version: u64,
641 filter_exprs: SmallVec<[String; 1]>,
642 ) -> Self {
643 let row_selection_bytes = row_selection
644 .as_ref()
645 .map(|selection| selection.len() * mem::size_of::<PrefilterRowSelector>())
646 .unwrap_or(0);
647 let spilled_expr_bytes = if filter_exprs.spilled() {
648 filter_exprs.capacity() * mem::size_of::<String>()
649 } else {
650 0
651 };
652 let expr_bytes = filter_exprs.iter().map(|s| s.capacity()).sum::<usize>();
653
654 Self {
655 file_id,
656 row_group_idx,
657 row_selection,
658 schema_version,
659 filter_exprs,
660 mem_usage: mem::size_of::<Self>()
661 + row_selection_bytes
662 + spilled_expr_bytes
663 + expr_bytes,
664 }
665 }
666
667 fn mem_usage(&self) -> usize {
668 self.mem_usage
669 }
670}
671
672type PrefilterResultCache = Cache<PrefilterKey, Arc<BooleanBuffer>>;
673
674fn new_prefilter_result_cache(capacity: u64) -> PrefilterResultCache {
675 Cache::builder()
676 .max_capacity(capacity)
677 .weigher(prefilter_result_cache_weight)
678 .eviction_listener(|k, v, cause| {
679 let size = prefilter_result_cache_weight(&k, &v);
680 CACHE_BYTES
681 .with_label_values(&[PREFILTER_RESULT_TYPE])
682 .sub(size.into());
683 CACHE_EVICTION
684 .with_label_values(&[PREFILTER_RESULT_TYPE, removal_cause_str(cause)])
685 .inc();
686 })
687 .build()
688}
689
690fn prefilter_result_cache_weight(k: &PrefilterKey, v: &Arc<BooleanBuffer>) -> u32 {
691 (k.mem_usage() + mem::size_of::<BooleanBuffer>() + v.values().len()) as u32
692}
693
694#[derive(Clone)]
696pub enum CacheStrategy {
697 EnableAll(CacheManagerRef),
700 Compaction(CacheManagerRef),
705 Disabled,
707}
708
709impl CacheStrategy {
710 pub(crate) fn sst_meta_runtime(&self) -> Runtime {
712 match self {
713 CacheStrategy::Compaction(_) => common_runtime::compact_runtime(),
714 CacheStrategy::EnableAll(_) | CacheStrategy::Disabled => {
715 common_runtime::global_runtime()
716 }
717 }
718 }
719
720 pub(crate) fn sst_meta_cache_enabled(&self) -> bool {
722 match self {
723 CacheStrategy::EnableAll(cache_manager) | CacheStrategy::Compaction(cache_manager) => {
724 cache_manager.sst_meta_cache_enabled()
725 }
726 CacheStrategy::Disabled => false,
727 }
728 }
729
730 #[cfg(test)]
731 pub(crate) async fn get_or_load_sst_meta<F>(
732 &self,
733 file_id: RegionFileId,
734 page_index_policy: PageIndexPolicy,
735 load: F,
736 ) -> Result<(Arc<CachedSstMeta>, bool)>
737 where
738 F: std::future::Future<Output = Result<(Arc<CachedSstMeta>, bool)>>,
739 {
740 self.get_or_load_observed_sst_meta(file_id, page_index_policy, async {
741 (load.await, SstMetaResolution::default())
742 })
743 .await
744 .0
745 }
746
747 pub(crate) async fn get_or_load_observed_sst_meta<F>(
749 &self,
750 file_id: RegionFileId,
751 page_index_policy: PageIndexPolicy,
752 load: F,
753 ) -> (Result<(Arc<CachedSstMeta>, bool)>, SstMetaResolution)
754 where
755 F: std::future::Future<Output = (Result<(Arc<CachedSstMeta>, bool)>, SstMetaResolution)>,
756 {
757 let cache = match self {
758 Self::EnableAll(manager) | Self::Compaction(manager) => {
759 manager.sst_decoded_meta_cache.as_ref()
760 }
761 Self::Disabled => None,
762 };
763 let Some(cache) = cache else {
764 return load.await;
765 };
766 let key = SstDecodedMetaKey::new(file_id, page_index_policy);
767 let result = cache
768 .try_get_with(&key, async {
769 let (result, resolution) = load.await;
770 let (metadata, cache_miss) = result.map_err(|source| SstMetaLoadError {
771 source: Arc::new(source),
772 resolution,
773 })?;
774 let value = SstMetaLoadResult::new(metadata, cache_miss, resolution);
775 Ok::<_, SstMetaLoadError>(value)
776 })
777 .await;
778 match result {
779 Ok(value) => (
780 Ok((value.metadata.clone(), value.cache_miss)),
781 value.resolution,
782 ),
783 Err(error) => (
784 Err(error.source.clone()).context(crate::error::LoadSstMetaSnafu),
785 error.resolution,
786 ),
787 }
788 }
789
790 pub(crate) async fn observe_sst_meta(
792 &self,
793 file_id: RegionFileId,
794 page_index_policy: PageIndexPolicy,
795 metrics: &mut MetadataCacheMetrics,
796 ) -> SstMetaObservation {
797 match self {
798 Self::EnableAll(manager) | Self::Compaction(manager) => {
799 manager
800 .observe_sst_meta(file_id, page_index_policy, metrics)
801 .await
802 }
803 Self::Disabled => SstMetaObservation::Miss,
804 }
805 }
806
807 pub(crate) async fn resolve_sst_meta(
809 &self,
810 observation: SstMetaObservation,
811 file_id: RegionFileId,
812 metrics: &mut MetadataCacheMetrics,
813 page_index_policy: PageIndexPolicy,
814 ) -> Option<Arc<CachedSstMeta>> {
815 match self {
816 Self::EnableAll(manager) | Self::Compaction(manager) => {
817 manager
818 .resolve_sst_meta(
819 observation,
820 file_id,
821 metrics,
822 page_index_policy,
823 &self.sst_meta_runtime(),
824 )
825 .await
826 }
827 Self::Disabled => {
828 metrics.cache_miss += 1;
829 None
830 }
831 }
832 }
833
834 pub(crate) async fn get_sst_meta_data_from_mem_cache(
836 &self,
837 file_id: RegionFileId,
838 page_index_policy: PageIndexPolicy,
839 ) -> Option<Arc<CachedSstMeta>> {
840 match self {
841 CacheStrategy::EnableAll(cache_manager) | CacheStrategy::Compaction(cache_manager) => {
842 cache_manager
843 .get_sst_meta_data_from_mem_cache(file_id, page_index_policy)
844 .await
845 }
846 CacheStrategy::Disabled => None,
847 }
848 }
849
850 pub async fn get_parquet_meta_data_from_mem_cache(
852 &self,
853 file_id: RegionFileId,
854 ) -> Option<Arc<ParquetMetaData>> {
855 self.get_sst_meta_data_from_mem_cache(file_id, PageIndexPolicy::Skip)
856 .await
857 .map(|metadata| metadata.parquet_metadata())
858 }
859
860 pub(crate) fn put_compact_sst_meta(
862 &self,
863 file_id: RegionFileId,
864 metadata: Arc<CompactSstMeta>,
865 ) {
866 match self {
867 CacheStrategy::EnableAll(cache_manager) | CacheStrategy::Compaction(cache_manager) => {
868 cache_manager.put_compact_sst_meta(file_id, metadata);
869 }
870 CacheStrategy::Disabled => {}
871 }
872 }
873
874 pub async fn put_parquet_meta_data(
876 &self,
877 file_id: RegionFileId,
878 metadata: Arc<ParquetMetaData>,
879 region_metadata: Option<RegionMetadataRef>,
880 ) {
881 match self {
882 CacheStrategy::EnableAll(cache_manager) | CacheStrategy::Compaction(cache_manager) => {
883 cache_manager
884 .put_parquet_meta_data(file_id, metadata, region_metadata)
885 .await;
886 }
887 CacheStrategy::Disabled => {}
888 }
889 }
890
891 pub(crate) fn get_prefilter_result(&self, key: &PrefilterKey) -> Option<Arc<BooleanBuffer>> {
894 match self {
895 CacheStrategy::EnableAll(cache_manager) => cache_manager.get_prefilter_result(key),
896 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
897 }
898 }
899
900 pub(crate) fn put_prefilter_result(&self, key: PrefilterKey, result: Arc<BooleanBuffer>) {
903 if let CacheStrategy::EnableAll(cache_manager) = self {
904 cache_manager.put_prefilter_result(key, result);
905 }
906 }
907
908 pub async fn remove_parquet_meta_data(&self, file_id: RegionFileId) {
910 match self {
911 CacheStrategy::EnableAll(cache_manager) => {
912 cache_manager.remove_parquet_meta_data(file_id).await;
913 }
914 CacheStrategy::Compaction(cache_manager) => {
915 cache_manager.remove_parquet_meta_data(file_id).await;
916 }
917 CacheStrategy::Disabled => {}
918 }
919 }
920
921 pub fn get_repeated_vector(
924 &self,
925 data_type: &ConcreteDataType,
926 value: &Value,
927 ) -> Option<VectorRef> {
928 match self {
929 CacheStrategy::EnableAll(cache_manager) => {
930 cache_manager.get_repeated_vector(data_type, value)
931 }
932 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
933 }
934 }
935
936 pub fn put_repeated_vector(&self, value: Value, vector: VectorRef) {
939 if let CacheStrategy::EnableAll(cache_manager) = self {
940 cache_manager.put_repeated_vector(value, vector);
941 }
942 }
943
944 pub(crate) fn parquet_page_cache(&self) -> Option<&Arc<ParquetPageCache>> {
946 match self {
947 Self::EnableAll(manager) => manager.page_cache.as_ref(),
948 Self::Compaction(_) | Self::Disabled => None,
949 }
950 }
951
952 pub async fn evict_puffin_cache(&self, file_id: RegionIndexId) {
954 match self {
955 CacheStrategy::EnableAll(cache_manager) => {
956 cache_manager.evict_puffin_cache(file_id).await
957 }
958 CacheStrategy::Compaction(cache_manager) => {
959 cache_manager.evict_puffin_cache(file_id).await
960 }
961 CacheStrategy::Disabled => {}
962 }
963 }
964
965 pub fn get_selector_result(
968 &self,
969 selector_key: &SelectorResultKey,
970 ) -> Option<Arc<SelectorResultValue>> {
971 match self {
972 CacheStrategy::EnableAll(cache_manager) => {
973 cache_manager.get_selector_result(selector_key)
974 }
975 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
976 }
977 }
978
979 pub fn put_selector_result(
982 &self,
983 selector_key: SelectorResultKey,
984 result: Arc<SelectorResultValue>,
985 ) {
986 if let CacheStrategy::EnableAll(cache_manager) = self {
987 cache_manager.put_selector_result(selector_key, result);
988 }
989 }
990
991 #[cfg(test)]
994 pub(crate) fn get_range_result(
995 &self,
996 key: &RangeScanCacheKey,
997 ) -> Option<Arc<RangeScanCacheValue>> {
998 match self {
999 CacheStrategy::EnableAll(cache_manager) => cache_manager.get_range_result(key),
1000 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
1001 }
1002 }
1003
1004 #[cfg(test)]
1007 pub(crate) fn put_range_result(
1008 &self,
1009 key: RangeScanCacheKey,
1010 result: Arc<RangeScanCacheValue>,
1011 ) {
1012 if let CacheStrategy::EnableAll(cache_manager) = self {
1013 cache_manager.put_range_result(key, result);
1014 }
1015 }
1016
1017 pub(crate) fn range_result_cache(&self) -> Option<&RangeResultCacheRef> {
1018 match self {
1019 Self::EnableAll(manager) => manager.range_result_cache.as_ref(),
1020 Self::Compaction(_) | Self::Disabled => None,
1021 }
1022 }
1023
1024 pub(crate) fn has_range_result_cache(&self) -> bool {
1026 match self {
1027 CacheStrategy::EnableAll(cache_manager) => cache_manager.has_range_result_cache(),
1028 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => false,
1029 }
1030 }
1031
1032 #[cfg(test)]
1033 pub(crate) fn range_concat_memory(&self) -> Option<&Arc<RangeResultMemoryLimiter>> {
1034 self.range_result_cache().map(|cache| cache.concat_memory())
1035 }
1036
1037 pub fn write_cache(&self) -> Option<&WriteCacheRef> {
1040 match self {
1041 CacheStrategy::EnableAll(cache_manager) => cache_manager.write_cache(),
1042 CacheStrategy::Compaction(cache_manager) => cache_manager.write_cache(),
1043 CacheStrategy::Disabled => None,
1044 }
1045 }
1046
1047 pub fn inverted_index_cache(&self) -> Option<&InvertedIndexCacheRef> {
1050 match self {
1051 CacheStrategy::EnableAll(cache_manager) => cache_manager.inverted_index_cache(),
1052 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
1053 }
1054 }
1055
1056 pub fn bloom_filter_index_cache(&self) -> Option<&BloomFilterIndexCacheRef> {
1059 match self {
1060 CacheStrategy::EnableAll(cache_manager) => cache_manager.bloom_filter_index_cache(),
1061 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
1062 }
1063 }
1064
1065 pub fn puffin_metadata_cache(&self) -> Option<&PuffinMetadataCacheRef> {
1068 match self {
1069 CacheStrategy::EnableAll(cache_manager) => cache_manager.puffin_metadata_cache(),
1070 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
1071 }
1072 }
1073
1074 pub fn index_result_cache(&self) -> Option<&IndexResultCache> {
1077 match self {
1078 CacheStrategy::EnableAll(cache_manager) => cache_manager.index_result_cache(),
1079 CacheStrategy::Compaction(_) | CacheStrategy::Disabled => None,
1080 }
1081 }
1082
1083 pub fn maybe_download_background(
1085 &self,
1086 index_key: IndexKey,
1087 remote_path: String,
1088 remote_store: ObjectStore,
1089 file_size: u64,
1090 ) {
1091 if let CacheStrategy::EnableAll(cache_manager) = self
1092 && let Some(write_cache) = cache_manager.write_cache()
1093 {
1094 write_cache.file_cache().maybe_download_background(
1095 index_key,
1096 remote_path,
1097 remote_store,
1098 file_size,
1099 );
1100 }
1101 }
1102}
1103
1104#[derive(Default)]
1108pub struct CacheManager {
1109 sst_meta_cache: Option<SstMetaCache>,
1111 _sst_meta_size_registration: Option<CacheSizeRegistration>,
1113 disable_sst_meta_cache_on_write: bool,
1115 sst_decoded_meta_cache: Option<SstDecodedMetaCache>,
1117 vector_cache: Option<VectorCache>,
1119 page_cache: Option<Arc<ParquetPageCache>>,
1121 write_cache: Option<WriteCacheRef>,
1123 inverted_index_cache: Option<InvertedIndexCacheRef>,
1125 bloom_filter_index_cache: Option<BloomFilterIndexCacheRef>,
1127 puffin_metadata_cache: Option<PuffinMetadataCacheRef>,
1129 selector_result_cache: Option<SelectorResultCache>,
1131 range_result_cache: Option<RangeResultCacheRef>,
1133 #[cfg(test)]
1135 range_result_cache_size: u64,
1136 index_result_cache: Option<IndexResultCache>,
1138 prefilter_result_cache: Option<PrefilterResultCache>,
1140}
1141
1142pub type CacheManagerRef = Arc<CacheManager>;
1143
1144impl CacheManager {
1145 pub fn builder() -> CacheManagerBuilder {
1147 CacheManagerBuilder::default()
1148 }
1149
1150 #[cfg(test)]
1152 pub(crate) async fn get_sst_meta_data(
1153 &self,
1154 file_id: RegionFileId,
1155 metrics: &mut MetadataCacheMetrics,
1156 page_index_policy: PageIndexPolicy,
1157 runtime: &Runtime,
1158 ) -> Option<Arc<CachedSstMeta>> {
1159 let observation = self
1160 .observe_sst_meta(file_id, page_index_policy, metrics)
1161 .await;
1162 self.resolve_sst_meta(observation, file_id, metrics, page_index_policy, runtime)
1163 .await
1164 }
1165
1166 async fn observe_sst_meta(
1167 &self,
1168 file_id: RegionFileId,
1169 page_index_policy: PageIndexPolicy,
1170 metrics: &mut MetadataCacheMetrics,
1171 ) -> SstMetaObservation {
1172 let cache_key = SstMetaKey(file_id.region_id(), file_id.file_id());
1173 let compact = self
1174 .get_compact_sst_meta(&cache_key)
1175 .filter(|metadata| metadata.satisfies_page_index_policy(page_index_policy));
1176 let decoded = self
1177 .get_decoded_sst_meta(file_id, page_index_policy)
1178 .await
1179 .filter(|metadata| metadata.satisfies_page_index_policy(page_index_policy));
1180 let observation = match (compact, decoded) {
1181 (_, Some(decoded)) => SstMetaObservation::Decoded(decoded),
1182 (Some(compact), None) => SstMetaObservation::Compact(compact),
1183 (None, None) => SstMetaObservation::Miss,
1184 };
1185 observation.record(metrics);
1186 observation
1187 }
1188
1189 async fn resolve_sst_meta(
1190 &self,
1191 observation: SstMetaObservation,
1192 file_id: RegionFileId,
1193 metrics: &mut MetadataCacheMetrics,
1194 page_index_policy: PageIndexPolicy,
1195 runtime: &Runtime,
1196 ) -> Option<Arc<CachedSstMeta>> {
1197 match observation {
1198 SstMetaObservation::Decoded(decoded) => return Some(decoded),
1199 SstMetaObservation::Compact(compact) => match compact.decode(runtime).await {
1200 Ok(decoded) => return Some(decoded),
1201 Err(err) => {
1202 warn!(err; "Failed to decode compact SST metadata, region_id: {}, file_id: {}", file_id.region_id(), file_id.file_id());
1203 self.remove_parquet_meta_data(file_id).await;
1204 }
1205 },
1206 SstMetaObservation::Miss => {}
1207 }
1208
1209 let key = IndexKey::new(file_id.region_id(), file_id.file_id(), FileType::Parquet);
1210 if let Some(write_cache) = &self.write_cache {
1211 let file_cache = write_cache.file_cache();
1212 if self.sst_meta_cache_enabled() {
1213 if let Some(metadata) = file_cache
1214 .get_sst_meta_data(key, metrics, page_index_policy, runtime)
1215 .await
1216 {
1217 metrics.file_cache_hit += 1;
1218 let decoded = metadata.decoded();
1219 match metadata {
1220 SstMetaPreparation::Prepared(metadata) => {
1221 self.put_compact_sst_meta(file_id, metadata.compact);
1222 }
1223 SstMetaPreparation::DecodedOnly { encoding_error, .. } => {
1224 warn!(
1225 encoding_error;
1226 "Failed to encode file-cached SST metadata for memory cache, region_id: {}, file_id: {}",
1227 file_id.region_id(),
1228 file_id.file_id()
1229 );
1230 }
1231 }
1232 return Some(decoded);
1233 }
1234 } else if let Some(decoded) = file_cache
1235 .get_decoded_sst_meta_data(key, metrics, page_index_policy, runtime)
1236 .await
1237 {
1238 metrics.file_cache_hit += 1;
1239 return Some(decoded);
1240 }
1241 }
1242
1243 metrics.cache_miss += 1;
1244 None
1245 }
1246
1247 pub(crate) async fn get_sst_meta_data_from_mem_cache(
1250 &self,
1251 file_id: RegionFileId,
1252 page_index_policy: PageIndexPolicy,
1253 ) -> Option<Arc<CachedSstMeta>> {
1254 let value = self
1255 .get_sst_meta_data_from_mem_cache_inner(file_id, page_index_policy)
1256 .await;
1257 update_hit_miss(value, SST_META_DECODED_TYPE)
1258 }
1259
1260 async fn get_sst_meta_data_from_mem_cache_inner(
1261 &self,
1262 file_id: RegionFileId,
1263 page_index_policy: PageIndexPolicy,
1264 ) -> Option<Arc<CachedSstMeta>> {
1265 self.get_decoded_sst_meta(file_id, page_index_policy).await
1266 }
1267
1268 pub async fn get_parquet_meta_data_from_mem_cache(
1271 &self,
1272 file_id: RegionFileId,
1273 ) -> Option<Arc<ParquetMetaData>> {
1274 self.get_sst_meta_data_from_mem_cache(file_id, PageIndexPolicy::Skip)
1275 .await
1276 .map(|metadata| metadata.parquet_metadata())
1277 }
1278
1279 pub(crate) async fn put_sst_meta_data(
1281 &self,
1282 file_id: RegionFileId,
1283 metadata: Arc<CachedSstMeta>,
1284 ) {
1285 if let Some(cache) = &self.sst_decoded_meta_cache {
1286 let key = SstDecodedMetaKey::new(file_id, metadata.page_index_policy);
1287 let value = SstMetaLoadResult::new(metadata, false, SstMetaResolution::default());
1288 cache.insert(key, value).await;
1289 }
1290 }
1291
1292 pub(crate) fn put_compact_sst_meta(
1294 &self,
1295 file_id: RegionFileId,
1296 metadata: Arc<CompactSstMeta>,
1297 ) {
1298 let key = SstMetaKey(file_id.region_id(), file_id.file_id());
1299 if let Some(cache) = &self.sst_meta_cache {
1300 cache.insert(key, metadata);
1301 }
1302 }
1303
1304 fn get_compact_sst_meta(&self, key: &SstMetaKey) -> Option<Arc<CompactSstMeta>> {
1305 self.sst_meta_cache.as_ref()?.get(key)
1306 }
1307
1308 async fn get_decoded_sst_meta(
1309 &self,
1310 file_id: RegionFileId,
1311 requested: PageIndexPolicy,
1312 ) -> Option<Arc<CachedSstMeta>> {
1313 let cache = self.sst_decoded_meta_cache.as_ref()?;
1314 for policy in [
1315 PageIndexPolicy::Skip,
1316 PageIndexPolicy::Optional,
1317 PageIndexPolicy::Required,
1318 ] {
1319 if let Some(value) = cache.get(&SstDecodedMetaKey::new(file_id, policy)).await
1322 && value.metadata.satisfies_page_index_policy(requested)
1323 {
1324 return Some(value.metadata.clone());
1325 }
1326 }
1327 None
1328 }
1329
1330 pub(crate) async fn get_compact_sst_meta_data(
1335 &self,
1336 file_id: RegionFileId,
1337 page_index_policy: PageIndexPolicy,
1338 ) -> Option<Arc<CachedSstMeta>> {
1339 let key = SstMetaKey(file_id.region_id(), file_id.file_id());
1340 let compact = self
1341 .get_compact_sst_meta(&key)
1342 .filter(|metadata| metadata.satisfies_page_index_policy(page_index_policy))?;
1343 match compact.decode(&common_runtime::global_runtime()).await {
1344 Ok(metadata) => Some(metadata),
1345 Err(err) => {
1346 warn!(err; "Failed to decode compact SST metadata, region_id: {}, file_id: {}", file_id.region_id(), file_id.file_id());
1347 self.remove_parquet_meta_data(file_id).await;
1348 None
1349 }
1350 }
1351 }
1352
1353 pub async fn put_parquet_meta_data(
1355 &self,
1356 file_id: RegionFileId,
1357 metadata: Arc<ParquetMetaData>,
1358 region_metadata: Option<RegionMetadataRef>,
1359 ) {
1360 if self.sst_decoded_meta_cache.is_some() {
1361 let file_path = format!(
1362 "region_id={}, file_id={}",
1363 file_id.region_id(),
1364 file_id.file_id()
1365 );
1366 match CachedSstMeta::try_new_with_region_metadata(
1367 &file_path,
1368 Arc::unwrap_or_clone(metadata),
1369 region_metadata,
1370 ) {
1371 Ok(metadata) => self.put_sst_meta_data(file_id, Arc::new(metadata)).await,
1372 Err(err) => warn!(
1373 err; "Failed to decode region metadata while caching parquet metadata, region_id: {}, file_id: {}",
1374 file_id.region_id(),
1375 file_id.file_id()
1376 ),
1377 }
1378 }
1379 }
1380
1381 pub async fn remove_parquet_meta_data(&self, file_id: RegionFileId) {
1383 let key = SstMetaKey(file_id.region_id(), file_id.file_id());
1384 if let Some(cache) = &self.sst_meta_cache {
1385 cache.remove(&key);
1386 }
1387 if let Some(cache) = &self.sst_decoded_meta_cache {
1388 for policy in [
1389 PageIndexPolicy::Skip,
1390 PageIndexPolicy::Optional,
1391 PageIndexPolicy::Required,
1392 ] {
1393 cache
1394 .invalidate(&SstDecodedMetaKey::new(file_id, policy))
1395 .await;
1396 }
1397 }
1398 }
1399
1400 pub(crate) fn sst_meta_cache_is_full(&self) -> bool {
1402 let Some(cache) = &self.sst_meta_cache else {
1403 return true;
1404 };
1405 cache
1406 .policy()
1407 .max_capacity()
1408 .is_some_and(|capacity| cache.weighted_size() >= capacity)
1409 }
1410
1411 pub(crate) fn sst_meta_cache_enabled(&self) -> bool {
1413 self.sst_meta_cache.is_some()
1414 }
1415
1416 pub(crate) fn sst_meta_cache_on_write_enabled(&self) -> bool {
1418 self.sst_meta_cache_enabled() && !self.disable_sst_meta_cache_on_write
1419 }
1420
1421 pub fn get_repeated_vector(
1423 &self,
1424 data_type: &ConcreteDataType,
1425 value: &Value,
1426 ) -> Option<VectorRef> {
1427 self.vector_cache.as_ref().and_then(|vector_cache| {
1428 let value = vector_cache.get(&(data_type.clone(), value.clone()));
1429 update_hit_miss(value, VECTOR_TYPE)
1430 })
1431 }
1432
1433 pub fn put_repeated_vector(&self, value: Value, vector: VectorRef) {
1435 if let Some(cache) = &self.vector_cache {
1436 let key = (vector.data_type(), value);
1437 CACHE_BYTES
1438 .with_label_values(&[VECTOR_TYPE])
1439 .add(vector_cache_weight(&key, &vector).into());
1440 cache.insert(key, vector);
1441 }
1442 }
1443
1444 pub async fn evict_puffin_cache(&self, file_id: RegionIndexId) {
1446 if let Some(cache) = &self.bloom_filter_index_cache {
1447 cache.invalidate_file(file_id.file_id());
1448 }
1449
1450 if let Some(cache) = &self.inverted_index_cache {
1451 cache.invalidate_file(file_id.file_id());
1452 }
1453
1454 if let Some(cache) = &self.index_result_cache {
1455 cache.invalidate_file(file_id.file_id());
1456 }
1457
1458 if let Some(cache) = &self.puffin_metadata_cache {
1459 cache.remove(&file_id.to_string()).await;
1460 }
1461
1462 if let Some(write_cache) = &self.write_cache {
1463 write_cache
1464 .remove(IndexKey::new(
1465 file_id.region_id(),
1466 file_id.file_id(),
1467 FileType::Puffin(file_id.version),
1468 ))
1469 .await;
1470 }
1471 }
1472
1473 pub fn get_selector_result(
1475 &self,
1476 selector_key: &SelectorResultKey,
1477 ) -> Option<Arc<SelectorResultValue>> {
1478 self.selector_result_cache
1479 .as_ref()
1480 .and_then(|selector_result_cache| selector_result_cache.get(selector_key))
1481 }
1482
1483 pub fn put_selector_result(
1485 &self,
1486 selector_key: SelectorResultKey,
1487 result: Arc<SelectorResultValue>,
1488 ) {
1489 if let Some(cache) = &self.selector_result_cache {
1490 CACHE_BYTES
1491 .with_label_values(&[SELECTOR_RESULT_TYPE])
1492 .add(selector_result_cache_weight(&selector_key, &result).into());
1493 cache.insert(selector_key, result);
1494 }
1495 }
1496
1497 pub(crate) fn invalidate_range_results(&self, file_id: FileId) {
1499 if let Some(worker) = &self.range_result_cache {
1500 worker.invalidate_file(file_id);
1501 }
1502 }
1503
1504 #[cfg(test)]
1506 pub(crate) fn get_range_result(
1507 &self,
1508 key: &RangeScanCacheKey,
1509 ) -> Option<Arc<RangeScanCacheValue>> {
1510 self.range_result_cache
1511 .as_ref()
1512 .and_then(|cache| update_hit_miss(cache.get(key), RANGE_RESULT_TYPE))
1513 }
1514
1515 #[cfg(test)]
1517 pub(crate) fn put_range_result(
1518 &self,
1519 key: RangeScanCacheKey,
1520 result: Arc<RangeScanCacheValue>,
1521 ) {
1522 if let Some(cache) = &self.range_result_cache {
1523 cache.insert(key, result);
1524 }
1525 }
1526
1527 pub(crate) fn has_range_result_cache(&self) -> bool {
1529 self.range_result_cache.is_some()
1530 }
1531
1532 #[cfg(test)]
1533 pub(crate) fn range_result_cache_size(&self) -> usize {
1534 self.range_result_cache_size as usize
1535 }
1536
1537 pub(crate) fn write_cache(&self) -> Option<&WriteCacheRef> {
1539 self.write_cache.as_ref()
1540 }
1541
1542 pub(crate) fn inverted_index_cache(&self) -> Option<&InvertedIndexCacheRef> {
1543 self.inverted_index_cache.as_ref()
1544 }
1545
1546 pub(crate) fn bloom_filter_index_cache(&self) -> Option<&BloomFilterIndexCacheRef> {
1547 self.bloom_filter_index_cache.as_ref()
1548 }
1549
1550 pub(crate) fn puffin_metadata_cache(&self) -> Option<&PuffinMetadataCacheRef> {
1551 self.puffin_metadata_cache.as_ref()
1552 }
1553
1554 pub(crate) fn index_result_cache(&self) -> Option<&IndexResultCache> {
1555 self.index_result_cache.as_ref()
1556 }
1557
1558 pub(crate) fn get_prefilter_result(&self, key: &PrefilterKey) -> Option<Arc<BooleanBuffer>> {
1559 self.prefilter_result_cache
1560 .as_ref()
1561 .and_then(|cache| update_hit_miss(cache.get(key), PREFILTER_RESULT_TYPE))
1562 }
1563
1564 pub(crate) fn put_prefilter_result(&self, key: PrefilterKey, result: Arc<BooleanBuffer>) {
1565 if let Some(cache) = &self.prefilter_result_cache {
1566 CACHE_BYTES
1567 .with_label_values(&[PREFILTER_RESULT_TYPE])
1568 .add(prefilter_result_cache_weight(&key, &result).into());
1569 cache.insert(key, result);
1570 }
1571 }
1572}
1573
1574pub fn selector_result_cache_miss() {
1576 CACHE_MISS.with_label_values(&[SELECTOR_RESULT_TYPE]).inc()
1577}
1578
1579pub fn selector_result_cache_hit() {
1581 CACHE_HIT.with_label_values(&[SELECTOR_RESULT_TYPE]).inc()
1582}
1583
1584#[derive(Default)]
1586pub struct CacheManagerBuilder {
1587 sst_meta_cache_size: u64,
1588 disable_sst_meta_cache_on_write: bool,
1589 vector_cache_size: u64,
1590 page_cache_size: u64,
1591 index_metadata_size: u64,
1592 index_content_size: u64,
1593 index_content_page_size: u64,
1594 index_result_cache_size: u64,
1595 prefilter_result_cache_size: u64,
1596 puffin_metadata_size: u64,
1597 write_cache: Option<WriteCacheRef>,
1598 selector_result_cache_size: u64,
1599 range_result_cache_size: u64,
1600}
1601
1602impl CacheManagerBuilder {
1603 pub fn sst_meta_cache_size(mut self, bytes: u64) -> Self {
1605 self.sst_meta_cache_size = bytes;
1606 self
1607 }
1608
1609 pub(crate) fn disable_sst_meta_cache_on_write(mut self) -> Self {
1611 self.disable_sst_meta_cache_on_write = true;
1612 self
1613 }
1614
1615 pub fn vector_cache_size(mut self, bytes: u64) -> Self {
1617 self.vector_cache_size = bytes;
1618 self
1619 }
1620
1621 pub fn page_cache_size(mut self, bytes: u64) -> Self {
1623 self.page_cache_size = bytes;
1624 self
1625 }
1626
1627 pub fn write_cache(mut self, cache: Option<WriteCacheRef>) -> Self {
1629 self.write_cache = cache;
1630 self
1631 }
1632
1633 pub fn index_metadata_size(mut self, bytes: u64) -> Self {
1635 self.index_metadata_size = bytes;
1636 self
1637 }
1638
1639 pub fn index_content_size(mut self, bytes: u64) -> Self {
1641 self.index_content_size = bytes;
1642 self
1643 }
1644
1645 pub fn index_content_page_size(mut self, bytes: u64) -> Self {
1647 self.index_content_page_size = bytes;
1648 self
1649 }
1650
1651 pub fn index_result_cache_size(mut self, bytes: u64) -> Self {
1653 self.index_result_cache_size = bytes;
1654 self
1655 }
1656
1657 pub fn prefilter_result_cache_size(mut self, bytes: u64) -> Self {
1659 self.prefilter_result_cache_size = bytes;
1660 self
1661 }
1662
1663 pub fn puffin_metadata_size(mut self, bytes: u64) -> Self {
1665 self.puffin_metadata_size = bytes;
1666 self
1667 }
1668
1669 pub fn selector_result_cache_size(mut self, bytes: u64) -> Self {
1671 self.selector_result_cache_size = bytes;
1672 self
1673 }
1674
1675 pub fn range_result_cache_size(mut self, bytes: u64) -> Self {
1677 self.range_result_cache_size = bytes;
1678 self
1679 }
1680
1681 pub fn build(self) -> CacheManager {
1683 self.build_with_sst_meta_size_metric(CACHE_BYTES.bind(&[SST_META_TYPE]))
1684 }
1685
1686 fn build_with_sst_meta_size_metric(
1687 self,
1688 sst_meta_size_metric: CacheSizeMetric,
1689 ) -> CacheManager {
1690 let compact_meta_capacity = self.sst_meta_cache_size.div_ceil(2);
1694 let decoded_meta_capacity = self.sst_meta_cache_size / 2;
1695 let sst_meta_cache = (compact_meta_capacity != 0).then(|| {
1696 Cache::builder()
1697 .max_capacity(compact_meta_capacity)
1698 .weigher(meta_cache_weight)
1699 .eviction_listener(|_k, _v, cause| {
1700 CACHE_EVICTION
1701 .with_label_values(&[SST_META_TYPE, removal_cause_str(cause)])
1702 .inc();
1703 })
1704 .build()
1705 });
1706 let sst_meta_size_registration = sst_meta_cache
1707 .clone()
1708 .map(|cache| sst_meta_size_metric.register(move || cache.weighted_size()));
1709 let sst_decoded_meta_cache = (decoded_meta_capacity != 0).then(|| {
1710 common_cache::Cache::new(
1711 "sst_decoded_metadata",
1712 decoded_meta_capacity,
1713 decoded_meta_cache_weight,
1714 |_k, _v, cause| {
1715 CACHE_EVICTION
1716 .with_label_values(&[SST_META_DECODED_TYPE, cause.as_str()])
1717 .inc();
1718 },
1719 )
1720 .with_size_metric(CACHE_BYTES.bind(&[SST_META_DECODED_TYPE]))
1721 .with_metrics(crate::metrics::cache_loading_metrics(SST_META_DECODED_TYPE))
1722 });
1723 let vector_cache = (self.vector_cache_size != 0).then(|| {
1724 Cache::builder()
1725 .max_capacity(self.vector_cache_size)
1726 .weigher(vector_cache_weight)
1727 .eviction_listener(|k, v, cause| {
1728 let size = vector_cache_weight(&k, &v);
1729 CACHE_BYTES
1730 .with_label_values(&[VECTOR_TYPE])
1731 .sub(size.into());
1732 CACHE_EVICTION
1733 .with_label_values(&[VECTOR_TYPE, removal_cause_str(cause)])
1734 .inc();
1735 })
1736 .build()
1737 });
1738 let page_cache =
1739 (self.page_cache_size != 0).then(|| ParquetPageCache::from_env(self.page_cache_size));
1740 let inverted_index_cache = InvertedIndexCache::new(
1741 self.index_metadata_size,
1742 self.index_content_size,
1743 self.index_content_page_size,
1744 );
1745 let bloom_filter_index_cache = BloomFilterIndexCache::new(
1747 self.index_metadata_size,
1748 self.index_content_size,
1749 self.index_content_page_size,
1750 );
1751 let index_result_cache = (self.index_result_cache_size != 0)
1752 .then(|| IndexResultCache::new(self.index_result_cache_size));
1753 let prefilter_result_cache = (self.prefilter_result_cache_size != 0)
1754 .then(|| new_prefilter_result_cache(self.prefilter_result_cache_size));
1755 let puffin_metadata_cache =
1756 PuffinMetadataCache::new(self.puffin_metadata_size, &CACHE_BYTES)
1757 .with_load_metrics(crate::metrics::cache_loading_metrics("puffin_metadata"));
1758 let selector_result_cache = (self.selector_result_cache_size != 0).then(|| {
1759 Cache::builder()
1760 .max_capacity(self.selector_result_cache_size)
1761 .weigher(selector_result_cache_weight)
1762 .eviction_listener(|k, v, cause| {
1763 let size = selector_result_cache_weight(&k, &v);
1764 CACHE_BYTES
1765 .with_label_values(&[SELECTOR_RESULT_TYPE])
1766 .sub(size.into());
1767 CACHE_EVICTION
1768 .with_label_values(&[SELECTOR_RESULT_TYPE, removal_cause_str(cause)])
1769 .inc();
1770 })
1771 .build()
1772 });
1773 let range_result_cache = (self.range_result_cache_size != 0)
1774 .then(|| range_cache::RangeResultCache::new(self.range_result_cache_size));
1775 CacheManager {
1776 sst_meta_cache,
1777 _sst_meta_size_registration: sst_meta_size_registration,
1778 disable_sst_meta_cache_on_write: self.disable_sst_meta_cache_on_write,
1779 sst_decoded_meta_cache,
1780 vector_cache,
1781 page_cache,
1782 write_cache: self.write_cache,
1783 inverted_index_cache: Some(Arc::new(inverted_index_cache)),
1784 bloom_filter_index_cache: Some(Arc::new(bloom_filter_index_cache)),
1785 puffin_metadata_cache: Some(Arc::new(puffin_metadata_cache)),
1786 selector_result_cache,
1787 range_result_cache,
1788 #[cfg(test)]
1789 range_result_cache_size: self.range_result_cache_size,
1790 index_result_cache,
1791 prefilter_result_cache,
1792 }
1793 }
1794}
1795
1796fn meta_cache_weight(k: &SstMetaKey, v: &Arc<CompactSstMeta>) -> u32 {
1797 let size = k.estimated_size() + mem::size_of::<CompactSstMeta>() + v.encoded_metadata.len();
1800 u32::try_from(size).unwrap_or(u32::MAX)
1801}
1802
1803fn decoded_meta_cache_weight(k: &SstDecodedMetaKey, v: &Arc<SstMetaLoadResult>) -> u32 {
1804 SstMetaLoadResult::weight(k, &v.metadata)
1805}
1806
1807fn vector_cache_weight(_k: &(ConcreteDataType, Value), v: &VectorRef) -> u32 {
1808 (mem::size_of::<ConcreteDataType>() + mem::size_of::<Value>() + v.memory_size()) as u32
1810}
1811
1812fn selector_result_cache_weight(k: &SelectorResultKey, v: &Arc<SelectorResultValue>) -> u32 {
1813 (mem::size_of_val(k) + v.estimated_size()) as u32
1814}
1815
1816fn range_result_cache_weight(k: &RangeScanCacheKey, v: &Arc<RangeScanCacheValue>) -> u32 {
1817 (k.estimated_size() + v.estimated_size()) as u32
1818}
1819
1820fn update_hit_miss<T>(value: Option<T>, cache_type: &str) -> Option<T> {
1822 if value.is_some() {
1823 CACHE_HIT.with_label_values(&[cache_type]).inc();
1824 } else {
1825 CACHE_MISS.with_label_values(&[cache_type]).inc();
1826 }
1827 value
1828}
1829
1830#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1832struct SstMetaKey(RegionId, FileId);
1833
1834impl SstMetaKey {
1835 fn estimated_size(&self) -> usize {
1837 mem::size_of::<Self>()
1838 }
1839}
1840
1841#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1843pub struct SelectorResultKey {
1844 pub file_id: FileId,
1846 pub row_group_idx: usize,
1848 pub selector: TimeSeriesRowSelector,
1850}
1851
1852pub enum SelectorResult {
1854 PrimaryKey(Vec<Batch>),
1856 Flat(Vec<RecordBatch>),
1858}
1859
1860pub struct SelectorResultValue {
1862 pub result: SelectorResult,
1864 pub read_cols: ParquetReadColumns,
1866 pub json_target_types: JsonTargetTypes,
1871}
1872
1873impl SelectorResultValue {
1874 pub fn new(result: Vec<Batch>, read_cols: ParquetReadColumns) -> SelectorResultValue {
1876 SelectorResultValue {
1877 result: SelectorResult::PrimaryKey(result),
1878 read_cols,
1879 json_target_types: Arc::default(),
1880 }
1881 }
1882
1883 pub fn new_flat(
1885 result: Vec<RecordBatch>,
1886 read_cols: ParquetReadColumns,
1887 json_target_types: JsonTargetTypes,
1888 ) -> SelectorResultValue {
1889 SelectorResultValue {
1890 result: SelectorResult::Flat(result),
1891 read_cols,
1892 json_target_types,
1893 }
1894 }
1895
1896 fn estimated_size(&self) -> usize {
1898 let result_size: usize = match &self.result {
1899 SelectorResult::PrimaryKey(batches) => {
1900 batches.iter().map(|batch| batch.memory_size()).sum()
1901 }
1902 SelectorResult::Flat(batches) => batches.iter().map(record_batch_estimated_size).sum(),
1903 };
1904 result_size
1905 + self.json_target_types.len() * (size_of::<ColumnId>() + size_of::<JsonNativeType>())
1906 }
1907}
1908
1909pub(crate) enum SstMetaObservation {
1911 Decoded(Arc<CachedSstMeta>),
1912 Compact(Arc<CompactSstMeta>),
1913 Miss,
1914}
1915
1916impl SstMetaObservation {
1917 fn record(&self, metrics: &mut MetadataCacheMetrics) {
1918 match self {
1919 Self::Decoded(_) | Self::Compact(_) => {
1920 metrics.mem_cache_hit += 1;
1921 CACHE_HIT.with_label_values(&[SST_META_TYPE]).inc();
1922 match self {
1923 Self::Decoded(_) => CACHE_HIT.with_label_values(&[SST_META_DECODED_TYPE]).inc(),
1924 _ => CACHE_MISS.with_label_values(&[SST_META_DECODED_TYPE]).inc(),
1925 }
1926 }
1927 Self::Miss => CACHE_MISS.with_label_values(&[SST_META_TYPE]).inc(),
1928 }
1929 }
1930}
1931
1932#[derive(Clone, Copy, Default)]
1939pub(crate) struct SstMetaResolution {
1940 pub(crate) file_cache_hit: usize,
1941 pub(crate) cache_miss: usize,
1942}
1943
1944struct SstMetaLoadError {
1945 source: Arc<crate::error::Error>,
1946 resolution: SstMetaResolution,
1947}
1948
1949struct SstMetaLoadResult {
1951 metadata: Arc<CachedSstMeta>,
1952 cache_miss: bool,
1954 resolution: SstMetaResolution,
1955}
1956
1957impl SstMetaLoadResult {
1958 fn weight(key: &SstDecodedMetaKey, metadata: &CachedSstMeta) -> u32 {
1959 let size = size_of_val(key)
1960 + size_of::<Self>()
1961 + metadata.parquet_metadata_size
1962 + metadata.region_metadata_weight;
1963 u32::try_from(size).unwrap_or(u32::MAX)
1964 }
1965
1966 fn new(
1967 metadata: Arc<CachedSstMeta>,
1968 cache_miss: bool,
1969 resolution: SstMetaResolution,
1970 ) -> Arc<Self> {
1971 Arc::new(Self {
1972 metadata,
1973 cache_miss,
1974 resolution,
1975 })
1976 }
1977}
1978
1979#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1981struct SstDecodedMetaKey {
1982 file: SstMetaKey,
1983 policy: mem::Discriminant<PageIndexPolicy>,
1984}
1985
1986impl SstDecodedMetaKey {
1987 fn new(file: RegionFileId, policy: PageIndexPolicy) -> Self {
1988 Self {
1989 file: SstMetaKey(file.region_id(), file.file_id()),
1990 policy: mem::discriminant(&policy),
1991 }
1992 }
1993}
1994
1995type SstMetaCache = Cache<SstMetaKey, Arc<CompactSstMeta>>;
1997type SstDecodedMetaCache = common_cache::Cache<SstDecodedMetaKey, Arc<SstMetaLoadResult>>;
1999type VectorCache = Cache<(ConcreteDataType, Value), VectorRef>;
2003type SelectorResultCache = Cache<SelectorResultKey, Arc<SelectorResultValue>>;
2005type RangeResultCacheRef = Arc<range_cache::RangeResultCache>;
2007
2008#[cfg(test)]
2009mod tests {
2010 use std::sync::Arc;
2011
2012 use api::v1::SemanticType;
2013 use api::v1::index::{BloomFilterMeta, InvertedIndexMetas};
2014 use datatypes::schema::ColumnSchema;
2015 use datatypes::vectors::Int64Vector;
2016 use parquet::file::page_index::offset_index::{OffsetIndexMetaData, PageLocation};
2017 use puffin::file_metadata::FileMetadata;
2018 use store_api::metadata::{ColumnMetadata, RegionMetadata, RegionMetadataBuilder};
2019 use store_api::storage::ColumnId;
2020
2021 use super::*;
2022 use crate::cache::index::bloom_filter_index::Tag;
2023 use crate::cache::index::result_cache::PredicateKey;
2024 use crate::cache::test_util::{
2025 parquet_meta, sst_parquet_meta, sst_parquet_meta_with_region_metadata,
2026 };
2027 use crate::read::range_cache::{RangeScanCacheKey, ScanRequestFingerprintBuilder};
2028 use crate::read::read_columns::ReadColumns;
2029 use crate::sst::parquet::row_selection::RowGroupSelection;
2030
2031 #[test]
2032 fn test_find_shared_page_source() {
2033 let source = Arc::new(
2034 UnexpectedSnafu {
2035 reason: "page read failed",
2036 }
2037 .build(),
2038 );
2039 let direct = io::Error::other(source.clone());
2040 let nested = io::Error::other(Arc::new(io::Error::other(direct)));
2041 let retained = find_shared_page_source::<crate::error::Error>(&nested).unwrap();
2042 assert!(Arc::ptr_eq(&source, &retained));
2043 assert!(
2044 find_shared_page_source::<crate::error::Error>(&io::Error::other("untyped error"))
2045 .is_none()
2046 );
2047 }
2048
2049 #[tokio::test]
2050 async fn test_disable_cache() {
2051 let cache = CacheManager::default();
2052 assert!(cache.sst_meta_cache.is_none());
2053 assert!(cache.sst_decoded_meta_cache.is_none());
2054 assert!(cache.vector_cache.is_none());
2055 assert!(cache.page_cache.is_none());
2056
2057 let region_id = RegionId::new(1, 1);
2058 let file_id = RegionFileId::new(region_id, FileId::random());
2059 let metadata = parquet_meta();
2060 let mut metrics = MetadataCacheMetrics::default();
2061 cache.put_parquet_meta_data(file_id, metadata, None).await;
2062 assert!(
2063 cache
2064 .get_sst_meta_data(
2065 file_id,
2066 &mut metrics,
2067 Default::default(),
2068 &common_runtime::global_runtime()
2069 )
2070 .await
2071 .is_none()
2072 );
2073
2074 let value = Value::Int64(10);
2075 let vector: VectorRef = Arc::new(Int64Vector::from_slice([10, 10, 10, 10]));
2076 cache.put_repeated_vector(value.clone(), vector.clone());
2077 assert!(
2078 cache
2079 .get_repeated_vector(&ConcreteDataType::int64_datatype(), &value)
2080 .is_none()
2081 );
2082
2083 assert!(cache.page_cache.is_none());
2084
2085 assert!(cache.write_cache().is_none());
2086 }
2087
2088 #[tokio::test]
2089 async fn test_sst_meta_load_coalescing() {
2090 use std::sync::atomic::{AtomicUsize, Ordering};
2091
2092 let manager = Arc::new(CacheManager::builder().sst_meta_cache_size(2).build());
2093 let strategy = CacheStrategy::EnableAll(manager.clone());
2094 let (metadata, region) = sst_parquet_meta();
2095 let metadata = Arc::new(
2096 CachedSstMeta::try_new("test.parquet", Arc::unwrap_or_clone(metadata)).unwrap(),
2097 );
2098 let file_id = RegionFileId::new(region.region_id, FileId::random());
2099 let calls = AtomicUsize::new(0);
2100 let gate = tokio::sync::Notify::new();
2101 let load = || async {
2102 calls.fetch_add(1, Ordering::Relaxed);
2103 gate.notified().await;
2104 Ok((metadata.clone(), true))
2105 };
2106 let mut first =
2107 Box::pin(strategy.get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, load()));
2108 let mut second =
2109 Box::pin(strategy.get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, load()));
2110 assert!(futures::poll!(&mut first).is_pending());
2111 assert!(futures::poll!(&mut second).is_pending());
2112 assert_eq!(1, calls.load(Ordering::Relaxed));
2113 gate.notify_waiters();
2114 let (first, second) = tokio::join!(first, second);
2115 let first = first.unwrap();
2116 let second = second.unwrap();
2117 assert!(Arc::ptr_eq(&first.0, &second.0));
2118 assert!(
2119 first.1 && second.1,
2120 "all waiters preserve the remote miss flag"
2121 );
2122
2123 manager
2125 .sst_decoded_meta_cache
2126 .as_ref()
2127 .unwrap()
2128 .run_pending_tasks()
2129 .await;
2130 let mut next =
2132 Box::pin(strategy.get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, load()));
2133 assert!(futures::poll!(&mut next).is_pending());
2134 assert_eq!(2, calls.load(Ordering::Relaxed));
2135 drop(next);
2136 manager
2137 .sst_decoded_meta_cache
2138 .as_ref()
2139 .unwrap()
2140 .run_pending_tasks()
2141 .await;
2142 assert_eq!(
2143 0,
2144 manager
2145 .sst_decoded_meta_cache
2146 .as_ref()
2147 .unwrap()
2148 .entry_count()
2149 );
2150 }
2151
2152 #[tokio::test]
2153 async fn test_compact_sst_meta_stronger_policy_reused_from_skip_key() {
2154 for policy in [PageIndexPolicy::Optional, PageIndexPolicy::Required] {
2155 let manager = Arc::new(
2156 CacheManager::builder()
2157 .sst_meta_cache_size(1024 * 1024)
2158 .build(),
2159 );
2160 let strategy = CacheStrategy::EnableAll(manager.clone());
2161 let (metadata, region) = sst_parquet_meta();
2162 let file_id = RegionFileId::new(region.region_id, FileId::random());
2163 let prepared = prepare_sst_meta(
2164 "test.parquet",
2165 with_offset_indexes(Arc::unwrap_or_clone(metadata)),
2166 None,
2167 policy,
2168 &common_runtime::global_runtime(),
2169 )
2170 .await
2171 .unwrap();
2172 let SstMetaPreparation::Prepared(prepared) = prepared else {
2173 panic!("valid metadata should produce a compact entry");
2174 };
2175 manager.put_compact_sst_meta(file_id, prepared.compact);
2176 let mut metrics = MetadataCacheMetrics::default();
2177 let observation = strategy
2178 .observe_sst_meta(file_id, PageIndexPolicy::Skip, &mut metrics)
2179 .await;
2180 assert!(matches!(&observation, SstMetaObservation::Compact(_)));
2181 let (decoded, _) = strategy
2182 .get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, async {
2183 let value = strategy
2184 .resolve_sst_meta(
2185 observation,
2186 file_id,
2187 &mut MetadataCacheMetrics::default(),
2188 PageIndexPolicy::Skip,
2189 )
2190 .await
2191 .expect("compact entry must decode without storage I/O");
2192 Ok((value, false))
2193 })
2194 .await
2195 .unwrap();
2196 assert!(decoded.satisfies_page_index_policy(policy));
2197 let cache = manager.sst_decoded_meta_cache.as_ref().unwrap();
2198 cache.run_pending_tasks().await;
2199 let weight = cache.weighted_size();
2200 assert_eq!(cache.entry_count(), 1);
2201
2202 manager
2204 .sst_meta_cache
2205 .as_ref()
2206 .unwrap()
2207 .invalidate(&SstMetaKey(file_id.region_id(), file_id.file_id()));
2208 for requested in [PageIndexPolicy::Optional, policy] {
2209 let mut metrics = MetadataCacheMetrics::default();
2210 let observation = strategy
2211 .observe_sst_meta(file_id, requested, &mut metrics)
2212 .await;
2213 let SstMetaObservation::Decoded(cached) = observation else {
2214 panic!("the completed Skip-key entry already satisfies this request");
2215 };
2216 assert!(Arc::ptr_eq(&decoded, &cached));
2217 assert_eq!(metrics.mem_cache_hit, 1);
2218 assert_eq!(metrics.cache_miss, 0);
2219 }
2220 cache.run_pending_tasks().await;
2221 assert_eq!(cache.entry_count(), 1);
2222 assert_eq!(cache.weighted_size(), weight);
2223 assert!(
2224 cache
2225 .get(&SstDecodedMetaKey::new(file_id, PageIndexPolicy::Optional))
2226 .await
2227 .is_none()
2228 );
2229 assert!(
2230 cache
2231 .get(&SstDecodedMetaKey::new(file_id, PageIndexPolicy::Required))
2232 .await
2233 .is_none()
2234 );
2235 }
2236 }
2237
2238 #[tokio::test]
2239 async fn test_async_sst_meta_policy_reuse_and_invalidation() {
2240 let manager = Arc::new(
2241 CacheManager::builder()
2242 .sst_meta_cache_size(1024 * 1024)
2243 .build(),
2244 );
2245 let strategy = CacheStrategy::EnableAll(manager.clone());
2246 let (parquet_metadata, region) = sst_parquet_meta();
2247 let file_id = RegionFileId::new(region.region_id, FileId::random());
2248 let policies = [
2249 PageIndexPolicy::Skip,
2250 PageIndexPolicy::Optional,
2251 PageIndexPolicy::Required,
2252 ];
2253 for loaded in policies {
2254 let metadata = Arc::new(
2255 CachedSstMeta::try_new_with_page_index_policy(
2256 "test.parquet",
2257 (*parquet_metadata).clone(),
2258 None,
2259 loaded,
2260 )
2261 .unwrap(),
2262 );
2263 let (result, miss) = strategy
2264 .get_or_load_sst_meta(file_id, loaded, async { Ok((metadata.clone(), true)) })
2265 .await
2266 .unwrap();
2267 assert!(Arc::ptr_eq(&metadata, &result));
2268 assert!(miss);
2269 for requested in policies {
2270 let mut metrics = MetadataCacheMetrics::default();
2271 let cached = strategy
2272 .observe_sst_meta(file_id, requested, &mut metrics)
2273 .await;
2274 let cached = match cached {
2275 SstMetaObservation::Decoded(value) => Some(value),
2276 _ => None,
2277 };
2278 assert_eq!(
2279 satisfies_page_index_policy(loaded, requested),
2280 cached.is_some()
2281 );
2282 if let Some(cached) = cached {
2283 assert!(Arc::ptr_eq(&metadata, &cached));
2284 assert_eq!(1, metrics.mem_cache_hit);
2285 }
2286 }
2287 manager.remove_parquet_meta_data(file_id).await;
2288 }
2289
2290 for loaded in policies {
2292 let metadata = Arc::new(
2293 CachedSstMeta::try_new_with_page_index_policy(
2294 "test.parquet",
2295 (*parquet_metadata).clone(),
2296 None,
2297 loaded,
2298 )
2299 .unwrap(),
2300 );
2301 manager.put_sst_meta_data(file_id, metadata).await;
2302 }
2303 manager.remove_parquet_meta_data(file_id).await;
2304 for requested in policies {
2305 assert!(
2306 manager
2307 .get_sst_meta_data_from_mem_cache(file_id, requested)
2308 .await
2309 .is_none()
2310 );
2311 }
2312 }
2313
2314 #[tokio::test]
2315 async fn test_sst_meta_load_cancellation_and_policy_isolation() {
2316 let strategy = CacheStrategy::EnableAll(Arc::new(
2317 CacheManager::builder().sst_meta_cache_size(2000).build(),
2318 ));
2319 let (metadata, region) = sst_parquet_meta();
2320 let metadata = Arc::new(
2321 CachedSstMeta::try_new("test.parquet", Arc::unwrap_or_clone(metadata)).unwrap(),
2322 );
2323 let file_id = RegionFileId::new(region.region_id, FileId::random());
2324 let mut first = Box::pin(strategy.get_or_load_sst_meta(
2325 file_id,
2326 PageIndexPolicy::Skip,
2327 std::future::pending(),
2328 ));
2329 assert!(futures::poll!(&mut first).is_pending());
2330 for (id, policy) in [
2332 (file_id, PageIndexPolicy::Optional),
2333 (file_id, PageIndexPolicy::Required),
2334 (
2335 RegionFileId::new(region.region_id, FileId::random()),
2336 PageIndexPolicy::Skip,
2337 ),
2338 ] {
2339 let mut independent = Box::pin(
2340 strategy.get_or_load_sst_meta(id, policy, async { Ok((metadata.clone(), false)) }),
2341 );
2342 assert!(futures::poll!(&mut independent).is_ready());
2343 }
2344 let mut waiter = Box::pin(strategy.get_or_load_sst_meta(
2345 file_id,
2346 PageIndexPolicy::Skip,
2347 async { Ok((metadata.clone(), false)) },
2348 ));
2349 assert!(futures::poll!(&mut waiter).is_pending());
2350 drop(first);
2351 let result = tokio::time::timeout(std::time::Duration::from_secs(5), waiter)
2352 .await
2353 .unwrap()
2354 .unwrap();
2355 assert!(Arc::ptr_eq(&metadata, &result.0));
2356 }
2357
2358 #[tokio::test]
2359 async fn test_sst_meta_load_failure_retry_and_disabled_cache() {
2360 use common_error::ext::ErrorExt;
2361
2362 let manager = Arc::new(CacheManager::builder().sst_meta_cache_size(2000).build());
2363 let strategy = CacheStrategy::EnableAll(manager);
2364 let (metadata, region) = sst_parquet_meta();
2365 let metadata = Arc::new(
2366 CachedSstMeta::try_new("test.parquet", Arc::unwrap_or_clone(metadata)).unwrap(),
2367 );
2368 let file_id = RegionFileId::new(region.region_id, FileId::random());
2369 let gate = tokio::sync::Notify::new();
2370 let mut first = Box::pin(strategy.get_or_load_observed_sst_meta(
2371 file_id,
2372 PageIndexPolicy::Skip,
2373 async {
2374 gate.notified().await;
2375 (
2376 UnexpectedSnafu {
2377 reason: "load failed",
2378 }
2379 .fail(),
2380 SstMetaResolution {
2381 file_cache_hit: 0,
2382 cache_miss: 1,
2383 },
2384 )
2385 },
2386 ));
2387 let mut waiter = Box::pin(strategy.get_or_load_observed_sst_meta(
2388 file_id,
2389 PageIndexPolicy::Skip,
2390 async { panic!("waiter must share the failure") },
2391 ));
2392 assert!(futures::poll!(&mut first).is_pending());
2393 assert!(futures::poll!(&mut waiter).is_pending());
2394 gate.notify_waiters();
2395 let (first, first_resolution) = first.await;
2396 let (second, second_resolution) = waiter.await;
2397 for resolution in [first_resolution, second_resolution] {
2398 assert_eq!(1, resolution.cache_miss);
2399 assert_eq!(0, resolution.file_cache_hit);
2400 }
2401 let first = first.unwrap_err();
2402 let second = second.unwrap_err();
2403 assert_eq!(first.status_code(), second.status_code());
2404 match (first, second) {
2405 (
2406 crate::error::Error::LoadSstMeta { source: a, .. },
2407 crate::error::Error::LoadSstMeta { source: b, .. },
2408 ) => assert!(Arc::ptr_eq(&a, &b)),
2409 errors => panic!("unexpected errors: {errors:?}"),
2410 }
2411 assert!(
2412 strategy
2413 .get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, async {
2414 Ok((metadata.clone(), true))
2415 })
2416 .await
2417 .is_ok()
2418 );
2419
2420 for strategy in [
2421 CacheStrategy::Disabled,
2422 CacheStrategy::EnableAll(Arc::new(CacheManager::default())),
2423 CacheStrategy::EnableAll(Arc::new(
2424 CacheManager::builder().sst_meta_cache_size(1).build(),
2425 )),
2426 ] {
2427 let mut blocked = Box::pin(strategy.get_or_load_sst_meta(
2428 file_id,
2429 PageIndexPolicy::Skip,
2430 std::future::pending(),
2431 ));
2432 assert!(futures::poll!(&mut blocked).is_pending());
2433 let mut independent = Box::pin(strategy.get_or_load_sst_meta(
2434 file_id,
2435 PageIndexPolicy::Skip,
2436 async { Ok((metadata.clone(), true)) },
2437 ));
2438 assert!(futures::poll!(&mut independent).is_ready());
2439 }
2440 }
2441
2442 #[test]
2443 fn test_compact_sst_meta_bytes_follow_cache_lifetime() {
2444 use common_cache::metrics::CacheSizeMetrics;
2445 use prometheus::Opts;
2446 use prometheus::core::Collector;
2447
2448 let metrics =
2449 CacheSizeMetrics::new(Opts::new("test_compact_sst_meta_bytes", "test"), &["type"])
2450 .unwrap();
2451 let (metadata, region) = sst_parquet_meta();
2452 let SstMetaPreparation::Prepared(prepared) = prepare_sst_meta_sync(
2453 "test.parquet",
2454 Arc::unwrap_or_clone(metadata),
2455 None,
2456 PageIndexPolicy::Skip,
2457 )
2458 .unwrap() else {
2459 panic!("valid metadata should produce a compact entry");
2460 };
2461 let file_id = RegionFileId::new(region.region_id, FileId::random());
2462 let expected_bytes = f64::from(meta_cache_weight(
2463 &SstMetaKey(file_id.region_id(), file_id.file_id()),
2464 &prepared.compact,
2465 ));
2466 let make_cache = || {
2467 let manager = Arc::new(
2468 CacheManager::builder()
2469 .sst_meta_cache_size(1024 * 1024)
2470 .build_with_sst_meta_size_metric(metrics.bind(&[SST_META_TYPE])),
2471 );
2472 manager.put_compact_sst_meta(file_id, prepared.compact.clone());
2473 manager.sst_meta_cache.as_ref().unwrap().run_pending_tasks();
2474 manager
2475 };
2476 let sample = || {
2477 metrics.collect()[0].get_metric()[0]
2478 .get_gauge()
2479 .as_ref()
2480 .unwrap()
2481 .value()
2482 };
2483 let resident = make_cache();
2484 assert!(expected_bytes > 0.0);
2485 assert_eq!(expected_bytes, sample());
2486
2487 for _ in 0..3 {
2489 let transient = make_cache();
2490 assert_eq!(2.0 * expected_bytes, sample());
2491 let reader = transient.clone();
2492 drop(transient);
2493 assert_eq!(2.0 * expected_bytes, sample());
2494 drop(reader);
2495 assert_eq!(expected_bytes, sample());
2496 }
2497 drop(resident);
2498 assert_eq!(0.0, sample());
2499 }
2500
2501 #[test]
2502 fn test_sst_meta_cache_splits_capacity() {
2503 let cache = CacheManager::builder().sst_meta_cache_size(101).build();
2504
2505 assert_eq!(
2506 Some(51),
2507 cache
2508 .sst_meta_cache
2509 .as_ref()
2510 .unwrap()
2511 .policy()
2512 .max_capacity()
2513 );
2514 assert_eq!(
2515 50,
2516 cache.sst_decoded_meta_cache.as_ref().unwrap().capacity()
2517 );
2518 }
2519
2520 #[tokio::test]
2521 async fn test_parquet_meta_cache() {
2522 let cache = CacheManager::builder().sst_meta_cache_size(2000).build();
2523 let mut metrics = MetadataCacheMetrics::default();
2524 let region_id = RegionId::new(1, 1);
2525 let file_id = RegionFileId::new(region_id, FileId::random());
2526 assert!(
2527 cache
2528 .get_sst_meta_data(
2529 file_id,
2530 &mut metrics,
2531 Default::default(),
2532 &common_runtime::global_runtime()
2533 )
2534 .await
2535 .is_none()
2536 );
2537 let (metadata, region_metadata) = sst_parquet_meta();
2538 cache.put_parquet_meta_data(file_id, metadata, None).await;
2539 let cached = cache
2540 .get_sst_meta_data(
2541 file_id,
2542 &mut metrics,
2543 Default::default(),
2544 &common_runtime::global_runtime(),
2545 )
2546 .await
2547 .unwrap();
2548 assert_eq!(region_metadata, cached.region_metadata());
2549 assert!(
2550 cached
2551 .parquet_metadata()
2552 .file_metadata()
2553 .key_value_metadata()
2554 .is_none_or(|key_values| {
2555 key_values
2556 .iter()
2557 .all(|key_value| key_value.key != PARQUET_METADATA_KEY)
2558 })
2559 );
2560 cache.remove_parquet_meta_data(file_id).await;
2561 assert!(
2562 cache
2563 .get_sst_meta_data(
2564 file_id,
2565 &mut metrics,
2566 Default::default(),
2567 &common_runtime::global_runtime()
2568 )
2569 .await
2570 .is_none()
2571 );
2572 }
2573
2574 #[tokio::test]
2575 async fn test_parquet_meta_cache_with_provided_region_metadata() {
2576 let cache = CacheManager::builder().sst_meta_cache_size(2000).build();
2577 let mut metrics = MetadataCacheMetrics::default();
2578 let region_id = RegionId::new(1, 1);
2579 let file_id = RegionFileId::new(region_id, FileId::random());
2580 let (metadata, region_metadata) = sst_parquet_meta();
2581
2582 cache
2583 .put_parquet_meta_data(file_id, metadata, Some(region_metadata.clone()))
2584 .await;
2585
2586 let cached = cache
2587 .get_sst_meta_data(
2588 file_id,
2589 &mut metrics,
2590 Default::default(),
2591 &common_runtime::global_runtime(),
2592 )
2593 .await
2594 .unwrap();
2595 assert!(Arc::ptr_eq(®ion_metadata, &cached.region_metadata()));
2596 }
2597
2598 fn with_offset_indexes(metadata: ParquetMetaData) -> ParquetMetaData {
2599 let offset_indexes = metadata
2600 .row_groups()
2601 .iter()
2602 .map(|row_group| {
2603 row_group
2604 .columns()
2605 .iter()
2606 .map(|_| OffsetIndexMetaData {
2607 page_locations: vec![PageLocation {
2608 offset: 42,
2609 compressed_page_size: 128,
2610 first_row_index: 0,
2611 }],
2612 unencoded_byte_array_data_bytes: None,
2613 })
2614 .collect()
2615 })
2616 .collect();
2617 metadata
2618 .into_builder()
2619 .set_offset_index(Some(offset_indexes))
2620 .build()
2621 }
2622
2623 #[tokio::test]
2624 async fn test_compact_sst_meta_round_trip() {
2625 let cache = CacheManager::builder()
2626 .sst_meta_cache_size(1024 * 1024)
2627 .build();
2628 let region_id = RegionId::new(1, 1);
2629 let file_id = RegionFileId::new(region_id, FileId::random());
2630 let (metadata, expected_region_metadata) = sst_parquet_meta();
2631 let metadata = Arc::unwrap_or_clone(metadata);
2632 let metadata = with_offset_indexes(metadata);
2633 let expected_rows = metadata.file_metadata().num_rows();
2634 let prepared = prepare_sst_meta(
2635 "test.parquet",
2636 metadata,
2637 None,
2638 PageIndexPolicy::Required,
2639 &common_runtime::global_runtime(),
2640 )
2641 .await
2642 .unwrap();
2643 let SstMetaPreparation::Prepared(prepared) = prepared else {
2644 panic!("valid metadata should produce a compact cache entry");
2645 };
2646
2647 cache.put_compact_sst_meta(file_id, prepared.compact);
2649 let mut metrics = MetadataCacheMetrics::default();
2650 let cached = cache
2651 .get_sst_meta_data(
2652 file_id,
2653 &mut metrics,
2654 PageIndexPolicy::Required,
2655 &common_runtime::global_runtime(),
2656 )
2657 .await
2658 .unwrap();
2659
2660 assert_eq!(1, metrics.mem_cache_hit);
2661 assert_eq!(0, metrics.cache_miss);
2662 assert_eq!(
2663 expected_rows,
2664 cached.parquet_metadata.file_metadata().num_rows()
2665 );
2666 assert!(cached.parquet_metadata.offset_index().is_some());
2667 assert!(
2668 cache
2669 .sst_decoded_meta_cache
2670 .as_ref()
2671 .unwrap()
2672 .get(&SstDecodedMetaKey::new(file_id, PageIndexPolicy::Required))
2673 .await
2674 .is_none(),
2675 "compact inspection must not fill the decoded reservation"
2676 );
2677 assert_eq!(expected_region_metadata, cached.region_metadata());
2678 assert!(
2679 cached
2680 .parquet_metadata
2681 .file_metadata()
2682 .key_value_metadata()
2683 .is_none_or(|key_values| key_values
2684 .iter()
2685 .all(|key_value| key_value.key != PARQUET_METADATA_KEY))
2686 );
2687 }
2688
2689 #[tokio::test]
2690 async fn test_cache_encoding_failure_preserves_decoded_metadata() {
2691 let (metadata, expected_region_metadata) = sst_parquet_meta();
2692 let expected_rows = metadata.file_metadata().num_rows();
2693 let cache_encoding: Result<(Bytes, usize)> = Err(UnexpectedSnafu {
2694 reason: "injected compact metadata encoding failure",
2695 }
2696 .build());
2697
2698 let preparation = finish_sst_meta_preparation(
2699 "test.parquet",
2700 Arc::unwrap_or_clone(metadata),
2701 None,
2702 PageIndexPolicy::Skip,
2703 cache_encoding,
2704 )
2705 .unwrap();
2706 let SstMetaPreparation::DecodedOnly {
2707 decoded,
2708 encoding_error,
2709 } = preparation
2710 else {
2711 panic!("cache encoding failure should return decoded-only metadata");
2712 };
2713
2714 assert_eq!(
2715 expected_rows,
2716 decoded.parquet_metadata.file_metadata().num_rows()
2717 );
2718 assert_eq!(expected_region_metadata, decoded.region_metadata());
2719 assert!(
2720 encoding_error
2721 .to_string()
2722 .contains("injected compact metadata encoding failure")
2723 );
2724 let file_id = RegionFileId::new(expected_region_metadata.region_id, FileId::random());
2725 let manager = Arc::new(
2726 CacheManager::builder()
2727 .sst_meta_cache_size(1024 * 1024)
2728 .build(),
2729 );
2730 let strategy = CacheStrategy::EnableAll(manager.clone());
2731 let (shared, miss) = strategy
2732 .get_or_load_sst_meta(file_id, PageIndexPolicy::Skip, async {
2733 Ok((decoded.clone(), true))
2734 })
2735 .await
2736 .unwrap();
2737 assert!(miss);
2738 assert!(Arc::ptr_eq(&decoded, &shared));
2739 assert!(
2740 manager
2741 .get_compact_sst_meta(&SstMetaKey(file_id.region_id(), file_id.file_id()))
2742 .is_none()
2743 );
2744 let mut metrics = MetadataCacheMetrics::default();
2745 let SstMetaObservation::Decoded(cached) = strategy
2746 .observe_sst_meta(file_id, PageIndexPolicy::Skip, &mut metrics)
2747 .await
2748 else {
2749 panic!("decoded fallback must be retained without a compact entry");
2750 };
2751 assert!(Arc::ptr_eq(&decoded, &cached));
2752 assert_eq!(1, metrics.mem_cache_hit);
2753 assert_eq!(0, metrics.cache_miss);
2754 }
2755
2756 #[tokio::test]
2757 async fn test_parquet_meta_cache_respects_page_index_policy() {
2758 let cache = CacheManager::builder().sst_meta_cache_size(2000).build();
2759 let region_id = RegionId::new(1, 1);
2760 let file_id = RegionFileId::new(region_id, FileId::random());
2761 let (metadata, _) = sst_parquet_meta();
2762
2763 let skip_metadata = Arc::new(
2764 CachedSstMeta::try_new_with_page_index_policy(
2765 "test.parquet",
2766 Arc::unwrap_or_clone(metadata.clone()),
2767 None,
2768 PageIndexPolicy::Skip,
2769 )
2770 .unwrap(),
2771 );
2772 cache.put_sst_meta_data(file_id, skip_metadata).await;
2773
2774 let mut metrics = MetadataCacheMetrics::default();
2775 assert!(
2776 cache
2777 .get_sst_meta_data(
2778 file_id,
2779 &mut metrics,
2780 PageIndexPolicy::Optional,
2781 &common_runtime::global_runtime()
2782 )
2783 .await
2784 .is_none()
2785 );
2786 assert_eq!(1, metrics.cache_miss);
2787
2788 let optional_metadata = Arc::new(
2789 CachedSstMeta::try_new_with_page_index_policy(
2790 "test.parquet",
2791 Arc::unwrap_or_clone(metadata),
2792 None,
2793 PageIndexPolicy::Optional,
2794 )
2795 .unwrap(),
2796 );
2797 cache.put_sst_meta_data(file_id, optional_metadata).await;
2798
2799 let mut metrics = MetadataCacheMetrics::default();
2800 assert!(
2801 cache
2802 .get_sst_meta_data(
2803 file_id,
2804 &mut metrics,
2805 PageIndexPolicy::Optional,
2806 &common_runtime::global_runtime()
2807 )
2808 .await
2809 .is_some()
2810 );
2811 assert_eq!(1, metrics.mem_cache_hit);
2812
2813 let mut metrics = MetadataCacheMetrics::default();
2814 assert!(
2815 cache
2816 .get_sst_meta_data(
2817 file_id,
2818 &mut metrics,
2819 PageIndexPolicy::Skip,
2820 &common_runtime::global_runtime()
2821 )
2822 .await
2823 .is_some()
2824 );
2825 assert_eq!(1, metrics.mem_cache_hit);
2826 }
2827
2828 #[test]
2829 fn test_decoded_meta_cache_weight_accounts_for_region_metadata() {
2830 let region_metadata = Arc::new(wide_region_metadata(128));
2831 let json_len = region_metadata.to_json().unwrap().len();
2832 let metadata = sst_parquet_meta_with_region_metadata(region_metadata.clone());
2833 let cached = Arc::new(
2834 CachedSstMeta::try_new("test.parquet", Arc::unwrap_or_clone(metadata)).unwrap(),
2835 );
2836 let key = SstDecodedMetaKey::new(
2837 RegionFileId::new(region_metadata.region_id, FileId::random()),
2838 PageIndexPolicy::Skip,
2839 );
2840 let value = SstMetaLoadResult::new(cached.clone(), false, SstMetaResolution::default());
2841
2842 assert!(cached.region_metadata_weight > json_len);
2843 assert_eq!(
2844 decoded_meta_cache_weight(&key, &value) as usize,
2845 size_of_val(&key)
2846 + size_of::<SstMetaLoadResult>()
2847 + cached.parquet_metadata_size
2848 + cached.region_metadata_weight
2849 );
2850 assert_eq!(
2851 cached.parquet_metadata_size,
2852 parquet_meta_size(&cached.parquet_metadata)
2853 );
2854 }
2855
2856 #[test]
2857 fn test_decoded_meta_cache_weight_saturates_on_overflow() {
2858 let region_metadata = Arc::new(wide_region_metadata(1));
2859 let metadata = sst_parquet_meta_with_region_metadata(region_metadata.clone());
2860 let mut cached =
2861 CachedSstMeta::try_new("test.parquet", Arc::unwrap_or_clone(metadata)).unwrap();
2862 cached.region_metadata_weight = u32::MAX as usize + 1;
2863 let cached = Arc::new(cached);
2864 let key = SstDecodedMetaKey::new(
2865 RegionFileId::new(region_metadata.region_id, FileId::random()),
2866 PageIndexPolicy::Skip,
2867 );
2868 let value = SstMetaLoadResult::new(cached.clone(), false, SstMetaResolution::default());
2869
2870 assert_eq!(u32::MAX, decoded_meta_cache_weight(&key, &value));
2871 }
2872
2873 #[test]
2874 fn test_repeated_vector_cache() {
2875 let cache = CacheManager::builder().vector_cache_size(4096).build();
2876 let value = Value::Int64(10);
2877 assert!(
2878 cache
2879 .get_repeated_vector(&ConcreteDataType::int64_datatype(), &value)
2880 .is_none()
2881 );
2882 let vector: VectorRef = Arc::new(Int64Vector::from_slice([10, 10, 10, 10]));
2883 cache.put_repeated_vector(value.clone(), vector.clone());
2884 let cached = cache
2885 .get_repeated_vector(&ConcreteDataType::int64_datatype(), &value)
2886 .unwrap();
2887 assert_eq!(vector, cached);
2888 }
2889
2890 #[test]
2891 fn test_selector_result_cache() {
2892 let cache = CacheManager::builder()
2893 .selector_result_cache_size(1000)
2894 .build();
2895 let file_id = FileId::random();
2896 let key = SelectorResultKey {
2897 file_id,
2898 row_group_idx: 0,
2899 selector: TimeSeriesRowSelector::LastRow { after_merge: false },
2900 };
2901 assert!(cache.get_selector_result(&key).is_none());
2902 let result = Arc::new(SelectorResultValue::new(
2903 Vec::new(),
2904 ParquetReadColumns::from_deduped(Vec::new()),
2905 ));
2906 cache.put_selector_result(key, result);
2907 assert!(cache.get_selector_result(&key).is_some());
2908 }
2909
2910 #[test]
2911 fn test_prefilter_result_cache() {
2912 let disabled = CacheManager::builder().build();
2913 let file_id = FileId::random();
2914 let key = PrefilterKey::new(
2915 file_id,
2916 0,
2917 None,
2918 1,
2919 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()]),
2920 );
2921 let selection = Arc::new(BooleanBuffer::new_set(3));
2922
2923 disabled.put_prefilter_result(key.clone(), selection.clone());
2924 assert!(disabled.get_prefilter_result(&key).is_none());
2925
2926 let cache = Arc::new(
2927 CacheManager::builder()
2928 .prefilter_result_cache_size(1000)
2929 .build(),
2930 );
2931 assert!(cache.get_prefilter_result(&key).is_none());
2932 cache.put_prefilter_result(key.clone(), selection.clone());
2933 assert_eq!(
2934 cache.get_prefilter_result(&key).unwrap().as_ref(),
2935 selection.as_ref()
2936 );
2937
2938 let enable_all = CacheStrategy::EnableAll(cache.clone());
2939 assert!(enable_all.get_prefilter_result(&key).is_some());
2940
2941 let compaction = CacheStrategy::Compaction(cache.clone());
2942 assert!(compaction.get_prefilter_result(&key).is_none());
2943 compaction.put_prefilter_result(key.clone(), selection.clone());
2944 assert!(cache.get_prefilter_result(&key).is_some());
2945
2946 let disabled_strategy = CacheStrategy::Disabled;
2947 assert!(disabled_strategy.get_prefilter_result(&key).is_none());
2948 disabled_strategy.put_prefilter_result(key.clone(), selection);
2949 assert!(cache.get_prefilter_result(&key).is_some());
2950 }
2951
2952 #[test]
2953 fn test_prefilter_key_distinguishes_dimensions() {
2954 let file_id = FileId::random();
2955 let row_selection = RowSelection::from(vec![RowSelector::skip(1), RowSelector::select(3)]);
2956 let other_row_selection =
2957 RowSelection::from(vec![RowSelector::skip(2), RowSelector::select(2)]);
2958 let row_selection = PrefilterKey::row_selection_snapshot(Some(&row_selection));
2959 let other_row_selection = PrefilterKey::row_selection_snapshot(Some(&other_row_selection));
2960 let base = PrefilterKey::new(
2961 file_id,
2962 0,
2963 row_selection.clone(),
2964 1,
2965 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()]),
2966 );
2967
2968 assert_ne!(
2969 base,
2970 PrefilterKey::new(
2971 FileId::random(),
2972 0,
2973 row_selection.clone(),
2974 1,
2975 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()])
2976 )
2977 );
2978 assert_ne!(
2979 base,
2980 PrefilterKey::new(
2981 file_id,
2982 1,
2983 row_selection.clone(),
2984 1,
2985 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()])
2986 )
2987 );
2988 assert_ne!(
2989 base,
2990 PrefilterKey::new(
2991 file_id,
2992 0,
2993 other_row_selection,
2994 1,
2995 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()])
2996 )
2997 );
2998 assert_ne!(
2999 base,
3000 PrefilterKey::new(
3001 file_id,
3002 0,
3003 row_selection.clone(),
3004 1,
3005 SmallVec::from_vec(vec!["tag_0 IN ([b])".to_string()])
3006 )
3007 );
3008 assert_ne!(
3009 base,
3010 PrefilterKey::new(
3011 file_id,
3012 0,
3013 row_selection.clone(),
3014 2,
3015 SmallVec::from_vec(vec!["tag_0 IN ([a])".to_string()])
3016 )
3017 );
3018 let pk_group = PrefilterKey::new(
3019 file_id,
3020 0,
3021 row_selection,
3022 1,
3023 SmallVec::from_vec(vec![
3024 "tag_0 IN ([a])".to_string(),
3025 "tag_1 IN ([x])".to_string(),
3026 ]),
3027 );
3028 assert_ne!(base, pk_group);
3029 }
3030
3031 #[test]
3032 fn test_range_result_cache() {
3033 let cache = Arc::new(
3034 CacheManager::builder()
3035 .range_result_cache_size(1024 * 1024)
3036 .build(),
3037 );
3038
3039 let key = RangeScanCacheKey {
3040 region_id: RegionId::new(1, 1),
3041 row_groups: vec![(FileId::random(), 0)],
3042 scan: ScanRequestFingerprintBuilder {
3043 read_columns: ReadColumns::new(std::iter::empty()),
3044 read_column_types: vec![],
3045 filters: vec!["tag_0 = 1".to_string()],
3046 time_filters: vec![],
3047 series_row_selector: None,
3048 append_mode: false,
3049 filter_deleted: true,
3050 merge_mode: crate::region::options::MergeMode::LastRow,
3051 sequence_range: None,
3052 partition_expr_version: 0,
3053 }
3054 .build(),
3055 };
3056 let value = Arc::new(RangeScanCacheValue::empty());
3057
3058 assert!(cache.get_range_result(&key).is_none());
3059 cache.put_range_result(key.clone(), value.clone());
3060 assert!(cache.get_range_result(&key).is_some());
3061
3062 let enable_all = CacheStrategy::EnableAll(cache.clone());
3063 assert!(enable_all.get_range_result(&key).is_some());
3064
3065 let compaction = CacheStrategy::Compaction(cache.clone());
3066 assert!(compaction.get_range_result(&key).is_none());
3067 compaction.put_range_result(key.clone(), value.clone());
3068 assert!(cache.get_range_result(&key).is_some());
3069
3070 let disabled = CacheStrategy::Disabled;
3071 assert!(disabled.get_range_result(&key).is_none());
3072 disabled.put_range_result(key.clone(), value);
3073 assert!(cache.get_range_result(&key).is_some());
3074 }
3075
3076 #[test]
3077 fn test_range_result_cache_size_configures_limiter() {
3078 let cache_size = 3 * 1024_u64;
3079 let cache = CacheManager::builder()
3080 .range_result_cache_size(cache_size)
3081 .build();
3082
3083 assert_eq!(cache.range_result_cache_size(), cache_size as usize);
3084 assert_eq!(
3085 cache
3086 .range_result_cache
3087 .as_ref()
3088 .unwrap()
3089 .concat_memory()
3090 .permit_bytes(),
3091 RANGE_RESULT_CONCAT_MEMORY_PERMIT.as_bytes() as usize
3092 );
3093 assert_eq!(
3094 cache
3095 .range_result_cache
3096 .as_ref()
3097 .unwrap()
3098 .concat_memory()
3099 .available_permits(),
3100 (cache_size as usize).div_ceil(RANGE_RESULT_CONCAT_MEMORY_PERMIT.as_bytes() as usize)
3101 );
3102 }
3103
3104 #[tokio::test]
3105 async fn test_range_concat_memory_rejects_oversized_request() {
3106 let limiter = RangeResultMemoryLimiter::new(2 * 1024, 1024);
3107 assert_eq!(limiter.available_permits(), 2);
3108
3109 let err = limiter.acquire(10 * 1024).await.unwrap_err();
3110 assert!(
3111 err.to_string().contains("exceeds limiter capacity"),
3112 "unexpected error: {err}"
3113 );
3114 assert_eq!(limiter.available_permits(), 2);
3115 }
3116
3117 #[tokio::test]
3118 async fn test_range_concat_memory_allows_request_up_to_capacity() {
3119 let limiter = RangeResultMemoryLimiter::new(2 * 1024, 1024);
3120 let permit = limiter.acquire(2 * 1024).await.unwrap();
3121 assert_eq!(limiter.available_permits(), 0);
3122 drop(permit);
3123 assert_eq!(limiter.available_permits(), 2);
3124 }
3125
3126 #[rstest::rstest]
3127 #[case::empty_result(0, Some(1))]
3128 #[case::rounded_result(1025, Some(2))]
3129 #[case::exhausted(4096, None)]
3130 #[case::oversized(usize::MAX, None)]
3131 #[test]
3132 fn test_range_result_memory_reservation_is_nonblocking(
3133 #[case] result_size: usize,
3134 #[case] expected_permits: Option<usize>,
3135 ) {
3136 let limiter = RangeResultMemoryLimiter::new(4096, 1024);
3137 let blocker = limiter.try_acquire(1024).unwrap();
3138 let result_permit = limiter.try_acquire(result_size);
3139 assert_eq!(
3140 result_permit.as_ref().map(|permit| permit.num_permits()),
3141 expected_permits
3142 );
3143 assert_eq!(
3144 limiter.available_permits(),
3145 3 - expected_permits.unwrap_or(0)
3146 );
3147 drop(result_permit);
3148 assert_eq!(limiter.available_permits(), 3);
3149 drop(blocker);
3150 assert_eq!(limiter.available_permits(), 4);
3151 }
3152
3153 #[tokio::test]
3154 async fn test_evict_puffin_cache_clears_all_entries() {
3155 use std::collections::{BTreeMap, HashMap};
3156
3157 let cache = CacheManager::builder()
3158 .index_metadata_size(128)
3159 .index_content_size(128)
3160 .index_content_page_size(64)
3161 .index_result_cache_size(128)
3162 .puffin_metadata_size(128)
3163 .build();
3164 let cache = Arc::new(cache);
3165
3166 let region_id = RegionId::new(1, 1);
3167 let index_id = RegionIndexId::new(RegionFileId::new(region_id, FileId::random()), 0);
3168 let column_id: ColumnId = 1;
3169
3170 let bloom_cache = cache.bloom_filter_index_cache().unwrap().clone();
3171 let inverted_cache = cache.inverted_index_cache().unwrap().clone();
3172 let result_cache = cache.index_result_cache().unwrap();
3173 let puffin_metadata_cache = cache.puffin_metadata_cache().unwrap().clone();
3174
3175 let bloom_key = (
3176 index_id.file_id(),
3177 index_id.version,
3178 Arc::new(::index::target::IndexTarget::ColumnId(column_id)),
3179 Tag::Skipping,
3180 );
3181 bloom_cache
3182 .put_metadata(bloom_key.clone(), Arc::new(BloomFilterMeta::default()))
3183 .await;
3184 inverted_cache
3185 .put_metadata(
3186 (index_id.file_id(), index_id.version),
3187 Arc::new(InvertedIndexMetas::default()),
3188 )
3189 .await;
3190 let predicate = PredicateKey::new_bloom(Arc::new(BTreeMap::new()));
3191 let selection = Arc::new(RowGroupSelection::default());
3192 result_cache.put(predicate.clone(), index_id.file_id(), selection);
3193 let file_id_str = index_id.to_string();
3194 let metadata = Arc::new(FileMetadata {
3195 blobs: Vec::new(),
3196 properties: HashMap::new(),
3197 });
3198 puffin_metadata_cache
3199 .put_metadata(file_id_str.clone(), metadata)
3200 .await;
3201
3202 assert!(bloom_cache.get_metadata(bloom_key.clone()).await.is_some());
3203 assert!(
3204 inverted_cache
3205 .get_metadata((index_id.file_id(), index_id.version))
3206 .await
3207 .is_some()
3208 );
3209 assert!(result_cache.get(&predicate, index_id.file_id()).is_some());
3210 assert!(
3211 puffin_metadata_cache
3212 .get_metadata(&file_id_str)
3213 .await
3214 .is_some()
3215 );
3216
3217 cache.evict_puffin_cache(index_id).await;
3218
3219 assert!(bloom_cache.get_metadata(bloom_key.clone()).await.is_none());
3220 assert!(
3221 inverted_cache
3222 .get_metadata((index_id.file_id(), index_id.version))
3223 .await
3224 .is_none()
3225 );
3226 assert!(result_cache.get(&predicate, index_id.file_id()).is_none());
3227 assert!(
3228 puffin_metadata_cache
3229 .get_metadata(&file_id_str)
3230 .await
3231 .is_none()
3232 );
3233
3234 bloom_cache
3236 .put_metadata(bloom_key.clone(), Arc::new(BloomFilterMeta::default()))
3237 .await;
3238 inverted_cache
3239 .put_metadata(
3240 (index_id.file_id(), index_id.version),
3241 Arc::new(InvertedIndexMetas::default()),
3242 )
3243 .await;
3244 result_cache.put(
3245 predicate.clone(),
3246 index_id.file_id(),
3247 Arc::new(RowGroupSelection::default()),
3248 );
3249 puffin_metadata_cache
3250 .put_metadata(
3251 file_id_str.clone(),
3252 Arc::new(FileMetadata {
3253 blobs: Vec::new(),
3254 properties: HashMap::new(),
3255 }),
3256 )
3257 .await;
3258
3259 let strategy = CacheStrategy::EnableAll(cache.clone());
3260 strategy.evict_puffin_cache(index_id).await;
3261
3262 assert!(bloom_cache.get_metadata(bloom_key.clone()).await.is_none());
3263 assert!(
3264 inverted_cache
3265 .get_metadata((index_id.file_id(), index_id.version))
3266 .await
3267 .is_none()
3268 );
3269 assert!(result_cache.get(&predicate, index_id.file_id()).is_none());
3270 assert!(
3271 puffin_metadata_cache
3272 .get_metadata(&file_id_str)
3273 .await
3274 .is_none()
3275 );
3276 }
3277
3278 fn wide_region_metadata(column_count: u32) -> RegionMetadata {
3279 let region_id = RegionId::new(1024, 7);
3280 let mut builder = RegionMetadataBuilder::new(region_id);
3281 let mut primary_key = Vec::new();
3282
3283 for column_id in 0..column_count {
3284 let semantic_type = if column_id < 32 {
3285 primary_key.push(column_id);
3286 SemanticType::Tag
3287 } else {
3288 SemanticType::Field
3289 };
3290 let mut column_schema = ColumnSchema::new(
3291 format!("wide_column_{column_id}"),
3292 ConcreteDataType::string_datatype(),
3293 true,
3294 );
3295 column_schema
3296 .mut_metadata()
3297 .insert(format!("cache_key_{column_id}"), "cache_value".repeat(4));
3298 builder.push_column_metadata(ColumnMetadata {
3299 column_schema,
3300 semantic_type,
3301 column_id,
3302 });
3303 }
3304
3305 builder.push_column_metadata(ColumnMetadata {
3306 column_schema: ColumnSchema::new(
3307 "ts",
3308 ConcreteDataType::timestamp_millisecond_datatype(),
3309 false,
3310 ),
3311 semantic_type: SemanticType::Timestamp,
3312 column_id: column_count,
3313 });
3314 builder.primary_key(primary_key);
3315
3316 builder.build().unwrap()
3317 }
3318}