-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: Use Cached Metadata for ListingTable Statistics #17022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
c51e16e
74359e0
c67ba4a
84f91b0
e3efe06
961fc24
f95ea46
0d6b5ff
c12bc46
c20b142
65fa8f7
2479a9f
230256c
17366f5
489763c
15914eb
a6d0bc0
fd761e7
f3ad6f1
5b4129a
c39b29f
2313aed
3bb321b
7d9128b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,7 +63,7 @@ use datafusion_physical_plan::Accumulator; | |
| use datafusion_physical_plan::{DisplayAs, DisplayFormatType, ExecutionPlan}; | ||
| use datafusion_session::Session; | ||
|
|
||
| use crate::reader::CachedParquetFileReaderFactory; | ||
| use crate::reader::{CachedParquetFileReaderFactory, CachedParquetMetaData}; | ||
| use crate::source::{parse_coerce_int96_string, ParquetSource}; | ||
| use async_trait::async_trait; | ||
| use bytes::Bytes; | ||
|
|
@@ -83,6 +83,7 @@ use parquet::arrow::async_reader::MetadataFetch; | |
| use parquet::arrow::{parquet_to_arrow_schema, ArrowSchemaConverter, AsyncArrowWriter}; | ||
| use parquet::basic::Type; | ||
|
|
||
| use datafusion_execution::cache::cache_manager::FileMetadataCache; | ||
| use parquet::errors::ParquetError; | ||
| use parquet::file::metadata::{ParquetMetaData, ParquetMetaDataReader, RowGroupMetaData}; | ||
| use parquet::file::properties::{WriterProperties, WriterPropertiesBuilder}; | ||
|
|
@@ -310,6 +311,8 @@ async fn fetch_schema_with_location( | |
| metadata_size_hint: Option<usize>, | ||
| file_decryption_properties: Option<&FileDecryptionProperties>, | ||
| coerce_int96: Option<TimeUnit>, | ||
| cache_metadata: bool, | ||
| file_metadata_cache: Option<Arc<dyn FileMetadataCache>>, | ||
| ) -> Result<(Path, Schema)> { | ||
| let loc_path = file.location.clone(); | ||
| let schema = fetch_schema( | ||
|
|
@@ -318,6 +321,8 @@ async fn fetch_schema_with_location( | |
| metadata_size_hint, | ||
| file_decryption_properties, | ||
| coerce_int96, | ||
| cache_metadata, | ||
| file_metadata_cache, | ||
| ) | ||
| .await?; | ||
| Ok((loc_path, schema)) | ||
|
|
@@ -371,6 +376,8 @@ impl FileFormat for ParquetFormat { | |
| self.metadata_size_hint(), | ||
| file_decryption_properties.as_ref(), | ||
| coerce_int96, | ||
| self.options.global.cache_metadata, | ||
| state.runtime_env().cache_manager.get_file_metadata_cache(), | ||
| ) | ||
| }) | ||
| .boxed() // Workaround https://github.com/rust-lang/rust/issues/64552 | ||
|
|
@@ -414,7 +421,7 @@ impl FileFormat for ParquetFormat { | |
|
|
||
| async fn infer_stats( | ||
| &self, | ||
| _state: &dyn Session, | ||
| state: &dyn Session, | ||
| store: &Arc<dyn ObjectStore>, | ||
| table_schema: SchemaRef, | ||
| object: &ObjectMeta, | ||
|
|
@@ -429,6 +436,8 @@ impl FileFormat for ParquetFormat { | |
| object, | ||
| self.metadata_size_hint(), | ||
| file_decryption_properties.as_ref(), | ||
| self.options.global.cache_metadata, | ||
| state.runtime_env().cache_manager.get_file_metadata_cache(), | ||
| ) | ||
| .await?; | ||
| Ok(stats) | ||
|
|
@@ -974,19 +983,47 @@ pub async fn fetch_parquet_metadata( | |
| meta: &ObjectMeta, | ||
| size_hint: Option<usize>, | ||
| #[allow(unused)] decryption_properties: Option<&FileDecryptionProperties>, | ||
| ) -> Result<ParquetMetaData> { | ||
| cache_metadata: bool, | ||
| file_metadata_cache: Option<Arc<dyn FileMetadataCache>>, | ||
| ) -> Result<Arc<ParquetMetaData>> { | ||
| // Check cache first if caching is enabled | ||
| if cache_metadata { | ||
| if let Some(cache) = &file_metadata_cache { | ||
| if let Some(cached_metadata) = cache.get(meta) { | ||
| if let Some(parquet_metadata) = cached_metadata | ||
| .as_any() | ||
| .downcast_ref::<CachedParquetMetaData>() | ||
| { | ||
| return Ok(Arc::clone(parquet_metadata.parquet_metadata())); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let file_size = meta.size; | ||
| let fetch = ObjectStoreFetch::new(store, meta); | ||
|
|
||
| let reader = ParquetMetaDataReader::new().with_prefetch_hint(size_hint); | ||
|
|
||
| #[cfg(feature = "parquet_encryption")] | ||
| let reader = reader.with_decryption_properties(decryption_properties); | ||
|
|
||
| reader | ||
| .load_and_finish(fetch, file_size) | ||
| .await | ||
| .map_err(DataFusionError::from) | ||
| let metadata = Arc::new( | ||
| reader | ||
| .load_and_finish(fetch, file_size) | ||
| .await | ||
| .map_err(DataFusionError::from)?, | ||
| ); | ||
|
|
||
| if cache_metadata { | ||
| if let Some(cache) = file_metadata_cache { | ||
| cache.put( | ||
| meta, | ||
| Arc::new(CachedParquetMetaData::new(Arc::clone(&metadata))), | ||
| ); | ||
| } | ||
| } | ||
|
Comment on lines
+996
to
+1094
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is an issue with the So this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh nice catch, will do! |
||
|
|
||
| Ok(metadata) | ||
| } | ||
|
|
||
| /// Read and parse the schema of the Parquet file at location `path` | ||
|
|
@@ -996,12 +1033,16 @@ async fn fetch_schema( | |
| metadata_size_hint: Option<usize>, | ||
| file_decryption_properties: Option<&FileDecryptionProperties>, | ||
| coerce_int96: Option<TimeUnit>, | ||
| cache_metadata: bool, | ||
| file_metadata_cache: Option<Arc<dyn FileMetadataCache>>, | ||
| ) -> Result<Schema> { | ||
| let metadata = fetch_parquet_metadata( | ||
| store, | ||
| file, | ||
| metadata_size_hint, | ||
| file_decryption_properties, | ||
| cache_metadata, | ||
| file_metadata_cache, | ||
| ) | ||
| .await?; | ||
| let file_metadata = metadata.file_metadata(); | ||
|
|
@@ -1026,10 +1067,18 @@ pub async fn fetch_statistics( | |
| file: &ObjectMeta, | ||
| metadata_size_hint: Option<usize>, | ||
| decryption_properties: Option<&FileDecryptionProperties>, | ||
| cache_metadata: bool, | ||
| file_metadata_cache: Option<Arc<dyn FileMetadataCache>>, | ||
| ) -> Result<Statistics> { | ||
| let metadata = | ||
| fetch_parquet_metadata(store, file, metadata_size_hint, decryption_properties) | ||
| .await?; | ||
| let metadata = fetch_parquet_metadata( | ||
| store, | ||
| file, | ||
| metadata_size_hint, | ||
| decryption_properties, | ||
| cache_metadata, | ||
| file_metadata_cache, | ||
| ) | ||
| .await?; | ||
| statistics_from_parquet_meta_calc(&metadata, table_schema) | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,7 @@ pub mod file_format; | |
| mod metrics; | ||
| mod opener; | ||
| mod page_filter; | ||
| mod reader; | ||
| pub mod reader; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why does this need to be made pub? I reverted the change and things still seem to compile just fine
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So downstream crates can use it. The reader file looked like it could generally be useful for downstream crates which is why I made the entire reader public. Currently in Sail we build our own cache for each cache type in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see -- my concern is that this change might be easy to accidentally undo / break in the future. Maybe to make it more deliberate, you could leave
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works with me! |
||
| mod row_filter; | ||
| mod row_group_filter; | ||
| pub mod source; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,7 +91,7 @@ impl DefaultParquetFileReaderFactory { | |
| /// This implementation does not coalesce I/O operations or cache bytes. Such | ||
| /// optimizations can be done either at the object store level or by providing a | ||
| /// custom implementation of [`ParquetFileReaderFactory`]. | ||
| pub(crate) struct ParquetFileReader { | ||
| pub struct ParquetFileReader { | ||
| pub file_metrics: ParquetFileMetrics, | ||
| pub inner: ParquetObjectReader, | ||
| } | ||
|
|
@@ -212,7 +212,7 @@ impl ParquetFileReaderFactory for CachedParquetFileReaderFactory { | |
| /// Implements [`AsyncFileReader`] for a Parquet file in object storage. Reads the file metadata | ||
| /// from the [`FileMetadataCache`], if available, otherwise reads it directly from the file and then | ||
| /// updates the cache. | ||
| pub(crate) struct CachedParquetFileReader { | ||
| pub struct CachedParquetFileReader { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise, I locally reverted these changes to visibility and everything seems to have compiled just fine
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| pub file_metrics: ParquetFileMetrics, | ||
| pub inner: ParquetObjectReader, | ||
| file_meta: FileMeta, | ||
|
|
@@ -253,10 +253,10 @@ impl AsyncFileReader for CachedParquetFileReader { | |
|
|
||
| // lookup if the metadata is already cached | ||
| if let Some(metadata) = metadata_cache.get(object_meta) { | ||
| if let Some(parquet_metadata) = | ||
| if let Some(cached_parquet_metadata) = | ||
| metadata.as_any().downcast_ref::<CachedParquetMetaData>() | ||
| { | ||
| return Ok(Arc::clone(&parquet_metadata.0)); | ||
| return Ok(Arc::clone(cached_parquet_metadata.parquet_metadata())); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -282,7 +282,17 @@ impl AsyncFileReader for CachedParquetFileReader { | |
| } | ||
|
|
||
| /// Wrapper to implement [`FileMetadata`] for [`ParquetMetaData`]. | ||
| struct CachedParquetMetaData(Arc<ParquetMetaData>); | ||
| pub struct CachedParquetMetaData(Arc<ParquetMetaData>); | ||
|
|
||
| impl CachedParquetMetaData { | ||
| pub fn new(metadata: Arc<ParquetMetaData>) -> Self { | ||
| Self(metadata) | ||
| } | ||
|
|
||
| pub fn parquet_metadata(&self) -> &Arc<ParquetMetaData> { | ||
| &self.0 | ||
| } | ||
| } | ||
|
|
||
| impl FileMetadata for CachedParquetMetaData { | ||
| fn as_any(&self) -> &dyn Any { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I re-reviewed these changes and since I think we always now have a file_metadata_cache we could probably make this be non optional. However, there are many tests that need to change