This is an automated email from the ASF dual-hosted git repository.

airborne12 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new f1fd1b432fb [improvement](fe) Scan index change jobs once in the light 
DROP INDEX pre-check (#69441)
f1fd1b432fb is described below

commit f1fd1b432fb4df3089c31e84cdfb6b4cccfe1999
Author: Jack <[email protected]>
AuthorDate: Sat Oct 24 16:34:31 2026 +0800

    [improvement](fe) Scan index change jobs once in the light DROP INDEX 
pre-check (#69441)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #68674
    
    Problem Summary:
    
    The light `DROP INDEX` pre-check added in #68674
    (`SchemaChangeHandler#checkNoIndexChangeJobOnPartitions`) calls
    `hasIndexChangeJobOnPartition` once per partition, and every call copies
    and scans the whole retained `indexChangeJobs` history (finished jobs
    are kept for days). For a table with P partitions and a history of H
    jobs that is O(P * H) work while the table write lock is held, on top of
    the existing per-partition check in `buildOrDeleteTableInvertedIndices`.
    A recent `BUILD INDEX` on a table with many partitions leaves at least
    one record per partition, which makes it quadratic in the number of
    partitions.
    
    The pre-check also reported a conflict with the `BUILD INDEX` wording
    `partition xxx has been built specified index. please check your build
    stmt.`, which is misleading for a `DROP INDEX`.
    
    Fix: the pre-check takes one snapshot of the job history and scans it
    once, matching jobs on any of the table's partitions. The match
    condition is extracted into `isConflictingIndexChangeJob` and shared
    with `hasIndexChangeJobOnPartition`, so both checks keep the same rule.
    The pre-check reports `partition xxx of table t has an unfinished index
    change job, please retry after it finishes.` The `BUILD INDEX` / `DROP
    INDEX ... PARTITION` check and its message are unchanged.
---
 .../apache/doris/alter/SchemaChangeHandler.java    | 35 ++++++++++++++--------
 .../doris/alter/SchemaChangeHandlerTest.java       |  4 +--
 2 files changed, 24 insertions(+), 15 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index 5d8f17fbef4..7538e0a4f9f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -3960,11 +3960,14 @@ public class SchemaChangeHandler extends AlterHandler {
     // Throws if any of the partitions still has an unfinished index change 
job on the base index.
     private void checkNoIndexChangeJobOnPartitions(Database db, OlapTable 
olapTable, List<Index> alterIndexes,
             Set<String> partitionNames) throws DdlException {
-        for (String partitionName : partitionNames) {
-            if (hasIndexChangeJobOnPartition(olapTable.getBaseIndexId(), 
db.getId(), olapTable.getId(),
-                    partitionName, alterIndexes, true)) {
-                throw new DdlException("partition " + partitionName + " has 
been built specified index."
-                        + " please check your build stmt.");
+        // Scan the retained job history once instead of once per partition, 
the table write lock is held here.
+        for (IndexChangeJob indexChangeJob : 
ImmutableList.copyOf(indexChangeJobs.values())) {
+            // Cheap partition check first, the predicate compares index lists.
+            if (partitionNames.contains(indexChangeJob.getPartitionName())
+                    && isConflictingIndexChangeJob(indexChangeJob, 
olapTable.getBaseIndexId(), db.getId(),
+                    olapTable.getId(), alterIndexes, true)) {
+                throw new DdlException("partition " + 
indexChangeJob.getPartitionName() + " of table "
+                        + olapTable.getName() + " has an unfinished index 
change job, please retry after it finishes.");
             }
         }
     }
@@ -3974,20 +3977,26 @@ public class SchemaChangeHandler extends AlterHandler {
             List<Index> alterIndexes, boolean isDrop) {
         // TODO: this is temporary methods
         for (IndexChangeJob indexChangeJob : 
ImmutableList.copyOf(indexChangeJobs.values())) {
-            if (indexChangeJob.getOriginIndexId() == originIndexId
-                    && indexChangeJob.getDbId() == dbId
-                    && indexChangeJob.getTableId() == tableId
-                    && indexChangeJob.getPartitionName().equals(partitionName)
-                    && ((Config.isNotCloudMode() && 
indexChangeJob.hasSameAlterInvertedIndex(isDrop, alterIndexes))
-                    || Config.isCloudMode())
-                    && !indexChangeJob.isDone()) {
-                // if JobState is done (CANCELLED or FINISHED), also allow 
user to create job again
+            if (indexChangeJob.getPartitionName().equals(partitionName)
+                    && isConflictingIndexChangeJob(indexChangeJob, 
originIndexId, dbId, tableId, alterIndexes,
+                    isDrop)) {
                 return true;
             }
         }
         return false;
     }
 
+    private static boolean isConflictingIndexChangeJob(IndexChangeJob 
indexChangeJob, long originIndexId,
+            long dbId, long tableId, List<Index> alterIndexes, boolean isDrop) 
{
+        // if JobState is done (CANCELLED or FINISHED), also allow user to 
create job again
+        return indexChangeJob.getOriginIndexId() == originIndexId
+                && indexChangeJob.getDbId() == dbId
+                && indexChangeJob.getTableId() == tableId
+                && ((Config.isNotCloudMode() && 
indexChangeJob.hasSameAlterInvertedIndex(isDrop, alterIndexes))
+                || Config.isCloudMode())
+                && !indexChangeJob.isDone();
+    }
+
     // schema change job is not allowed when index change job is running
     boolean hasIndexChangeJobOnTable(long dbId, long tableId) {
         for (IndexChangeJob indexChangeJob : 
ImmutableList.copyOf(indexChangeJobs.values())) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
index 1189f5a5903..b3ac191c658 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/alter/SchemaChangeHandlerTest.java
@@ -1328,14 +1328,14 @@ public class SchemaChangeHandlerTest extends 
TestWithFeService {
         try {
             String dropIndexStmt = "ALTER TABLE test." + tableName + " DROP 
INDEX idx_v1";
             Exception e = Assertions.assertThrows(Exception.class, () -> 
alterTable(dropIndexStmt, connectContext));
-            Assertions.assertTrue(e.getMessage().contains("has been built 
specified index"), e.getMessage());
+            Assertions.assertTrue(e.getMessage().contains("has an unfinished 
index change job"), e.getMessage());
             Assertions.assertTrue(tbl.getIndexes().stream().anyMatch(idx -> 
idx.getIndexName().equals("idx_v1")));
             Assertions.assertEquals(schemaVersion, 
tbl.getIndexMetaByIndexId(tbl.getBaseIndexId()).getSchemaVersion());
 
             // The check must run before DROP COLUMN updates the bloom filter 
columns in place.
             String dropColumnAndIndexStmt = "ALTER TABLE test." + tableName + 
" DROP COLUMN v2, DROP INDEX idx_v1";
             e = Assertions.assertThrows(Exception.class, () -> 
alterTable(dropColumnAndIndexStmt, connectContext));
-            Assertions.assertTrue(e.getMessage().contains("has been built 
specified index"), e.getMessage());
+            Assertions.assertTrue(e.getMessage().contains("has an unfinished 
index change job"), e.getMessage());
             Assertions.assertEquals(Sets.newHashSet("v2"), 
tbl.getCopiedBfColumns());
             Assertions.assertNotNull(tbl.getColumn("v2"));
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to