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

yiguolei pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/doris.git

commit 411b66a94e6a1ee7eaf7548642b9ddd5c931ed78
Author: yujun <yu.jun.re...@gmail.com>
AuthorDate: Wed Apr 24 22:22:03 2024 +0800

    [fix](decommission) fix cann't decommission mtmv (#33823)
---
 .../java/org/apache/doris/alter/SystemHandler.java |   2 +-
 .../main/java/org/apache/doris/catalog/Table.java  |  25 -----
 .../clone/ColocateTableCheckerAndBalancer.java     |  25 ++++-
 .../java/org/apache/doris/clone/TabletChecker.java |  30 ++++--
 .../doris/cluster/DecommissionBackendTest.java     | 106 ++++++++++++++++++++-
 .../apache/doris/utframe/TestWithFeService.java    |   6 +-
 .../data/clone_p0/test_decommission_mtmv.out       |  13 +++
 .../org/apache/doris/regression/Config.groovy      |   3 +
 .../apache/doris/regression/ConfigOptions.groovy   |   8 ++
 .../org/apache/doris/regression/suite/Suite.groovy |  13 ++-
 .../doris/regression/suite/SuiteCluster.groovy     |   9 +-
 .../suites/clone_p0/test_decommission_mtmv.groovy  |  93 ++++++++++++++++++
 run-regression-test.sh                             |   1 +
 13 files changed, 291 insertions(+), 43 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/alter/SystemHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/SystemHandler.java
index 6803f699ea4..d4895dd6877 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SystemHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SystemHandler.java
@@ -314,7 +314,7 @@ public class SystemHandler extends AlterHandler {
             for (Table table : db.getTables()) {
                 table.readLock();
                 try {
-                    if (!table.needSchedule()) {
+                    if (!table.isManagedTable()) {
                         continue;
                     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
index 8e26c28ee5e..341c2b76774 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
@@ -564,31 +564,6 @@ public abstract class Table extends MetaObject implements 
Writable, TableIf {
         return table;
     }
 
-    /*
-     * 1. Only schedule OLAP table.
-     * 2. If table is colocate with other table, not schedule it.
-     * 3. (deprecated). if table's state is ROLLUP or SCHEMA_CHANGE, but alter 
job's state is FINISHING, we should also
-     *      schedule the tablet to repair it(only for VERSION_INCOMPLETE case, 
this will be checked in
-     *      TabletScheduler).
-     * 4. Even if table's state is ROLLUP or SCHEMA_CHANGE, check it. Because 
we can repair the tablet of base index.
-     */
-    public boolean needSchedule() {
-        if (type != TableType.OLAP) {
-            return false;
-        }
-
-        OlapTable olapTable = (OlapTable) this;
-
-        if (Env.getCurrentColocateIndex().isColocateTable(olapTable.getId())) {
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("table {} is a colocate table, skip tablet 
checker.", name);
-            }
-            return false;
-        }
-
-        return true;
-    }
-
     public boolean isHasCompoundKey() {
         return hasCompoundKey;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/clone/ColocateTableCheckerAndBalancer.java
 
b/fe/fe-core/src/main/java/org/apache/doris/clone/ColocateTableCheckerAndBalancer.java
index 945183cc488..740acd331c3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/clone/ColocateTableCheckerAndBalancer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/clone/ColocateTableCheckerAndBalancer.java
@@ -852,6 +852,8 @@ public class ColocateTableCheckerAndBalancer extends 
MasterDaemon {
 
                 int targetSeqIndex = -1;
                 long minDataSizeDiff = Long.MAX_VALUE;
+                boolean destBeContainsAllBuckets = true;
+                boolean theSameHostContainsAllBuckets = true;
                 for (int seqIndex : seqIndexes) {
                     // the bucket index.
                     // eg: 0 / 3 = 0, so that the bucket index of the 4th 
backend id in flatBackendsPerBucketSeq is 0.
@@ -859,9 +861,15 @@ public class ColocateTableCheckerAndBalancer extends 
MasterDaemon {
                     List<Long> backendsSet = 
backendsPerBucketSeq.get(bucketIndex);
                     List<String> hostsSet = hostsPerBucketSeq.get(bucketIndex);
                     // the replicas of a tablet can not locate in same Backend 
or same host
-                    if (backendsSet.contains(destBeId) || 
hostsSet.contains(destBe.getHost())) {
+                    if (backendsSet.contains(destBeId)) {
                         continue;
                     }
+                    destBeContainsAllBuckets = false;
+
+                    if (!Config.allow_replica_on_same_host && 
hostsSet.contains(destBe.getHost())) {
+                        continue;
+                    }
+                    theSameHostContainsAllBuckets = false;
 
                     Preconditions.checkState(backendsSet.contains(srcBeId), 
srcBeId);
                     long bucketDataSize =
@@ -890,8 +898,19 @@ public class ColocateTableCheckerAndBalancer extends 
MasterDaemon {
 
                 if (targetSeqIndex < 0) {
                     // we use next node as dst node
-                    LOG.info("unable to replace backend {} with backend {} in 
colocate group {}",
-                            srcBeId, destBeId, groupId);
+                    String failedReason;
+                    if (destBeContainsAllBuckets) {
+                        failedReason = "dest be contains all the same buckets";
+                    } else if (theSameHostContainsAllBuckets) {
+                        failedReason = "dest be's host contains all the same 
buckets "
+                                + "and 
Config.allow_replica_on_same_host=false";
+                    } else {
+                        failedReason = "dest be has no fit path, maybe disk 
usage is exceeds "
+                                + 
"Config.storage_high_watermark_usage_percent";
+                    }
+                    LOG.info("unable to replace backend {} with dest backend 
{} in colocate group {}, "
+                            + "failed reason: {}",
+                            srcBeId, destBeId, groupId, failedReason);
                     continue;
                 }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/clone/TabletChecker.java 
b/fe/fe-core/src/main/java/org/apache/doris/clone/TabletChecker.java
index afd62ec8819..0a23894ab3e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/clone/TabletChecker.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/clone/TabletChecker.java
@@ -19,6 +19,7 @@ package org.apache.doris.clone;
 
 import org.apache.doris.analysis.AdminCancelRepairTableStmt;
 import org.apache.doris.analysis.AdminRepairTableStmt;
+import org.apache.doris.catalog.ColocateTableIndex;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.MaterializedIndex;
@@ -241,6 +242,8 @@ public class TabletChecker extends MasterDaemon {
             copiedPrios = HashBasedTable.create(prios);
         }
 
+        ColocateTableIndex colocateTableIndex = Env.getCurrentColocateIndex();
+
         OUT:
         for (long dbId : copiedPrios.rowKeySet()) {
             Database db = env.getInternalCatalog().getDbNullable(dbId);
@@ -250,17 +253,21 @@ public class TabletChecker extends MasterDaemon {
             List<Long> aliveBeIds = infoService.getAllBackendIds(true);
             Map<Long, Set<PrioPart>> tblPartMap = copiedPrios.row(dbId);
             for (long tblId : tblPartMap.keySet()) {
-                OlapTable tbl = (OlapTable) db.getTableNullable(tblId);
-                if (tbl == null) {
+                Table tbl = db.getTableNullable(tblId);
+                if (tbl == null || !tbl.isManagedTable()) {
                     continue;
                 }
-                tbl.readLock();
+                OlapTable olapTable = (OlapTable) tbl;
+                olapTable.readLock();
                 try {
-                    if (!tbl.needSchedule()) {
+                    if (colocateTableIndex.isColocateTable(olapTable.getId())) 
{
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("table {} is a colocate table, skip 
tablet checker.", olapTable.getName());
+                        }
                         continue;
                     }
-                    for (Partition partition : tbl.getAllPartitions()) {
-                        LoopControlStatus st = handlePartitionTablet(db, tbl, 
partition, true, aliveBeIds, start,
+                    for (Partition partition : olapTable.getAllPartitions()) {
+                        LoopControlStatus st = handlePartitionTablet(db, 
olapTable, partition, true, aliveBeIds, start,
                                 counter);
                         if (st == LoopControlStatus.BREAK_OUT) {
                             break OUT;
@@ -269,7 +276,7 @@ public class TabletChecker extends MasterDaemon {
                         }
                     }
                 } finally {
-                    tbl.readUnlock();
+                    olapTable.readUnlock();
                 }
             }
         }
@@ -291,9 +298,16 @@ public class TabletChecker extends MasterDaemon {
             List<Long> aliveBeIds = infoService.getAllBackendIds(true);
 
             for (Table table : tableList) {
+                if (!table.isManagedTable()) {
+                    continue;
+                }
+
                 table.readLock();
                 try {
-                    if (!table.needSchedule()) {
+                    if (colocateTableIndex.isColocateTable(table.getId())) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("table {} is a colocate table, skip 
tablet checker.", table.getName());
+                        }
                         continue;
                     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/cluster/DecommissionBackendTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/cluster/DecommissionBackendTest.java
index f1efadcbc83..e50a7ba6323 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/cluster/DecommissionBackendTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/cluster/DecommissionBackendTest.java
@@ -22,6 +22,10 @@ import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.MaterializedIndex;
 import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.Replica;
+import org.apache.doris.catalog.Tablet;
+import org.apache.doris.clone.RebalancerTestUtil;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.FeConstants;
@@ -56,10 +60,14 @@ public class DecommissionBackendTest extends 
TestWithFeService {
     @Override
     protected void beforeCreatingConnectContext() throws Exception {
         FeConstants.default_scheduler_interval_millisecond = 1000;
-        Config.tablet_checker_interval_ms = 1000;
+        Config.tablet_checker_interval_ms = 100;
+        Config.tablet_schedule_interval_ms = 100;
         Config.tablet_repair_delay_factor_second = 1;
         Config.allow_replica_on_same_host = true;
         Config.disable_balance = true;
+        Config.schedule_batch_size = 1000;
+        Config.schedule_slot_num_per_hdd_path = 1000;
+        Config.heartbeat_interval_second = 5;
     }
 
     @Test
@@ -76,6 +84,7 @@ public class DecommissionBackendTest extends 
TestWithFeService {
 
         // 3. create table tbl1
         createTable("create table db1.tbl1(k1 int) distributed by hash(k1) 
buckets 3 properties('replication_num' = '1');");
+        RebalancerTestUtil.updateReplicaPathHash();
 
         // 4. query tablet num
         int tabletNum = 
Env.getCurrentInvertedIndex().getTabletMetaMap().size();
@@ -125,6 +134,7 @@ public class DecommissionBackendTest extends 
TestWithFeService {
 
         // 3. create table tbl1
         createTable("create table db2.tbl1(k1 int) distributed by hash(k1) 
buckets 3 properties('replication_num' = '1');");
+        RebalancerTestUtil.updateReplicaPathHash();
 
         // 4. query tablet num
         int tabletNum = 
Env.getCurrentInvertedIndex().getTabletMetaMap().size();
@@ -182,6 +192,7 @@ public class DecommissionBackendTest extends 
TestWithFeService {
         createTable("create table db3.tbl1(k1 int) distributed by hash(k1) 
buckets 3 properties('replication_num' = '"
                 + availableBeNum + "');");
         createTable("create table db3.tbl2(k1 int) distributed by hash(k1) 
buckets 3 properties('replication_num' = '1');");
+        RebalancerTestUtil.updateReplicaPathHash();
 
         // 4. query tablet num
         int tabletNum = 
Env.getCurrentInvertedIndex().getTabletMetaMap().size();
@@ -229,11 +240,104 @@ public class DecommissionBackendTest extends 
TestWithFeService {
         // recover tbl1, because tbl1 has more than one replica, so it still 
can be recovered
         Assertions.assertDoesNotThrow(() -> recoverTable("db3.tbl1"));
         Assertions.assertDoesNotThrow(() -> showCreateTable(sql));
+        dropTable("db3.tbl1", false);
 
         addNewBackend();
         Assertions.assertEquals(backendNum(), 
Env.getCurrentSystemInfo().getIdToBackend().size());
     }
 
+    @Test
+    public void testDecommissionBackendWithMTMV() throws Exception {
+        // 1. create connect context
+        connectContext = createDefaultCtx();
+
+        ImmutableMap<Long, Backend> idToBackendRef = 
Env.getCurrentSystemInfo().getIdToBackend();
+        Assertions.assertEquals(backendNum(), idToBackendRef.size());
+
+        // 2. create database db1
+        createDatabase("db4");
+        System.out.println(Env.getCurrentInternalCatalog().getDbNames());
+
+        // 3. create table
+        createTable("CREATE TABLE db4.table1 (\n"
+                + " `c1` varchar(20) NULL,\n"
+                + " `c2` bigint(20) NULL,\n"
+                + " `c3` int(20) not NULL,\n"
+                + " `k4` bitmap BITMAP_UNION NULL,\n"
+                + " `k5` bitmap BITMAP_UNION NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "AGGREGATE KEY(`c1`, `c2`, `c3`)\n"
+                + "COMMENT 'OLAP'\n"
+                + "DISTRIBUTED BY HASH(`c2`) BUCKETS 1\n"
+                + ";");
+
+        createTable("CREATE TABLE db4.table2 (\n"
+                + " `c1` bigint(20) NULL,\n"
+                + " `c2` bigint(20) NULL,\n"
+                + " `c3` bigint(20) not NULL,\n"
+                + " `k4` bitmap BITMAP_UNION NULL,\n"
+                + " `k5` bitmap BITMAP_UNION NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "AGGREGATE KEY(`c1`, `c2`, `c3`)\n"
+                + "COMMENT 'OLAP'\n"
+                + "DISTRIBUTED BY HASH(`c2`) BUCKETS 1\n"
+                + ";");
+
+        createMvByNereids("create materialized view db4.mv1 BUILD IMMEDIATE 
REFRESH COMPLETE ON MANUAL\n"
+                + "DISTRIBUTED BY RANDOM BUCKETS 1\n"
+                + "as "
+                + "select t1.c1, t2.c2, t2.k4 "
+                + "from db4.table1 t1 "
+                + "inner join db4.table2 t2 on t1.c1= t2.c2;");
+
+        createMvByNereids("create materialized view db4.mv2 BUILD IMMEDIATE 
REFRESH COMPLETE ON MANUAL\n"
+                + "DISTRIBUTED BY HASH(c1) BUCKETS 20 \n"
+                + "PROPERTIES ( 'colocate_with' = 'foo', 'replication_num' = 
'3' ) "
+                + "as "
+                + "select t1.c1 as c1, t2.c3, t2.k5 "
+                + "from db4.table1 t1 "
+                + "inner join db4.table2 t2 on t1.c1= t2.c3;");
+
+        RebalancerTestUtil.updateReplicaPathHash();
+
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrMetaException("db4");
+        OlapTable tbl = (OlapTable) db.getTableOrMetaException("mv1");
+        Assertions.assertNotNull(tbl);
+
+        Partition partition = tbl.getPartitions().iterator().next();
+        Tablet tablet = 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)
+                .iterator().next().getTablets().iterator().next();
+        Assertions.assertNotNull(tablet);
+        Backend srcBackend = 
Env.getCurrentSystemInfo().getBackend(tablet.getReplicas().get(0).getBackendId());
+        Assertions.assertNotNull(srcBackend);
+
+        // 4. query tablet num
+        int tabletNum = 
Env.getCurrentInvertedIndex().getTabletMetaMap().size();
+
+        String decommissionStmtStr = "alter system decommission backend 
\"127.0.0.1:"
+                + srcBackend.getHeartbeatPort() + "\"";
+        AlterSystemStmt decommissionStmt = (AlterSystemStmt) 
parseAndAnalyzeStmt(decommissionStmtStr);
+        
Env.getCurrentEnv().getAlterInstance().processAlterCluster(decommissionStmt);
+
+        Assertions.assertTrue(srcBackend.isDecommissioned());
+        long startTimestamp = System.currentTimeMillis();
+        while (System.currentTimeMillis() - startTimestamp < 90000
+            && 
Env.getCurrentSystemInfo().getIdToBackend().containsKey(srcBackend.getId())) {
+            Thread.sleep(1000);
+        }
 
+        Assertions.assertEquals(backendNum() - 1, 
Env.getCurrentSystemInfo().getIdToBackend().size());
 
+        // For now, we have pre-built internal table: analysis_job and 
column_statistics
+        Assertions.assertEquals(tabletNum,
+                Env.getCurrentInvertedIndex().getTabletMetaMap().size());
+
+        for (Replica replica : tablet.getReplicas()) {
+            Assertions.assertTrue(replica.getBackendId() != 
srcBackend.getId());
+        }
+
+        // 6. add backend
+        addNewBackend();
+        Assertions.assertEquals(backendNum(), 
Env.getCurrentSystemInfo().getIdToBackend().size());
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java 
b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
index 37bc5f431f8..b590234a3e8 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
@@ -431,7 +431,7 @@ public abstract class TestWithFeService {
     }
 
     private void checkBEHeartbeat(List<Backend> bes) throws 
InterruptedException {
-        int maxTry = 10;
+        int maxTry = Config.heartbeat_interval_second + 2;
         boolean allAlive = false;
         while (maxTry-- > 0 && !allAlive) {
             Thread.sleep(1000);
@@ -463,7 +463,9 @@ public abstract class TestWithFeService {
     }
 
     protected Backend addNewBackend() throws IOException, InterruptedException 
{
-        return createBackend("127.0.0.1", lastFeRpcPort);
+        Backend be = createBackend("127.0.0.1", lastFeRpcPort);
+        checkBEHeartbeat(Lists.newArrayList(be));
+        return be;
     }
 
     protected Backend createBackend(String beHost, int feRpcPort) throws 
IOException, InterruptedException {
diff --git a/regression-test/data/clone_p0/test_decommission_mtmv.out 
b/regression-test/data/clone_p0/test_decommission_mtmv.out
new file mode 100644
index 00000000000..054a55377be
--- /dev/null
+++ b/regression-test/data/clone_p0/test_decommission_mtmv.out
@@ -0,0 +1,13 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select1 --
+1      100     200
+
+-- !select2 --
+2      1000    2000
+
+-- !select3 --
+1      100     200
+
+-- !select4 --
+2      1000    2000
+
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/Config.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/Config.groovy
index 615f12b7884..7d8a45d7209 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/Config.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/Config.groovy
@@ -68,6 +68,7 @@ class Config {
     public String image
     public String dockerCoverageOutputDir
     public Boolean dockerEndDeleteFiles
+    public Boolean dockerEndNoKill
     public Boolean excludeDockerTest
 
     public String testGroups
@@ -170,6 +171,7 @@ class Config {
         config.sslCertificatePath = 
FileUtils.getCanonicalPath(cmd.getOptionValue(sslCertificateOpt, 
config.sslCertificatePath))
         config.dorisComposePath = 
FileUtils.getCanonicalPath(config.dorisComposePath)
         config.image = cmd.getOptionValue(imageOpt, config.image)
+        config.dockerEndNoKill = cmd.hasOption(noKillDockerOpt)
         config.suiteWildcard = cmd.getOptionValue(suiteOpt, config.testSuites)
                 .split(",")
                 .collect({s -> s.trim()})
@@ -325,6 +327,7 @@ class Config {
         config.image = configToString(obj.image)
         config.dockerCoverageOutputDir = 
configToString(obj.dockerCoverageOutputDir)
         config.dockerEndDeleteFiles = configToBoolean(obj.dockerEndDeleteFiles)
+        config.dockerEndNoKill = configToBoolean(obj.dockerEndNoKill)
         config.excludeDockerTest = configToBoolean(obj.excludeDockerTest)
 
         def declareFileNames = config.getClass()
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/ConfigOptions.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/ConfigOptions.groovy
index 0f5b63de5bf..0caac8c894f 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/ConfigOptions.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/ConfigOptions.groovy
@@ -49,6 +49,7 @@ class ConfigOptions {
     static Option pluginOpt
     static Option sslCertificateOpt
     static Option imageOpt
+    static Option noKillDockerOpt
     static Option suiteOpt
     static Option excludeSuiteOpt
     static Option groupsOpt
@@ -183,6 +184,12 @@ class ConfigOptions {
                 .desc("the docker image")
                 .build()
 
+        noKillDockerOpt = Option.builder("noKillDocker")
+                .required(false)
+                .hasArg(false)
+                .desc("don't kill docker containers")
+                .build()
+
         suiteOpt = Option.builder("s")
                 .argName("suiteName")
                 .required(false)
@@ -400,6 +407,7 @@ class ConfigOptions {
                 .addOption(pluginOpt)
                 .addOption(sslCertificateOpt)
                 .addOption(imageOpt)
+                .addOption(noKillDockerOpt)
                 .addOption(confOpt)
                 .addOption(suiteOpt)
                 .addOption(excludeSuiteOpt)
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
index d17f9182817..cfe0e06b31d 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
@@ -275,7 +275,16 @@ class Suite implements GroovyInterceptable {
                 }
                 Thread.sleep(1000)
             }
+
             assertNotNull(fe)
+            if (!dockerIsCloud) {
+                for (def be : cluster.getAllBackends()) {
+                    be_report_disk(be.host, be.httpPort)
+                }
+            }
+
+            // wait be report
+            Thread.sleep(5000)
             def url = String.format(
                     
"jdbc:mysql://%s:%s/?useLocalSessionState=false&allowLoadLocalInfile=false",
                     fe.host, fe.queryPort)
@@ -288,7 +297,9 @@ class Suite implements GroovyInterceptable {
             logger.info("connect to docker cluster: suite={}, url={}", name, 
url)
             connect(user, password, url, actionSupplier)
         } finally {
-            cluster.destroy(context.config.dockerEndDeleteFiles)
+            if (!context.config.dockerEndNoKill) {
+                cluster.destroy(context.config.dockerEndDeleteFiles)
+            }
         }
     }
 
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/SuiteCluster.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/SuiteCluster.groovy
index e556c1896bb..a915851b938 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/SuiteCluster.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/SuiteCluster.groovy
@@ -34,8 +34,13 @@ class ClusterOptions {
 
     int feNum = 1
     int beNum = 3
-    List<String> feConfigs = ['heartbeat_interval_second=5']
-    List<String> beConfigs = []
+    List<String> feConfigs = [
+        'heartbeat_interval_second=5',
+    ]
+
+    List<String> beConfigs = [
+        'report_random_wait=false',
+    ]
     boolean connectToFollower = false
 
     // 1. cloudMode = true, only create cloud cluster.
diff --git a/regression-test/suites/clone_p0/test_decommission_mtmv.groovy 
b/regression-test/suites/clone_p0/test_decommission_mtmv.groovy
new file mode 100644
index 00000000000..24853aa718c
--- /dev/null
+++ b/regression-test/suites/clone_p0/test_decommission_mtmv.groovy
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import org.apache.doris.regression.suite.ClusterOptions
+
+suite('test_decommission_mtmv') {
+    def options = new ClusterOptions()
+    options.feConfigs += [
+        'disable_balance=true',
+        'tablet_checker_interval_ms=100',
+        'tablet_schedule_interval_ms=100',
+        'schedule_batch_size=1000',
+        'schedule_slot_num_per_hdd_path=1000',
+        'storage_high_watermark_usage_percent=99',
+        'storage_flood_stage_usage_percent=99',
+    ]
+
+    docker(options) {
+        sql '''CREATE TABLE t1 (k1 INT, k2 INT, v INT)
+               DISTRIBUTED BY HASH(k2) BUCKETS 1
+            '''
+
+        sql '''CREATE TABLE t2 (k1 INT, k2 INT, v INT)
+               DISTRIBUTED BY HASH(k2) BUCKETS 1
+            '''
+
+        sql '''CREATE MATERIALIZED VIEW mv1 BUILD DEFERRED REFRESH AUTO ON 
MANUAL
+               DISTRIBUTED BY RANDOM BUCKETS 1
+               AS
+               SELECT t1.k1, t1.v as v1, t2.v as v2
+               FROM t1 INNER JOIN t2
+               ON t1.k1 = t2.k1
+            '''
+
+        sql '''CREATE MATERIALIZED VIEW mv2 BUILD DEFERRED REFRESH AUTO ON 
MANUAL
+               DISTRIBUTED BY HASH(k2) BUCKETS 6
+               PROPERTIES ( 'colocate_with' = 'foo' )
+               AS
+               SELECT t1.k2 as k2, t1.v as v1, t2.v as v2
+               FROM t1 INNER JOIN t2
+               ON t1.k2 = t2.k2
+            '''
+
+        sql 'INSERT INTO t1 VALUES (1, 0, 100), (0, 2, 1000)'
+        sql 'INSERT INTO t2 VALUES (1, 1, 200), (2, 2, 2000)'
+
+        sql 'REFRESH MATERIALIZED VIEW mv1 AUTO'
+        sql 'REFRESH MATERIALIZED VIEW mv2 AUTO'
+
+        def dbName = context.config.getDbNameByFile(context.file)
+        waitingMTMVTaskFinished(getJobName(dbName, 'mv1'))
+        waitingMTMVTaskFinished(getJobName(dbName, 'mv2'))
+
+        order_qt_select1 'SELECT * FROM mv1'
+        order_qt_select2 'SELECT * FROM mv2'
+
+        def newBeIndex = cluster.addBackend(1)[0]
+        sleep(10 * 1000)
+
+        def decommissionBeIdx = 1
+        def decommissionBe = cluster.getBeByIndex(decommissionBeIdx)
+        cluster.decommissionBackends(decommissionBeIdx)
+
+        def backends = sql_return_maparray  'show backends'
+        assertEquals(3, backends.size())
+        for (def be : backends) {
+            assertNotEquals(be.BackendId as long, decommissionBe.backendId)
+        }
+
+        cluster.stopBackends(2, 3)
+        sleep(10 * 1000)
+        cluster.checkBeIsAlive(2, false)
+        cluster.checkBeIsAlive(3, false)
+        cluster.checkBeIsAlive(4, true)
+
+        order_qt_select3 'SELECT * FROM mv1'
+        order_qt_select4 'SELECT * FROM mv2'
+    }
+}
diff --git a/run-regression-test.sh b/run-regression-test.sh
index 78523ea2200..21d5ffdcfac 100755
--- a/run-regression-test.sh
+++ b/run-regression-test.sh
@@ -44,6 +44,7 @@ Usage: $0 <shell_options> <framework_options>
      -forceGenOut                      delete and generate .out file
      -parallel                         run tests using specified threads
      -randomOrder                      run tests in a random order
+     -noKillDocker                     don't kill container when finish docker 
suites
      -times                            rum tests {times} times
 
   Eg.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to