Skip to content

Commit 91c062f

Browse files
upgrade datafusion, arrow, object_store (#1772)
* upgrade datafusion, arrow, object_store * upgrade df to 55.0.0 * preserve order to true * track parquet null counts in manifest
1 parent 44bc788 commit 91c062f

9 files changed

Lines changed: 556 additions & 342 deletions

File tree

‎Cargo.lock‎

Lines changed: 295 additions & 264 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,31 @@ name = "parseable"
33
version = "3.1.1"
44
authors = ["Parseable Team <hi@parseable.com>"]
55
edition = "2024"
6-
rust-version = "1.91.0"
6+
rust-version = "1.94.0"
77
categories = ["logs", "observability", "metrics", "traces"]
88
build = "build.rs"
99

1010
[dependencies]
1111
# Arrow and DataFusion ecosystem
12-
arrow = "58.0.0"
13-
arrow-array = "58.0.0"
14-
arrow-flight = { version = "58.0.0", features = [
12+
arrow = "59.2.0"
13+
arrow-array = "59.2.0"
14+
arrow-flight = { version = "59.2.0", features = [
1515
"tls-aws-lc",
1616
"tls-native-roots",
1717
] }
18-
arrow-ipc = { version = "58.0.0", features = ["zstd"] }
19-
arrow-json = "58.0.0"
20-
arrow-schema = { version = "58.0.0", features = ["serde"] }
21-
arrow-select = "58.0.0"
22-
datafusion = "53.0.0"
23-
datafusion-proto = "53.0.0"
24-
object_store = { version = "0.13.1", features = [
18+
arrow-ipc = { version = "59.2.0", features = ["zstd"] }
19+
arrow-json = "59.2.0"
20+
arrow-schema = { version = "59.2.0", features = ["serde"] }
21+
arrow-select = "59.2.0"
22+
datafusion = "55.0.0"
23+
datafusion-proto = "55.0.0"
24+
object_store = { version = "0.13.2", features = [
2525
"cloud",
2626
"aws",
2727
"azure",
2828
"gcp",
2929
] }
30-
parquet = "58.0.0"
30+
parquet = "59.2.0"
3131

3232
# Web server and HTTP-related
3333
actix-cors = "0.7.0"
@@ -222,7 +222,7 @@ anyhow = "1.0"
222222

223223
[dev-dependencies]
224224
rstest = "0.26.1"
225-
arrow = "58.0.0"
225+
arrow = "59.2.0"
226226
temp-dir = "0.1.14"
227227

228228
[package.metadata.parseable_ui]

‎src/catalog/column.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ fn is_valid_float_range(min: f64, max: f64) -> bool {
201201
pub struct Column {
202202
pub name: String,
203203
pub stats: Option<TypedStatistics>,
204+
#[serde(default, skip_serializing_if = "Option::is_none")]
205+
pub null_count: Option<u64>,
204206
pub uncompressed_size: u64,
205207
pub compressed_size: u64,
206208
}

‎src/catalog/manifest.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use itertools::Itertools;
2222
use parquet::file::{
2323
metadata::{RowGroupMetaData, SortingColumn},
2424
reader::FileReader,
25+
statistics::Statistics,
2526
};
2627

2728
use crate::metastore::metastore_traits::MetastoreObject;
@@ -267,6 +268,10 @@ fn column_statistics(row_groups: &[RowGroupMetaData]) -> HashMap<String, Column>
267268
if let Some(entry) = columns.get_mut(&col_name) {
268269
entry.compressed_size += col.compressed_size() as u64;
269270
entry.uncompressed_size += col.uncompressed_size() as u64;
271+
entry.null_count = entry
272+
.null_count
273+
.zip(col.statistics().and_then(Statistics::null_count_opt))
274+
.and_then(|(current, other)| current.checked_add(other));
270275
if let Some(other) = col.statistics().and_then(|stats| stats.try_into().ok()) {
271276
entry.stats = entry.stats.clone().and_then(|this| this.update(other));
272277
}
@@ -276,6 +281,7 @@ fn column_statistics(row_groups: &[RowGroupMetaData]) -> HashMap<String, Column>
276281
Column {
277282
name: col_name,
278283
stats: col.statistics().and_then(|stats| stats.try_into().ok()),
284+
null_count: col.statistics().and_then(Statistics::null_count_opt),
279285
uncompressed_size: col.uncompressed_size() as u64,
280286
compressed_size: col.compressed_size() as u64,
281287
},
@@ -301,6 +307,7 @@ mod codec_tests {
301307
.map(|c| Column {
302308
name: format!("some_reasonably_long_column_name_{c}"),
303309
stats: None,
310+
null_count: None,
304311
uncompressed_size: 1024,
305312
compressed_size: 512,
306313
})

‎src/parseable/staging/reader.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ mod tests {
297297
types::Int64Type,
298298
};
299299
use arrow_ipc::writer::{
300-
CompressionContext, DictionaryTracker, IpcDataGenerator, IpcWriteOptions, StreamWriter,
300+
DictionaryTracker, IpcDataGenerator, IpcWriteContext, IpcWriteOptions, StreamWriter,
301301
write_message,
302302
};
303303
use arrow_schema::{DataType, Field, Schema};
@@ -389,7 +389,7 @@ mod tests {
389389
let options = IpcWriteOptions::default();
390390
let mut dictionary_tracker = DictionaryTracker::new(error_on_replacement);
391391
let data_gen = IpcDataGenerator {};
392-
let mut compression_context = CompressionContext::default();
392+
let mut compression_context = IpcWriteContext::default();
393393

394394
let mut buf = Vec::new();
395395
let rb1 = rb(1);

‎src/parseable/streams.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ impl Stream {
750750
};
751751
props = props
752752
.set_column_bloom_filter_enabled(column_path.clone(), true)
753-
.set_column_bloom_filter_ndv(column_path, METRIC_NAME_BLOOM_FILTER_NDV)
753+
.set_column_bloom_filter_max_ndv(column_path, METRIC_NAME_BLOOM_FILTER_NDV)
754754
.set_bloom_filter_position(bloom_filter_position);
755755
}
756756
sorting_column_vec.push(SortingColumn {

‎src/query/listing_table_builder.rs‎

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,10 @@ use datafusion::{
2525
listing::{ListingOptions, ListingTable, ListingTableConfig, ListingTableUrl},
2626
},
2727
error::DataFusionError,
28-
logical_expr::col,
2928
};
3029
use itertools::Itertools;
3130

32-
use crate::{
33-
OBJECT_STORE_DATA_GRANULARITY, event::DEFAULT_TIMESTAMP_KEY, storage::ObjectStorage,
34-
utils::time::TimeRange,
35-
};
31+
use crate::{OBJECT_STORE_DATA_GRANULARITY, storage::ObjectStorage, utils::time::TimeRange};
3632

3733
use super::PartialTimeFilter;
3834

@@ -121,23 +117,14 @@ impl ListingTableBuilder {
121117
self,
122118
schema: Arc<Schema>,
123119
map: impl Fn(Vec<String>) -> Vec<ListingTableUrl>,
124-
time_partition: Option<String>,
125120
) -> Result<Option<Arc<ListingTable>>, DataFusionError> {
126121
if self.listing.is_empty() {
127122
return Ok(None);
128123
}
129124

130-
let file_sort_order = vec![vec![
131-
time_partition
132-
.map_or_else(|| col(DEFAULT_TIMESTAMP_KEY), col)
133-
.sort(true, false),
134-
]];
135125
let file_format = ParquetFormat::default().with_enable_pruning(true);
136-
let listing_options = ListingOptions::new(Arc::new(file_format))
137-
.with_file_extension(".parquet")
138-
.with_file_sort_order(file_sort_order)
139-
.with_collect_stat(true)
140-
.with_target_partitions(1);
126+
let listing_options =
127+
ListingOptions::new(Arc::new(file_format)).with_file_extension(".parquet");
141128
let config = ListingTableConfig::new_with_multi_paths(map(self.listing))
142129
.with_listing_options(listing_options)
143130
.with_schema(schema);

‎src/query/mod.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ impl Query {
284284
let mut config = SessionConfig::default()
285285
.with_parquet_pruning(true)
286286
.with_prefer_existing_sort(true)
287+
.with_collect_statistics(true)
287288
//batch size has been made configurable via environment variable
288289
//default value is 20000
289290
.with_batch_size(PARSEABLE.options.execution_batch_size)
@@ -296,6 +297,24 @@ impl Query {
296297
// Reorder filters allows DF to decide the order of filters minimizing the cost of filter evaluation
297298
config.options_mut().execution.parquet.reorder_filters = true;
298299
config.options_mut().execution.parquet.binary_as_string = true;
300+
// Allow unordered Parquet scans to split files into row-group morsels and let idle scan
301+
// partitions steal work. Ordered scans set FileScanConfig::preserve_order, which prevents
302+
// file reassignment while retaining their advertised output ordering.
303+
config
304+
.options_mut()
305+
.execution
306+
.enable_file_stream_work_stealing = true;
307+
config.options_mut().optimizer.repartition_file_scans = true;
308+
309+
// Feed changing TopK / aggregate bounds into Parquet pruning. This is especially useful
310+
// for observability queries such as `ORDER BY p_timestamp DESC LIMIT N`.
311+
config
312+
.options_mut()
313+
.optimizer
314+
.enable_dynamic_filter_pushdown = true;
315+
config.options_mut().optimizer.enable_topk_aggregation = true;
316+
config.options_mut().optimizer.enable_topk_repartition = true;
317+
config.options_mut().optimizer.enable_sort_pushdown = true;
299318
// Bump footer-read hint from the 512 KiB default. Streams with
300319
// many label columns + page-indexed value columns can have
301320
// parquet footers in the 1-2 MiB range; sizing the hint above
@@ -458,6 +477,7 @@ impl Query {
458477
LogicalPlan::Explain(Explain {
459478
explain_format: plan.explain_format,
460479
verbose: plan.verbose,
480+
show_statistics: plan.show_statistics,
461481
stringified_plans: vec![
462482
transformed
463483
.data

0 commit comments

Comments
 (0)