Files
knowledge-governance/cmd/worker/library_source_candidate.go
T
姚章浩 4421dd38ce
build / build (push) Successful in 1m35s
fix(worker): 封存来源校验失败写入失败终态
verifyCapturedSource 失败时原实现只 return err:队列作业已是 failed,业务行
library_jobs 却停在 running/export、error 为空,页面永远显示"导出中"(实测
libseal-lib-internal-20261102T234053)。同函数其余四条失败分支都同时写 stage 与
job,这一条漏了。

现在 ensureCapturedSource 在校验失败时按既有惯例写 stage failed + job failed,
原错原样进取值、stage detail 与 job.error,恢复文案指向"修配置后重新封存"(快照
字节完好,不需要重采)。成功路径不变。

验证:
- RED:新增用例在修复前失败于 "stage writes = [], want exactly one failed stage"
- GREEN:TestCapturedSourceMismatchPersistsFailedStageAndJob /
  TestCapturedSourceMatchStaysSuccessful 通过
- make verify:16 项必需检查全绿(tests-go 含新用例)
2026-11-03 07:59:40 +08:00

54 lines
2.2 KiB
Go

package main
import (
"context"
"errors"
"knowledge-governance/internal/appconfig"
"knowledge-governance/internal/governance"
"knowledge-governance/internal/librarysnapshot"
"time"
)
func configuredCaptureSource(config appconfig.Config, scope string) governance.LibrarySourceIdentity {
kb := config.LibrarySourceInternalKB
if scope == "external" {
kb = config.LibrarySourceExternalKB
}
return governance.LibrarySourceIdentity{Project: config.SourceProject(), App: config.LibrarySourceApp, Scope: scope, TenantID: config.LibrarySourceTenantID, KnowledgeBaseID: kb}
}
func verifyCapturedSource(root, snapshotID string, source governance.LibrarySourceIdentity) error {
if _, err := source.ID(); err != nil {
return err
}
archive, err := librarysnapshot.Open(root, 256*1024*1024)
if err != nil {
return err
}
defer archive.Close()
manifest, err := archive.Load(snapshotID)
if err != nil {
return err
}
if manifest.Scope != source.Scope || manifest.TenantID != source.TenantID || manifest.KnowledgeBaseID != source.KnowledgeBaseID {
return errors.New("captured manifest differs from configured source")
}
return nil
}
// ensureCapturedSource 校验快照来源。校验失败必须把失败写进业务行:
// 队列侧此时已 failed,只 return err 会让页面永远停在"导出中"
// (实际事故:libseal-lib-internal-20261102T234053 running/export/error 空)。
func ensureCapturedSource(ctx context.Context, store libraryStageStore, jobID, root, snapshotID string, source governance.LibrarySourceIdentity, now time.Time) error {
err := verifyCapturedSource(root, snapshotID, source)
if err == nil {
return nil
}
// 快照本身已产生且字节完好,错的只是 worker 读到的来源身份;因此恢复路径是
// "修配置后重新登记/重新封存",不是"重采数据"。原错原样写进 stage、job 与
// 返回值:改写成更友好的文案会让真正的配置漂移查不出来。
libraryStageRecorder{store: store, jobID: jobID}.Stage(ctx, "export", "failed", err.Error())
_ = store.AdvanceLibraryJob(ctx, jobID, governance.LibraryJobFailed, "export", err.Error(),
"快照已产生但来源身份与配置不一致,未登记为测试封存;修正来源配置后重新封存", "", "", now)
return err
}