Skip to content

Parquet: Introduce a limit to evaluate IN predicates - #1665

Closed
aokolnychyi wants to merge 2 commits into
apache:masterfrom
aokolnychyi:parquet-in-limit
Closed

aokolnychyi wants to merge 2 commits into
apache:masterfrom
aokolnychyi:parquet-in-limit

Conversation

@aokolnychyi

@aokolnychyi aokolnychyi commented Oct 26, 2020 •

Copy link
Copy Markdown
Contributor

This PR introduces a limit for the number of elements inside IN predicates to apply min/max stats filtering.

Resolves #1588.

public static final int PARQUET_BATCH_SIZE_DEFAULT = 5000;

public static final String PARQUET_IN_LIMIT = "read.parquet.in-predicate-limit";
public static final int PARQUET_IN_LIMIT_DEFAULT = 200;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Spark's default value is 10 but it also rewrites IN as OR/EQUALS. We can do better that this because of the way we evaluate IN predicates. On datasets we tested this, we saw performance improvements on IN predicates with up to 200 elements (on sorted column). We may increase the default value a bit but I am very reluctant to make it more than 300 since our jobs were 20% slower with IN predicates that contained 300 elements. That being said, the optimal value still depends on a particular query and table.

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

This PR touches a lot of places (i.e. each query engine integration) since it relies on the mechanism to propagate table props to the Parquet reader builder. We have something similar for our write options.

We have to be careful with serialization since table properties can be Guava ImmutableList that will cause troubles for custom serialization mechanisms that query engines may use (e.g. Kryo). If I remember correctly, Spark still uses Java serialization for its tasks but I am not sure about other query engines. That being said, we already propagate write properties so the issue (if any) is already there.

StructType struct = schema.asStruct();
this.expr = Binder.bind(struct, Expressions.rewriteNot(unbound), caseSensitive);

if (options.getPropertyNames().contains(PARQUET_IN_LIMIT)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am using table properties directly here. We may consider mapping table properties into Parquet read options like we already do for page sizes inside the write builder. That would mean we need to introduce some sort of ExtendedParquetReadOptions since Parquet does not have a matching option for our case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm against the idea of Extending ParquetReadOptions since it's an internal class already, I think we are best off relying on the method you are currently using and just putting the options in "properties" I don't think we gain much by having specific getters/setters for the properties we add.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's my thinking as well. I am also not sure we would gain anything by translating out table properties like read.parquet.in-predicate-limit into custom Parquet props that only Iceberg knows about.

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

We also need to figure out what to do with IN predicates while evaluating metadata. We should have this limit there as well but metrics evaluators are used in places that don't have access to table properties.

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

I haven't updated the docs yet. We will need to do that in a separate PR since we don't have quite a few props there. Plus, we need a new column to state the version since a particular property is available.

}

// TODO: propagate properties for Avro
// properties.forEach(builder::set);

@RussellSpitzer RussellSpitzer Oct 26, 2020 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we should have an Precondition until this is implemented throwing an exception if a property is passed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As it stands today, all table props are passed here so this precondition will always fail. We may want to consider setting only those table props that start with read.parquet for Parquet or read.avro for Avro. Then a precondition here would work as we ensure props only contain relevant configs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can do that in our builders but that will be extra work.

this.expr = Binder.bind(struct, Expressions.rewriteNot(unbound), caseSensitive);

if (options.getPropertyNames().contains(PARQUET_IN_LIMIT)) {
this.inPredicateLimit = Integer.parseInt(options.getProperty(PARQUET_IN_LIMIT));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we be protecting against negatives here? Just wondering if there are any values we might set here that don't make sense.

@rdblue

rdblue commented Oct 27, 2020

Copy link
Copy Markdown
Contributor

Overall, I think this looks good. It would be nice to separate the actual fix into a separate PR, though. Right now, there are a lot of changes that just add properties to different methods and objects. It would be great to have a PR focused on just the changes to Parquet filters that uses a hard-coded limit (200). That would make it easier to check those changes and would be easier for maintainers to cherry-pick this to internal branches.

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

I can separate the work into multiple PRs. I am actually worried about changing so many places just before the release. So we can discuss whether we want to include the table property part into 0.10.

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

@rdblue @RussellSpitzer What about IN predicates in places like InclusiveMetricsEvaluator? Can we do there any better than a hardcoded limit? The only workable option I can think if is to have a system property but I don't think it is a good idea.

@rdblue

rdblue commented Oct 27, 2020

Copy link
Copy Markdown
Contributor

I think for a release, it would be fine to use a hard-coded limit. It's better than no limit, after all. We can follow up with the rest of the changes afterwards and can backport to a patch release if anyone needs it.

@RussellSpitzer

RussellSpitzer commented Oct 27, 2020 •

Copy link
Copy Markdown
Member

@rdblue @RussellSpitzer What about IN predicates in places like InclusiveMetricsEvaluator? Can we do there any better than a hardcoded limit? The only workable option I can think if is to have a system property but I don't think it is a good idea.

I'm hoping this really isn't as big an issue for manifest checks since there should be hopefully many less manifests than rowgroups :/ so I'm not as worried there. But if we are hardcoding one place for the release we may as well hardcode both.

@aokolnychyi

aokolnychyi commented Oct 27, 2020 •

Copy link
Copy Markdown
Contributor Author

@RussellSpitzer, InclusiveMetricsEvaluator is actually applied on manifest entries so it is proportional to the number of files we have to scan.

@rdblue

rdblue commented Nov 1, 2020

Copy link
Copy Markdown
Contributor

I think this should be ready soon. Could you rebase it please?

@rdblue

rdblue commented Nov 20, 2020

Copy link
Copy Markdown
Contributor

@aokolnychyi, any update? Do we want to get this into 0.11.0?

@aokolnychyi

Copy link
Copy Markdown
Contributor Author

Sorry, I am running a bit behind on this one. Let me try to rebase this in a couple of days.

@rdblue

rdblue commented Dec 28, 2020

Copy link
Copy Markdown
Contributor

I'm going to close this one and we can revisit it when everyone has time.

@rdblue rdblue closed this Dec 28, 2020
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.

Don't evaluate IN predicates when it is too expensive

3 participants