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

morrysnow 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 0be0b8ff58 [opt](stats) Support display of auto analyze jobs (#24135)
0be0b8ff58 is described below

commit 0be0b8ff586201f7941d13949a38e7512d78ee11
Author: AKIRA <[email protected]>
AuthorDate: Thu Sep 14 18:10:04 2023 +0900

    [opt](stats) Support display of auto analyze jobs (#24135)
    
    ### Support dispaly of auto analyze jobs
    
    After this PR, users and DBA could use such grammar to check the execution 
status of auto analyze jobs:
    
    ```sql
    
    SHOW AUTO ANALYZE [tbl_name] [WHERE STATE='SOME STATE']
    ```
    
    Record count of history auto analyze job could be configured by setting FE 
option: auto_analyze_job_record_count, default value is 2000
    
    ### Enhance auto analyze
    
    After this PR, auto jobs those created automatically will no longer execute 
beyond a specific time frame.
---
 .../main/java/org/apache/doris/common/Config.java  |   7 ++
 fe/fe-core/src/main/cup/sql_parser.cup             |  12 ++-
 .../org/apache/doris/analysis/ShowAnalyzeStmt.java |  84 +++------------
 .../org/apache/doris/journal/JournalEntity.java    |   5 +
 .../java/org/apache/doris/persist/EditLog.java     |   9 ++
 .../org/apache/doris/persist/OperationType.java    |   3 +
 .../java/org/apache/doris/qe/ShowExecutor.java     |   4 +-
 .../java/org/apache/doris/qe/StmtExecutor.java     |   1 +
 .../org/apache/doris/statistics/AnalysisInfo.java  |   4 +
 .../apache/doris/statistics/AnalysisManager.java   | 113 +++++++++++++++++++--
 .../doris/statistics/AnalysisTaskExecutor.java     |   5 +
 .../doris/statistics/AnalysisTaskWrapper.java      |  12 +++
 .../apache/doris/statistics/BaseAnalysisTask.java  |   2 +-
 .../apache/doris/statistics/ColumnStatistic.java   |   2 +-
 .../doris/statistics/StatisticsAutoCollector.java  |  79 +++++---------
 .../apache/doris/statistics/util/SimpleQueue.java  |  65 ++++++++++++
 .../doris/statistics/util/StatisticsUtil.java      |  54 ++++++++++
 .../doris/statistics/AnalysisManagerTest.java      |  84 +++++++++++++++
 .../statistics/StatisticsAutoCollectorTest.java    |   2 +-
 19 files changed, 405 insertions(+), 142 deletions(-)

diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java 
b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index 7217acebcc..1d4f35fef7 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -2190,4 +2190,11 @@ public class Config extends ConfigBase {
             + "统计信息", "Whether to enable automatic sampling for large tables, 
which, when enabled, automatically"
             + "collects statistics through sampling for tables larger than 
'huge_table_lower_bound_size_in_bytes'"})
     public static boolean enable_auto_sample = false;
+
+    @ConfField(description = {
+            "控制统计信息的自动触发作业执行记录的持久化行数",
+            "Determine the persist number of automatic triggered analyze job 
execution status"
+    })
+    public static long auto_analyze_job_record_count = 20000;
+
 }
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index ecf4708d0b..8f9119308a 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -4237,13 +4237,17 @@ show_param ::=
         RESULT = new ShowCreateMaterializedViewStmt(mvName, tableName);
     :}
     /* show analyze job */
-    | KW_ANALYZE opt_table_name:tbl opt_wild_where 
order_by_clause:orderByClause limit_clause:limitClause
+    | KW_ANALYZE opt_table_name:tbl opt_wild_where
     {:
-        RESULT = new ShowAnalyzeStmt(tbl, parser.where, orderByClause, 
limitClause);
+        RESULT = new ShowAnalyzeStmt(tbl, parser.where, false);
     :}
-    | KW_ANALYZE INTEGER_LITERAL:jobId opt_wild_where 
order_by_clause:orderByClause limit_clause:limitClause
+    | KW_ANALYZE INTEGER_LITERAL:jobId opt_wild_where
     {:
-        RESULT = new ShowAnalyzeStmt(jobId, parser.where, orderByClause, 
limitClause);
+        RESULT = new ShowAnalyzeStmt(jobId, parser.where);
+    :}
+    | KW_AUTO KW_ANALYZE opt_table_name:tbl opt_wild_where
+    {:
+        RESULT = new ShowAnalyzeStmt(tbl, parser.where, true);
     :}
     | KW_ANALYZE KW_TASK KW_STATUS INTEGER_LITERAL:jobId
     {:
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
index 95035641a7..07c3029ee0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
@@ -25,7 +25,6 @@ import org.apache.doris.common.Config;
 import org.apache.doris.common.ErrorCode;
 import org.apache.doris.common.ErrorReport;
 import org.apache.doris.common.UserException;
-import org.apache.doris.common.util.OrderByPair;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.ShowResultSetMetaData;
@@ -35,10 +34,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.IntStream;
-
 /**
  * ShowAnalyzeStmt is used to show statistics job info.
  * syntax:
@@ -69,36 +64,30 @@ public class ShowAnalyzeStmt extends ShowStmt {
             .build();
 
     private long jobId;
-    private TableName dbTableName;
-    private Expr whereClause;
-    private LimitElement limitElement;
-    private List<OrderByElement> orderByElements;
+    private final TableName dbTableName;
+    private final Expr whereClause;
+
+    // extract from predicate
     private String stateValue;
-    private ArrayList<OrderByPair> orderByPairs;
 
-    public ShowAnalyzeStmt() {
-    }
+    private final boolean auto;
+
 
     public ShowAnalyzeStmt(TableName dbTableName,
-                           Expr whereClause,
-                           List<OrderByElement> orderByElements,
-                           LimitElement limitElement) {
+                           Expr whereClause, boolean auto) {
         this.dbTableName = dbTableName;
         this.whereClause = whereClause;
-        this.orderByElements = orderByElements;
-        this.limitElement = limitElement;
+        this.auto = auto;
+
     }
 
     public ShowAnalyzeStmt(long jobId,
-            Expr whereClause,
-            List<OrderByElement> orderByElements,
-            LimitElement limitElement) {
+            Expr whereClause) {
         Preconditions.checkArgument(jobId > 0, "JobId must greater than 0.");
         this.jobId = jobId;
         this.dbTableName = null;
         this.whereClause = whereClause;
-        this.orderByElements = orderByElements;
-        this.limitElement = limitElement;
+        this.auto = false;
     }
 
     public long getJobId() {
@@ -111,12 +100,6 @@ public class ShowAnalyzeStmt extends ShowStmt {
         return stateValue;
     }
 
-    public ArrayList<OrderByPair> getOrderByPairs() {
-        Preconditions.checkArgument(isAnalyzed(),
-                "The orderByPairs must be obtained after the parsing is 
complete");
-        return orderByPairs;
-    }
-
     public Expr getWhereClause() {
         Preconditions.checkArgument(isAnalyzed(),
                 "The whereClause must be obtained after the parsing is 
complete");
@@ -124,13 +107,6 @@ public class ShowAnalyzeStmt extends ShowStmt {
         return whereClause;
     }
 
-    public long getLimit() {
-        if (limitElement != null && limitElement.hasLimit()) {
-            return limitElement.getLimit();
-        }
-        return -1L;
-    }
-
     @Override
     public void analyze(Analyzer analyzer) throws UserException {
         if (!Config.enable_stats) {
@@ -149,21 +125,6 @@ public class ShowAnalyzeStmt extends ShowStmt {
         if (whereClause != null) {
             analyzeSubPredicate(whereClause);
         }
-
-        // analyze order by
-        if (orderByElements != null && !orderByElements.isEmpty()) {
-            orderByPairs = new ArrayList<>();
-            for (OrderByElement orderByElement : orderByElements) {
-                if (orderByElement.getExpr() instanceof SlotRef) {
-                    SlotRef slotRef = (SlotRef) orderByElement.getExpr();
-                    int index = analyzeColumn(slotRef.getColumnName());
-                    OrderByPair orderByPair = new OrderByPair(index, 
!orderByElement.getIsAsc());
-                    orderByPairs.add(orderByPair);
-                } else {
-                    throw new AnalysisException("Should order by column");
-                }
-            }
-        }
     }
 
     @Override
@@ -279,25 +240,6 @@ public class ShowAnalyzeStmt extends ShowStmt {
             sb.append(whereClause.toSql());
         }
 
-        // Order By clause
-        if (orderByElements != null) {
-            sb.append(" ");
-            sb.append("ORDER BY");
-            sb.append(" ");
-            IntStream.range(0, orderByElements.size()).forEach(i -> {
-                sb.append(orderByElements.get(i).getExpr().toSql());
-                sb.append((orderByElements.get(i).getIsAsc()) ? " ASC" : " 
DESC");
-                sb.append((i + 1 != orderByElements.size()) ? ", " : "");
-            });
-        }
-
-        if (getLimit() != -1L) {
-            sb.append(" ");
-            sb.append("LIMIT");
-            sb.append(" ");
-            sb.append(getLimit());
-        }
-
         return sb.toString();
     }
 
@@ -309,4 +251,8 @@ public class ShowAnalyzeStmt extends ShowStmt {
     public TableName getDbTableName() {
         return dbTableName;
     }
+
+    public boolean isAuto() {
+        return auto;
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java 
b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
index 0415e43581..017a535c54 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -885,6 +885,11 @@ public class JournalEntity implements Writable {
                 isRead = true;
                 break;
             }
+            case OperationType.OP_PERSIST_AUTO_JOB: {
+                data = AnalysisInfo.read(in);
+                isRead = true;
+                break;
+            }
             default: {
                 IOException e = new IOException();
                 LOG.error("UNKNOWN Operation Type {}", opCode, e);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
index 76c10e8567..9695df482b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
@@ -1108,6 +1108,10 @@ public class EditLog {
                     
env.getAnalysisManager().replayUpdateTableStatsStatus((TableStats) 
journal.getData());
                     break;
                 }
+                case OperationType.OP_PERSIST_AUTO_JOB: {
+                    
env.getAnalysisManager().replayPersistSysJob((AnalysisInfo) journal.getData());
+                    break;
+                }
                 default: {
                     IOException e = new IOException();
                     LOG.error("UNKNOWN Operation Type {}", opCode, e);
@@ -1945,4 +1949,9 @@ public class EditLog {
     public void logCreateTableStats(TableStats tableStats) {
         logEdit(OperationType.OP_UPDATE_TABLE_STATS, tableStats);
     }
+
+    public void logAutoJob(AnalysisInfo analysisInfo) {
+        logEdit(OperationType.OP_PERSIST_AUTO_JOB, analysisInfo);
+    }
+
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
index ccfa283177..a1af8da41b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
@@ -333,6 +333,8 @@ public class OperationType {
 
     public static final short OP_UPDATE_TABLE_STATS = 455;
 
+    public static final short OP_PERSIST_AUTO_JOB = 456;
+
     /**
      * Get opcode name by op code.
      **/
@@ -354,4 +356,5 @@ public class OperationType {
         }
         return "Not Found";
     }
+
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
index 465f6a3bd5..bb435c443d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
@@ -2675,7 +2675,9 @@ public class ShowExecutor {
                             ZoneId.systemDefault())));
             row.add(analysisInfo.state.toString());
             try {
-                
row.add(Env.getCurrentEnv().getAnalysisManager().getJobProgress(analysisInfo.jobId));
+                row.add(showStmt.isAuto()
+                        ? analysisInfo.progress
+                        : 
Env.getCurrentEnv().getAnalysisManager().getJobProgress(analysisInfo.jobId));
             } catch (Exception e) {
                 row.add("N/A");
                 LOG.warn("Failed to get progress for job: {}", analysisInfo, 
e);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index f8d9b16be8..5100e2d436 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -2582,6 +2582,7 @@ public class StmtExecutor {
                     analyze(context.getSessionVariable().toThrift());
                 }
             } catch (Exception e) {
+                LOG.warn("Failed to run internal SQL: {}", originStmt, e);
                 throw new RuntimeException("Failed to execute internal SQL. " 
+ Util.getRootCauseMessage(e), e);
             }
             planner.getFragments();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
index f23707b799..00b8c7cdaa 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
@@ -48,6 +48,7 @@ public class AnalysisInfo implements Writable {
 
     private static final Logger LOG = LogManager.getLogger(AnalysisInfo.class);
 
+    // TODO: useless, remove it later
     public enum AnalysisMode {
         INCREMENTAL,
         FULL
@@ -166,6 +167,9 @@ public class AnalysisInfo implements Writable {
     @SerializedName("cronExpr")
     public String cronExprStr;
 
+    @SerializedName("progress")
+    public String progress;
+
     public CronExpression cronExpression;
 
     @SerializedName("forceFull")
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
index d78434df73..f25cbe8a2b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
@@ -29,6 +29,7 @@ import org.apache.doris.analysis.TableName;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DatabaseIf;
 import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.Partition;
 import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.TableIf;
 import org.apache.doris.catalog.View;
@@ -39,11 +40,13 @@ import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.common.FeConstants;
 import org.apache.doris.common.ThreadPoolManager.BlockedPolicy;
+import org.apache.doris.common.io.Text;
 import org.apache.doris.common.io.Writable;
 import org.apache.doris.common.util.Daemon;
 import org.apache.doris.common.util.Util;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.persist.AnalyzeDeletionLog;
+import org.apache.doris.persist.gson.GsonUtils;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.ShowResultSet;
 import org.apache.doris.qe.ShowResultSetMetaData;
@@ -52,11 +55,13 @@ import 
org.apache.doris.statistics.AnalysisInfo.AnalysisMode;
 import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
 import org.apache.doris.statistics.AnalysisInfo.JobType;
 import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
+import org.apache.doris.statistics.util.SimpleQueue;
 import org.apache.doris.statistics.util.StatisticsUtil;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -66,6 +71,7 @@ import org.quartz.CronExpression;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -73,6 +79,7 @@ import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -94,7 +101,7 @@ public class AnalysisManager extends Daemon implements 
Writable {
     private static final Logger LOG = 
LogManager.getLogger(AnalysisManager.class);
 
     // Tracking running manually submitted async tasks, keep in mem only
-    private final ConcurrentMap<Long, Map<Long, BaseAnalysisTask>> 
analysisJobIdToTaskMap = new ConcurrentHashMap<>();
+    protected final ConcurrentMap<Long, Map<Long, BaseAnalysisTask>> 
analysisJobIdToTaskMap = new ConcurrentHashMap<>();
 
     private StatisticsCache statisticsCache;
 
@@ -107,13 +114,15 @@ public class AnalysisManager extends Daemon implements 
Writable {
     private final Map<Long, AnalysisInfo> analysisJobInfoMap = 
Collections.synchronizedMap(new TreeMap<>());
 
     // Tracking system submitted job, keep in mem only
-    private final Map<Long, AnalysisInfo> systemJobInfoMap = new 
ConcurrentHashMap<>();
+    protected final Map<Long, AnalysisInfo> systemJobInfoMap = new 
ConcurrentHashMap<>();
 
     // Tracking and control sync analyze tasks, keep in mem only
     private final ConcurrentMap<ConnectContext, SyncTaskCollection> 
ctxToSyncTask = new ConcurrentHashMap<>();
 
     private final Map<Long, TableStats> idToTblStats = new 
ConcurrentHashMap<>();
 
+    protected SimpleQueue<AnalysisInfo> autoJobs = createSimpleQueue(null, 
this);
+
     private final Function<TaskStatusWrapper, Void> userJobStatusUpdater = w 
-> {
         AnalysisInfo info = w.info;
         AnalysisState taskState = w.taskState;
@@ -174,26 +183,44 @@ public class AnalysisManager extends Daemon implements 
Writable {
         return null;
     };
 
-    private final Function<TaskStatusWrapper, Void> systemJobStatusUpdater = w 
-> {
+    private final String progressDisplayTemplate = "%d Finished  |  %d Failed  
|  %d In Progress  |  %d Total";
+
+    protected final Function<TaskStatusWrapper, Void> systemJobStatusUpdater = 
w -> {
         AnalysisInfo info = w.info;
         info.state = w.taskState;
+        info.message = w.message;
         AnalysisInfo job = systemJobInfoMap.get(info.jobId);
         if (job == null) {
             return null;
         }
-        for (BaseAnalysisTask task : 
analysisJobIdToTaskMap.get(info.jobId).values()) {
-            if (!task.info.state.equals(AnalysisState.FINISHED)) {
-                if (task.info.state.equals(AnalysisState.FAILED)) {
-                    systemJobInfoMap.remove(info.jobId);
-                }
+        int failedCount = 0;
+        StringJoiner reason = new StringJoiner(", ");
+        Map<Long, BaseAnalysisTask> taskMap = 
analysisJobIdToTaskMap.get(info.jobId);
+        for (BaseAnalysisTask task : taskMap.values()) {
+            if (task.info.state.equals(AnalysisState.RUNNING) || 
task.info.state.equals(AnalysisState.PENDING)) {
                 return null;
             }
+            if (task.info.state.equals(AnalysisState.FAILED)) {
+                failedCount++;
+                reason.add(task.info.message);
+            }
         }
         try {
             updateTableStats(job);
         } catch (Throwable e) {
             LOG.warn("Failed to update Table statistics in job: {}", 
info.toString(), e);
         } finally {
+            job.lastExecTimeInMs = System.currentTimeMillis();
+            job.message = reason.toString();
+            job.progress = String.format(progressDisplayTemplate,
+                    taskMap.size() - failedCount, failedCount, 0, 
taskMap.size());
+            if (failedCount > 0) {
+                job.message = reason.toString();
+                job.state = AnalysisState.FAILED;
+            } else {
+                job.state = AnalysisState.FINISHED;
+            }
+            autoJobs.offer(job);
             systemJobInfoMap.remove(info.jobId);
         }
         return null;
@@ -202,7 +229,6 @@ public class AnalysisManager extends Daemon implements 
Writable {
     private final Function<TaskStatusWrapper, Void>[] updaters =
             new Function[] {userJobStatusUpdater, systemJobStatusUpdater};
 
-
     public AnalysisManager() {
         
super(TimeUnit.SECONDS.toMillis(StatisticConstants.ANALYZE_MANAGER_INTERVAL_IN_SECS));
         if (!Env.isCheckpointThread()) {
@@ -616,9 +642,19 @@ public class AnalysisManager extends Daemon implements 
Writable {
     }
 
     public List<AnalysisInfo> showAnalysisJob(ShowAnalyzeStmt stmt) {
+        if (stmt.isAuto()) {
+            // It's ok to sync on this field, it would only be assigned when 
instance init or do checkpoint
+            synchronized (autoJobs) {
+                return findShowAnalyzeResult(autoJobs, stmt);
+            }
+        }
+        return findShowAnalyzeResult(analysisJobInfoMap.values(), stmt);
+    }
+
+    protected List<AnalysisInfo> 
findShowAnalyzeResult(Collection<AnalysisInfo> analysisInfos, ShowAnalyzeStmt 
stmt) {
         String state = stmt.getStateValue();
         TableName tblName = stmt.getDbTableName();
-        return analysisJobInfoMap.values().stream()
+        return analysisInfos.stream()
                 .filter(a -> stmt.getJobId() == 0 || a.jobId == 
stmt.getJobId())
                 .filter(a -> state == null || 
a.state.equals(AnalysisState.valueOf(state)))
                 .filter(a -> tblName == null || 
a.catalogName.equals(tblName.getCtl())
@@ -649,7 +685,7 @@ public class AnalysisManager extends Daemon implements 
Writable {
                     break;
             }
         }
-        return String.format("%d Finished/%d Failed/%d In Progress/%d Total", 
finished, failed, inProgress, total);
+        return String.format(progressDisplayTemplate, finished, failed, 
inProgress, total);
     }
 
     @VisibleForTesting
@@ -879,6 +915,7 @@ public class AnalysisManager extends Daemon implements 
Writable {
         readAnalysisInfo(in, analysisManager.analysisJobInfoMap, true);
         readAnalysisInfo(in, analysisManager.analysisTaskInfoMap, false);
         readIdToTblStats(in, analysisManager.idToTblStats);
+        readAutoJobs(in, analysisManager);
         return analysisManager;
     }
 
@@ -898,11 +935,18 @@ public class AnalysisManager extends Daemon implements 
Writable {
         }
     }
 
+    private static void readAutoJobs(DataInput in, AnalysisManager 
analysisManager) throws IOException {
+        Type type = new TypeToken<LinkedList<AnalysisInfo>>() {}.getType();
+        Collection<AnalysisInfo> autoJobs = 
GsonUtils.GSON.fromJson(Text.readString(in), type);
+        analysisManager.autoJobs = analysisManager.createSimpleQueue(autoJobs, 
analysisManager);
+    }
+
     @Override
     public void write(DataOutput out) throws IOException {
         writeJobInfo(out, analysisJobInfoMap);
         writeJobInfo(out, analysisTaskInfoMap);
         writeTableStats(out);
+        writeAutoJobsStatus(out);
     }
 
     private void writeJobInfo(DataOutput out, Map<Long, AnalysisInfo> infoMap) 
throws IOException {
@@ -919,6 +963,12 @@ public class AnalysisManager extends Daemon implements 
Writable {
         }
     }
 
+    private void writeAutoJobsStatus(DataOutput output) throws IOException {
+        Type type = new TypeToken<LinkedList<AnalysisInfo>>() {}.getType();
+        String autoJobs = GsonUtils.GSON.toJson(this.autoJobs, type);
+        Text.writeString(output, autoJobs);
+    }
+
     // For unit test use only.
     public void addToJobIdTasksMap(long jobId, Map<Long, BaseAnalysisTask> 
tasks) {
         analysisJobIdToTaskMap.put(jobId, tasks);
@@ -954,4 +1004,45 @@ public class AnalysisManager extends Daemon implements 
Writable {
         systemJobInfoMap.put(jobInfo.jobId, jobInfo);
         analysisJobIdToTaskMap.put(jobInfo.jobId, taskInfos);
     }
+
+    @VisibleForTesting
+    protected Set<String> findReAnalyzeNeededPartitions(TableIf table) {
+        TableStats tableStats = findTableStatsStatus(table.getId());
+        if (tableStats == null) {
+            return table.getPartitionNames().stream().map(table::getPartition)
+                    
.filter(Partition::hasData).map(Partition::getName).collect(Collectors.toSet());
+        }
+        return table.getPartitionNames().stream()
+                .map(table::getPartition)
+                .filter(Partition::hasData)
+                .filter(partition ->
+                        partition.getVisibleVersionTime() >= 
tableStats.updatedTime).map(Partition::getName)
+                .collect(Collectors.toSet());
+    }
+
+    protected void logAutoJob(AnalysisInfo autoJob) {
+        Env.getCurrentEnv().getEditLog().logAutoJob(autoJob);
+    }
+
+    public void replayPersistSysJob(AnalysisInfo analysisInfo) {
+        autoJobs.offer(analysisInfo);
+    }
+
+    protected SimpleQueue<AnalysisInfo> 
createSimpleQueue(Collection<AnalysisInfo> collection,
+            AnalysisManager analysisManager) {
+        return new SimpleQueue<>(Config.auto_analyze_job_record_count,
+                a -> {
+                    // FE is not ready when replaying log and operations 
triggered by replaying
+                    // shouldn't be logged again.
+                    if (Env.getCurrentEnv().isReady() && 
!Env.isCheckpointThread()) {
+                        analysisManager.logAutoJob(a);
+                    }
+                    return null;
+                },
+                a -> {
+                    // DO NOTHING
+                    return null;
+                }, null);
+    }
+
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
index a7b0073bb4..4b133ce0eb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
@@ -99,4 +99,9 @@ public class AnalysisTaskExecutor extends Thread {
     public boolean idle() {
         return executors.getQueue().isEmpty();
     }
+
+    public void clear() {
+        executors.getQueue().clear();
+        taskQueue.clear();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
index 7f55469f53..800c446576 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
@@ -18,11 +18,15 @@
 package org.apache.doris.statistics;
 
 import org.apache.doris.catalog.Env;
+import org.apache.doris.common.util.TimeUtils;
 import org.apache.doris.common.util.Util;
+import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
+import org.apache.doris.statistics.util.StatisticsUtil;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.time.LocalTime;
 import java.util.concurrent.FutureTask;
 
 public class AnalysisTaskWrapper extends FutureTask<Void> {
@@ -52,6 +56,14 @@ public class AnalysisTaskWrapper extends FutureTask<Void> {
             if (task.killed) {
                 return;
             }
+            if (task.info.scheduleType.equals(ScheduleType.AUTOMATIC) && 
!StatisticsUtil.checkAnalyzeTime(
+                    LocalTime.now(TimeUtils.getTimeZone().toZoneId()))) {
+                // TODO: Do we need a separate AnalysisState here?
+                Env.getCurrentEnv().getAnalysisManager()
+                        .updateTaskStatus(task.info, AnalysisState.FAILED, 
"Auto task"
+                                + "doesn't get executed within specified time 
range", System.currentTimeMillis());
+                return;
+            }
             executor.putJob(this);
             super.run();
             Object result = get();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
index 8940b7182d..92fcc870c8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
@@ -115,7 +115,7 @@ public abstract class BaseAnalysisTask {
         init(info);
     }
 
-    private void init(AnalysisInfo info) {
+    protected void init(AnalysisInfo info) {
         catalog = 
Env.getCurrentEnv().getCatalogMgr().getCatalog(info.catalogName);
         if (catalog == null) {
             Env.getCurrentEnv().getAnalysisManager().updateTaskStatus(info, 
AnalysisState.FAILED,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
index b4a261847d..11f924b5d1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
@@ -163,7 +163,7 @@ public class ColumnStatistic {
             String colName = row.get(5);
             Column col = StatisticsUtil.findColumn(catalogId, dbID, tblId, 
idxId, colName);
             if (col == null) {
-                LOG.warn("Failed to deserialize column statistics, ctlId: {} 
dbId: {}"
+                LOG.debug("Failed to deserialize column statistics, ctlId: {} 
dbId: {}"
                                 + "tblId: {} column: {} not exists",
                         catalogId, dbID, tblId, colName);
                 return ColumnStatistic.UNKNOWN;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
index 53bebf53e8..64ea89ff63 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
@@ -17,9 +17,7 @@
 
 package org.apache.doris.statistics;
 
-import org.apache.doris.analysis.SetType;
 import org.apache.doris.analysis.TableName;
-import org.apache.doris.analysis.VariableExpr;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DatabaseIf;
 import org.apache.doris.catalog.Env;
@@ -27,23 +25,19 @@ import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.TableIf;
 import org.apache.doris.catalog.external.ExternalTable;
 import org.apache.doris.common.Config;
-import org.apache.doris.common.Pair;
 import org.apache.doris.common.util.TimeUtils;
 import org.apache.doris.datasource.CatalogIf;
-import org.apache.doris.qe.SessionVariable;
-import org.apache.doris.qe.VariableMgr;
 import org.apache.doris.statistics.AnalysisInfo.AnalysisMethod;
 import org.apache.doris.statistics.AnalysisInfo.JobType;
+import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
 import org.apache.doris.statistics.util.StatisticsUtil;
 
-import org.apache.commons.lang3.StringUtils;
+import com.google.common.collect.Maps;
 import org.apache.hudi.common.util.VisibleForTesting;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -64,7 +58,8 @@ public class StatisticsAutoCollector extends 
StatisticsCollector {
 
     @Override
     protected void collect() {
-        if 
(!checkAnalyzeTime(LocalTime.now(TimeUtils.getTimeZone().toZoneId()))) {
+        if 
(!StatisticsUtil.checkAnalyzeTime(LocalTime.now(TimeUtils.getTimeZone().toZoneId())))
 {
+            analysisTaskExecutor.clear();
             return;
         }
         if (Config.enable_full_auto_analyze) {
@@ -147,7 +142,7 @@ public class StatisticsAutoCollector extends 
StatisticsCollector {
                 .setAnalysisMode(AnalysisInfo.AnalysisMode.INCREMENTAL)
                 .setAnalysisMethod(analysisMethod)
                 .setSamplePercent(Config.huge_table_default_sample_rows)
-                .setScheduleType(AnalysisInfo.ScheduleType.ONCE)
+                .setScheduleType(ScheduleType.AUTOMATIC)
                 .setState(AnalysisState.PENDING)
                 .setTaskIds(new ArrayList<>())
                 .setLastExecTimeInMs(System.currentTimeMillis())
@@ -175,51 +170,27 @@ public class StatisticsAutoCollector extends 
StatisticsCollector {
         return new 
AnalysisInfoBuilder(jobInfo).setColToPartitions(needRunPartitions).build();
     }
 
-
-    private boolean checkAnalyzeTime(LocalTime now) {
-        try {
-            Pair<LocalTime, LocalTime> range = findRangeFromGlobalSessionVar();
-            DateTimeFormatter timeFormatter = 
DateTimeFormatter.ofPattern("HH:mm:ss");
-            LocalTime start = range == null
-                    ? LocalTime.parse(Config.full_auto_analyze_start_time, 
timeFormatter) : range.first;
-            LocalTime end = range == null
-                    ? LocalTime.parse(Config.full_auto_analyze_end_time, 
timeFormatter) : range.second;
-
-            if (start.isAfter(end) && (now.isAfter(start) || 
now.isBefore(end))) {
-                return true;
-            } else {
-                return now.isAfter(start) && now.isBefore(end);
-            }
-        } catch (DateTimeParseException e) {
-            LOG.warn("Parse analyze start/end time format fail", e);
-            return true;
-        }
-    }
-
-    private Pair<LocalTime, LocalTime> findRangeFromGlobalSessionVar() {
-        try {
-            String startTime =
-                    
findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_START_TIME)
-                            .fullAutoAnalyzeStartTime;
-            if (StringUtils.isEmpty(startTime)) {
-                return null;
-            }
-            String endTime = 
findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_END_TIME)
-                    .fullAutoAnalyzeEndTime;
-            if (StringUtils.isEmpty(startTime)) {
-                return null;
+    @VisibleForTesting
+    protected AnalysisInfo getAnalysisJobInfo(AnalysisInfo jobInfo, TableIf 
table,
+            Set<String> needRunPartitions) {
+        Map<String, Set<String>> newColToPartitions = Maps.newHashMap();
+        Map<String, Set<String>> colToPartitions = jobInfo.colToPartitions;
+        if (colToPartitions == null) {
+            for (Column c : table.getColumns()) {
+                if (StatisticsUtil.isUnsupportedType(c.getType())) {
+                    continue;
+                }
+                newColToPartitions.put(c.getName(), needRunPartitions);
             }
-            DateTimeFormatter timeFormatter = 
DateTimeFormatter.ofPattern("HH:mm:ss");
-            return Pair.of(LocalTime.parse(startTime, timeFormatter), 
LocalTime.parse(endTime, timeFormatter));
-        } catch (Exception e) {
-            return null;
+        } else {
+            colToPartitions.keySet().forEach(colName -> {
+                Column column = table.getColumn(colName);
+                if (column != null) {
+                    newColToPartitions.put(colName, needRunPartitions);
+                }
+            });
         }
-    }
-
-    private SessionVariable findRangeFromGlobalSessionVar(String varName) 
throws Exception {
-        SessionVariable sessionVariable = VariableMgr.newSessionVariable();
-        VariableExpr variableExpr = new VariableExpr(varName, SetType.GLOBAL);
-        VariableMgr.getValue(sessionVariable, variableExpr);
-        return sessionVariable;
+        return new AnalysisInfoBuilder(jobInfo)
+                .setColToPartitions(newColToPartitions).build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java
new file mode 100644
index 0000000000..5740c4e308
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java
@@ -0,0 +1,65 @@
+// 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.
+
+package org.apache.doris.statistics.util;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.function.Function;
+
+// Any operation on this structure should be thread-safe
+public class SimpleQueue<T> extends LinkedList<T> {
+
+    private final long limit;
+
+    private final Function<T, Void> offerFunc;
+
+    private final Function<T, Void> evictFunc;
+
+
+    public SimpleQueue(long limit, Function<T, Void> offerFunc, Function<T, 
Void> evictFunc) {
+        this.limit = limit;
+        this.offerFunc = offerFunc;
+        this.evictFunc = evictFunc;
+    }
+
+    @Override
+    public synchronized boolean offer(T analysisInfo) {
+        while (size() >= limit) {
+            remove();
+        }
+        super.offer(analysisInfo);
+        offerFunc.apply(analysisInfo);
+        return true;
+    }
+
+    @Override
+    public synchronized T remove() {
+        T analysisInfo = super.remove();
+        evictFunc.apply(analysisInfo);
+        return analysisInfo;
+    }
+
+    public SimpleQueue(long limit, Function<T, Void> offerFunc, Function<T, 
Void> evictFunc, Collection<T> collection) {
+        this(limit, offerFunc, evictFunc);
+        if (collection != null) {
+            for (T e : collection) {
+                offer(e);
+            }
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
index 7a0d700fbb..d7c635bf01 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
@@ -25,10 +25,12 @@ import org.apache.doris.analysis.FloatLiteral;
 import org.apache.doris.analysis.IntLiteral;
 import org.apache.doris.analysis.LargeIntLiteral;
 import org.apache.doris.analysis.LiteralExpr;
+import org.apache.doris.analysis.SetType;
 import org.apache.doris.analysis.StatementBase;
 import org.apache.doris.analysis.StringLiteral;
 import org.apache.doris.analysis.TableName;
 import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.analysis.VariableExpr;
 import org.apache.doris.catalog.ArrayType;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DatabaseIf;
@@ -49,6 +51,7 @@ import org.apache.doris.catalog.external.HMSExternalTable;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.Pair;
 import org.apache.doris.common.UserException;
 import org.apache.doris.datasource.CatalogIf;
 import org.apache.doris.datasource.HMSExternalCatalog;
@@ -62,6 +65,7 @@ import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.QueryState;
 import org.apache.doris.qe.SessionVariable;
 import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.qe.VariableMgr;
 import org.apache.doris.statistics.ColumnStatistic;
 import org.apache.doris.statistics.ColumnStatisticBuilder;
 import org.apache.doris.statistics.Histogram;
@@ -87,6 +91,9 @@ import org.apache.logging.log4j.Logger;
 
 import java.net.InetSocketAddress;
 import java.text.SimpleDateFormat;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -711,4 +718,51 @@ public class StatisticsUtil {
         }
         return table instanceof ExternalTable;
     }
+
+    public static boolean checkAnalyzeTime(LocalTime now) {
+        try {
+            Pair<LocalTime, LocalTime> range = findRangeFromGlobalSessionVar();
+            DateTimeFormatter timeFormatter = 
DateTimeFormatter.ofPattern("HH:mm:ss");
+            LocalTime start = range == null
+                    ? LocalTime.parse(Config.full_auto_analyze_start_time, 
timeFormatter) : range.first;
+            LocalTime end = range == null
+                    ? LocalTime.parse(Config.full_auto_analyze_end_time, 
timeFormatter) : range.second;
+
+            if (start.isAfter(end) && (now.isAfter(start) || 
now.isBefore(end))) {
+                return true;
+            } else {
+                return now.isAfter(start) && now.isBefore(end);
+            }
+        } catch (DateTimeParseException e) {
+            LOG.warn("Parse analyze start/end time format fail", e);
+            return true;
+        }
+    }
+
+    private static Pair<LocalTime, LocalTime> findRangeFromGlobalSessionVar() {
+        try {
+            String startTime =
+                    
findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_START_TIME)
+                            .fullAutoAnalyzeStartTime;
+            if (StringUtils.isEmpty(startTime)) {
+                return null;
+            }
+            String endTime = 
findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_END_TIME)
+                    .fullAutoAnalyzeEndTime;
+            if (StringUtils.isEmpty(startTime)) {
+                return null;
+            }
+            DateTimeFormatter timeFormatter = 
DateTimeFormatter.ofPattern("HH:mm:ss");
+            return Pair.of(LocalTime.parse(startTime, timeFormatter), 
LocalTime.parse(endTime, timeFormatter));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static SessionVariable findRangeFromGlobalSessionVar(String 
varName) throws Exception {
+        SessionVariable sessionVariable =  VariableMgr.newSessionVariable();
+        VariableExpr variableExpr = new VariableExpr(varName, SetType.GLOBAL);
+        VariableMgr.getValue(sessionVariable, variableExpr);
+        return sessionVariable;
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/statistics/AnalysisManagerTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/statistics/AnalysisManagerTest.java
index 16ef1705d8..2146722db9 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/statistics/AnalysisManagerTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/statistics/AnalysisManagerTest.java
@@ -21,6 +21,9 @@ import org.apache.doris.analysis.AnalyzeProperties;
 import org.apache.doris.analysis.AnalyzeTblStmt;
 import org.apache.doris.analysis.PartitionNames;
 import org.apache.doris.analysis.TableName;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
 import org.apache.doris.statistics.AnalysisInfo.JobType;
@@ -33,6 +36,7 @@ import mockit.Injectable;
 import mockit.Mock;
 import mockit.MockUp;
 import mockit.Mocked;
+import org.apache.hadoop.util.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -40,6 +44,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 // CHECKSTYLE OFF
@@ -262,4 +267,83 @@ public class AnalysisManagerTest {
         };
     }
 
+    @Test
+    public void testSystemJobStatusUpdater() {
+        new MockUp<BaseAnalysisTask>() {
+
+            @Mock
+            protected void init(AnalysisInfo info) {
+
+            }
+        };
+
+        new MockUp<AnalysisManager>() {
+            @Mock
+            public void updateTableStats(AnalysisInfo jobInfo) {}
+
+            @Mock
+            protected void logAutoJob(AnalysisInfo autoJob) {
+
+            }
+        };
+
+        AnalysisManager analysisManager = new AnalysisManager();
+        AnalysisInfo job = new AnalysisInfoBuilder()
+                .setJobId(0)
+                .setColName("col1, col2").build();
+        analysisManager.systemJobInfoMap.put(job.jobId, job);
+        AnalysisInfo task1 = new AnalysisInfoBuilder()
+                .setJobId(0)
+                .setTaskId(1)
+                .setState(AnalysisState.RUNNING)
+                .setColName("col1").build();
+        AnalysisInfo task2 = new AnalysisInfoBuilder()
+                .setJobId(0)
+                .setTaskId(1)
+                .setState(AnalysisState.FINISHED)
+                .setColName("col2").build();
+        OlapAnalysisTask ot1 = new OlapAnalysisTask(task1);
+        OlapAnalysisTask ot2 = new OlapAnalysisTask(task2);
+        Map<Long, BaseAnalysisTask> taskMap = new HashMap<>();
+        taskMap.put(ot1.info.taskId, ot1);
+        taskMap.put(ot2.info.taskId, ot2);
+        analysisManager.analysisJobIdToTaskMap.put(job.jobId, taskMap);
+
+        // test invalid job
+        AnalysisInfo invalidJob = new 
AnalysisInfoBuilder().setJobId(-1).build();
+        analysisManager.systemJobStatusUpdater.apply(new 
TaskStatusWrapper(invalidJob,
+                AnalysisState.FAILED, "", 0));
+
+        // test finished
+        analysisManager.systemJobStatusUpdater.apply(new 
TaskStatusWrapper(task1, AnalysisState.FAILED, "", 0));
+        analysisManager.systemJobStatusUpdater.apply(new 
TaskStatusWrapper(task1, AnalysisState.FINISHED, "", 0));
+        Assertions.assertEquals(1, analysisManager.autoJobs.size());
+        Assertions.assertTrue(analysisManager.systemJobInfoMap.isEmpty());
+    }
+
+    @Test
+    public void testReAnalyze() {
+        new MockUp<OlapTable>() {
+
+            int count = 0;
+            int[] rowCount = new int[]{100, 200};
+            @Mock
+            public long getRowCount() {
+                return rowCount[count++];
+            }
+
+            @Mock
+            public List<Column> getBaseSchema() {
+                return Lists.newArrayList(new Column("col1", 
PrimitiveType.INT));
+            }
+
+        };
+        OlapTable olapTable = new OlapTable();
+        TableStats stats1 = new TableStats(0, 50, new 
AnalysisInfoBuilder().setColName("col1").build());
+        Assertions.assertTrue(olapTable.needReAnalyzeTable(stats1));
+        TableStats stats2 = new TableStats(0, 190, new 
AnalysisInfoBuilder().setColName("col1").build());
+        Assertions.assertFalse(olapTable.needReAnalyzeTable(stats2));
+
+    }
+
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/statistics/StatisticsAutoCollectorTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/statistics/StatisticsAutoCollectorTest.java
index 152e5cf948..2ceda0a888 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/statistics/StatisticsAutoCollectorTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/statistics/StatisticsAutoCollectorTest.java
@@ -209,7 +209,7 @@ public class StatisticsAutoCollectorTest {
                 .setDbName("db")
                 .setTblName("tbl").build();
         
Assertions.assertNotNull(statisticsAutoCollector.getReAnalyzeRequiredPart(analysisInfo2));
-        // uncomment it when updatedRows get ready
+        // uncomment it when updatedRows gets ready
         // 
Assertions.assertNull(statisticsAutoCollector.getReAnalyzeRequiredPart(analysisInfo2));
         
Assertions.assertNotNull(statisticsAutoCollector.getReAnalyzeRequiredPart(analysisInfo2));
     }


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

Reply via email to