refactor: remove redundant partitioned_by_file_group file scan field#23189
Conversation
|
Thank you for opening this pull request! Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch). Details |
e9ed249 to
30a0d3c
Compare
kosiew
left a comment
There was a problem hiding this comment.
@Phoenix500526
Thanks for the refactor here.
think there is one backward compatibility issue in the protobuf deserialization path that should be addressed before merging.
30a0d3c to
d1f5ed7
Compare
There was a problem hiding this comment.
@Phoenix500526
Thanks for the follow-up here. The legacy protobuf compatibility concern looks addressed now. One remaining release-facing item: since this intentionally removes public Rust APIs in favor of output_partitioning, please document the migration path in the version-specific upgrade guide.
|
FYI @gene-bordegaray |
gene-bordegaray
left a comment
There was a problem hiding this comment.
Flushing these comments as I think this is pretty critical for the direciton partitining is heading.
We claim hash partitioning on this when in reality we are not. This is not a fault of this PR but something we should cnsider in how we want to handle this.
This will have correctness implications for things like turning on dynamic filters on for a preserved hash parttioning stemming from this.
Seems like the right direction may be another type of partitioning
| Some(output_partitioning) | ||
| } else if partitioned_by_file_group { | ||
| // Files are grouped by partition column values: declare Hash | ||
| // partitioning on those columns so the optimizer can skip hash |
There was a problem hiding this comment.
I am realizing this is another type of partitioning we are misrepresenting. See issue: #23236 .
I think in this case we aren't actually hash partitioned. We are some other type of partitioning something like ValuePartitioned
There was a problem hiding this comment.
The comment is updated.
There was a problem hiding this comment.
The concern is not the comment itself but the actual behavior of this feature is lying to DF. This claims a hash partitioning but in reality we are not partitioned by the hash function at all. Let me know if you have questions 😄
| /// `schema`) with `partition_count` partitions. Returns `None` when there are no | ||
| /// partition columns. Callers use this to declare the output partitioning of a scan | ||
| /// whose file groups are organized by partition column values. | ||
| pub fn hash_partitioning_from_partition_fields( |
There was a problem hiding this comment.
yes, we aren't actually hash it seems
@alamb it seems we have some other type of partitioning we need to represent correctly 🤔
There was a problem hiding this comment.
also cc: @gabotechs @adriangb @NGA-TRAN and @jayshrivastava
There was a problem hiding this comment.
Thanks for the review. I've renamed it as output_partitioning_from_partition_fields.
|
I merged up to resolve a conflict |
`FileScanConfig` had two overlapping ways to declare file scan output partitioning: the `partitioned_by_file_group` bool and `output_partitioning`. Collapse them onto `output_partitioning` as the single source of truth. - Remove the `partitioned_by_file_group` field, the builder field, and the `with_partitioned_by_file_group` builder method. - `ListingTable` now derives the partition-column `Partitioning::Hash` once its file groups are finalized and passes it via `with_output_partitioning`; `hash_partitioning_from_partition_fields` is made `pub` for this. - proto already round-trips `output_partitioning`, so the now-vestigial wire bool is left unset on write and ignored on read (the proto field is kept for backward compatibility). Closes apache#23099. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com>
After collapsing `partitioned_by_file_group` onto `output_partitioning`, the declared Hash partitioning is now stored on the scan and therefore rendered by `DataSourceExec`'s Display. Update the affected sqllogictest expected plans accordingly. Behavior is unchanged; only the EXPLAIN text gains an `output_partitioning=Hash(...)` entry on partition-grouped scans. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com>
Honor the old partitioned_by_file_group protobuf flag when output_partitioning is absent, so previously serialized plans still decode to hash output partitioning. Rename test-only helpers to declared output partitioning wording. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com>
Add an upgrade guide note for the removal of partitioned_by_file_group and its builder API. Point users to output_partitioning and with_output_partitioning. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com>
f5bb5fb to
2f4967b
Compare
gene-bordegaray
left a comment
There was a problem hiding this comment.
This PR makes sense from an existing behavior standpoint bu raises concerns with what we should do gong forward 👍
|
|
||
| Some(Partitioning::Hash(exprs, partition_count)) |
There was a problem hiding this comment.
The point I am making is we need to discuss this.
We are claiming Partitioning::Hash on these while in reality we are not has partitioned we are actaully partitioned by the partition column values. This was the same situation which users faced when decalring Partitioning::Range as hash. This behavior was here before but we should discuss what this means going forward with this feature.
Should we:
- Introduce another variant of the
Partitioningenum likeValuePartitioned(this would be much easier now thatPartitioning::Rangehas really started to lay the ground work for doing this - Does this justify
Partitioningbecoming a trait of some type where users can implmenet their own custome partitioning types - Some other option I am not seeing
There was a problem hiding this comment.
I think ValuePartitioned is covered by range, right?
There was a problem hiding this comment.
We are claiming Partitioning::Hash on these while in reality we are not has partitioned we are actaully partitioned by the partition column values. This was the same situation which users faced when decalring Partitioning::Range as hash.
+1. This is exactly the pain I'm experiencing. It leads to correctness issues ex. dynamic filtering breaks if you are not hash partitioned but claim to be hash partitioned.
There was a problem hiding this comment.
Yes. We would have to take the column parttion values, sort them, and represent them as a range partitioning where each partition only contains the intended values.
Example:
partition 1: col = A
partition 2: col = Z
partition 3: col = M
Represented like:
Range Partitioning:
split points: [A, M, Z]
sort order: Asc
Thus:
partition 1: col <= A
parttion 2: A < col <= M
partition 3: M <= col (which contains Z)
There was a problem hiding this comment.
I think we shoudl actually pursue this ^ rather than hash in this PR.
@jayshrivastava @gabotechs thoughts?
There was a problem hiding this comment.
My first instinct would be to remove Partitioning::Hash in favor of (very rought draft):
pub enum Partitioning {
...
#[deprecated]
Hash(Vec<Expr>),
Value {
expressions: Vec<Expr>, // Same as Partitioning::Hash
method: &'static str // The value partitioning method (e.g., "ahash(0,0,0,0)", "ahash(1,2,3,4)" ...)
}
}There was a problem hiding this comment.
Ya this is a great point. A question I have is would we be able to represent the partitioning scheme that is happening with this column value partitioning?
Maybe the method could be an enum with some variants like column baked in?
There was a problem hiding this comment.
🤔 That's a good question. However, how would that work regarding partitioning compatibility?
for example, Partitioning::Value { .., method: Ahash0 } is co-partitioned with Partitioning::Value { .., method: Ahash0 }, but would Partitioning::Value { .., method: Column } be co-partitioned with other Partitioning::Value { .., method: Column }?
There was a problem hiding this comment.
No we couldnt claim that just given {..., method: Column} unless we gave information about the values / ordering of the partitioned columns. There may be other ways to describe this so we can prove co-partitioning like a having the Column enum variant carry a vec of the values where index position represents the partition number
There was a problem hiding this comment.
It seems to me like this thread is raising an excellent point (we could use a more precise partitioning in this case of hive partitioning)
I think main also chooses this Hash partitioning for HIVE tables
Thus I think we should merge this PR in and then file a ticket to discuss/ plan for potentially using Value partitioning for HIVE partitioned tables in the future.
Avoid naming the partitioned-file-group helper as hash-specific. This keeps the existing physical representation while making the API and docs less likely to imply literal hash partitioning. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com>
2f4967b to
9d6db0d
Compare
alamb
left a comment
There was a problem hiding this comment.
Thank you @Phoenix500526 @gabotechs @gene-bordegaray and @kosiew -- let's merge this one in and keep iterating as a follow on
|
|
||
| Some(Partitioning::Hash(exprs, partition_count)) |
There was a problem hiding this comment.
It seems to me like this thread is raising an excellent point (we could use a more precise partitioning in this case of hive partitioning)
I think main also chooses this Hash partitioning for HIVE tables
Thus I think we should merge this PR in and then file a ticket to discuss/ plan for potentially using Value partitioning for HIVE partitioned tables in the future.
| batch_size: conf.batch_size.map(|s| s as u64), | ||
| projection_exprs, | ||
| partitioned_by_file_group: Some(conf.partitioned_by_file_group), | ||
| // Partition grouping is now encoded in `output_partitioning`; this legacy |
There was a problem hiding this comment.
FWIW we don't typically guartantee wire format compatibility: https://docs.rs/datafusion-proto/latest/datafusion_proto/#version-compatibility
Maybe we can remove the backward compatiblity shim in a follow on
Which issue does this PR close?
Rationale for this change
FileScanConfighad two overlapping ways to declare a file scan's outputpartitioning:
partitioned_by_file_group: bool— a shorthand meaning "the file groups areorganized by Hive partition column values, so the output is Hash-partitioned on
those columns", and
output_partitioning: Option<Partitioning>— a general, explicit declaredpartitioning (added in Add
ListingOptions::output_partitioningandFileScanConfig::output_partitioningfor pre-defined file partitioning #22657).The bool is just a lazy shorthand for one specific
output_partitioningvalue(
Partitioning::Hashover the partition columns), and every place that consumedit (
output_partitioning(),repartitioned(),create_sibling_state()) alreadychecked
output_partitioning.is_some() || partitioned_by_file_group. Keeping bothis redundant and the
ListingTablebuilder ended up setting both. This PR makesoutput_partitioningthe single source of truth.What changes are included in this PR?
Following the issue's first option ("Remove
partitioned_by_file_group"):FileScanConfig::partitioned_by_file_group, the correspondingFileScanConfigBuilderfield, and thewith_partitioned_by_file_groupbuildermethod.
ListingTable::scannow derives the partition-columnPartitioning::Hashitself (once its file groups are finalized, so the partition count is correct)
and passes it through the existing
with_output_partitioning. The previouswith_output_partitioning(declared)+with_partitioned_by_file_group(...)double-set is collapsed into one branch.
hash_partitioning_from_partition_fieldsis madepubsoListingTable(a separate crate) can reuse the derivation instead of duplicating the
column-index resolution.
output_partitioning, so no behavior is lost: thenow-vestigial
partitioned_by_file_groupwire field is left unset on write andignored on read. The field is kept in the
.protodefinition for backwardcompatibility.
output_partitioning()/create_sibling_state()/repartitioned()now keysolely off
output_partitioning.Are these changes tested?
Yes — by existing tests, updated to the new single-field model:
datafusion-datasource:test_output_partitioning_with_partition_columns,test_output_partitioning_no_partition_columns,test_declared_output_partitioning_projects_with_scan, and thefile_streamwork-stealing test
morsel_partitioned_by_file_group_keeps_files_local(whichverifies that a declared output partitioning keeps each stream's files local).
datafusion-proto:roundtrip_parquet_exec_output_partitioning(and the otherroundtrip_parquet_exec_*cases) cover the partitioning round-trip. The oldroundtrip_parquet_exec_partitioned_by_file_grouptest exercised the removedAPI and is dropped, as its coverage is subsumed by the
output_partitioninground-trip test.
All of the above pass, along with
cargo fmt --all --checkandcargo clippy --all-targets --all-features -- -D warningsfor the affectedcrates.
Are there any user-facing changes?
Yes — public API changes :
FileScanConfig::partitioned_by_file_groupfield and theFileScanConfigBuilder::with_partitioned_by_file_groupmethod. Callers shouldset
with_output_partitioning(Some(Partitioning::Hash(..)))instead (or use thenow-public
hash_partitioning_from_partition_fieldshelper).hash_partitioning_from_partition_fieldsis nowpub.Query results, optimizer decisions (e.g. eliding
RepartitionExec), and theserialized (proto) wire format are unchanged. There is one display-only
change: EXPLAIN now renders
output_partitioning=Hash(...)onDataSourceExecfor partition-grouped scans. The scan already produced that partitioning before
(it was derived lazily inside
output_partitioning()); it is now stored on theoutput_partitioningfield and therefore shown. Therepartition_subset_satisfactionandpreserve_file_partitioningslt expectedplans are updated accordingly.
cargo-semver-checkswill flag the removals as breaking, which is expected forthis cleanup.