Fix optimized parquet reader complex hive types processing#9156
Fix optimized parquet reader complex hive types processing#9156kgalieva wants to merge 1 commit intoprestodb:masterfrom
Conversation
|
@nezihyigitbasi could you please take a look |
|
@nezihyigitbasib Any comments or suggestions? |
|
@kgalieva does this patch handle arbitrary level of nesting for complex types? |
|
@nezihyigitbasi yes it does |
nezihyigitbasi
left a comment
There was a problem hiding this comment.
I made a quick pass and left some comments/questions. I will take a detailed look in the second round.
There was a problem hiding this comment.
what does this change solve? how abut the cases where valueIsNull() == false (the else part)?
There was a problem hiding this comment.
For non required fields there are three options:
- Value is defined
- Value is null
- Value does not exist, because one of it's optional parent fields is null.
There was a problem hiding this comment.
please add a comment that explains why definitionLevel == columnDescriptor.getMaxDefinitionLevel() - 1 indicates a null value.
There was a problem hiding this comment.
we don't mark method parameters as final.
There was a problem hiding this comment.
This class is a copy of https://github.com/apache/parquet-mr/blob/master/parquet-hive/parquet-hive-storage-handler/src/main/java/org/apache/hadoop/hive/ql/io/parquet/write/DataWritableWriteSupport.java
The only difference is the writer. I added copy of org.apache.hadoop.hive.ql.io.parquet.write.DataWritableWriter with patch for empty array and empty map (https://issues.apache.org/jira/browse/HIVE-13632) to be able to test how optimised parquet reader processes empty arrays/maps.
If you insist, I will apply the changes suggested below. But I'd rather leave those classes as they are defined in hive with just one necessary patch.
There was a problem hiding this comment.
OK then please add a class level javadoc saying that we copied this file from this and that etc. and leave this as is.
There was a problem hiding this comment.
You can define int lastOffset = offsets.get(offsets.size() - 1); and update usages below.
There was a problem hiding this comment.
Can you explain what valueOffset tracks here?
There was a problem hiding this comment.
refactored this method. Here valueOffset used to track offset(number of elements) of the array or map being processed. In case when array/map is defined and not empty, offset value is increased by the number of elements in a collection.
There was a problem hiding this comment.
the same as above, need your opinion regarding updating copy of Hive Parquet writer
There was a problem hiding this comment.
tgt -> bytes or target ?
There was a problem hiding this comment.
the same as above, need your opinion regarding updating copy of Hive Parquet writer
There was a problem hiding this comment.
this method looks similar to skipValues (the core read logic is the same), can we merge the two somehow so that we get rid of code duplication?
There was a problem hiding this comment.
what is the reason behind this change?
There was a problem hiding this comment.
this line was moved to skipValues method
There was a problem hiding this comment.
this part of the code looks error prone, please add some comments/links to the relevant parts of the spec that explains what you do here.
e27d714 to
d3e02a6
Compare
|
@nezihyigitbasi Made changes you suggested in comments. Please review one more time when you have time :) |
|
@nezihyigitbasi any review updates on this PR? |
nezihyigitbasi
left a comment
There was a problem hiding this comment.
thanks @kgalieva! I made another pass and left some more comments. This is getting close.
There was a problem hiding this comment.
!isRequired && (definitionLevel == maxDefinitionLevel - 1) will make this easier to read.
There was a problem hiding this comment.
isRequired -> required
There was a problem hiding this comment.
rename isNullField -> isValueNull
There was a problem hiding this comment.
what happens when definitionLevel != columnDescriptor.getMaxDefinitionLevel() && !isNullValue()? If that's not a valid case please add a final else and throw an appropriate exception.
There was a problem hiding this comment.
@nezihyigitbasi This is a valid case. Value can be undefined when it belongs to block under some nested structure.
For primitive columns it's true, that their value either defined or null.
For columns with nested structure it's different, because null can be at any level of optional field.
For example, column can be a struct with two nested optional fields A.B
In this case:
A can be defined or null
B can be defined, null or not defined (meaning A was null).
Presto spi blocks contain only defined and null values. So when block with values for B field is being written, cases where A field is null need to skipped.
When complex type is being decoded, be it RowType.getObjectValue(), ArrayType.getObjectValue() or MapType.getObjectValue() they all first check parent value for being not null and only if it's not, decode nested values from underlying blocks.
This means, that value from block B will be read only if corresponding parent field A value was not null.
There was a problem hiding this comment.
please rename as this is confusing. maybe just testStructOfMaps
There was a problem hiding this comment.
the parameter list is huge here so let's reformat this as:
private void calculateStructOffsets(
String[] path,
IntList structOffsets,
BooleanList structIsNull,
IntList definitionLevels,
IntList repetitionLevels,
IntList fieldDefinitionLevels,
IntList fieldRepetitionLevels)
There was a problem hiding this comment.
definitionLevels and repetitionLevels are for the struct I guess, so let's rename them as such. e.g., structDefinitionLevels ...
There was a problem hiding this comment.
please rename i to be more descriptive.
d3e02a6 to
47ecdc7
Compare
|
there are compilation failures. |
47ecdc7 to
520afc6
Compare
520afc6 to
5dcf82b
Compare
|
sorry for the late reply. I will send parquet data to reproduce the problem to @kgalieva |
|
Thanks @zhenxiao! When do you think you will be able to send me a parquet file? |
|
Any updates on this PR? |
|
@meghapthakkar unfortunately, no. I'm still waiting for @zhenxiao to send me the testing data to reproduce his problem. |
|
Hi @zhenxiao, just a friendly reminder about the file :) |
|
Hi @zhenxiao! Any updates from you? |
|
We at Netflix has been running with this patch for over 3 months now. The few fixes that we had to make are all part of the updated patch. I would recommend to merge this in. |
|
sorry for the late reply. I will do one more round test, with our highly nested data. @kgalieva will send you parquet file to reproduce problems, or greenlight. |
|
@zhenxiao @Parth-Brahmbhatt Thank you very much for your help with testing it! @zhenxiao I would really appreciate your feedback and will be happy to check your test cases! :) |
nezihyigitbasi
left a comment
There was a problem hiding this comment.
@kgalieva I left some more comments. Thanks again for working on this.
@Parth-Brahmbhatt Thanks for your input and testing this patch.
@zhenxiao Please provide feedback asap as we want to merge this soon.
This has been open for quite some time and I want to merge this as soon as possible. Thanks for everyone's patience.
There was a problem hiding this comment.
requireNonNull(type, "type is required");
There was a problem hiding this comment.
requireNonNull(children, "children is required");
There was a problem hiding this comment.
return ImmutableList.copyOf(children);
There was a problem hiding this comment.
Changed children type to ImmutableList instead, to make it obvious that copying is not needed
There was a problem hiding this comment.
We can use ImmutableList.Builder<Optional<Field>> fieldsBuilder = ImmutableList.builder(); to be consistent with the other fields.
There was a problem hiding this comment.
What's the reason behind removing the systemMemoryContext field? It seems unrelated to the purpose of the PR. Same for the dataSource field.
There was a problem hiding this comment.
ParquetPageSource class has a lot of constructor parameters. To simplify it a little bit it is possible to get systemMemoryContext and dataSource from parquetReader.
There was a problem hiding this comment.
static import calculateCollectionOffsets.
There was a problem hiding this comment.
Same question about optional above applies to here and to L159 below.
There was a problem hiding this comment.
This is because an array always has a single inner element and a map always has two inner elements (key and value). We do isPresent check for structs because some parameters can be missing due to schema evolution.
There was a problem hiding this comment.
static import OPTIONAL
There was a problem hiding this comment.
namedTypeSignature.getName() is Optional so get call may fail.
There was a problem hiding this comment.
Struct fields are always named (unlike, for instance, array and map inner elements). This is why we can safely call get() here.
There was a problem hiding this comment.
this else is unnecessary, we can just return.
zhenxiao
left a comment
There was a problem hiding this comment.
a few minor things during my testing
There was a problem hiding this comment.
following Presto coding style, all parameters in the same line
There was a problem hiding this comment.
following Presto coding style, all parameters in the same line
There was a problem hiding this comment.
group final variables and non-final variables
9544e17 to
58d395b
Compare
- Fix reading repeated fields, when parquet consists of multiple pages, so the beginning of the field can be on one page and it's ending on the next page. - Support empty arrays read - Determine null values of optional fields - Add tests for hive complex types: arrays, maps and structs - Rewrite tests to read parquets consising of multiple pages - Add TestDataWritableWriter with patch for empty array and empty map because the bag https://issues.apache.org/jira/browse/HIVE-13632 is already fixed in current hive version, so presto should be able to read empty arrays too - Backward-compatibility rules support for arrays https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#lists - Backward-compatibility rules support for maps https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#maps
58d395b to
592b74a
Compare
|
@nezihyigitbasi @zhenxiao thank you the feedback! I've made the changes you suggested and replied to comments you left. |
|
Merged, thanks @kgalieva. Also thanks everyone for their contributions and patience. |
|
this patch works good on our highly nested data, it passed 2 day's production workloads, very nice contribution @kgalieva |
|
Hi @kgalieva we found a problem for reading a table of schema: "_hoodie_commit_time","varchar","","" Seems related to reading of: array<structtype:string> Could you please help take a look? I sent you an email with attached parquet file. |
|
get some clue for it. @nezihyigitbasi @kgalieva Will send to you for review. |
|
a fix: |
Fix reading repeated fields, when parquet consists of multiple pages,
so the beginning of the field can be on one page
and it's ending on the next page.
Support empty arrays read
Determine null values of optional fields
Add tests for hive complex types: arrays, maps and structs
Rewrite tests to read parquets consising of multiple pages
Add TestDataWritableWriter with patch for empty array and empty map
because the bug https://issues.apache.org/jira/browse/HIVE-13632
is already fixed in current hive version,
so presto should be able to read empty arrays too