Skip to content

[Bug] LogicalOlapTableStreamScan can loop forever after partition pruning #65654

Closed

Description

@yujun777

Search before asking

  • I had searched in the issues and found no similar issues.

Version

master (reproduced on a local master-based development branch)

What's Wrong?

LogicalOlapTableStreamScan can enter an optimizer rewrite loop after partition pruning.

The issue is not specific to MTMV itself. MTMV incremental refresh just made it easy to expose, because its generated plan contains stream scans and goes through the same pruning path. The real problem is in the generic stream-scan builder contract used by the optimizer.

In the bad case, FE repeatedly rewrites the same stream scan, the query/task does not finish, and FE keeps allocating plans and burning CPU.

This issue is related to the Table Stream / row binlog stack tracked by:

What You Expected?

Once partition pruning has been applied to a stream scan, the rewritten plan should be marked as already pruned, so the same pruning rule should not keep matching it forever.

How to Reproduce?

One easy reproduction path is a stream-based plan produced during IVM regression. For example, regression-test/suites/mtmv_p0/ivm/test_ivm_partition_unique_key.groovy can trigger the problem before the fix.

The important part is not MTMV semantics themselves, but that the final refresh plan contains LogicalOlapTableStreamScan and partition pruning rewrites it.

Observed symptoms before the fix:

  • the task remains RUNNING
  • FE stack traces stay in optimizer rewrite
  • FE repeatedly hits PruneOlapScanPartition on a stream scan

Current Behavior

Observed on FE:

  • TopDownVisitorRewriteJob.doRewrite keeps revisiting the same subtree
  • PruneOlapScanPartition continues matching the same stream scan
  • the rewritten LogicalOlapTableStreamScan is still considered not pruned

Root Cause

LogicalOlapTableStreamScan.withSelectedPartitionIds(List<Long>, boolean) does not follow the parent LogicalOlapScan contract.

In the parent class, the second argument means hasPartitionPredicate, and the rebuilt scan is always marked as partitionPruned=true.

In LogicalOlapTableStreamScan, the override interprets the second argument as isPartitionPruned and passes it into the constructor directly. As a result, when PruneOlapScanPartition calls:

scan.withSelectedPartitionIds(prunedPartitions, hasPartitionPredicate)

and hasPartitionPredicate is false, the rebuilt stream scan also gets partitionPruned=false. Then the same pruning rule matches again, rewrites again, and the optimizer loops.

Anything Else?

The local fix is to align LogicalOlapTableStreamScan.withSelectedPartitionIds(List<Long>, boolean) with the parent contract:

  • treat the boolean parameter as hasPartitionPredicate
  • always rebuild the stream scan with partitionPruned=true

After applying that fix locally, the previously hanging reproduction path finishes normally.

Activity

  1. zclllyybb commented on Jul 15, 2026

    @zclllyybb
    Contributor

    Breakwater-GitHub-Analysis-Slot: slot_1e4f4948da0f
    This content is generated by AI for reference only.

    Initial analysis from the current upstream/master code path:

    This looks like a real FE/Nereids rewrite-loop bug in the stream-scan subclass contract, not an MTMV-only issue.

    The confirmed chain is:

    1. PruneOlapScanPartition matches only unpruned OLAP scans (!scan.isPartitionPruned()).
    2. After pruning, it calls scan.withSelectedPartitionIds(prunedPartitions, hasPartitionPredicate).
    3. In the parent LogicalOlapScan, that second argument means hasPartitionPredicate, and the rebuilt scan is always created with partitionPruned=true.
    4. LogicalOlapTableStreamScan extends LogicalOlapScan, so it is eligible for the same pruning rule, but its override currently treats the same second argument as isPartitionPruned and passes it into the stream-scan constructor as the partitionPruned value.
    5. Therefore, when pruning succeeds but hasPartitionPredicate=false, the rebuilt LogicalOlapTableStreamScan still has partitionPruned=false. The pruning rule can keep matching the same logical shape and repeatedly rebuild the plan.

    That matches the reported symptoms: repeated PruneOlapScanPartition, TopDownVisitorRewriteJob revisiting the same subtree, task remaining RUNNING, and FE CPU/allocation pressure.

    I also found the linked public fix PR: #65657. Its diff aligns with the code-level diagnosis: keep the boolean parameter as hasPartitionPredicate and pass true for the rebuilt stream scan's partitionPruned flag. That is the right fix direction for this specific loop.

    Suggested next steps:

    • Review and merge [fix](stream) Fix stream scan partition prune state propagation #65657 after CI/review passes.
    • Keep the regression evidence visible. The PR states ./run-regression-test.sh --run -d mtmv_p0 -s test_ivm_partition_unique_key was run manually; if possible, attach/keep that result or add a deterministic regression assertion so this does not regress silently.
    • Check whether any maintained release branch already contains LogicalOlapTableStreamScan and the same override. If yes, it should receive the same fix because the bug is in a generic builder contract used by the optimizer.

    Missing information is not blocking the root-cause assessment, because the current master code already shows the contract mismatch. For impact scoping/backport decisions, the useful extra data would be the exact commit SHA of the reproduced development branch and, if available, one FE stack sample from the hanging task showing the repeated pruning rewrite.

  2. yujun777 commented on Jul 22, 2026

    @yujun777
    ContributorAuthor

    FIX PR: #65657

  3. added a commit that references this issue on Jul 23, 2026
    07bd92a
  4. added a commit that references this issue on Jul 27, 2026
    428d474
  5. added a commit that references this issue on Aug 17, 2026
    fcf2505
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions