Skip to content

executor: Improve HashAgg string collation key caching#10978

Open
ChangRui-Ryan wants to merge 3 commits into
pingcap:masterfrom
ChangRui-Ryan:changrui_ci_cache
Open

executor: Improve HashAgg string collation key caching#10978
ChangRui-Ryan wants to merge 3 commits into
pingcap:masterfrom
ChangRui-Ryan:changrui_ci_cache

Conversation

@ChangRui-Ryan

@ChangRui-Ryan ChangRui-Ryan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #10979

Problem Summary

For string GROUP BY keys using a case-insensitive collation such as utf8mb4_general_ci, HashAgg needs to generate a collation sort key before probing or inserting into the hash table.

Previously, the sort key was generated for every input row. When the grouped column has a low number of distinct raw values, the same raw string is converted to the same sort key repeatedly, causing unnecessary collation processing and memory writes.

For example:

SELECT l_shipmode, COUNT(*)
FROM lineitem
GROUP BY l_shipmode;

l_shipmode usually has a very low NDV. If it uses utf8mb4_general_ci, repeatedly generating sort keys for millions of rows can consume a significant portion of the HashAgg execution time.

At the same time, unconditionally caching sort keys is not suitable for high-NDV inputs because maintaining the cache may introduce additional hash lookups and memory usage without enough cache hits. Therefore, the optimization needs to provide substantial benefits for low-NDV workloads while keeping the high-NDV overhead small and bounded.

What is changed and how it works

This PR adds an always-on collation sort-key cache to the batch string-key processing path of HashAgg.

For each input Block, the cache maps a raw string value to its generated collation sort key:

raw string -> collation sort key

When processing a row:

  1. Look up the raw string in the cache.
  2. If it is found, reuse the cached sort key.
  3. If it is not found, generate the sort key once and insert it into the cache.
  4. Use the resulting sort key for the existing hash-table lookup or insertion.

The cache is enabled only when all of the following conditions are satisfied:

  • HashAgg uses the batch string-key processing path.
  • The string key uses a supported case-insensitive collation.
  • The current processing range contains at least 1,024 rows.

To bound the cost for high-NDV inputs, the maximum number of cached distinct values is calculated from the number of rows in the current Block:

cache distinct limit = rows in the Block / 32

For a typical maximum runtime Block containing 65,536 rows, the cache can hold at most 2,048 distinct raw values.

If a cache miss occurs after the distinct-value limit has been reached, the current Block falls back to direct sort-key generation. The fallback state is also recorded in the worker's AggregatedDataVariants, so subsequent Blocks processed by the same aggregation worker do not attempt to initialize the cache again. This worker-level once-fallback behavior prevents high-NDV workloads from repeatedly paying the cache construction and lookup overhead for every Block.

The cache itself remains local to one Block. It only stores references to raw strings owned by that Block, so no input-column memory needs to be retained across Blocks. Only the fallback decision is preserved across Blocks.

The optimization does not change collation or grouping semantics. Hash-table keys are still the sort keys generated by the existing collator; the change only avoids regenerating an identical sort key for repeated raw string values.

The benchmark uses 128 Blocks with 65,536 rows per Block:

Workload Baseline With cache Change
NDV 64 27.16 M rows/s 73.41 M rows/s +170.3%
NDV 2,048 26.05 M rows/s 55.71 M rows/s +113.9%
NDV 2,049, fallback 26.95 M rows/s 26.85 M rows/s -0.4%
NDV 65,536, fallback 24.17 M rows/s 23.99 M rows/s -0.7%
First 64 Blocks low NDV, remaining Blocks high NDV 25.40 M rows/s 35.75 M rows/s +40.8%

The results show substantial improvements for low-NDV and mixed workloads, while the worker-level fallback keeps the high-NDV overhead below 1% in the benchmark.


Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

None

Summary by CodeRabbit

Summary

  • Performance

    • Faster case-insensitive string aggregation by caching collation sort keys during batch processing when cache conditions are met.
    • Cache automatically stops after a distinct-key threshold to avoid unnecessary work, falling back to uncached sort-key generation.
  • Bug Fixes

    • Correctly reports and propagates “cache fallback” so later processing behaves consistently across batches/workers.
  • Tests

    • Added new unit tests and benchmarks validating behavior across low/high NDV scenarios and verifying the fallback switch.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue release-note-none Denotes a PR that doesn't merit a release note. labels Jul 14, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign flowbehappy for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds CI collation sort-key caching to string hash aggregation, tracks persistent fallback after cache thresholds are exceeded, wires initialization and status propagation through Aggregator, and adds executor tests plus NDV-focused benchmarks.

Changes

Collation sort-key caching

Layer / File(s) Summary
Cache-enabled string hashing
dbms/src/Common/ColumnsHashing.h, dbms/src/Common/ColumnsHashingImpl.h
String batching caches CI collation sort keys until the distinct-key limit, then switches to direct computation through new base and string-hash APIs.
Aggregation cache lifecycle
dbms/src/Interpreters/Aggregator.h, dbms/src/Interpreters/Aggregator.cpp
Aggregation state records fallback, initializes caching for eligible batches, and propagates fallback status across later blocks.
Behavior validation and performance coverage
dbms/src/Flash/tests/gtest_aggregation_executor.cpp, dbms/src/Flash/tests/bench_aggregation_hash_map.cpp
Tests cover CI equivalence, collation key sizing, NDV behavior, and worker fallback; benchmarks cover low, threshold, high, and transitioning NDV workloads.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Aggregator
  participant HashMethodString
  participant Collator
  participant AggregatedDataVariants
  Aggregator->>AggregatedDataVariants: check fallback state
  Aggregator->>HashMethodString: initialize cache for batch
  HashMethodString->>Collator: generate or reuse sort keys
  HashMethodString-->>Aggregator: return fallback status
  Aggregator->>AggregatedDataVariants: persist fallback status
Loading

Poem

I’m a rabbit with keys in a row,
Reusing the sort signs I know.
When distinct paths grow wide,
I switch cache aside—
And hop through the blocks as they flow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes match #10979 by caching repeated collation sort keys for string GROUP BY keys in HashAgg.
Out of Scope Changes check ✅ Passed The added benchmark and tests are directly tied to the caching optimization, with no obvious unrelated changes.
Title check ✅ Passed The title is concise and accurately summarizes the main change: HashAgg string collation key caching.
Description check ✅ Passed The description covers the required problem, change, tests, side effects, and release note sections, with only minor checklist gaps.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
dbms/src/Common/ColumnsHashing.h (1)

102-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reserve the lookup hash map here too.

cached_sort_keys is already reserved for the expected distinct count, but raw_to_sort_key_index still grows from its default capacity and will rehash repeatedly as the cache fills. Reserving it alongside the vector avoids avoidable rehashes.

♻️ Proposed fix
         collation_sort_key_cache_active = true;
         collation_sort_key_cache_distinct_limit = rows / max_collation_sort_key_cache_distinct_ratio;
         cached_sort_keys.reserve(collation_sort_key_cache_distinct_limit + 1);
+        raw_to_sort_key_index.reserve(collation_sort_key_cache_distinct_limit + 1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dbms/src/Common/ColumnsHashing.h` around lines 102 - 141, Reserve
raw_to_sort_key_index for collation_sort_key_cache_distinct_limit when
initializing the sort-key cache, alongside the existing cached_sort_keys
reservation, so filling the cache does not trigger repeated hash-map rehashes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@dbms/src/Common/ColumnsHashing.h`:
- Around line 102-141: Reserve raw_to_sort_key_index for
collation_sort_key_cache_distinct_limit when initializing the sort-key cache,
alongside the existing cached_sort_keys reservation, so filling the cache does
not trigger repeated hash-map rehashes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 06cf7ea1-eedb-4167-958d-12fb9dbf3da4

📥 Commits

Reviewing files that changed from the base of the PR and between b5093dd and 8eb6ffc.

📒 Files selected for processing (6)
  • dbms/src/Common/ColumnsHashing.h
  • dbms/src/Common/ColumnsHashingImpl.h
  • dbms/src/Flash/tests/bench_aggregation_hash_map.cpp
  • dbms/src/Flash/tests/gtest_aggregation_executor.cpp
  • dbms/src/Interpreters/Aggregator.cpp
  • dbms/src/Interpreters/Aggregator.h

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

3 similar comments
@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot

ti-chi-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

@ChangRui-Ryan: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-sanitizer-tsan 7e54367 link false /test pull-sanitizer-tsan
pull-integration-next-gen-columnar 7e54367 link true /test pull-integration-next-gen-columnar

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize HashAgg by caching collation sort keys for repeated string keys

1 participant