StarRocks FE 后台守护线程 `TaskRunStateSynchronizer` 周期性(每 5 秒)抛出 `NullPointerException`,导致 FE 无法建立新连接,但进程和端口仍处于活跃状态。

为了更快的定位您的问题,请提供以下信息,谢谢
【详述】StarRocks FE 后台守护线程 TaskRunStateSynchronizer 周期性(每 5 秒)抛出 NullPointerException,导致 FE 无法建立新连接,但进程和端口仍处于活跃状态。
【背景】
【业务影响】
【是否存算分离】否
【StarRocks版本】 3.3.13
【集群规模】例如:3fe+2be(fe与be独立节点)
【机器信息】fe 16c 32G. be 32c 96G
【联系方式】liji9988@yeah.net
【附件】

2. 错误日志信息

2.1 错误堆栈


ERROR (TaskRunStateSynchronizer|216) [Daemon.run():111] daemon thread got exception. name: TaskRunStateSynchronizer

java.lang.NullPointerException: null

at com.starrocks.scheduler.TaskRun.getStatus(TaskRun.java:329)

at com.starrocks.leader.TaskRunStateSynchronizer.runAfterCatalogReady(TaskRunStateSynchronizer.java:57)

at com.starrocks.common.util.FrontendDaemon.runOneCycle(FrontendDaemon.java:72)

at com.starrocks.common.util.Daemon.run(Daemon.java:109)

2.2 错误发生时间线

  • 首次出现: 2025-12-15 09:59:51.659+08:00

  • 持续频率: 每 5 秒一次(符合 TaskRunStateSynchronizer 的调度间隔)

  • 最后出现: 2025-12-15 14:05:03.119+08:00

  • 持续时间: 约 4 小时 5 分钟

  • 错误总数: 约 3000+ 次(根据日志统计)

2.3 相关任务信息

从日志中观察到以下任务频繁初始化:


2025-12-15 00:52:55.054+08:00 INFO (leaderCheckpointer|204) [TaskRun.initStatus():365] init task status, task:mv-35129752, query_id:b45c0f79-ae64-11f0-931f-00505691212e, create_time:1761040799438

2025-12-15 00:52:55.054+08:00 INFO (leaderCheckpointer|204) [TaskRun.initStatus():365] init task status, task:mv-35129752, query_id:b45c84ae-ae64-11f0-931f-00505691212e, create_time:1761040799441

...

2025-12-15 04:50:06.974+08:00 INFO (leaderCheckpointer|204) [TaskRun.initStatus():365] init task status, task:mv-35129752, ...

2025-12-15 07:28:18.632+08:00 INFO (leaderCheckpointer|204) [TaskRun.initStatus():365] init task status, task:mv-35129752, ...

关键发现:

  • mv-35129752 任务在日志中反复初始化,但在当前 SHOW MATERIALIZED VIEWS 查询结果中不存在

  • 该任务疑似为"孤儿任务"(MV 已被删除或迁移,但 TaskRun 元数据残留)

  • 其他相关任务:mv-138078 (DIM_CHANNEL_MV), mv-141071 (DIM_GOODS_MV)fe.warn.log.20251215-1 (22.1 MB)

3.1 TaskRun.getStatus() 方法实现

文件位置: fe/fe-core/src/main/java/com/starrocks/scheduler/TaskRun.java

关键代码(第 316-343 行):

public TaskRunStatus getStatus() {
    if (status == null) {
        return null;  // ⚠️ 关键点:当 status 为 null 时返回 null
    }
    switch (status.getState()) {
        case RUNNING:
            if (runCtx != null) {
                StmtExecutor executor = runCtx.getExecutor();
                if (executor != null && executor.getCoordinator() != null) {
                    long jobId = executor.getCoordinator().getLoadJobId();
                    if (jobId != -1) {
                        InsertLoadJob job = (InsertLoadJob) GlobalStateMgr.getCurrentState()
                                .getLoadMgr().getLoadJob(jobId);
                        int progress = job.getProgress();  // ⚠️ 错误堆栈指向的第 329 行
                        if (progress == 100) {
                            progress = 99;
                        }
                        status.setProgress(progress);
                    }
                }
            }
            break;
        case SUCCESS:
            status.setProgress(100);
            break;
    }
    return status;
}

分析:

  • getStatus() 方法在 status == null 时返回 null
  • 但调用方(TaskRunStateSynchronizer没有检查返回值是否为 null

3.2 TaskRunStateSynchronizer.runAfterCatalogReady() 方法实现

文件位置: fe/fe-core/src/main/java/com/starrocks/leader/TaskRunStateSynchronizer.java

关键代码(第 52-84 行):

@Override
protected void runAfterCatalogReady() {
    Set<TaskRun> runningTaskRuns = taskRunScheduler.getCopiedRunningTaskRuns();
    Map<Long, Integer> jobProgressMap = new HashMap<>();
    for (TaskRun taskRun : runningTaskRuns) {
        Long taskId = taskRun.getTaskId();
        int nowProgress = taskRun.getStatus().getProgress();  // ⚠️ 第 57 行:NPE 发生位置
        // nowProgress == 100 indicates that the job is finished
        // the progress will be updated by TaskRunStatusChange
        if (nowProgress == 100) {
            if (runningTaskRunProgressMap.containsKey(taskId)) {
                runningTaskRunProgressMap.remove(taskId);
            }
            continue;
        }
        // ... 后续处理逻辑
    }
    // ...
}

问题代码分析:

  • 第 57 行:taskRun.getStatus().getProgress()
    • 如果 getStatus() 返回 null,则调用 .getProgress() 会抛出 NullPointerException
    • 缺少 null 检查

3.3 TaskRun 构造函数和 status 字段初始化

文件位置: fe/fe-core/src/main/java/com/starrocks/scheduler/TaskRun.java

关键代码(第 88-97 行):

private TaskRunStatus status;  // 第 88 行:字段声明,默认值为 null

TaskRun() {
    future = new CompletableFuture<>();
    taskRunId = UUIDUtil.genUUID().toString();
    // ⚠️ 注意:构造函数中没有初始化 status 字段
}

status 字段初始化逻辑(第 345-368 行):

public TaskRunStatus initStatus(String queryId, Long createTime) {
    TaskRunStatus status = new TaskRunStatus();
    // ... 设置各种属性
    LOG.info("init task status, task:{}, query_id:{}, create_time:{}", task.getName(), queryId, status.getCreateTime());
    this.status = status;  // 第 366 行:在这里才真正初始化 status
    return status;
}

分析:

  • TaskRun 的构造函数不初始化 status 字段,默认值为 null
  • status 字段只有在调用 initStatus() 方法后才会被初始化
  • 如果某个 TaskRun 对象没有调用 initStatus(),其 status 字段将保持为 null

3.4 TaskRunStateSynchronizer 构造函数中的同样问题

文件位置: fe/fe-core/src/main/java/com/starrocks/leader/TaskRunStateSynchronizer.java

关键代码(第 39-49 行):

public TaskRunStateSynchronizer() {
    super("TaskRunStateSynchronizer", FeConstants.SYNC_TASK_RUNS_STATE_INTERVAL);
    taskRunManager = GlobalStateMgr.getCurrentState().getTaskManager().getTaskRunManager();
    runningTaskRunProgressMap = new HashMap<>();
    taskRunScheduler = taskRunManager.getTaskRunScheduler();

    Set<TaskRun> runningTaskRuns = taskRunScheduler.getCopiedRunningTaskRuns();
    for (TaskRun taskRun : runningTaskRuns) {
        runningTaskRunProgressMap.put(taskRun.getTaskId(), taskRun.getStatus().getProgress());  // ⚠️ 第 47 行:同样的问题
    }
}

分析:

  • 构造函数中第 47 行也存在同样的问题:没有检查 getStatus() 返回值是否为 null

分析了波源码 有大佬帮忙看下不

应该是已经修复过了,升级到对应的高版本应该可以解决

我看了 3.3.20 (3.3.X 最新版本)中有
https://github.com/StarRocks/starrocks/pull/63820
这个修复 但是 看内容和我的报错位置并不一样
你说的是这个吗