From f3481a315fd10ac144b2a003a5485a1929b69abc Mon Sep 17 00:00:00 2001 From: shuwenwei Date: Tue, 4 Aug 2026 10:55:00 +0800 Subject: [PATCH] Fix IndexOutOfBoundsException caused by concurrent sort and delete on TVList A query may sort the shared working TVList in place while a DELETE is running on the same list. sort() is synchronized, but the delete methods (AlignedTVList.delete/deleteTime/delete(column)/deleteColumn and TVList.delete) were not, so the delete thread could observe the half-rebuilt indices during sort and throw IndexOutOfBoundsException ("Index 0 out of bounds for length 0") or silently delete wrong rows. Synchronize the delete methods with sort() on the same TVList instance, consistent with putAlignedValue/clone/cloneForFlushSort which are already synchronized. The runtime delete path always holds TsFileProcessor.flushQueryLock.writeLock() before touching the TVList, so the lock order (flushQueryLock -> tvlist monitor) stays consistent with the query path and no deadlock is introduced. --- .../db/utils/datastructure/AlignedTVList.java | 23 ++++++++++++++++--- .../iotdb/db/utils/datastructure/TVList.java | 8 ++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java index 9e3a556710cc9..591c91a664000 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java @@ -539,7 +539,13 @@ public List getTsDataTypes() { } @Override - public int delete(long lowerBound, long upperBound) { + /* + * Must be synchronized with sort() on the same TVList instance: a query may sort + * this list in place (sort() is synchronized), and a concurrent delete would + * otherwise read the half-rebuilt indices and throw IndexOutOfBoundsException + * or delete wrong rows. + */ + public synchronized int delete(long lowerBound, long upperBound) { int deletedNumber = 0; for (int i = 0; i < dataTypes.size(); i++) { deletedNumber += delete(lowerBound, upperBound, i).left; @@ -550,12 +556,17 @@ public int delete(long lowerBound, long upperBound) { /** * Delete points in a specific column. * + *

Must be synchronized with {@link #sort()} on the same TVList instance: a query may sort this + * list in place ({@code sort()} is synchronized), and a concurrent delete would otherwise read + * the half-rebuilt {@code indices} and throw IndexOutOfBoundsException or delete wrong rows. + * * @param lowerBound deletion lower bound * @param upperBound deletion upper bound * @param columnIndex column index to be deleted * @return Delete info pair. Left: deletedNumber int; right: ifDeleteColumn boolean */ - public Pair delete(long lowerBound, long upperBound, int columnIndex) { + public synchronized Pair delete( + long lowerBound, long upperBound, int columnIndex) { if (columnIndex >= values.size()) { return new Pair<>(0, false); } @@ -577,7 +588,13 @@ public Pair delete(long lowerBound, long upperBound, int colum return new Pair<>(deletedNumber, deleteColumn); } - public void deleteColumn(int columnIndex) { + /* + * Must be synchronized with sort() on the same TVList instance: a query may sort + * this list in place (sort() is synchronized), and a concurrent delete would + * otherwise read the half-rebuilt indices and throw IndexOutOfBoundsException + * or delete wrong rows. + */ + public synchronized void deleteColumn(int columnIndex) { if (bitMaps == null) { List> localBitMaps = new ArrayList<>(dataTypes.size()); for (int j = 0; j < dataTypes.size(); j++) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java index d21ecc83e0f1b..220ca87a9a8a3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java @@ -558,7 +558,13 @@ public TVList clone(long version) { return clone(); } - public int delete(long lowerBound, long upperBound) { + /* + * Must be synchronized with sort() on the same TVList instance: a query may sort + * this list in place (sort() is synchronized), and a concurrent delete would + * otherwise read the half-rebuilt indices and throw IndexOutOfBoundsException + * or delete wrong rows. + */ + public synchronized int delete(long lowerBound, long upperBound) { int deletedNumber = 0; long maxTime = Long.MIN_VALUE; long minTime = Long.MAX_VALUE;