Hi Xing,

Thanks for your suggestion. Yes, this may help, and if I get your idea
right, I already had it in my reproducer:
1) Create the converted physical input:
https://github.com/devozerov/calcite-optimizer/blob/master/src/main/java/devozerov/physical/ProjectPhysicalRule.java#L49
2) Use it in case no physical children were found:
https://github.com/devozerov/calcite-optimizer/blob/master/src/main/java/devozerov/physical/ProjectPhysicalRule.java#L79

This idea is borrowed from Apache Drill physical rules. But the problem is
that this approach leads to a suboptimal plan - parent node doesn't know
the future distribution of a child node. And as a result, it doesn't know
it's own distribution. So the final plan is constructed in that way:
1.1) Root enforced "SINGLETON" on its child:
-> PhysicalRoot[SINGLETON]
 -> Converter[SINGLETON]
  -> PhysicalProject[*ANY*]
   -> PhysicalScan[REPLICATED]

1.2) But since the child (PhysicalProject) failed to infer distribution
during rule call, it falls back to ANY distribution. In order to satisfy
SINGLETON distribution of a parent, we inject an exchange in the final plan:
-> PhysicalRoot[SINGLETON]
* -> Exchange[SINGLETON]*
  -> PhysicalProject[*ANY*]
   -> PhysicalScan[REPLICATED]

2) But this is a suboptimal plan. The optimal plan is:
-> PhysicalRoot[SINGLETON]
 -> PhysicalProject[REPLICATED]
  -> PhysicalScan[REPLICATED]

You may observe it in my tests:
1)
https://github.com/devozerov/calcite-optimizer/blob/master/src/test/java/devozerov/OptimizerTest.java#L46
-
works as you described and produces not optimal plan with exchange
2)
https://github.com/devozerov/calcite-optimizer/blob/master/src/test/java/devozerov/OptimizerTest.java#L30
-
rely on AbstractConverter-s and produce an optimal plan with bottom-up
trait propagation at the cost of significantly increased planning time

Regards,
Vladimir.

пт, 8 нояб. 2019 г. в 16:15, XING JIN <jinxing.co...@gmail.com>:

> Hi Vladimir,
>
> I think the way PlannerTests#GoodSingleRule and EnumerableXXXRule work may
> help you ~
> They work by a top-down fashion, but when matching parent, they convert the
> children explicitly.
> You may try below steps:
> 1. Construct a rule LogicalParentRule to match the LogicalParent without
> distribution/physical requirement for the LogicalChild;
> 2. In this rule, you call 'planner.changeTraits' on the LogicalChild to
> build a new child with physical convention. Note that at this moment only
> an empty RelSubset is created and no PhysicalChild exists.
> 3. Then set the RelNode to be the new input of LogicalParent;
>
> By above steps, you can build a parent-child relationship between
> LogicalParent and PhysicalChild, and at last the PhysicalParentRule will be
> fired based on this relationship.
>
> I have a commit to illustrate my idea, check VolcanoPlannerTest#testDEV in
> below link, hope it may help you ~
> https://github.com/jinxing64/calcite/tree/demo
>
> Also I'm +1 with Seliverstov that to get all parents of a set, which
> against the current check in RelSubset#getParentRels
>
> Best,
> Jin
>
> Vladimir Ozerov <ppoze...@gmail.com> 于2019年11月5日周二 下午6:41写道:
>
> > Hi Xiening,
> >
> > I read the thread about on-demand trait requests. It seems pretty similar
> > to what I am trying to achieve, as it facilitates the bottom-up
> propagation
> > of physical traits. In fact, both your and my strategy propagate traits
> > bottom-up, but I do this through rules, which also fire bottom-up, while
> in
> > your case only the traits are propagated bottom-up, while rules continue
> > working in a top-down fashion.
> >
> > However, I am thinking of how I would potentially implement my optimizer
> > with your approach, and it feels like with on-demand traits resulting
> > implementation of metadata queries may become very complex to that point
> > that it will look like another set of rules, parallel to the already
> > existing ruleset. For example, consider that I have a couple of
> distributed
> > tables in an OLTP application. These tables have a number of indexes,
> and I
> > would like to join them. First, I have a number of choices on how to join
> > tables with respect to distribution. Then, I have a number of choices on
> > which access method to use. Because sometimes it is beneficial to pick
> > index scans instead of table scans even without index conditions, for
> > example, to preserve a comfortable collation. So when my logical scan
> > receives such metadata request, it typically cannot return all possible
> > combinations, because there are too many of them. Instead, some
> heuristical
> > or cost-based logic will be used to calculate a couple of most
> prospective
> > ones. But it seems that we will have to duplicate the same logic in the
> > corresponding rule, aren't we?
> >
> > I would love to read your design because this is a really interesting
> > topic, and it is of great importance for the distributed engines
> developed
> > on top of Calcite since proper use of distribution and collation is the
> key
> > success factor for efficient query optimization.
> >
> > Regards,
> > Vladimir.
> >
> > пт, 1 нояб. 2019 г. в 00:40, Xiening Dai <xndai....@gmail.com>:
> >
> > > Actually we solved this problem in our setup using a mechanism called
> > > “Pull-Up Traits”, which explores the possible trait set of children’s
> > input
> > > to decide parent’s physical properties. In order to determine child
> input
> > > trait, you would have to look at child’s children, and all the way to
> the
> > > leaves nodes or a barrier. A barrier is a rel node which cannot derive
> > any
> > > traits regardless the input. A good example would be a user define
> > function
> > > which would throw off any distribution or collation. Then we realize
> just
> > > pulling up is not enough, sometimes we would need to look at parent’s
> > > requirement as well. So we try to solve this in a unified framework,
> > which
> > > we call “On Demand Trait” and implement it as part of the framework so
> > > anyone can be benefited. I hope Haisheng can share a design doc once we
> > > have more concrete ideas.
> > >
> > >
> > > > On Oct 31, 2019, at 11:37 AM, Jinfeng Ni <j...@apache.org> wrote:
> > > >
> > > > Hi Vladimir,
> > > >
> > > > The SubsetTransformer interface and the iterating over the RelNodes
> > > > within a RelSubset in Drill  is exactly implemented to do the trait
> > > > propagation. We also had to rely on AbstractConverter to fire
> > > > necessary rule to avoid the CanNotPlan issue. At some point, Calcite
> > > > community chooses to remove AbstractConverter and Drill had to add it
> > > > back, which is probably one of the main reasons for us to continue
> > > > using a Calcite fork.  I still remember we constantly had to deal
> with
> > > > the dilemma between "CanNotPlan" and long planing time due to
> explored
> > > > search space.
> > > >
> > > > Glad to see more people are joining the effort to solve this long
> > > > overdue issue, something missing in Calcite's core optimizer
> framework
> > > > "since before Calcite was Calcite" (Jacques's words).
> > > >
> > > > Jinfeng
> > > >
> > > >
> > > > On Thu, Oct 31, 2019 at 3:38 AM Vladimir Ozerov <ppoze...@gmail.com>
> > > wrote:
> > > >>
> > > >> Hi Danny,
> > > >>
> > > >> Thank you very much for the links. What is described here is pretty
> > much
> > > >> similar to the problem I describe. Especially the discussion about
> > trait
> > > >> propagation, as this is basically what I need - to explore potential
> > > traits
> > > >> of children before optimizing parents. And this is basically what
> > Drill
> > > >> already does with it's SubsetTransformer:
> > > >> 1) There is a SubsetTransformer interface, which iterates over
> > physical
> > > >> relations of the given subset [1]
> > > >> 2) If you want to make a physical project, you iterate over physical
> > > >> relations of the input subset and create possible physical projects
> > [2]
> > > >> 3) But if you cannot find any physical input, then you trigger
> > creation
> > > of
> > > >> a "bad" physical project, which is very likely to have poor cost
> > > because it
> > > >> cannot take advantage of input's distribution information [3]
> > > >> So, step 2 - is a trait set propagation which is needed by many
> > > >> distributed engines. Step 3 - an attempt to workaround current
> > > >> VolcanoPlanner behavior, when a parent rule is fired only if
> produced
> > > child
> > > >> node has compatible trait set.
> > > >>
> > > >> I do not know Calcite's architecture that good but at first glance,
> > the
> > > >> proposed ability to re-fire rules of a specific Rel seems good
> enough.
> > > It
> > > >> doesn't expand search space, because no new nodes are created, and
> it
> > > seems
> > > >> to be relatively simple to implement.
> > > >>
> > > >> [1]
> > > >>
> > >
> >
> https://github.com/apache/drill/blob/1.16.0/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/SubsetTransformer.java
> > > >> [2]
> > > >>
> > >
> >
> https://github.com/apache/drill/blob/1.16.0/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/ProjectPrule.java#L66
> > > >> [3]
> > > >>
> > >
> >
> https://github.com/apache/drill/blob/1.16.0/exec/java-exec/src/main/java/org/apache/drill/exec/planner/physical/ProjectPrule.java#L69
> > > >>
> > > >> чт, 31 окт. 2019 г. в 12:21, Danny Chan <yuzhao....@gmail.com>:
> > > >>
> > > >>> Thanks Vladimir for bringing up this discussion !
> > > >>>
> > > >>> Did you notice that there is a JIRA issue about this problem ? [1]
> > > Also a
> > > >>> discussion about how to propagate the traits [2]
> > > >>>
> > > >>> [1] https://issues.apache.org/jira/browse/CALCITE-2970
> > > >>> [2]
> > > >>>
> > >
> >
> https://ponymail-vm.apache.org/_GUI_/thread.html/79dac47ea50b5dfbd3f234e368ed61d247fb0eb989f87fe01aedaf25@%3Cdev.calcite.apache.org%3E
> > > >>>
> > > >>> Best,
> > > >>> Danny Chan
> > > >>> 在 2019年10月31日 +0800 PM3:56,Vladimir Ozerov <ppoze...@gmail.com
> >,写道:
> > > >>>> Hi colleagues,
> > > >>>>
> > > >>>> I would like to discuss with the community the possibility of
> > adding a
> > > >>> new
> > > >>>> public method to VolcanoPlanner which will forcefully re-trigger
> the
> > > >>> rules
> > > >>>> for the specific rel. This is a follow up of a discussion started
> in
> > > the
> > > >>>> other thread [1].
> > > >>>>
> > > >>>> **Problem statement**
> > > >>>> When converting between conventions during optimization
> > VolcanoPlanner
> > > >>>> prefers the top-bottom approach, so that the nodes are converted
> > from
> > > the
> > > >>>> root. But in some cases, the intermediate node must be converted
> > after
> > > >>> its
> > > >>>> children. This is especially true for distributed SQL engines,
> which
> > > rely
> > > >>>> on distribution traits during the optimization process: it is not
> > > >>> possible
> > > >>>> to efficiently choose a proper physical implementation of a parent
> > > node
> > > >>>> unless the physical representation of a child node is known.
> > > >>>>
> > > >>>> It seems that presently VolcanoPlanner cannot address such cases
> by
> > > >>>> default. Consider that we have two nodes and associated rules
> which
> > > >>> convert
> > > >>>> them to physical counterparts:
> > > >>>> [LogicalParent <- LogicalChild]
> > > >>>> The parent should be converted after the child. When the child is
> > > >>>> converted, the physical node is created:
> > > >>>> [LogicalParent <- {LogicalChild, PhysicalChild}]
> > > >>>> In order to finish the optimization process, we need to convert
> the
> > > >>> parent.
> > > >>>> But parent rules are not fired, because PhysicalChild has traits
> > > >>>> incompatible with LogicalParent.
> > > >>>>
> > > >>>> Presently the problem could be solved in two ways:
> > > >>>> 1) Always produce conversions when going top-down. In this case, I
> > > first
> > > >>>> create a physical parent, then a physical child. The problem is
> that
> > > >>>> created parent is not optimal because it didn't know child
> > > distribution
> > > >>> at
> > > >>>> the time of creation. So the full flow would be: create a bad
> > parent,
> > > >>>> create a good child, create a good parent.
> > > >>>> 1.1) [LogicalParent <- LogicalChild]
> > > >>>> 1.2) [{LogicalParent, PhysicalParentBad} <- LogicalChild]
> > > >>>> 1.3) [{LogicalParent, PhysicalParentBad} <- {LogicalChild,
> > > >>> PhysicalChild}]
> > > >>>> 1.4) [{LogicalParent, PhysicalParentBad, PhysicalParentGood} <-
> > > >>>> {LogicalChild, PhysicalChild}]
> > > >>>> What is worse, the creation of a not optimal parent will trigger
> > > rules on
> > > >>>> its parent, which in turn may create a not optimal parent-parent
> > node,
> > > >>> etc.
> > > >>>>
> > > >>>> 2) Make sure that your convention returns true for
> > > >>>> Convention.canConvertConvention. In this case, every time a new
> rel
> > is
> > > >>>> added to a RelSet, its traitset will be compared to traitsets of
> all
> > > >>> other
> > > >>>> rels in the set. For every pair of traitset we may ask the engine
> to
> > > >>> create
> > > >>>> a relevant AbstractConverter. The net effect is that
> > > >>> "physical-to-logical"
> > > >>>> converter is created, which re-triggers the rule on the logical
> > parent
> > > >>>> since their conventions are compatible:
> > > >>>> 2.1) [LogicalParent <- LogicalChild]
> > > >>>> 2.2) [LogicalParent <- {LogicalChild, PhysicalChild}]
> > > >>>> 2.3) [LogicalParent <- {LogicalChild, PhysicalChild,
> > > >>>> PhysicalToLogicalConverter}]
> > > >>>> 2.4) [{LogicalParent, PhysicalParent} <- {LogicalChild,
> > PhysicalChild,
> > > >>>> PhysicalToLogicalConverter}]
> > > >>>>
> > > >>>> The problem with that approach is that it is too coarse-grained
> > since
> > > we
> > > >>>> operate on traitsets rather than rels. As a result, additional
> > memory
> > > and
> > > >>>> CPU pressure are introduced because usually too many converters
> are
> > > >>>> created, which triggers the rules over and over again.
> > > >>>>
> > > >>>> **Affected products**
> > > >>>> At the moment two distributed engines are being developed for
> > > Hazelcast
> > > >>> and
> > > >>>> Apache Ignite. Both require bottom-up optimization and currently
> > rely
> > > on
> > > >>>> the second workaround.
> > > >>>> Another example is Apache Drill. I do not know whether its
> community
> > > is
> > > >>>> concerned with the issue, but it also uses bottom-up optimization
> > for
> > > >>> many
> > > >>>> rules and employs both the aforementioned workarounds. As a
> result,
> > I
> > > >>> guess
> > > >>>> that Drill's optimizer also creates too many rels during
> > optimization
> > > and
> > > >>>> suffer from huge search space. Please correct me if I am wrong.
> > > >>>>
> > > >>>> **Proposal**
> > > >>>> The key problem is that there is no way to re-fire rules on a
> > specific
> > > >>>> node. The original problem could have been solved, if it would be
> > > >>> possible
> > > >>>> to re-fire rules on a LogicalParent without creating any
> additional
> > > rels.
> > > >>>> That would lead to a clear optimization flow:
> > > >>>> 2.1) [LogicalParent <- LogicalChild]
> > > >>>> 2.2) [LogicalParent <- {LogicalChild, PhysicalChild}]
> > > >>>> 2.3) [{LogicalParent, PhysicalParent} <- {LogicalChild,
> > > PhysicalChild}]
> > > >>>>
> > > >>>> We can add the following method to VolcanoPlanner (RelOptPlanner?)
> > > >>>> interface:
> > > >>>> void fireRules(RelNode rel)
> > > >>>>
> > > >>>> This method will fire the rules on a passed node in a deferred
> mode
> > > as if
> > > >>>> it was the new node just added to the optimizer. This would
> require
> > > >>> slight
> > > >>>> changes to RuleQueue.addMatch method, and possibly some other
> > places.
> > > >>>>
> > > >>>> Usage example:
> > > >>>> class PhysicalChildRule extends RelOptRule {
> > > >>>> void onMatch(RelOptRuleCall call) {
> > > >>>> LogicalChild logicalRel = call.get(0);
> > > >>>> PhysicalChild physicalRel = ...;
> > > >>>>
> > > >>>> call.transformTo(physicalRel);
> > > >>>> optimizer.fireRules(logicalRel);
> > > >>>> }
> > > >>>> }
> > > >>>>
> > > >>>> What does the community think about such a method? Does it make
> any
> > > sense
> > > >>>> to you? If not, do you aware of any other ways on how to organize
> > > >>> bottom-up
> > > >>>> optimization with VolcanoPlanner without the creation of
> additional
> > > rels?
> > > >>>>
> > > >>>> If the community is OK in general, I can create try to create a PR
> > > with a
> > > >>>> prototype.
> > > >>>>
> > > >>>> Would appreciate your feedback.
> > > >>>>
> > > >>>> Regards,
> > > >>>> Vladimir.
> > > >>>>
> > > >>>> [1]
> > > >>>>
> > > >>>
> > >
> >
> https://ponymail-vm.apache.org/_GUI_/thread.html/13e7ab2040bfa4902db6647992ec4203ceb0262cfcb28d38ef7e9e32@%3Cdev.calcite.apache.org%3E
> > > >>>
> > >
> > >
> >
>

Reply via email to