1
This commit is contained in:
@@ -184,6 +184,18 @@ func (d *modelTrainingDao) ListRunning(ctx context.Context) ([]*entity.ModelTrai
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// FailUnstarted 重启恢复:running 且 pid 未落(发起准备阶段服务中断,进程已随服务消亡)的任务置 failed
|
||||
func (d *modelTrainingDao) FailUnstarted(ctx context.Context) error {
|
||||
_, err := g.DB().Model(consts.TableTraining).Ctx(ctx).
|
||||
Where("status", consts.TrainingStatusRunning).Where("pid", 0).
|
||||
Data(g.Map{
|
||||
"status": consts.TrainingStatusFailed,
|
||||
"error": "服务器重启,训练发起未完成",
|
||||
"finished_at": gtime.Now(),
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// LatestByDatasets 批量取各数据集最新一条训练记录(列表卡片训练状态用;
|
||||
// IN 一次取回按 id 倒序,应用层按 dataset_id 去重;数据集表小、记录少,单次查询足够)
|
||||
func (d *modelTrainingDao) LatestByDatasets(ctx context.Context, datasetIds []int64) (map[int64]*entity.ModelTraining, error) {
|
||||
|
||||
@@ -33,6 +33,7 @@ type AdminDatasetItem struct {
|
||||
Description string `json:"description"` // 描述
|
||||
TrainingId int64 `json:"trainingId"` // 最新训练记录 id(发布/详情用)
|
||||
TrainingStatus string `json:"trainingStatus"` // 最新训练记录状态 running|success|failed|空
|
||||
TrainingError string `json:"trainingError"` // 最新训练记录失败原因(failed 时展示用)
|
||||
TrainingCurrentEpoch int `json:"trainingCurrentEpoch"`
|
||||
TrainingTotalEpochs int `json:"trainingTotalEpochs"`
|
||||
CreatedAt *gtime.Time `json:"createdAt"`
|
||||
|
||||
@@ -75,6 +75,7 @@ func (s *datasetService) AdminListDatasets(ctx context.Context, req *dto.AdminDa
|
||||
if t, ok := latest[v.Id]; ok {
|
||||
item.TrainingId = t.Id
|
||||
item.TrainingStatus = t.Status
|
||||
item.TrainingError = t.Error
|
||||
item.TrainingCurrentEpoch = t.CurrentEpoch
|
||||
item.TrainingTotalEpochs = t.TotalEpochs
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ var Training = &trainingService{}
|
||||
// 单协程生命周期任务(非并行工作负载),不做池封装。
|
||||
func (s *trainingService) StartBackgroundJobs(ctx context.Context) {
|
||||
LabelTask.recoverLabelTasks(ctx)
|
||||
if err := dao.Training.FailUnstarted(ctx); err != nil {
|
||||
g.Log().Errorf(ctx, "恢复未启动训练任务失败: %+v", err)
|
||||
}
|
||||
go s.pollTrainings(ctx)
|
||||
}
|
||||
|
||||
@@ -92,12 +95,16 @@ func (s *trainingService) pollOne(ctx context.Context, runner common.TrainingRun
|
||||
s.finishSuccess(ctx, runner, job, t, result, tail)
|
||||
return
|
||||
}
|
||||
alive, err := runner.IsAlive(ctx, job)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "训练 %d 存活探测失败: %+v", t.Id, err)
|
||||
return
|
||||
alive := true
|
||||
if t.Pid != 0 {
|
||||
a, err := runner.IsAlive(ctx, job)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "训练 %d 存活探测失败: %+v", t.Id, err)
|
||||
return
|
||||
}
|
||||
alive = a
|
||||
}
|
||||
// 超时判死(started_at 起算)
|
||||
// 超时判死(started_at 起算;含发起准备阶段)
|
||||
cfg, _ := common.TrainingConfigOf(ctx)
|
||||
timeout := time.Duration(cfg.TimeoutMins) * time.Minute
|
||||
if t.StartedAt != nil && time.Since(t.StartedAt.Time) > timeout {
|
||||
@@ -105,6 +112,10 @@ func (s *trainingService) pollOne(ctx context.Context, runner common.TrainingRun
|
||||
_ = s.finishFailed(ctx, t, "训练超时(超过 %d 分钟无结果,已终止)", cfg.TimeoutMins)
|
||||
return
|
||||
}
|
||||
// pid 未落 = 发起准备阶段(写任务参数/同步数据集/启动进程)尚未完成,不判死
|
||||
if t.Pid == 0 {
|
||||
return
|
||||
}
|
||||
if !alive {
|
||||
_ = s.finishFailed(ctx, t, "训练进程已退出(无结果文件)")
|
||||
return
|
||||
@@ -186,7 +197,9 @@ func mergeNamesIntoMetrics(metricsJSON, namesJSON string) string {
|
||||
}
|
||||
|
||||
func (s *trainingService) finishFailed(ctx context.Context, t *entity.ModelTraining, format string, args ...any) error {
|
||||
return dao.Training.Finish(ctx, t.Id, consts.TrainingStatusFailed, "", "", fmt.Sprintf(format, args...))
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
g.Log().Errorf(ctx, "训练 %d 置失败: %s", t.Id, msg)
|
||||
return dao.Training.Finish(ctx, t.Id, consts.TrainingStatusFailed, "", "", msg)
|
||||
}
|
||||
|
||||
// buildJob 组装训练机路径布局的 runner 任务
|
||||
|
||||
@@ -278,7 +278,12 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag :type="trainStatusTag[row.trainingStatus] || 'info'" size="small">
|
||||
<el-tooltip v-if="row.trainingStatus === 'failed' && row.trainingError" :content="row.trainingError" placement="top">
|
||||
<el-tag :type="trainStatusTag[row.trainingStatus] || 'info'" size="small">
|
||||
{{ trainStatusMap[row.trainingStatus] || row.trainingStatus }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag v-else :type="trainStatusTag[row.trainingStatus] || 'info'" size="small">
|
||||
{{ trainStatusMap[row.trainingStatus] || row.trainingStatus }}
|
||||
</el-tag>
|
||||
<el-button v-if="row.trainingStatus === 'success' && row.trainingId" size="small" type="success" @click="publishModel(row)">
|
||||
|
||||
Reference in New Issue
Block a user