You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Calcite升级至1.32/1.34后,有状态规则与Volcano Planner兼容异常

Apache Calcite升级后有状态规则matches返回false触发AssertionError问题

问题概述

将Apache Calcite从1.21.0升级到1.32.0或1.34.0版本后,自定义有状态规则StateFullRule(继承RelOptRule)与Volcano Planner配合时出现异常:当matches方法返回false时,会抛出AssertionError,该规则在1.21.0版本中可正常运行。

规则核心代码如下:

public class StateFullRule extends RelOptRule {

    // 规则内部维护的状态
    //private boolean isProccessed = false;
    //private Map<RelNodeKey, Object> ancestorsMap;

    public StateFullRule() {
        super(operand(LogicalUnion.class, operand(LogicalProject.class, any()), new RelOptRuleOperand[]{operand(LogicalProject.class, any())}), "StateFullRule");
    }

    @Override
    public boolean matches(RelOptRuleCall call) {
            // 基于输入构建中间状态
            Boolean matches = determineIfRuleMatches();  // 可能返回true或false
            if(matches) {
                // 更新规则内部状态
                //ancestorsMap = subsetAncestorsMap;
            }
            return matches;
    }

    private Boolean determineIfRuleMatches() {
        // 基于中间状态和规则自身状态判断是否匹配
        return false;
    }
    

    public void onMatch(RelOptRuleCall call)   {
        // 使用状态执行转换逻辑
    }
}

异常堆栈:

java.lang.AssertionError: null
    at org.apache.calcite.plan.volcano.IterativeRuleDriver.drive(IterativeRuleDriver.java:57) ~[calcite-core-1.34.0.jar:1.34.0]
    at org.apache.calcite.plan.volcano.VolcanoPlanner.findBestExp(VolcanoPlanner.java:523) ~[calcite-core-1.34.0.jar:1.34.0]

原因分析

  1. 规则设计规范冲突:Calcite的RelOptRule设计初衷是无状态的,规则实例会在规划过程中被复用、甚至多线程调用。自定义规则中维护内部状态的做法本身就不符合Calcite的设计原则,旧版本未做严格校验,但新版本增加了断言检查。
  2. 匹配流程的断言强化:1.32.0+版本的IterativeRuleDriver和TopDownRuleDriver中,当规则的operand模式匹配成功(即RelNode结构符合规则定义的操作数模板)后,如果matches方法返回false,会触发断言。Calcite认为operand应足够精确过滤不需要的场景,matches仅用于operand无法覆盖的细粒度判断,频繁出现operand匹配但matches返回false属于规则设计缺陷。

解决方案

1. 移除规则内部状态(推荐)

将规则依赖的状态转移到外部上下文,比如:

  • 通过RelOptRuleCall.getPlanner().getContext()存储全局状态;
  • 利用RelMetadataQuery获取RelNode元数据,避免规则内部维护状态;
  • 将状态附加到RelNode的TraitSet或自定义属性中传递。

修改后的无状态规则示例:

public class StatelessRule extends RelOptRule {

    public StatelessRule() {
        super(operand(LogicalUnion.class, operand(LogicalProject.class, any()), operand(LogicalProject.class, any())), "StatelessRule");
    }

    @Override
    public boolean matches(RelOptRuleCall call) {
        // 仅基于RelNode本身和元数据判断匹配,不维护规则内部状态
        LogicalUnion union = call.rel(0);
        return determineIfRuleMatches(union);
    }

    private boolean determineIfRuleMatches(LogicalUnion union) {
        // 基于RelNode的属性、元数据做判断
        return union.getInputs().size() == 2;
    }
    

    public void onMatch(RelOptRuleCall call)   {
        // 转换逻辑仅依赖输入RelNode和上下文,不依赖规则内部状态
        LogicalUnion union = call.rel(0);
        // ... 执行转换逻辑
    }
}

2. 优化Operand定义,减少matches判断

尽可能通过Operand模式精确匹配目标RelNode结构,减少matches方法的判断逻辑,避免出现operand匹配但matches返回false的情况。比如针对特定字段的LogicalProject,可在Operand中增加条件过滤。

3. 临时禁用断言(不推荐)

若需快速验证,可通过JVM启动参数-ea:org.apache.calcite.plan.volcano.-关闭Volcano模块的断言,但这仅为临时方案,无法解决有状态规则带来的线程安全、规划结果不可预测等潜在问题。

关键疑问解答

Calcite并非完全不允许matches返回false,而是当Operand已匹配成功的前提下,matches返回false会触发断言。这是新版本对规则设计合理性的校验,引导开发者写出更精确的规则。实现TransformationRule接口无法解决问题,因为该接口本质是RelOptRule的扩展,核心约束一致。

内容的提问来源于stack exchange,提问作者Parag Chimanpure

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 16:20:40