fix(ai): AI 异步 runtime 链补 updater 回填,修 scheduled 写 muse_ai_* updater null 死锁

问题: 剥离 sourceSnapshotId 后 AI task 首次真正进入 executor 执行,撞上预存 bug——AI 异步 runtime 链(dispatcher failClaimedJob、recorder recordFinished、projection createShadow)在 scheduled 线程用 new DO + updateById,yudao DefaultDBFieldHandler.updateFill 仅当登录用户存在才回填 updater(if userId!=null && updater==null);worker 线程无登录上下文 → updater 保持 null → updater 列(fill=UPDATE)进 SET=null → muse_ai_job/call/generation updater NOT NULL 违约 → 写回失败 → job 永久 running、task 卡 queued。

根因对比: knowledge parse worker 用 selectById DO(updater 保留原值)故不中招;AI 异步链用 new DO(updater=null)中招。该链此前从未在真实 scheduled 环境跑过(长期被 runtime permission 死锁遮蔽),方案 1 修复后首次触达才暴露。

修正(局部): 三处 new-DO updateById 显式 setUpdater(ownerUserId 保持 task 链审计连贯,fallback ai-runtime);insert 路径靠列 default '' 兜底不违约,不改。

验证: dispatcher 5 + projection 5 + permission 15 + MuseAiTaskService 40 全绿;待 e2e 重跑 AI task 坐实 job 推进 + 检索 snapshot。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-24 01:51:57 -07:00
parent d647f0c8d9
commit 5d0f17a410
3 changed files with 7 additions and 0 deletions

View File

@ -96,6 +96,8 @@ public class MuseAiRuntimeCallRecorder {
update.setNextRetryAt(succeeded || failure == null ? null : toLocalDateTime(failure.nextRetryAt()));
update.setFinishedAt(succeeded ? toLocalDateTime(result.completedAt())
: toLocalDateTime(failure == null ? Instant.now() : failure.failedAt()));
// scheduled worker 线程无登录用户上下文,显式回填 updater(call owner),避免 auto-fill 落空触发 updater NOT NULL 违约。
update.setUpdater(call.getOwnerUserId() == null ? "ai-runtime" : String.valueOf(call.getOwnerUserId()));
runtimeCallMapper.updateById(update);
return update;
}

View File

@ -74,6 +74,9 @@ public class MuseAiRuntimeJobDispatcher {
update.setErrorMessage(errorMessage);
update.setFinishedAt(LocalDateTime.now());
update.setTenantId(claimedJob.getTenantId());
// scheduled worker 线程无登录用户上下文,yudao auto-fill 不会回填 updater(new DO);显式回填 owner 作操作者,
// 避免 updater NOT NULL 违约导致写回失败、job 永久 running。
update.setUpdater(claimedJob.getOwnerUserId() == null ? "ai-runtime" : String.valueOf(claimedJob.getOwnerUserId()));
// claim 已经把 job 推到 running;任何前置异常都必须关闭该 job,避免 worker 留下永久 running。
TenantUtils.executeIgnore(() -> jobMapper.updateById(update));
}

View File

@ -172,6 +172,8 @@ public class MuseAiRuntimeProjectionService {
MuseAiGenerationDO update = new MuseAiGenerationDO();
update.setId(generation.getId());
update.setCandidateId(suggestionId);
// scheduled worker 线程无登录用户上下文,显式回填 updater(job owner),避免 auto-fill 落空触发 updater NOT NULL 违约。
update.setUpdater(job.getOwnerUserId() == null ? "ai-runtime" : String.valueOf(job.getOwnerUserId()));
generationMapper.updateById(update);
return suggestionId;
}