Skip to content

Fix optimized parquet reader complex hive types processing#9156

Closed
kgalieva wants to merge 1 commit intoprestodb:masterfrom
kgalieva:complex-hive-types
Closed

Fix optimized parquet reader complex hive types processing#9156
kgalieva wants to merge 1 commit intoprestodb:masterfrom
kgalieva:complex-hive-types

Conversation

@kgalieva
Copy link
Contributor

@kgalieva kgalieva commented Oct 15, 2017

  • 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

@kgalieva
Copy link
Contributor Author

@nezihyigitbasi could you please take a look

@nezihyigitbasi
Copy link
Contributor

@kgalieva PR #9110 also fixes the bugs in reading repeated fields. I will first review that and then we can take a look at this one.

@nezihyigitbasi nezihyigitbasi self-assigned this Oct 16, 2017
@kgalieva
Copy link
Contributor Author

@nezihyigitbasib Any comments or suggestions?

@nezihyigitbasi
Copy link
Contributor

@kgalieva does this patch handle arbitrary level of nesting for complex types?

@kgalieva
Copy link
Contributor Author

@nezihyigitbasi yes it does

Copy link
Contributor

@nezihyigitbasi nezihyigitbasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a quick pass and left some comments/questions. I will take a detailed look in the second round.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this change solve? how abut the cases where valueIsNull() == false (the else part)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non required fields there are three options:

  1. Value is defined
  2. Value is null
  3. Value does not exist, because one of it's optional parent fields is null.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a comment that explains why definitionLevel == columnDescriptor.getMaxDefinitionLevel() - 1 indicates a null value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't mark method parameters as final.

Copy link
Contributor Author

@kgalieva kgalieva Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK then please add a class level javadoc saying that we copied this file from this and that etc. and leave this as is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can define int lastOffset = offsets.get(offsets.size() - 1); and update usages below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what valueOffset tracks here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prec -> precision

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same as above, need your opinion regarding updating copy of Hive Parquet writer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tgt -> bytes or target ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same as above, need your opinion regarding updating copy of Hive Parquet writer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, refactored

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason behind this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line was moved to skipValues method

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored

@kgalieva kgalieva closed this Oct 27, 2017
@kgalieva kgalieva reopened this Oct 27, 2017
@kgalieva kgalieva closed this Oct 28, 2017
@kgalieva kgalieva reopened this Oct 28, 2017
@kgalieva kgalieva closed this Oct 28, 2017
@kgalieva kgalieva reopened this Oct 28, 2017
@kgalieva kgalieva force-pushed the complex-hive-types branch 3 times, most recently from e27d714 to d3e02a6 Compare October 28, 2017 22:13
@kgalieva
Copy link
Contributor Author

@nezihyigitbasi Made changes you suggested in comments. Please review one more time when you have time :)

@kgalieva
Copy link
Contributor Author

kgalieva commented Nov 3, 2017

@nezihyigitbasi any review updates on this PR?

Copy link
Contributor

@nezihyigitbasi nezihyigitbasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @kgalieva! I made another pass and left some more comments. This is getting close.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!isRequired && (definitionLevel == maxDefinitionLevel - 1) will make this easier to read.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's -> its

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRequired -> required

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename isNullField -> isValueNull

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens when definitionLevel != columnDescriptor.getMaxDefinitionLevel() && !isNullValue()? If that's not a valid case please add a final else and throw an appropriate exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testArrayOfMaps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rename as this is confusing. maybe just testStructOfMaps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitionLevels and repetitionLevels are for the struct I guess, so let's rename them as such. e.g., structDefinitionLevels ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rename i to be more descriptive.

@nezihyigitbasi
Copy link
Contributor

there are compilation failures.

@kgalieva kgalieva closed this Nov 8, 2017
@kgalieva kgalieva reopened this Nov 8, 2017
@kgalieva kgalieva closed this Nov 9, 2017
@kgalieva kgalieva reopened this Nov 9, 2017
@nezihyigitbasi
Copy link
Contributor

@zhenxiao @kgalieva were you able to fix the issue? Let me know when you are done, so that we can proceed with this.

@zhenxiao
Copy link
Collaborator

sorry for the late reply. I will send parquet data to reproduce the problem to @kgalieva

@kgalieva
Copy link
Contributor Author

Thanks @zhenxiao! When do you think you will be able to send me a parquet file?

@meghapthakkar
Copy link

Any updates on this PR?

@kgalieva
Copy link
Contributor Author

kgalieva commented May 1, 2018

@meghapthakkar unfortunately, no. I'm still waiting for @zhenxiao to send me the testing data to reproduce his problem.

@kgalieva
Copy link
Contributor Author

kgalieva commented May 4, 2018

Hi @zhenxiao, just a friendly reminder about the file :)

@kgalieva
Copy link
Contributor Author

Hi @zhenxiao! Any updates from you?

@Parth-Brahmbhatt
Copy link
Contributor

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.

@zhenxiao
Copy link
Collaborator

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.

@kgalieva
Copy link
Contributor Author

@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! :)

Copy link
Contributor

@nezihyigitbasi nezihyigitbasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireNonNull(type, "type is required");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireNonNull(children, "children is required");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return ImmutableList.copyOf(children);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed children type to ImmutableList instead, to make it obvious that copying is not needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use ImmutableList.Builder<Optional<Field>> fieldsBuilder = ImmutableList.builder(); to be consistent with the other fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason behind removing the systemMemoryContext field? It seems unrelated to the purpose of the PR. Same for the dataSource field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParquetPageSource class has a lot of constructor parameters. To simplify it a little bit it is possible to get systemMemoryContext and dataSource from parquetReader.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static import calculateCollectionOffsets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question about optional above applies to here and to L159 below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static import OPTIONAL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namedTypeSignature.getName() is Optional so get call may fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Struct fields are always named (unlike, for instance, array and map inner elements). This is why we can safely call get() here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this else is unnecessary, we can just return.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

@zhenxiao zhenxiao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few minor things during my testing

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

following Presto coding style, all parameters in the same line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

following Presto coding style, all parameters in the same line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

group final variables and non-final variables

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@kgalieva kgalieva force-pushed the complex-hive-types branch 3 times, most recently from 9544e17 to 58d395b Compare May 24, 2018 00:09
- 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
@kgalieva kgalieva force-pushed the complex-hive-types branch from 58d395b to 592b74a Compare May 24, 2018 00:31
@kgalieva
Copy link
Contributor Author

@nezihyigitbasi @zhenxiao thank you the feedback! I've made the changes you suggested and replied to comments you left.

@nezihyigitbasi
Copy link
Contributor

Merged, thanks @kgalieva. Also thanks everyone for their contributions and patience.

@zhenxiao
Copy link
Collaborator

this patch works good on our highly nested data, it passed 2 day's production workloads, very nice contribution @kgalieva

@zhenxiao
Copy link
Collaborator

Hi @kgalieva we found a problem for reading a table of schema:

​"_hoodie_commit_time","varchar","",""
"_hoodie_commit_seqno","varchar","",""
"_hoodie_record_key","varchar","",""
"_hoodie_partition_path","varchar","",""
"_hoodie_file_name","varchar","",""
"ts","double","",""
"host","varchar","",""
"level","varchar","",""
"dc","varchar","",""
"msg_offset","bigint","",""
"uuid","varchar","",""
"schema_id","integer","",""
"msg","row(menuentityuuid varchar, createdat bigint, updatedat bigint, menuentitytype varchar, title row(defaultvalue varchar, overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue varchar))), description row(defaultvalue varchar, overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue varchar))), childmenuentityuuids row(defaultvalue array(varchar), overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue array(varchar)))), prerequisiteoptionuuids row(defaultvalue array(varchar), overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue array(varchar)))), tags row(tags array(row(uuid varchar, key varchar, name varchar, type varchar))), vendorinfo row(externalid varchar, externaldata varchar), paymentinfo row(priceinfo row(defaultvalue row(price bigint), overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue row(price bigint))))), suspensioninfo row(defaultvalue row(suspenduntil bigint, suspendreason varchar), overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue row(suspenduntil bigint, suspendreason varchar)))), classifications row(cuisineuuid varchar, mealtypeuuid varchar, proteintypeuuid varchar, hasside boolean, ishot boolean, isentree boolean, alcoholicitems bigint), nutritionalinfo row(allergens array(varchar), caloricinfo row(lowerrange bigint, higherrange bigint, displaytype varchar), jouleinfo row(lowerrange bigint, higherrange bigint, displaytype varchar)), badges row(badges array(row(type varchar, iconurl varchar, text varchar))), taxinfo row(taxrate double, vatrate double), options row(options array(row(uuid varchar))), comboentities row(entities array(row(uuid varchar))), image row(imageurl varchar, rawimageurl varchar), quantityinfo row(defaultvalue row(minpermitted bigint, maxpermitted bigint, defaultquantity bigint, chargeabove bigint, refundunder bigint), overrides array(row(contexttype varchar, contextvalue varchar, overriddenvalue row(minpermitted bigint, maxpermitted bigint, defaultquantity bigint, chargeabove bigint, refundunder bigint)))), deletedat bigint, storeuuid varchar, isfrombulkupdate boolean, eventuuid varchar, isinsubsections boolean, insubsectionuuids array(varchar), notes varchar)","",""
"hadoop_row_key","varchar","",""
"hadoop_ref_key","bigint","",""
"hadoop_ref_key_version","integer","",""
"hadoop_data_source","varchar","",""
"hadoop_isdeleted","boolean","",""
"hadoop_isdeleted_uname","varchar","",""
"hadoop_forceharddelete","boolean","",""
"hadoop_forceupdate","boolean","",""
"hadoop_timestamp","bigint","",""
"hadoop_datacenter","varchar","",""
"hadoop_host","varchar","",""
"hadoop_schema_version","integer","",""
"hadoop_message_offset","bigint","",""
"hadoop_row_partition_datetime_str","varchar","",""
"jaeger_context","varbinary","",""
"datestr","varchar","partition key",""

Seems related to reading of: array<structtype:string>

Could you please help take a look?

I sent you an email with attached parquet file.

@zhenxiao
Copy link
Collaborator

get some clue for it. @nezihyigitbasi @kgalieva Will send to you for review.

@zhenxiao
Copy link
Collaborator

a fix:
#10849

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants