Repository navigation
Batch aligned memtable column expansion to reduce region lock contention - #18816
Merged
jt2594838 merged 2 commits intoOct 12, 2026
Merged
Conversation
jt2594838
reviewed
Oct 12, 2026
jt2594838
approved these changes
Oct 12, 2026
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
An aligned tablet that introduces many measurements previously expanded its TVList and copied full-width accounting, value and bitmap arrays once per missing measurement while holding the DataRegion write lock. Repeated scans and copies made expansion quadratic in schema width, delaying queries waiting for the region read lock.
Reorder existing measurements in a single pass. Only the first missing measurement creates the new-type list and scans the remaining schemas to register missing measurements in insertion order. Extend the TVList in one batch and resize the reordered value and bitmap arrays to the final width once. Writes without schema extension keep one map lookup per measurement and do not create a new-type list. Memory estimates are refreshed once per expansion batch. Existing rows retain implicit nulls, and new value arrays and bitmaps remain lazy. Single-column extension delegates to the batch API.
Regression tests cover mixed existing/new/deleted schemas, duplicate measurement names, preservation of existing-column values and bitmaps before the first new column, no-extension reordering, bitmap alignment with a nonzero tablet offset, historical nulls across array blocks, binary and primitive memory accounting, clone/clear/reuse, WAL recovery, handover, and a 10,000-column tablet followed by another write.
Validation
mvn spotless:apply -pl iotdb-core/datanodemvn test -pl iotdb-core/datanode -am -Dtest=AlignedWritableMemChunkTest,AlignedTVListTest,MemChunkDeserializeTest,MemTableFlushTaskTest,PrimitiveMemTableTest -Dsurefire.failIfNoSpecifiedTests=false— 66 tests passed with no failures, errors or skips; the reactor compiled the DataNode and its dependencies from a fresh worktree.git diff --checkPerformance
Local microbenchmarks used Oracle JDK 17.0.15, Windows 11, an Intel Core i9-13900H, G1 and a fixed 512 MiB heap. Base is
21d53dd, initial PR is1f05d5d, and revised is5f27eb1. Each variant compiles the actualAlignedWritableMemChunkandAlignedTVListsources against the same freshly built reactor dependencies.No-extension measurements use JMH 1.37: one thread, three independent JVM forks, three 300 ms warmup iterations and five 300 ms measurement iterations per fork. Every measurement already exists, incoming schema objects are distinct and reverse column order, values are dense INT64, and a bitmap array is present with unmarked columns. The schema-reorder benchmark invokes the private method through the same cached MethodHandle and consumes its result. The tablet benchmark times the public
writeAlignedTabletcall; clearing and prepopulating one row are outside the timed call, so the measured write uses existing value blocks. Values below are means with JMH's 99.9% confidence interval. Allocation is reported for the isolated reorder only; the tablet profiler also counts invocation setup.The initial PR's additional schema scan increased isolated reorder time by approximately 66%–78% in these runs. The revised path removes that scan. Revised/base latency confidence intervals overlap for every tested scenario, and revised reorder allocation matches the base at each width. For the 1,000-column/32-row tablet case, the revised point estimate is 8.9% above the base (40.975 ± 5.591 vs 37.621 ± 3.040 µs); these measurements do not establish a statistically distinguishable regression, and they do not prove zero overhead. At 1,000 columns the initial PR also allocated about 24 extra bytes per isolated reorder; lazy initialization removes that allocation.
Expansion measurements write one tablet row after one existing populated INT64 column. Setup and release are excluded. Sparse values supply only the first and last new columns; dense values supply every column. Each JVM warms up with 30 expansions of 256 columns; the reported values are medians of three independent JVMs. Current-thread allocation during
writeAlignedTabletis measured withThreadMXBean, rather than retained heap size. These are local microbenchmarks and do not measure distributed throughput or region-lock wait time.This PR has:
Key changed classes:
AlignedWritableMemChunk,AlignedTVList.Remote comparison: parent vs merged commit (2026-10-12)
On
192.168.130.19(fit19), adding 10,000 INT64 columns reduced the median run-level mean RPC write latency from 175.47 ms to 31.98 ms (5.49x). Concurrent same-region query P99 fell from 138.28 ms to 4.09 ms (97.0% lower). Existing-schema measurements also recorded small overheads and tail variation, detailed below.Environment and version isolation
5c27559f02f525e75ee07a58d06ff8ee54578527; target: merged commit932c1da25f7e472e84d2a997b376e10b02270c26./ssd, Ubuntu 20.04.3, Linux 5.15.0-139-generic, Oracle JDK 17.0.9, G1. Existing IoTDB and Redis workloads were stopped before measurement; the CPU governor remainedpowersave.mvn -B -ntp -nsu package -pl distribution -am -DskipTests -Dmaven.test.skip=true -Ddevelocity.off=true. Both variants then compiled their actualAlignedWritableMemChunkandAlignedTVListsources on the test host withjavac --release 17, placing the resulting classes in copies of this distribution. These are the only two production source files changed by the commit. SHA-256 checks confirmed that all other jars were identical.SHOW REGIONSafter every run. SimpleConsensus for all three consensus protocols, replication factor 1, WALASYNC, automatic schema creation disabled, compaction disabled. ConfigNode heap 1 GiB, DataNode heap 8 GiB / direct memory 2 GiB, client heap 1 GiB. JVM processor limits: 8 for servers, 4 for client and microbenchmarks.RPC writes and concurrent queries
Three independent clean server starts per variant, alternating parent/target, target/parent, parent/target. Every timed write is the public
Session.insertAlignedTabletcall over loopback, including client serialization, RPC and server execution; tablet construction is excluded. All values are dense INT64 and schemas are created beforehand.Existing-schema cases use 32 rows per tablet, 150 warmup writes and 500 measured writes per run. Expansion cases start each device with one populated
s0row, then insert one row containing 1,000 or 10,000 other measurements into that device's existing memchunk. There are 12 / 6 expansion warmups respectively and 24 measured, separate devices per case and run.During each expansion burst, one separate session continuously executes and fully consumes
SELECT count(s0) FROM root.pr18816.anchor, an existing one-row device in the same DataRegion. The reader performs 100 untimed warmup queries. The writer leaves a 10 ms gap after each completed write. Query samples whose RPC starts during the burst are retained. Numbers below are medians of the three run-level means / percentiles, rather than percentiles pooled across different runs; sample counts are totals over the three runs.At 10,000 new columns, the three run-level mean write latencies ranged from 162.31–175.94 ms for the parent and 30.14–37.63 ms for the target. Query P99 ranged from 131.08–141.22 ms and 3.78–4.76 ms respectively. Query P50 was higher for the target (0.524 vs 0.363 ms), so the main query benefit in this workload is the reduction in long stalls. Burst durations and completed query counts differ because write duration differs; this is a fixed-write-count contention experiment, not a fixed-duration throughput benchmark. Query latency includes RPC, planning, queueing, execution and GC effects; individual lock-wait times were not instrumented.
Existing-schema RPC means were similar at 10 and 100 columns. At 1,000 columns, the target's median run-level mean was 3.7% higher (1.149 vs 1.108 ms) and P99 was 27.2% higher (2.342 vs 1.841 ms). P99 varied between 1.43–2.14 ms for the parent and 1.64–2.37 ms for the target. These runs should not be read as evidence of zero overhead or a general steady-state throughput improvement.
In-process expansion cost and allocation
Fixed 2 GiB heap; three independent JVMs per variant in the same alternating order. Each JVM warms up with 120 expansions of 256 columns, alternating 1 / 1,000 historical rows and mixing sparse / dense values. Each measured operation adds the specified number of new columns after one existing INT64 column, then writes one row. Sparse input supplies only the first and last new column; dense input supplies every new column. A bitmap array is present with unmarked entries.
Each case has five measured operations per JVM. Latency is the median within each JVM, then the median across the three JVMs.
ThreadMXBeanmeasures current-thread bytes allocated only aroundwriteAlignedTablet; fixture setup, correctness checks, release, explicit GC and the 40 ms pause before measurement are excluded. Primitive-array pools are warm and can reuse value blocks, so these are allocated bytes during the write, not retained heap or guaranteed first-ever dense-write allocations. The sparse cases call the memtable directly: the current Java Session client filters entirely-null columns before RPC.At 10 new columns the target had higher latency point estimates, by about 7–41 µs depending on history and density. The batch path's benefit grows with schema width; the small-width timings use a short manual benchmark and have less precision than the JMH measurements below.
Existing-schema JMH check
JMH 1.37, one thread, three independent forks, three 500 ms warmup iterations and five 500 ms measurement iterations per fork, G1, fixed 512 MiB heap. Incoming schemas are distinct objects in reverse column order, all measurements already exist, and a bitmap array is present with unmarked entries. Isolated reorder invokes the actual private method through a cached MethodHandle and consumes its result. Tablet invocation setup clears and prepopulates one row outside the timed call, so the write uses existing value blocks. Values are means with JMH's 99.9% confidence interval.
Because the first JMH session ran parent before target and showed small reorder differences, an additional three-fork reorder-only session used the reverse variant order with the same settings:
The 10-column reorder difference changed sign with order, and the 1,000-column difference reduced to about 0.1% in the reverse check. The 100-column target remained about 2.2% slower in that check, compared with 5.9% in the first session. Thus the remote runs retain evidence of a small reorder cost at this width. Reorder allocation stayed approximately 136 / 856 / 8,056 B/op in both variants at 10 / 100 / 1,000 columns. Full tablet confidence intervals overlapped in all six tested cases; that does not prove zero overhead. Tablet GC-profiler allocation includes invocation setup and is not reported as write-only allocation.
All 480 measured expansion operations validated dimensions, preserved historical values, historical nulls and the first/last new values. All 9,000 measured existing-schema RPC writes and 288 measured expansion RPC writes completed, with count checks after expansion, checked query results and exactly one DataRegion in each run. Raw CSV / JSON, source hashes, jar hashes, configuration, GC logs and harnesses are retained under
/ssd/pr18816-perf-20261012on the host; the local results bundle and report are undertmp-pr18816-comparison/. These measurements cover a single-node tree-model workload.