Hi all, I'd like to share a bug fix introduced by the following PRs: - Master: https://github.com/apache/iotdb/pull/18389 - Dev/1.3: https://github.com/apache/iotdb/pull/18390 This bug may throw IndexOutOfBoundsException: Index 0 out of bounds for length 0 in AlignedTVList.delete() / deleteTime() when a DELETE races with a query that sorts the shared working TVList in place. The root cause is a concurrency issue on TVList: - The query path calls AlignedTVList.sort() in place on the shared (still writable) working TVList. sort() is synchronized on the TVList instance and rebuilds indices non-atomically (indices = new ArrayList<>() then fills it). - The delete path (AlignedTVList.delete() / deleteTime() / delete(column) / deleteColumn() and TVList.delete()) was not synchronized. A concurrent DELETE could call indices.get(0) while indices was momentarily empty, throwing IndexOutOfBoundsException, or read half-rebuilt indices and silently delete wrong rows. The fix makes the delete methods of AlignedTVList (and TVList.delete() for the non-aligned path) synchronized on the same TVList instance as sort(), matching the existing monitor already used by putAlignedValue / clone / cloneForFlushSort. A concurrency audit of the delete path was also done: - All runtime delete paths converge to TsFileProcessor.deleteDataInMemory(), which holds flushQueryLock.writeLock() before touching the TVList, so the lock order (flushQueryLock -> tvlist monitor) stays consistent with the query path and no deadlock is introduced. - WAL recovery paths only call memTable.delete() during single-threaded startup, so there is no concurrent query/sort there. Main changes include: - Synchronize the delete methods of AlignedTVList (delete / deleteTime / delete(column) / deleteColumn) and TVList.delete() with sort(). - Audit the lock order of the delete path to confirm no deadlock is introduced. Feedback and suggestions are welcome. 大家好, 我想分享以下 PR 中修复的一个并发问题: - Master:https://github.com/apache/iotdb/pull/18389 - Dev/1.3:https://github.com/apache/iotdb/pull/18390 当 DELETE 与在共享的 working TVList 上执行原地排序的查询并发发生时,该问题可能抛出 IndexOutOfBoundsException: Index 0 out of bounds for length 0。 根本原因是 TVList 上的并发同步问题: - 查询路径会调用 AlignedTVList.sort() 对共享(仍可写)的 working TVList 进行原地排序。sort() 对 TVList 实例加 synchronized,并以非原子方式重建 indices(先 indices = new ArrayList<>(),再填充)。 - 删除路径(AlignedTVList.delete() / deleteTime() / delete(column) / deleteColumn() 以及非对齐路径的 TVList.delete())此前没有加同步。并发的 DELETE 可能恰好在 indices 为空的瞬间调用 indices.get(0),从而抛出 IndexOutOfBoundsException,或者读到重建了一半的 indices,导致静默删除错误的行。 本次修复将 AlignedTVList 的删除方法与 sort() 在同一个 TVList 实例上设置为 synchronized,与 putAlignedValue / clone / cloneForFlushSort 已有的 monitor 保持一致。 同时对该修复进行了删除路径的并发审计: - 所有运行时删除路径最终都汇聚到 TsFileProcessor.deleteDataInMemory(),该路径在访问 TVList 前持有 flushQueryLock.writeLock(),因此锁顺序(flushQueryLock -> tvlist monitor)与查询路径保持一致,不会引入死锁。 - WAL 恢复路径仅在单线程启动阶段调用 memTable.delete(),不存在并发的查询/排序。 主要修改包括: - 将 AlignedTVList 的删除方法(delete / deleteTime / delete(column) / deleteColumn)与 TVList.delete() 与 sort() 同步。 - 审计删除路径的锁顺序,确认不会引入死锁。 欢迎大家提出反馈和建议。 Best regards, Wenwei Shu
