From 750e2d35f56b88c5805ab2c432cd65f7c2ff508e Mon Sep 17 00:00:00 2001 From: shirly121 Date: Wed, 4 Sep 2024 14:41:14 +0800 Subject: [PATCH 1/3] [GIE Compiler] fix bugs of CBO for concurrent queries --- .../common/config/PlannerConfig.java | 12 + .../common/ir/planner/GraphRelOptimizer.java | 341 +++++++++++------- 2 files changed, 213 insertions(+), 140 deletions(-) diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/config/PlannerConfig.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/config/PlannerConfig.java index a645c8502452..8bc5caae9f1e 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/config/PlannerConfig.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/config/PlannerConfig.java @@ -34,6 +34,10 @@ public class PlannerConfig { // in Neo4j public static final Config JOIN_BY_EDGE_ENABLED = Config.boolConfig("graph.planner.join.by.edge.enabled", false); + public static final Config GRAPH_PLANNER_GROUP_SIZE = + Config.intConfig("graph.planner.group.size", 8); + public static final Config GRAPH_PLANNER_GROUP_CLEAR_INTERVAL_MINUTES = + Config.intConfig("graph.planner.group.clear.interval.minutes", 30); private final Configs configs; private final List rules; @@ -96,6 +100,14 @@ public String getJoinByForeignKeyUri() { return GraphConfig.GRAPH_FOREIGN_KEY_URI.get(configs); } + public int getPlannerGroupSize() { + return GRAPH_PLANNER_GROUP_SIZE.get(configs); + } + + public int getPlannerGroupClearIntervalMinutes() { + return GRAPH_PLANNER_GROUP_CLEAR_INTERVAL_MINUTES.get(configs); + } + @Override public String toString() { return "PlannerConfig{" diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java index 544c29b7140e..59c750a1506f 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java @@ -37,7 +37,6 @@ import com.google.common.collect.Lists; import org.apache.calcite.plan.*; -import org.apache.calcite.plan.hep.HepPlanner; import org.apache.calcite.plan.hep.HepProgram; import org.apache.calcite.plan.hep.HepProgramBuilder; import org.apache.calcite.plan.volcano.VolcanoPlanner; @@ -51,30 +50,31 @@ import org.apache.calcite.rel.rules.FilterJoinRule; import org.apache.calcite.tools.RelBuilderFactory; import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Optimize graph relational tree which consists of match and other relational operators */ public class GraphRelOptimizer { - private final Configs graphConfig; + private static final Logger logger = LoggerFactory.getLogger(GraphRelOptimizer.class); private final PlannerConfig config; - private final RelOptPlanner relPlanner; - private final RelOptPlanner matchPlanner; - private final RelOptPlanner physicalPlanner; private final RelBuilderFactory relBuilderFactory; private final GlogueHolder glogueHolder; + private final PlannerGroupManager plannerGroupManager; public GraphRelOptimizer(Configs graphConfig) { - this.graphConfig = graphConfig; this.config = new PlannerConfig(graphConfig); this.relBuilderFactory = new GraphBuilderFactory(graphConfig); - this.relPlanner = createRelPlanner(); - this.matchPlanner = createMatchPlanner(); - this.physicalPlanner = createPhysicalPlanner(); this.glogueHolder = new GlogueHolder(graphConfig); + this.plannerGroupManager = new PlannerGroupManager(); } public GlogueHolder getGlogueHolder() { @@ -82,15 +82,13 @@ public GlogueHolder getGlogueHolder() { } public RelOptPlanner getMatchPlanner() { - return matchPlanner; + PlannerGroup currentGroup = this.plannerGroupManager.getCurrentGroup(); + return currentGroup.matchPlanner; } - public RelOptPlanner getPhysicalPlanner() { - return physicalPlanner; - } - - public RelOptPlanner getRelPlanner() { - return relPlanner; + public RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { + PlannerGroup currentGroup = this.plannerGroupManager.getCurrentGroup(); + return currentGroup.optimize(before, ioProcessor); } public @Nullable RelMetadataQuery createMetaDataQuery(IrMeta irMeta) { @@ -98,36 +96,18 @@ public RelOptPlanner getRelPlanner() { GlogueQuery gq = this.glogueHolder.getGlogue(); Preconditions.checkArgument(gq != null, "glogue is not ready"); return new GraphRelMetadataQuery( - new GraphMetadataHandlerProvider(this.matchPlanner, gq, this.config)); + new GraphMetadataHandlerProvider(getMatchPlanner(), gq, this.config)); } return null; } - public RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { - if (config.isOn()) { - // apply rules of 'FilterPushDown' before the match optimization - relPlanner.setRoot(before); - RelNode relOptimized = relPlanner.findBestExp(); - if (config.getOpt() == PlannerConfig.Opt.CBO) { - relOptimized = relOptimized.accept(new MatchOptimizer(ioProcessor)); - } - // apply rules of 'FieldTrim' after the match optimization - if (config.getRules().contains(FieldTrimRule.class.getSimpleName())) { - relOptimized = FieldTrimRule.trim(ioProcessor.getBuilder(), relOptimized); - } - physicalPlanner.setRoot(relOptimized); - RelNode physicalOptimized = physicalPlanner.findBestExp(); - clear(); - return physicalOptimized; - } - return before; - } - private class MatchOptimizer extends GraphShuttle { private final GraphIOProcessor ioProcessor; + private final RelOptPlanner matchPlanner; - public MatchOptimizer(GraphIOProcessor ioProcessor) { + public MatchOptimizer(GraphIOProcessor ioProcessor, RelOptPlanner matchPlanner) { this.ioProcessor = ioProcessor; + this.matchPlanner = matchPlanner; } @Override @@ -220,116 +200,197 @@ public void visit(RelNode node, int ordinal, @Nullable RelNode parent) { } } - private RelOptPlanner createRelPlanner() { - HepProgramBuilder hepBuilder = HepProgram.builder(); - if (config.isOn()) { - List ruleConfigs = Lists.newArrayList(); - config.getRules() - .forEach( - k -> { - if (k.equals( - FilterJoinRule.FilterIntoJoinRule.class.getSimpleName())) { - ruleConfigs.add(CoreRules.FILTER_INTO_JOIN.config); - } else if (k.equals(FilterMatchRule.class.getSimpleName())) { - ruleConfigs.add(FilterMatchRule.Config.DEFAULT); - } - }); - ruleConfigs.forEach( - k -> { - hepBuilder.addRuleInstance( - k.withRelBuilderFactory(relBuilderFactory).toRule()); - }); + private class PlannerGroup { + private final RelOptPlanner relPlanner; + private final RelOptPlanner matchPlanner; + private final RelOptPlanner physicalPlanner; + + public PlannerGroup() { + this.relPlanner = createRelPlanner(); + this.matchPlanner = createMatchPlanner(); + this.physicalPlanner = createPhysicalPlanner(); } - return new HepPlanner(hepBuilder.build()); - } - private RelOptPlanner createMatchPlanner() { - if (config.isOn() && config.getOpt() == PlannerConfig.Opt.CBO) { - VolcanoPlanner planner = new VolcanoPlannerX(); - planner.addRelTraitDef(ConventionTraitDef.INSTANCE); - planner.setTopDownOpt(true); - planner.setNoneConventionHasInfiniteCost(false); - config.getRules() - .forEach( - k -> { - RelRule.Config ruleConfig = null; - if (k.equals(ExtendIntersectRule.class.getSimpleName())) { - ruleConfig = - ExtendIntersectRule.Config.DEFAULT - .withMaxPatternSizeInGlogue( - config.getGlogueSize()) - .withLabelConstraintsEnabled( - config.labelConstraintsEnabled()); - } else if (k.equals(JoinDecompositionRule.class.getSimpleName())) { - ruleConfig = - JoinDecompositionRule.Config.DEFAULT - .withMinPatternSize( - config.getJoinMinPatternSize()) - .withJoinQueueCapacity( - config.getJoinQueueCapacity()) - .withJoinByEdgeEnabled( - config.isJoinByEdgeEnabled()); - ForeignKeyMeta foreignKeyMeta = - config.getJoinByForeignKeyUri().isEmpty() - ? null - : new ForeignKeyMeta( - config.getJoinByForeignKeyUri()); - ((JoinDecompositionRule.Config) ruleConfig) - .withForeignKeyMeta(foreignKeyMeta); - } - if (ruleConfig != null) { - planner.addRule( - ruleConfig - .withRelBuilderFactory(relBuilderFactory) - .toRule()); - } - }); - return planner; + public synchronized RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { + if (config.isOn()) { + // apply rules of 'FilterPushDown' before the match optimization + relPlanner.setRoot(before); + RelNode relOptimized = relPlanner.findBestExp(); + if (config.getOpt() == PlannerConfig.Opt.CBO) { + relOptimized = + relOptimized.accept(new MatchOptimizer(ioProcessor, matchPlanner)); + } + // apply rules of 'FieldTrim' after the match optimization + if (config.getRules().contains(FieldTrimRule.class.getSimpleName())) { + relOptimized = FieldTrimRule.trim(ioProcessor.getBuilder(), relOptimized); + } + physicalPlanner.setRoot(relOptimized); + RelNode physicalOptimized = physicalPlanner.findBestExp(); + clear(); + return physicalOptimized; + } + return before; } - // todo: re-implement heuristic rules in ir core match - return new HepPlanner(HepProgram.builder().build()); - } - private RelOptPlanner createPhysicalPlanner() { - HepProgramBuilder hepBuilder = HepProgram.builder(); - if (config.isOn()) { - List ruleConfigs = Lists.newArrayList(); - config.getRules() - .forEach( - k -> { - if (k.equals(ExpandGetVFusionRule.class.getSimpleName())) { - ruleConfigs.add( - ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config - .DEFAULT); - ruleConfigs.add( - ExpandGetVFusionRule.PathBaseExpandGetVFusionRule.Config - .DEFAULT); - } - }); - ruleConfigs.forEach( - k -> { - hepBuilder.addRuleInstance( - k.withRelBuilderFactory(relBuilderFactory).toRule()); - }); + private RelOptPlanner createRelPlanner() { + HepProgramBuilder hepBuilder = HepProgram.builder(); + if (config.isOn()) { + List ruleConfigs = Lists.newArrayList(); + config.getRules() + .forEach( + k -> { + if (k.equals( + FilterJoinRule.FilterIntoJoinRule.class + .getSimpleName())) { + ruleConfigs.add(CoreRules.FILTER_INTO_JOIN.config); + } else if (k.equals(FilterMatchRule.class.getSimpleName())) { + ruleConfigs.add(FilterMatchRule.Config.DEFAULT); + } + }); + ruleConfigs.forEach( + k -> { + hepBuilder.addRuleInstance( + k.withRelBuilderFactory(relBuilderFactory).toRule()); + }); + } + return new GraphHepPlanner(hepBuilder.build()); } - return new GraphHepPlanner(hepBuilder.build()); - } - private void clear() { - List logicalRBORules = this.relPlanner.getRules(); - this.relPlanner.clear(); - for (RelOptRule rule : logicalRBORules) { - this.relPlanner.addRule(rule); + private RelOptPlanner createMatchPlanner() { + if (config.isOn() && config.getOpt() == PlannerConfig.Opt.CBO) { + VolcanoPlanner planner = new VolcanoPlannerX(); + planner.addRelTraitDef(ConventionTraitDef.INSTANCE); + planner.setTopDownOpt(true); + planner.setNoneConventionHasInfiniteCost(false); + config.getRules() + .forEach( + k -> { + RelRule.Config ruleConfig = null; + if (k.equals(ExtendIntersectRule.class.getSimpleName())) { + ruleConfig = + ExtendIntersectRule.Config.DEFAULT + .withMaxPatternSizeInGlogue( + config.getGlogueSize()) + .withLabelConstraintsEnabled( + config.labelConstraintsEnabled()); + } else if (k.equals( + JoinDecompositionRule.class.getSimpleName())) { + ruleConfig = + JoinDecompositionRule.Config.DEFAULT + .withMinPatternSize( + config.getJoinMinPatternSize()) + .withJoinQueueCapacity( + config.getJoinQueueCapacity()) + .withJoinByEdgeEnabled( + config.isJoinByEdgeEnabled()); + ForeignKeyMeta foreignKeyMeta = + config.getJoinByForeignKeyUri().isEmpty() + ? null + : new ForeignKeyMeta( + config.getJoinByForeignKeyUri()); + ((JoinDecompositionRule.Config) ruleConfig) + .withForeignKeyMeta(foreignKeyMeta); + } + if (ruleConfig != null) { + planner.addRule( + ruleConfig + .withRelBuilderFactory(relBuilderFactory) + .toRule()); + } + }); + return planner; + } + // todo: re-implement heuristic rules in ir core match + return new GraphHepPlanner(HepProgram.builder().build()); } - List logicalCBORules = this.matchPlanner.getRules(); - this.matchPlanner.clear(); - for (RelOptRule rule : logicalCBORules) { - this.matchPlanner.addRule(rule); + + private RelOptPlanner createPhysicalPlanner() { + HepProgramBuilder hepBuilder = HepProgram.builder(); + if (config.isOn()) { + List ruleConfigs = Lists.newArrayList(); + config.getRules() + .forEach( + k -> { + if (k.equals(ExpandGetVFusionRule.class.getSimpleName())) { + ruleConfigs.add( + ExpandGetVFusionRule.BasicExpandGetVFusionRule + .Config.DEFAULT); + ruleConfigs.add( + ExpandGetVFusionRule.PathBaseExpandGetVFusionRule + .Config.DEFAULT); + } + }); + ruleConfigs.forEach( + k -> { + hepBuilder.addRuleInstance( + k.withRelBuilderFactory(relBuilderFactory).toRule()); + }); + } + return new GraphHepPlanner(hepBuilder.build()); } - List physicalRBORules = this.physicalPlanner.getRules(); - this.physicalPlanner.clear(); - for (RelOptRule rule : physicalRBORules) { - this.physicalPlanner.addRule(rule); + + private synchronized void clear() { + List logicalRBORules = this.relPlanner.getRules(); + this.relPlanner.clear(); + for (RelOptRule rule : logicalRBORules) { + this.relPlanner.addRule(rule); + } + List logicalCBORules = this.matchPlanner.getRules(); + this.matchPlanner.clear(); + for (RelOptRule rule : logicalCBORules) { + this.matchPlanner.addRule(rule); + } + List physicalRBORules = this.physicalPlanner.getRules(); + this.physicalPlanner.clear(); + for (RelOptRule rule : physicalRBORules) { + this.physicalPlanner.addRule(rule); + } + } + } + + private class PlannerGroupManager { + private final List plannerGroups; + private final ScheduledExecutorService clearScheduler; + + public PlannerGroupManager() { + Preconditions.checkArgument( + config.getPlannerGroupSize() > 0, + "planner group size should be greater than 0"); + this.plannerGroups = new ArrayList(config.getPlannerGroupSize()); + for (int i = 0; i < config.getPlannerGroupSize(); ++i) { + this.plannerGroups.add(new PlannerGroup()); + } + this.clearScheduler = new ScheduledThreadPoolExecutor(1); + int clearInterval = config.getPlannerGroupClearIntervalMinutes(); + this.clearScheduler.scheduleAtFixedRate( + () -> { + try { + long freeMemBytes = Runtime.getRuntime().freeMemory(); + long totalMemBytes = Runtime.getRuntime().totalMemory(); + Preconditions.checkArgument( + totalMemBytes > 0, "total memory should be greater than 0"); + if (freeMemBytes / (double) totalMemBytes < 0.2d) { + logger.warn( + "start to clear planner groups. There are no enough memory" + + " in JVM, with free memory: {}, total memory: {}", + freeMemBytes, + totalMemBytes); + plannerGroups.forEach(PlannerGroup::clear); + } + } catch (Throwable t) { + logger.error("failed to clear planner group.", t); + } + }, + clearInterval, + clearInterval, + TimeUnit.MINUTES); + } + + public PlannerGroup getCurrentGroup() { + Preconditions.checkArgument( + !plannerGroups.isEmpty(), "planner groups should not be empty"); + int groupId = (int) Thread.currentThread().getId() % plannerGroups.size(); + return plannerGroups.get(groupId); } } } From 14999a06a326818f2ca39029c3fd805e3b0a43d7 Mon Sep 17 00:00:00 2001 From: shirly121 Date: Fri, 27 Sep 2024 14:37:23 +0800 Subject: [PATCH 2/3] fix JoinDecompositionRule in path expand --- .../common/ir/planner/rules/JoinDecompositionRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/JoinDecompositionRule.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/JoinDecompositionRule.java index 470c86a1f205..b49f2115deab 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/JoinDecompositionRule.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/JoinDecompositionRule.java @@ -418,7 +418,7 @@ private void addPxdInnerVDecompositions() { if (probePattern.getVertexNumber() > buildPattern.getVertexNumber()) continue; PatternVertex src = edge.getSrcVertex(); PatternVertex dst = edge.getDstVertex(); - if (maxHop >= config.getMinPatternSize() - 1 && maxHop == minHop) { + if (maxHop >= config.getMinPatternSize() - 1) { for (int i = 0; i <= minHop; ++i) { for (int j = 1; j <= maxHop - 1; ++j) { if (i <= j && (minHop - i) <= (maxHop - j)) { From fe55805db3818c0d9c5cc52b3b147c7611f641ff Mon Sep 17 00:00:00 2001 From: shirly121 Date: Sun, 29 Sep 2024 13:36:17 +0800 Subject: [PATCH 3/3] fix ci tests --- .../common/ir/planner/GraphRelOptimizer.java | 234 ++---------------- .../common/ir/planner/PlannerGroup.java | 194 +++++++++++++++ .../ir/planner/PlannerGroupManager.java | 107 ++++++++ .../common/ir/tools/GraphPlanner.java | 4 +- 4 files changed, 322 insertions(+), 217 deletions(-) create mode 100644 interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroup.java create mode 100644 interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroupManager.java diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java index 59c750a1506f..ae3039bd132f 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java @@ -21,9 +21,6 @@ import com.alibaba.graphscope.common.ir.meta.IrMeta; import com.alibaba.graphscope.common.ir.meta.glogue.calcite.GraphRelMetadataQuery; import com.alibaba.graphscope.common.ir.meta.glogue.calcite.handler.GraphMetadataHandlerProvider; -import com.alibaba.graphscope.common.ir.meta.schema.foreign.ForeignKeyMeta; -import com.alibaba.graphscope.common.ir.planner.rules.*; -import com.alibaba.graphscope.common.ir.planner.volcano.VolcanoPlannerX; import com.alibaba.graphscope.common.ir.rel.GraphShuttle; import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalSource; import com.alibaba.graphscope.common.ir.rel.graph.match.AbstractLogicalMatch; @@ -36,45 +33,44 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import org.apache.calcite.plan.*; -import org.apache.calcite.plan.hep.HepProgram; -import org.apache.calcite.plan.hep.HepProgramBuilder; -import org.apache.calcite.plan.volcano.VolcanoPlanner; +import org.apache.calcite.plan.GraphOptCluster; +import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.rel.core.Filter; import org.apache.calcite.rel.core.JoinRelType; import org.apache.calcite.rel.logical.LogicalJoin; import org.apache.calcite.rel.metadata.RelMetadataQuery; -import org.apache.calcite.rel.rules.CoreRules; -import org.apache.calcite.rel.rules.FilterJoinRule; import org.apache.calcite.tools.RelBuilderFactory; import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Optimize graph relational tree which consists of match and other relational operators */ public class GraphRelOptimizer { - private static final Logger logger = LoggerFactory.getLogger(GraphRelOptimizer.class); private final PlannerConfig config; private final RelBuilderFactory relBuilderFactory; private final GlogueHolder glogueHolder; private final PlannerGroupManager plannerGroupManager; + public GraphRelOptimizer(Configs graphConfig, Class instance) { + try { + this.config = new PlannerConfig(graphConfig); + this.relBuilderFactory = new GraphBuilderFactory(graphConfig); + this.glogueHolder = new GlogueHolder(graphConfig); + this.plannerGroupManager = + instance.getDeclaredConstructor(PlannerConfig.class, RelBuilderFactory.class) + .newInstance(this.config, this.relBuilderFactory); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public GraphRelOptimizer(Configs graphConfig) { - this.config = new PlannerConfig(graphConfig); - this.relBuilderFactory = new GraphBuilderFactory(graphConfig); - this.glogueHolder = new GlogueHolder(graphConfig); - this.plannerGroupManager = new PlannerGroupManager(); + this(graphConfig, PlannerGroupManager.Dynamic.class); } public GlogueHolder getGlogueHolder() { @@ -83,7 +79,7 @@ public GlogueHolder getGlogueHolder() { public RelOptPlanner getMatchPlanner() { PlannerGroup currentGroup = this.plannerGroupManager.getCurrentGroup(); - return currentGroup.matchPlanner; + return currentGroup.getMatchPlanner(); } public RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { @@ -101,7 +97,7 @@ public RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { return null; } - private class MatchOptimizer extends GraphShuttle { + public static class MatchOptimizer extends GraphShuttle { private final GraphIOProcessor ioProcessor; private final RelOptPlanner matchPlanner; @@ -199,198 +195,4 @@ public void visit(RelNode node, int ordinal, @Nullable RelNode parent) { return decomposable.get(); } } - - private class PlannerGroup { - private final RelOptPlanner relPlanner; - private final RelOptPlanner matchPlanner; - private final RelOptPlanner physicalPlanner; - - public PlannerGroup() { - this.relPlanner = createRelPlanner(); - this.matchPlanner = createMatchPlanner(); - this.physicalPlanner = createPhysicalPlanner(); - } - - public synchronized RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { - if (config.isOn()) { - // apply rules of 'FilterPushDown' before the match optimization - relPlanner.setRoot(before); - RelNode relOptimized = relPlanner.findBestExp(); - if (config.getOpt() == PlannerConfig.Opt.CBO) { - relOptimized = - relOptimized.accept(new MatchOptimizer(ioProcessor, matchPlanner)); - } - // apply rules of 'FieldTrim' after the match optimization - if (config.getRules().contains(FieldTrimRule.class.getSimpleName())) { - relOptimized = FieldTrimRule.trim(ioProcessor.getBuilder(), relOptimized); - } - physicalPlanner.setRoot(relOptimized); - RelNode physicalOptimized = physicalPlanner.findBestExp(); - clear(); - return physicalOptimized; - } - return before; - } - - private RelOptPlanner createRelPlanner() { - HepProgramBuilder hepBuilder = HepProgram.builder(); - if (config.isOn()) { - List ruleConfigs = Lists.newArrayList(); - config.getRules() - .forEach( - k -> { - if (k.equals( - FilterJoinRule.FilterIntoJoinRule.class - .getSimpleName())) { - ruleConfigs.add(CoreRules.FILTER_INTO_JOIN.config); - } else if (k.equals(FilterMatchRule.class.getSimpleName())) { - ruleConfigs.add(FilterMatchRule.Config.DEFAULT); - } - }); - ruleConfigs.forEach( - k -> { - hepBuilder.addRuleInstance( - k.withRelBuilderFactory(relBuilderFactory).toRule()); - }); - } - return new GraphHepPlanner(hepBuilder.build()); - } - - private RelOptPlanner createMatchPlanner() { - if (config.isOn() && config.getOpt() == PlannerConfig.Opt.CBO) { - VolcanoPlanner planner = new VolcanoPlannerX(); - planner.addRelTraitDef(ConventionTraitDef.INSTANCE); - planner.setTopDownOpt(true); - planner.setNoneConventionHasInfiniteCost(false); - config.getRules() - .forEach( - k -> { - RelRule.Config ruleConfig = null; - if (k.equals(ExtendIntersectRule.class.getSimpleName())) { - ruleConfig = - ExtendIntersectRule.Config.DEFAULT - .withMaxPatternSizeInGlogue( - config.getGlogueSize()) - .withLabelConstraintsEnabled( - config.labelConstraintsEnabled()); - } else if (k.equals( - JoinDecompositionRule.class.getSimpleName())) { - ruleConfig = - JoinDecompositionRule.Config.DEFAULT - .withMinPatternSize( - config.getJoinMinPatternSize()) - .withJoinQueueCapacity( - config.getJoinQueueCapacity()) - .withJoinByEdgeEnabled( - config.isJoinByEdgeEnabled()); - ForeignKeyMeta foreignKeyMeta = - config.getJoinByForeignKeyUri().isEmpty() - ? null - : new ForeignKeyMeta( - config.getJoinByForeignKeyUri()); - ((JoinDecompositionRule.Config) ruleConfig) - .withForeignKeyMeta(foreignKeyMeta); - } - if (ruleConfig != null) { - planner.addRule( - ruleConfig - .withRelBuilderFactory(relBuilderFactory) - .toRule()); - } - }); - return planner; - } - // todo: re-implement heuristic rules in ir core match - return new GraphHepPlanner(HepProgram.builder().build()); - } - - private RelOptPlanner createPhysicalPlanner() { - HepProgramBuilder hepBuilder = HepProgram.builder(); - if (config.isOn()) { - List ruleConfigs = Lists.newArrayList(); - config.getRules() - .forEach( - k -> { - if (k.equals(ExpandGetVFusionRule.class.getSimpleName())) { - ruleConfigs.add( - ExpandGetVFusionRule.BasicExpandGetVFusionRule - .Config.DEFAULT); - ruleConfigs.add( - ExpandGetVFusionRule.PathBaseExpandGetVFusionRule - .Config.DEFAULT); - } - }); - ruleConfigs.forEach( - k -> { - hepBuilder.addRuleInstance( - k.withRelBuilderFactory(relBuilderFactory).toRule()); - }); - } - return new GraphHepPlanner(hepBuilder.build()); - } - - private synchronized void clear() { - List logicalRBORules = this.relPlanner.getRules(); - this.relPlanner.clear(); - for (RelOptRule rule : logicalRBORules) { - this.relPlanner.addRule(rule); - } - List logicalCBORules = this.matchPlanner.getRules(); - this.matchPlanner.clear(); - for (RelOptRule rule : logicalCBORules) { - this.matchPlanner.addRule(rule); - } - List physicalRBORules = this.physicalPlanner.getRules(); - this.physicalPlanner.clear(); - for (RelOptRule rule : physicalRBORules) { - this.physicalPlanner.addRule(rule); - } - } - } - - private class PlannerGroupManager { - private final List plannerGroups; - private final ScheduledExecutorService clearScheduler; - - public PlannerGroupManager() { - Preconditions.checkArgument( - config.getPlannerGroupSize() > 0, - "planner group size should be greater than 0"); - this.plannerGroups = new ArrayList(config.getPlannerGroupSize()); - for (int i = 0; i < config.getPlannerGroupSize(); ++i) { - this.plannerGroups.add(new PlannerGroup()); - } - this.clearScheduler = new ScheduledThreadPoolExecutor(1); - int clearInterval = config.getPlannerGroupClearIntervalMinutes(); - this.clearScheduler.scheduleAtFixedRate( - () -> { - try { - long freeMemBytes = Runtime.getRuntime().freeMemory(); - long totalMemBytes = Runtime.getRuntime().totalMemory(); - Preconditions.checkArgument( - totalMemBytes > 0, "total memory should be greater than 0"); - if (freeMemBytes / (double) totalMemBytes < 0.2d) { - logger.warn( - "start to clear planner groups. There are no enough memory" - + " in JVM, with free memory: {}, total memory: {}", - freeMemBytes, - totalMemBytes); - plannerGroups.forEach(PlannerGroup::clear); - } - } catch (Throwable t) { - logger.error("failed to clear planner group.", t); - } - }, - clearInterval, - clearInterval, - TimeUnit.MINUTES); - } - - public PlannerGroup getCurrentGroup() { - Preconditions.checkArgument( - !plannerGroups.isEmpty(), "planner groups should not be empty"); - int groupId = (int) Thread.currentThread().getId() % plannerGroups.size(); - return plannerGroups.get(groupId); - } - } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroup.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroup.java new file mode 100644 index 000000000000..110c615ab82a --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroup.java @@ -0,0 +1,194 @@ +/* + * + * * Copyright 2020 Alibaba Group Holding Limited. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package com.alibaba.graphscope.common.ir.planner; + +import com.alibaba.graphscope.common.config.PlannerConfig; +import com.alibaba.graphscope.common.ir.meta.schema.foreign.ForeignKeyMeta; +import com.alibaba.graphscope.common.ir.planner.rules.*; +import com.alibaba.graphscope.common.ir.planner.volcano.VolcanoPlannerX; +import com.google.common.collect.Lists; + +import org.apache.calcite.plan.ConventionTraitDef; +import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptRule; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.plan.hep.HepProgram; +import org.apache.calcite.plan.hep.HepProgramBuilder; +import org.apache.calcite.plan.volcano.VolcanoPlanner; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.rules.CoreRules; +import org.apache.calcite.rel.rules.FilterJoinRule; +import org.apache.calcite.tools.RelBuilderFactory; + +import java.util.List; + +public class PlannerGroup { + private final RelOptPlanner relPlanner; + private final RelOptPlanner matchPlanner; + private final RelOptPlanner physicalPlanner; + private final PlannerConfig config; + private final RelBuilderFactory relBuilderFactory; + + public PlannerGroup(PlannerConfig config, RelBuilderFactory relBuilderFactory) { + this.config = config; + this.relBuilderFactory = relBuilderFactory; + this.relPlanner = createRelPlanner(); + this.matchPlanner = createMatchPlanner(); + this.physicalPlanner = createPhysicalPlanner(); + } + + public synchronized RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { + if (config.isOn()) { + // apply rules of 'FilterPushDown' before the match optimization + relPlanner.setRoot(before); + RelNode relOptimized = relPlanner.findBestExp(); + if (config.getOpt() == PlannerConfig.Opt.CBO) { + relOptimized = + relOptimized.accept( + new GraphRelOptimizer.MatchOptimizer(ioProcessor, matchPlanner)); + } + // apply rules of 'FieldTrim' after the match optimization + if (config.getRules().contains(FieldTrimRule.class.getSimpleName())) { + relOptimized = FieldTrimRule.trim(ioProcessor.getBuilder(), relOptimized); + } + physicalPlanner.setRoot(relOptimized); + RelNode physicalOptimized = physicalPlanner.findBestExp(); + clear(); + return physicalOptimized; + } + return before; + } + + private RelOptPlanner createRelPlanner() { + HepProgramBuilder hepBuilder = HepProgram.builder(); + if (config.isOn()) { + List ruleConfigs = Lists.newArrayList(); + config.getRules() + .forEach( + k -> { + if (k.equals( + FilterJoinRule.FilterIntoJoinRule.class.getSimpleName())) { + ruleConfigs.add(CoreRules.FILTER_INTO_JOIN.config); + } else if (k.equals(FilterMatchRule.class.getSimpleName())) { + ruleConfigs.add(FilterMatchRule.Config.DEFAULT); + } + }); + ruleConfigs.forEach( + k -> { + hepBuilder.addRuleInstance( + k.withRelBuilderFactory(relBuilderFactory).toRule()); + }); + } + return new GraphHepPlanner(hepBuilder.build()); + } + + private RelOptPlanner createMatchPlanner() { + if (config.isOn() && config.getOpt() == PlannerConfig.Opt.CBO) { + VolcanoPlanner planner = new VolcanoPlannerX(); + planner.addRelTraitDef(ConventionTraitDef.INSTANCE); + planner.setTopDownOpt(true); + planner.setNoneConventionHasInfiniteCost(false); + config.getRules() + .forEach( + k -> { + RelRule.Config ruleConfig = null; + if (k.equals(ExtendIntersectRule.class.getSimpleName())) { + ruleConfig = + ExtendIntersectRule.Config.DEFAULT + .withMaxPatternSizeInGlogue( + config.getGlogueSize()) + .withLabelConstraintsEnabled( + config.labelConstraintsEnabled()); + } else if (k.equals(JoinDecompositionRule.class.getSimpleName())) { + ruleConfig = + JoinDecompositionRule.Config.DEFAULT + .withMinPatternSize( + config.getJoinMinPatternSize()) + .withJoinQueueCapacity( + config.getJoinQueueCapacity()) + .withJoinByEdgeEnabled( + config.isJoinByEdgeEnabled()); + ForeignKeyMeta foreignKeyMeta = + config.getJoinByForeignKeyUri().isEmpty() + ? null + : new ForeignKeyMeta( + config.getJoinByForeignKeyUri()); + ((JoinDecompositionRule.Config) ruleConfig) + .withForeignKeyMeta(foreignKeyMeta); + } + if (ruleConfig != null) { + planner.addRule( + ruleConfig + .withRelBuilderFactory(relBuilderFactory) + .toRule()); + } + }); + return planner; + } + // todo: re-implement heuristic rules in ir core match + return new GraphHepPlanner(HepProgram.builder().build()); + } + + private RelOptPlanner createPhysicalPlanner() { + HepProgramBuilder hepBuilder = HepProgram.builder(); + if (config.isOn()) { + List ruleConfigs = Lists.newArrayList(); + config.getRules() + .forEach( + k -> { + if (k.equals(ExpandGetVFusionRule.class.getSimpleName())) { + ruleConfigs.add( + ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config + .DEFAULT); + ruleConfigs.add( + ExpandGetVFusionRule.PathBaseExpandGetVFusionRule.Config + .DEFAULT); + } + }); + ruleConfigs.forEach( + k -> { + hepBuilder.addRuleInstance( + k.withRelBuilderFactory(relBuilderFactory).toRule()); + }); + } + return new GraphHepPlanner(hepBuilder.build()); + } + + public synchronized void clear() { + List logicalRBORules = this.relPlanner.getRules(); + this.relPlanner.clear(); + for (RelOptRule rule : logicalRBORules) { + this.relPlanner.addRule(rule); + } + List logicalCBORules = this.matchPlanner.getRules(); + this.matchPlanner.clear(); + for (RelOptRule rule : logicalCBORules) { + this.matchPlanner.addRule(rule); + } + List physicalRBORules = this.physicalPlanner.getRules(); + this.physicalPlanner.clear(); + for (RelOptRule rule : physicalRBORules) { + this.physicalPlanner.addRule(rule); + } + } + + public RelOptPlanner getMatchPlanner() { + return this.matchPlanner; + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroupManager.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroupManager.java new file mode 100644 index 000000000000..c7f10be17928 --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/PlannerGroupManager.java @@ -0,0 +1,107 @@ +/* + * + * * Copyright 2020 Alibaba Group Holding Limited. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package com.alibaba.graphscope.common.ir.planner; + +import com.alibaba.graphscope.common.config.PlannerConfig; +import com.google.common.base.Preconditions; + +import org.apache.calcite.tools.RelBuilderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public abstract class PlannerGroupManager { + protected final PlannerConfig config; + protected final RelBuilderFactory relBuilderFactory; + + public PlannerGroupManager(PlannerConfig config, RelBuilderFactory relBuilderFactory) { + this.config = config; + this.relBuilderFactory = relBuilderFactory; + } + + public abstract PlannerGroup getCurrentGroup(); + + public static class Static extends PlannerGroupManager { + private final PlannerGroup singleGroup; + + public Static(PlannerConfig config, RelBuilderFactory relBuilderFactory) { + super(config, relBuilderFactory); + this.singleGroup = new PlannerGroup(config, relBuilderFactory); + } + + @Override + public PlannerGroup getCurrentGroup() { + return this.singleGroup; + } + } + + public static class Dynamic extends PlannerGroupManager { + private final Logger logger = LoggerFactory.getLogger(PlannerGroupManager.class); + private final List plannerGroups; + private final ScheduledExecutorService clearScheduler; + + public Dynamic(PlannerConfig config, RelBuilderFactory relBuilderFactory) { + super(config, relBuilderFactory); + Preconditions.checkArgument( + config.getPlannerGroupSize() > 0, + "planner group size should be greater than 0"); + this.plannerGroups = new ArrayList(config.getPlannerGroupSize()); + for (int i = 0; i < config.getPlannerGroupSize(); ++i) { + this.plannerGroups.add(new PlannerGroup(config, relBuilderFactory)); + } + this.clearScheduler = new ScheduledThreadPoolExecutor(1); + int clearInterval = config.getPlannerGroupClearIntervalMinutes(); + this.clearScheduler.scheduleAtFixedRate( + () -> { + try { + long freeMemBytes = Runtime.getRuntime().freeMemory(); + long totalMemBytes = Runtime.getRuntime().totalMemory(); + Preconditions.checkArgument( + totalMemBytes > 0, "total memory should be greater than 0"); + if (freeMemBytes / (double) totalMemBytes < 0.2d) { + logger.warn( + "start to clear planner groups. There are no enough memory" + + " in JVM, with free memory: {}, total memory: {}", + freeMemBytes, + totalMemBytes); + plannerGroups.forEach(PlannerGroup::clear); + } + } catch (Throwable t) { + logger.error("failed to clear planner group.", t); + } + }, + clearInterval, + clearInterval, + TimeUnit.MINUTES); + } + + @Override + public PlannerGroup getCurrentGroup() { + Preconditions.checkArgument( + !plannerGroups.isEmpty(), "planner groups should not be empty"); + int groupId = (int) Thread.currentThread().getId() % plannerGroups.size(); + return plannerGroups.get(groupId); + } + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java index cb69a5d6f987..42c1820c1478 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java @@ -30,6 +30,7 @@ import com.alibaba.graphscope.common.ir.meta.schema.IrGraphSchema; import com.alibaba.graphscope.common.ir.planner.GraphIOProcessor; import com.alibaba.graphscope.common.ir.planner.GraphRelOptimizer; +import com.alibaba.graphscope.common.ir.planner.PlannerGroupManager; import com.alibaba.graphscope.common.ir.runtime.PhysicalBuilder; import com.alibaba.graphscope.common.ir.runtime.PhysicalPlan; import com.alibaba.graphscope.common.ir.runtime.ProcedurePhysicalBuilder; @@ -232,7 +233,8 @@ public static void main(String[] args) throws Exception { + " 'optional '"); } Configs configs = Configs.Factory.create(args[0]); - GraphRelOptimizer optimizer = new GraphRelOptimizer(configs); + GraphRelOptimizer optimizer = + new GraphRelOptimizer(configs, PlannerGroupManager.Static.class); IrMetaFetcher metaFetcher = createIrMetaFetcher(configs, optimizer.getGlogueHolder()); String query = FileUtils.readFileToString(new File(args[1]), StandardCharsets.UTF_8); GraphPlanner planner =