Skip to content

SQL planning slow with huge IN filters #7904

Description

@gianm

Affected Version

0.14.2

Description

When running a SQL query with an IN clause with ~14k elements, planning on the broker took quite a long time (over a minute). A lot of time seems to be spent in areas of Calcite code that look like the following (I saw this pattern repeat over lots of thread dumps). Maybe some algorithmic blowup when there are a lot of OR conditions, which is what a big IN would get translated to.

"sql[af077a4e-23e8-4440-9f12-42919d7b8b5b]" #113 daemon prio=5 os_prio=0 tid=0x00007f3142179000 nid=0x36c runnable [0x00007f3096fb3000]
   java.lang.Thread.State: RUNNABLE
	at org.apache.calcite.rex.RexUtil.decompose(RexUtil.java:355)
	at org.apache.calcite.rex.RexUtil.gatherConstraints(RexUtil.java:366)
	at org.apache.calcite.rex.RexUtil.predicateConstants(RexUtil.java:323)
	at org.apache.calcite.plan.RelOptPredicateList.of(RelOptPredicateList.java:145)
	at org.apache.calcite.plan.RelOptPredicateList.union(RelOptPredicateList.java:158)
	at org.apache.calcite.rex.RexSimplify.simplifyOrTerms(RexSimplify.java:350)
	at org.apache.calcite.rex.RexSimplify.simplifyOr(RexSimplify.java:1058)
	at org.apache.calcite.rex.RexSimplify.simplify_(RexSimplify.java:183)
	at org.apache.calcite.rex.RexSimplify.lambda$simplify$0(RexSimplify.java:175)
	at org.apache.calcite.rex.RexSimplify$$Lambda$258/1039224919.apply(Unknown Source)
	at org.apache.calcite.rex.RexSimplify.verify(RexSimplify.java:1097)
	at org.apache.calcite.rex.RexSimplify.simplify(RexSimplify.java:175)
	at org.apache.calcite.rex.RexUtil$ExprSimplifier.visitCall(RexUtil.java:2607)
	at org.apache.calcite.rex.RexUtil$ExprSimplifier.visitCall(RexUtil.java:2567)
	at org.apache.calcite.rex.RexCall.accept(RexCall.java:107)
	at org.apache.calcite.rex.RexShuttle.visitList(RexShuttle.java:151)
	at org.apache.calcite.rex.RexShuttle.visitCall(RexShuttle.java:100)
	at org.apache.calcite.rex.RexUtil$ExprSimplifier.visitCall(RexUtil.java:2604)
	at org.apache.calcite.rex.RexUtil$ExprSimplifier.visitCall(RexUtil.java:2567)

Activity

  1. xueyumusic commented on Jun 16, 2019

    @xueyumusic
    Contributor

    Calcite has default value of DEFAULT_IN_SUB_QUERY_THRESHOLD as 20. If length of values of in clause exceeds this config calcite will not convert in to or and convert in to a semi join of table.
    However druid set this config as MAX_VALUE in PlannerFactory . If there is no special purpose of setting it as MAX_VALUE, maybe we could try using default value and see whether still has this performance problem. What do you think? @gianm

  2. gianm commented on Jun 16, 2019

    @gianm
    ContributorAuthor

    I think right now the Druid planner rules can not convert that kind of structure into a Druid query. Maybe if they could, then that approach would work. (However there might still be a performance issue if a user specified a ton of OR conditions rather than using IN?)

  3. gianm commented on Jul 7, 2019

    @gianm
    ContributorAuthor

    I did some looking into this and found not just one, but a few different root causes. Here they are, in order of how much they contributed to query planning time (most to least):

    1. Calcite has an O(N^2) OR simplification step in RexSimplify.simplifyOr: https://issues.apache.org/jira/browse/CALCITE-3178. This was the biggest contributor, and a hacky workaround of simply disabling this step cut the planning time down to ~8s.
    2. Druid's Expressions.toSimpleLeafFilter unconditionally attempts to check every "leaf" filter (non-boolean) to see if it's a time floor filter, for purposes of potentially converting FLOOR(__time TO DAY) = X into a range filter. This imposes a few seconds of overhead, which could be eliminated by only checking leaf filters that look like they might be time floors (their operator matches FLOOR, TIME_FLOOR, or CAST(x AS DATE)).
    3. Druid's CombineAndSimplifyBounds builds a TreeRangeSet<BoundValue> out of every equality or bound leaf filter, to see if they can be simplified. Algorithmic complexity looks fine from a quick glance at the method, but there is overhead imposed by the fact that BoundValue.compareTo, which is called internally and often by the TreeRangeSet, always compares bounds as Strings. For numeric bounds involves a lot of wasteful converting of numbers and strings back and forth. This is only an issue when the bounds are numeric, but, in the case of my test query, they were (it was a long-typed column).
    4. Calcite's parser also seems to take a while (1–2s) to read through the SQL. Possibly sql support for dynamic parameters #6974 would help with that, if it allows constructs like col IN (?) where ? is an array.

    Profiling indicates that if all of the above were addressed, query planning time should come down to more acceptable levels.

  4. gianm commented on Jul 7, 2019

    @gianm
    ContributorAuthor

    By the way, the native query equivalent of my test query runs in about 900ms.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions