Skip to content

fix(function): optimize serial_extract to avoid full tuple deserialization - #24246

Merged
mergify[bot] merged 7 commits into
matrixorigin:mainfrom
jiangxinmeng1:fix/serial-extract-perf-24241
Apr 30, 2026
Merged

fix(function): optimize serial_extract to avoid full tuple deserialization#24246
mergify[bot] merged 7 commits into
matrixorigin:mainfrom
jiangxinmeng1:fix/serial-extract-perf-24241

Conversation

@jiangxinmeng1

@jiangxinmeng1 jiangxinmeng1 commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • API-change
  • BUG
  • Improvement
  • Documentation
  • Feature
  • Test and CI
  • Code Refactoring

Which issue(s) this PR fixes:

fixes #24241

What this PR does / why we need it:

  • Add UnpackNthElement to decode only up to the target element in a serialized tuple, instead of deserializing the entire blob
  • Use fast path in serial_extract when the index parameter is constant (the common case in GROUP BY on composite secondary index)
  • Reduces serial_extract CPU overhead ~6x for DISTINCT queries on leading columns of composite secondary indexes

Background

When running SELECT DISTINCT col FROM table WHERE col IN (...) on the leading key of a composite secondary index, the query uses serial_extract(__mo_index_idx_col, 0, VARCHAR) as the GROUP BY key. The old implementation deserialized the entire composite tuple for every row just to extract the first element, causing ~35s of CPU overhead on 45M rows.

With this optimization, only the first element is decoded, reducing the aggregate phase from ~171ms to ~28ms on 100K rows (6x improvement).

Test plan

  • TestUnpackNthElement — verifies correctness for all types, nil values, single elements, out-of-range
  • TestSerialExtract — existing test still passes
  • TestSimpleTupleAllTypes — existing tuple tests pass
  • Manual verification: DISTINCT aggregate time reduced from 171ms to 28ms on 100K rows

🤖 Generated with Claude Code

…ation (matrixorigin#24241)

When serial_extract is called with a constant index (the common case in
GROUP BY on composite secondary index), use UnpackNthElement to decode
only up to the target element instead of deserializing the entire tuple.
This reduces CPU overhead ~6x for DISTINCT queries on leading columns of
composite secondary indexes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Secondary index tables were missing Primary: true on the
__mo_index_idx_col ColDef, causing the storage layer to not
recognize it as a sort key. This meant index table data was
unsorted during flush/merge, zone maps had maximal overlap,
and prefix_in block filter could not prune any blocks.

With this fix, newly created secondary index tables have their
index column marked as primary, enabling sorted data layout
and effective block-level zone map filtering.

Refs matrixorigin#24241

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mergify mergify Bot added the kind/bug Something isn't working label Apr 29, 2026
heni02 pushed a commit that referenced this pull request Apr 29, 2026
## What type of PR is this?

- [ ] API-change
- [x] BUG
- [ ] Improvement
- [ ] Documentation
- [ ] Feature
- [ ] Test and CI
- [ ] Code Refactoring

## Which issue(s) this PR fixes:

fixes #24241

## What this PR does / why we need it:
- Cherry-pick of #24246 to 3.0-dev branch
- Add `UnpackNthElement` to deserialize only the Nth element from a
serialized tuple, avoiding full deserialization
- Add fast path in `serial_extract` for constant index parameter (the
common case in DISTINCT queries on secondary index)
- **7.9x speedup** on DISTINCT queries over composite secondary index
(14.9s → 1.9s aggregate phase on 5M rows)

## Adaptation for 3.0
- Removed `yearCode` case (not present in 3.0)
- Adapted nil test case (3.0 Packer has no `EncodeNull`)

## Test plan
- [x] Unit test `TestUnpackNthElement` passes
- [x] Local verification: 10M row table with 6-column composite index,
DISTINCT aggregate 1209ms (vs ~15s without fix)
- [x] Builds successfully on 3.0-dev

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: XuPeng-SH <xupeng3112@163.com>
- Cover all type branches in UnpackNthElement (int8/16/32/64, uint8/16/32/64, bool, float32/64, date, datetime, timestamp, time, year, decimal64/128, varchar, bit, objectid, uuid, nil, error paths)
- Add TestSerialExtractConstIndex to exercise the new constant-index fast path in serialExtractExceptStrings and serialExtractForString
- Test null p1, null tuple elements, and normal extraction for both string and non-string result types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Ariznawlll

Copy link
Copy Markdown
Collaborator

/auto-test-pr

@mergify

mergify Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-04-30 07:45 UTC · Rule: main
  • Checks started · in-place
  • 🚫 Left the queue2026-04-30 07:47 UTC · at 52c8d32515240a387799d2edd1a73bc477347417

This pull request spent 2 minutes 6 seconds in the queue, with no time running CI.

Waiting for:
  • any of:
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
  • any of:
    • check-neutral = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-skipped = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-success = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
  • any of:
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
  • any of:
    • check-neutral = Matrixone CI / SCA Test on Ubuntu/x86
    • check-skipped = Matrixone CI / SCA Test on Ubuntu/x86
    • check-success = Matrixone CI / SCA Test on Ubuntu/x86
  • any of:
    • check-neutral = Matrixone CI / UT Test on Ubuntu/x86
    • check-skipped = Matrixone CI / UT Test on Ubuntu/x86
    • check-success = Matrixone CI / UT Test on Ubuntu/x86
  • any of:
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
  • any of:
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
  • any of:
    • check-neutral = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-skipped = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-success = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
  • any of:
    • check-neutral = Matrixone Utils CI / Coverage
    • check-skipped = Matrixone Utils CI / Coverage
    • check-success = Matrixone Utils CI / Coverage
All conditions
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-skipped = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-success = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone CI / SCA Test on Ubuntu/x86
    • check-skipped = Matrixone CI / SCA Test on Ubuntu/x86
    • check-success = Matrixone CI / SCA Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone CI / UT Test on Ubuntu/x86
    • check-skipped = Matrixone CI / UT Test on Ubuntu/x86
    • check-success = Matrixone CI / UT Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-skipped = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-success = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
  • any of [🛡 GitHub branch protection]:
    • check-neutral = Matrixone Utils CI / Coverage
    • check-skipped = Matrixone Utils CI / Coverage
    • check-success = Matrixone Utils CI / Coverage
  • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
  • #changes-requested-reviews-by = 0 [🛡 GitHub branch protection]
  • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
  • branch-protection-review-decision = APPROVED [🛡 GitHub branch protection]

Reason

The merge conditions cannot be satisfied due to failing checks

Hint

You may have to fix your CI before adding the pull request to the queue again.
If you update this pull request, to fix the CI, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

@Ariznawlll

Copy link
Copy Markdown
Collaborator

/auto-test-pr

@Ariznawlll

Copy link
Copy Markdown
Collaborator

PR #24246 测试覆盖分析

变更摘要

本 PR 优化了 serial_extract 函数在处理序列化 tuple 时的性能,新增 UnpackNthElement 方法用于只解码目标元素(避免全量反序列化),并在 serial_extract 中对常量 index 参数走快速路径。主要影响 tuple 解码、内置函数实现、以及相关测试。

6 类测试覆盖情况

测试类型 覆盖状态 说明
BVT 涉及 function 类(serial_extract),已有 func_builtin_test.go 和 tuple 测试覆盖,且 PR 已补充/验证相关 case。
稳定性 serial_extract 优化影响 GROUP BY、DISTINCT 等高频场景,TPCH/TPCC 等长稳测试会覆盖此路径。无需新增。
Chaos 本次仅为函数性能优化,无故障注入相关代码路径。
大数据 ⚠️ 优化针对大表 DISTINCT/GROUP BY 场景,需补充大数据量下 serial_extract 查询 case(如 DISTINCT/聚合)。
PITR 未涉及备份恢复相关代码。
Snapshot 未涉及快照相关代码。

图例: ✅ 已覆盖 ⚠️ 需补充 ➖ 不相关

建议

  • 大数据测试:建议在 mo-nightly-regression 的 big_data suite 中补充如下 SQL 查询,覆盖 composite secondary index 上的 serial_extract 快速路径:

    -- 假设 lineitem 表有 __mo_index_idx_col,模拟 composite index
    SELECT DISTINCT serial_extract(__mo_index_idx_col, 0, 'VARCHAR') FROM lineitem;

    SELECT serial_extract(__mo_index_idx_col, 0, 'VARCHAR'), COUNT(*) FROM lineitem GROUP BY serial_extract(__mo_index_idx_col, 0, 'VARCHAR');

    可针对 tpch_1g/tpch_10g 等大表,验证性能和正确性。

  • BVT:已有 func_builtin_test.go 和 tuple_test.go 覆盖,无需补充。

🤖 自动生成的测试 PR

✅ 大数据 PR 已提交:https://github.com/Ariznawlll/mo-nightly-regression/pull/5


由 AI Test Analyzer 自动生成 · workflow run

@mergify

mergify Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-04-30 10:36 UTC · Rule: main
  • Checks skipped · PR is already up-to-date
  • Merged2026-04-30 10:36 UTC · at 52c8d32515240a387799d2edd1a73bc477347417 · squash

This pull request spent 20 seconds in the queue, including 2 seconds running CI.

Required conditions to merge
  • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
  • #changes-requested-reviews-by = 0 [🛡 GitHub branch protection]
  • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
  • branch-protection-review-decision = APPROVED [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-neutral = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-skipped = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / SCA Test on Ubuntu/x86
    • check-neutral = Matrixone CI / SCA Test on Ubuntu/x86
    • check-skipped = Matrixone CI / SCA Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / UT Test on Ubuntu/x86
    • check-neutral = Matrixone CI / UT Test on Ubuntu/x86
    • check-skipped = Matrixone CI / UT Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-neutral = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-skipped = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Utils CI / Coverage
    • check-neutral = Matrixone Utils CI / Coverage
    • check-skipped = Matrixone Utils CI / Coverage

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

Labels

kind/bug Something isn't working size/M Denotes a PR that changes [100,499] lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DISTINCT on indexed column scans full index table, prefix_in block filter ineffective

6 participants