ddl: PRE_SPLIT_REGIONS silently has no effect on tables with a clustered multi-column primary key
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Bug Report
Please answer these questions before submitting your issue. Thanks!
### 1. Minimal reproduce step (Required)
1. Minimal reproduce step (Required)
Two identical tables with a clustered multi-column primary key, using AUTO_RANDOM + PRE_SPLIT_REGIONS. The only difference is that tB additionally gets an explicit SPLIT TABLE statement.
CREATE DATABASE demo4; USE demo4;
```
CREATE TABLE tA (
id BIGINT NOT NULL AUTO_RANDOM(3),
ts DATETIME(6) NOT NULL,
pad CHAR(200) NOT NULL,
PRIMARY KEY (id, ts) CLUSTERED
) PRE_SPLIT_REGIONS = 3;
CREATE TABLE tB LIKE tA;
```
-- only tB gets this
`SPLIT TABLE tB BETWEEN (0,'2026-01-01') AND (9223372036854775807,'2027-01-01') REGIONS 8;
`
Load 1000 rows into each, as 8 separate transactions of 125 rows. Separate sessions are required: the AUTO_RANDOM shard is drawn once per transaction, so a single bulk insert would use only one shard.
-- run 8 times per table, each in its own session
```
INSERT INTO tA (ts,pad) SELECT '2026-06-01', REPEAT('x',200)
FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<125) SELECT n FROM s) x;
```
Then inspect:
```
SHOW TABLE tA REGIONS;
SHOW TABLE tB REGIONS;
```
-- which shard slot each row's id falls into (2^60 spacing = one shard for AUTO_RANDOM(3))
```
SELECT FLOOR(id / 1152921504606846976) AS slot, COUNT(*) FROM tA GROUP BY 1 ORDER BY 1;
SELECT FLOOR(id / 1152921504606846976) AS slot, COUNT(*) FROM tB GROUP BY 1 ORDER BY 1;
```
-- the actual encoded row keys
```
SELECT MIN(TIDB_ENCODE_RECORD_KEY('demo4','tA',id,ts)),
MAX(TIDB_ENCODE_RECORD_KEY('demo4','tA',id,ts)) FROM tA;
SELECT MIN(TIDB_ENCODE_RECORD_KEY('demo4','tB',id,ts)),
MAX(TIDB_ENCODE_RECORD_KEY('demo4','tB',id,ts)) FROM tB;
```
### 2. What did you expect to see? (Required)
Rows distributed across the 2^PRE_SPLIT_REGIONS = 8 pre-split Regions in tA.
### 3. What did you see instead (Required)
All 1000 rows of tA are in a single Region, even though AUTO_RANDOM scattered their ids perfectly across all 8 shards. None of the 8 pre-split Regions can ever receive a row.
Region layout — tA (PRE_SPLIT_REGIONS only), 8 Regions:
```
t_188_i_1_ -> t_188_r_1152921504606846976
t_188_r_1152921504606846976 -> t_188_r_2305843009213693952
t_188_r_2305843009213693952 -> t_188_r_3458764513820540928
t_188_r_3458764513820540928 -> t_188_r_4611686018427387904
t_188_r_4611686018427387904 -> t_188_r_5764607523034234880
t_188_r_5764607523034234880 -> t_188_r_6917529027641081856
t_188_r_6917529027641081856 -> t_188_r_8070450532247928832
t_188_r_8070450532247928832 -> t_190_
```
Boundaries look correct and evenly spaced. CREATE TABLE emitted no warning.
Region layout — tB (same, plus SPLIT TABLE), 15 Regions:
```
t_190_i_1_ -> t_190_r_038fffffffffffffff \
t_190_r_038fffffffffffffff -> t_190_r_039ffffffffffffffe |
t_190_r_039ffffffffffffffe -> t_190_r_03affffffffffffffd |
t_190_r_03affffffffffffffd -> t_190_r_03bffffffffffffffc | created by SPLIT TABLE
t_190_r_03bffffffffffffffc -> t_190_r_03cffffffffffffffb | (03-prefixed)
t_190_r_03cffffffffffffffb -> t_190_r_03dffffffffffffffa |
t_190_r_03dffffffffffffffa -> t_190_r_03effffffffffffff9 |
t_190_r_03effffffffffffff9 -> t_190_r_1152921504606846976 /
t_190_r_1152921504606846976 -> t_190_r_2305843009213693952 \
t_190_r_2305843009213693952 -> t_190_r_3458764513820540928 |
t_190_r_3458764513820540928 -> t_190_r_4611686018427387904 |
t_190_r_4611686018427387904 -> t_190_r_5764607523034234880 | created by PRE_SPLIT_REGIONS
t_190_r_5764607523034234880 -> t_190_r_6917529027641081856 |
t_190_r_6917529027641081856 -> t_190_r_8070450532247928832 |
t_190_r_8070450532247928832 -> t_281474976710648_ /
```
Id scatter — tA achieved a perfect spread:
tA: slot0=125 slot1=125 slot2=125 slot3=125 slot4=125 slot5=125 slot6=125 slot7=125
tB: slot1=250 slot2=125 slot4=125 slot5=125 slot6=125 slot7=250
tA's 8 transactions drew 8 distinct shards, 125 rows each — the ideal outcome. Every one of its 8 pre-split Regions has a slot's worth of rows destined for it.
But every row key begins 03, below every boundary.
tA min 7480000000000000bc_5f72_03 80000000000002ef 0419ba020000000000
max 7480000000000000bc_5f72_03 f00000000000007d 0419ba020000000000
tB min 7480000000000000be_5f72_03 90000000000000 01 0419ba020000000000
max 7480000000000000be_5f72_03 f00000000000036b 0419ba020000000000
5f72 is the _r record marker. What follows is the handle. For a clustered multi-column PK the handle is a common handle — codec-encoded datums, prefixed with the int type flag 0x03 — not a bare integer.
Compared against each table's first boundary:
```
┌─────┬───────────────────────────────────────────┬─────────────────────────┬─────────────────────────────────────┐
│ │ first boundary after _r │ all row keys start with │ every row below the first boundary? │
├─────┼───────────────────────────────────────────┼─────────────────────────┼─────────────────────────────────────┤
│ tA │ 90 00 00 … (decoded: 1152921504606846976) │ 03 … │ yes │
├─────┼───────────────────────────────────────────┼─────────────────────────┼─────────────────────────────────────┤
│ tB │ 03 8f ff … │ 03 90 … and up │ no — they interleave │
└─────┴───────────────────────────────────────────┴─────────────────────────┴─────────────────────────────────────┘
```
So all 1000 rows of tA sit in its first Region, t_188_i_1_ -> t_188_r_1152921504606846976. The other seven receive nothing, and never can — 0x03 is below 0x90 for every row the table will ever hold.
tB's rows span 03 90… to 03 f0…, straddling boundaries 03 8f… through 03 ef…, so they land across 6 of its 7 SPLIT-created Regions — matching the 6 occupied slots exactly. tB's 8 PRE_SPLIT_REGIONS Regions remain empty, same as tA's.
```
┌───────────────────────────────────┬──────────────────┬───────────────────────────┐
│ │ tA (option only) │ tB (option + SPLIT TABLE) │
├───────────────────────────────────┼──────────────────┼───────────────────────────┤
│ Distinct shard slots used by ids │ 8 │ 6 │
├───────────────────────────────────┼──────────────────┼───────────────────────────┤
│ Regions actually holding rows │ 1 │ 6 │
├───────────────────────────────────┼──────────────────┼───────────────────────────┤
│ Rows in PRE_SPLIT_REGIONS Regions │ 0 │ 0 │
└───────────────────────────────────┴──────────────────┴───────────────────────────┘
```
### 4. What is your TiDB version? (Required)
```
Release Version: v8.5.5
Edition: Community
Git Commit Hash: 1fa258b833ff113883beeba40bc130be7ce66610
Git Branch: HEAD
UTC Build Time: 2026-03-23 14:08:54
GoVersion: go1.25.5
Race Enabled: false
Check Table Before Drop: false
Store: tikv
```
Contributor guide
Research direction
Start by running the supplied CREATE TABLE, INSERT, SHOW TABLE REGIONS, and encoded-key queries against TiDB v8.5.5. Then trace PRE_SPLIT_REGIONS handling alongside clustered multi-column primary-key common-handle encoding; done means pre-split boundaries and row keys share the same key space so rows can occupy those regions. No source file or test is named in the report.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, sql
- Domain
- databases, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100