1use parquet::file::metadata::ParquetMetaData;
18
19use crate::sst::parquet::DEFAULT_READ_BATCH_SIZE;
20
21pub(crate) const TARGET_BATCH_BYTES: usize = 64 * 1024 * 1024;
23
24pub(crate) fn largest_row_group_statistic(metadata: &ParquetMetaData) -> Option<(u64, u64)> {
27 metadata
28 .row_groups()
29 .iter()
30 .map(|row_group| {
31 let uncompressed_bytes = row_group
32 .columns()
33 .iter()
34 .map(|column| column.uncompressed_size() as u64)
35 .sum();
36 (row_group.num_rows() as u64, uncompressed_bytes)
37 })
38 .max_by_key(|(_, uncompressed_bytes)| *uncompressed_bytes)
39}
40
41pub fn estimate_batch_size(sources: impl IntoIterator<Item = (u64, u64)>) -> usize {
47 let max_row_width = sources
48 .into_iter()
49 .filter_map(|(rows, bytes)| estimate_row_width(rows, bytes))
50 .max();
51
52 let Some(row_width) = max_row_width else {
53 return DEFAULT_READ_BATCH_SIZE;
54 };
55
56 (TARGET_BATCH_BYTES as u64 / row_width).clamp(1, DEFAULT_READ_BATCH_SIZE as u64) as usize
57}
58
59fn estimate_row_width(rows: u64, bytes: u64) -> Option<u64> {
61 if rows == 0 || bytes == 0 {
62 return None;
63 }
64
65 Some(bytes / rows + u64::from(!bytes.is_multiple_of(rows)))
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_estimate_batch_size_without_stats() {
74 assert_eq!(DEFAULT_READ_BATCH_SIZE, estimate_batch_size([]));
75 assert_eq!(
76 DEFAULT_READ_BATCH_SIZE,
77 estimate_batch_size([(0, 100), (100, 0)])
78 );
79 }
80
81 #[test]
82 fn test_estimate_batch_size_for_narrow_and_wide_rows() {
83 assert_eq!(DEFAULT_READ_BATCH_SIZE, estimate_batch_size([(100, 100)]));
84 assert_eq!(256, estimate_batch_size([(1, 256 * 1024)]));
85 assert_eq!(1, estimate_batch_size([(1, TARGET_BATCH_BYTES as u64 + 1)]));
86 }
87
88 #[test]
89 fn test_estimate_batch_size_uses_widest_known_source() {
90 assert_eq!(
91 256,
92 estimate_batch_size([(100, 0), (100, 100), (4, 1024 * 1024)])
93 );
94 }
95
96 #[test]
97 fn test_estimate_batch_size_saturates() {
98 assert_eq!(
99 DEFAULT_READ_BATCH_SIZE,
100 estimate_batch_size([(u64::MAX, u64::MAX)])
101 );
102 assert_eq!(1, estimate_batch_size([(1, u64::MAX)]));
103 assert_eq!(1, estimate_batch_size([(2, u64::MAX)]));
104 }
105}