Files
datastore/tests/performance/lazy_final_replacing.xml
Nikolai KochetovandClaude Opus 4.6 16a6c5fa1f Add performance test for lazy FINAL optimization
Benchmark `ReplacingMergeTree` FINAL with a concentrated filter
(`category = 1`, 50k out of 10M keys) and evenly distributed updates
(every 100th key has version 2, defeating `split_intersecting`).
Uses separate partitions to prevent merges from combining parts.
Query reads full payload via `cityHash64(payload)`.

Compares `query_plan_optimize_lazy_final = 0` (median 154ms) vs `= 1`
(median 117ms) — 24% improvement.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-07 15:49:24 +00:00

51 lines
3.1 KiB
XML

<test>
<!--
Test lazy FINAL optimization for ReplacingMergeTree.
The optimization builds a set of primary key values from filtered rows
and uses it for index analysis to prune parts/granules in the FINAL read.
It helps when:
- Updates are evenly distributed (defeating split_intersecting optimization)
- The filter narrows to a small key range (enabling PK pruning)
- The query reads full column data
-->
<settings>
<max_insert_threads>1</max_insert_threads>
</settings>
<create_query>
CREATE TABLE lazy_final_perf
(
part UInt8,
key UInt64,
version UInt64,
category UInt8,
payload String
)
ENGINE = ReplacingMergeTree(version)
PARTITION BY part
ORDER BY key
SETTINGS index_granularity = 256
</create_query>
<!-- 10 partitions, 1M rows each, non-overlapping key ranges.
category=1 only for keys 0..49999 (in partition 0). -->
<fill_query>INSERT INTO lazy_final_perf SELECT 0, number, 1, if(number &lt; 50000, 1, 0), repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 1, number + 1000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 2, number + 2000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 3, number + 3000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 4, number + 4000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 5, number + 5000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 6, number + 6000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 7, number + 7000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 8, number + 8000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<fill_query>INSERT INTO lazy_final_perf SELECT 9, number + 9000000, 1, 0, repeat('x', 200) FROM numbers(1000000)</fill_query>
<!-- Evenly distributed updates across all partitions: every 100th key gets version 2. -->
<fill_query>INSERT INTO lazy_final_perf SELECT toUInt8(number * 100 / 1000000), number * 100, 2, if(number * 100 &lt; 50000, 1, 0), repeat('y', 200) FROM numbers(100000)</fill_query>
<!-- Concentrated filter + full payload read. Optimization should prune to ~2 parts. -->
<query>SELECT count(), max(cityHash64(payload)) FROM lazy_final_perf FINAL WHERE category = 1 SETTINGS query_plan_optimize_lazy_final = 0</query>
<query>SELECT count(), max(cityHash64(payload)) FROM lazy_final_perf FINAL WHERE category = 1 SETTINGS query_plan_optimize_lazy_final = 1, max_rows_for_lazy_final = 10000000</query>
<drop_query>DROP TABLE IF EXISTS lazy_final_perf</drop_query>
</test>