0088004cd6
接管分类、基线判定、已知索引映射、同步闸口的创建判据与归档保留的保护标记, 原来都用 LibraryVersions(scope, 50/100) 这个分页窗口再在内存里筛。生效版本每 scope 只有一行、可能很久以前登记,历史一长就被挤出窗口 —— 于是"已生效"被读成 "没有生效":接管会把已经上线的目标重写一遍,基线入口会给已有权威版本的目标 再建一次基线。 现在统一走两条精确查询:LibraryCurrentVersion(kind='production_effective' AND superseded_at IS NULL,每 scope 至多一行)与 LibraryVersionOfSnapshot(按快照 身份取回"已顶替"证据)。多行、自相矛盾、读不到一律 fail closed;同一含义只留 一份实现(knownIndexMapping 同步与恢复共用)。LibraryVersions 仍是 1..100 的 展示窗口,只用于列表。 验证:真实 PG 上先复现(窗口实现 ⇒ found=false),改后 7 条 store 契约用例与 worker 端到端判定用例全绿;本机 make verify 15 项 success(用例 1264 通过 / 0 失败 / 93 已声明可选跳过)。
84 lines
3.5 KiB
Go
84 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"knowledge-governance/internal/governance"
|
|
)
|
|
|
|
// libraryVersionIndex 是"当前生效版本"与"某个快照自己的登记"的精确查询口。
|
|
//
|
|
// 为什么不是 LibraryVersions(scope, N):那是分页展示用的窗口,历史超过上限后
|
|
// "存在"会被读成"不存在"。接管/基线/同步闸口的判定都必须走这里,同一含义只有
|
|
// 一份实现(两个查询在 Store 里都是按条件精确命中,没有排序窗口)。
|
|
type libraryVersionIndex interface {
|
|
LibraryCurrentVersion(ctx context.Context, scope string) (governance.LibraryVersion, bool, error)
|
|
LibraryVersionOfSnapshot(ctx context.Context, scope string, kind governance.LibraryVersionKind,
|
|
snapshotID string) (governance.LibraryVersion, bool, error)
|
|
}
|
|
|
|
// libraryVersionReader 是"当前生效版本 + 展示窗口"的组合:已知索引映射只需要这两样。
|
|
type libraryVersionReader interface {
|
|
LibraryCurrentVersion(ctx context.Context, scope string) (governance.LibraryVersion, bool, error)
|
|
LibraryVersions(ctx context.Context, scope string, limit int) ([]governance.LibraryVersion, error)
|
|
}
|
|
|
|
// libraryVersionFacts 是接管判定需要的两个事实:
|
|
// - Current/HasCurrent:这个 scope 现在生效的是哪一份(每 scope 至多一行);
|
|
// - Snapshot/HasSnapshot:本次作业的目标快照自己有没有生效登记(含"已顶替")。
|
|
//
|
|
// Err 非空表示"读不到",调用方必须按未知处理,不许降级成"没有证据"。
|
|
type libraryVersionFacts struct {
|
|
Current governance.LibraryVersion
|
|
HasCurrent bool
|
|
Snapshot governance.LibraryVersion
|
|
HasSnapshot bool
|
|
Err error
|
|
}
|
|
|
|
// loadLibraryVersionFacts 用两次精确查询拼出判定事实;任一查询失败都返回 Err。
|
|
func loadLibraryVersionFacts(ctx context.Context, store libraryVersionIndex, scope, targetSnapshotID string) libraryVersionFacts {
|
|
facts := libraryVersionFacts{}
|
|
current, hasCurrent, err := store.LibraryCurrentVersion(ctx, scope)
|
|
if err != nil {
|
|
facts.Err = fmt.Errorf("读不到当前生效版本:%w", err)
|
|
return facts
|
|
}
|
|
facts.Current, facts.HasCurrent = current, hasCurrent
|
|
if targetSnapshotID == "" {
|
|
return facts
|
|
}
|
|
snapshot, hasSnapshot, err := store.LibraryVersionOfSnapshot(ctx, scope,
|
|
governance.LibraryVersionProductionEffective, targetSnapshotID)
|
|
if err != nil {
|
|
facts.Err = fmt.Errorf("读不到快照 %s 的生效登记:%w", targetSnapshotID, err)
|
|
return facts
|
|
}
|
|
facts.Snapshot, facts.HasSnapshot = snapshot, hasSnapshot
|
|
return facts
|
|
}
|
|
|
|
// knownIndexMapping 取"上一次登记过的索引映射",作为装配目标时的映射基线。
|
|
//
|
|
// 权威来源是**当前生效版本**的登记证据;只有在它没有记录映射时,才退回到最近
|
|
// 成功记录里的那一份(恢复会把旧版本顶替掉,这时仍要拿到生产实际用的映射)。
|
|
// 退化只会让"索引被改动"判定偏严,不会误放行覆盖。同步与恢复共用这一份实现。
|
|
func knownIndexMapping(ctx context.Context, store libraryVersionReader, scope string) string {
|
|
if current, found, err := store.LibraryCurrentVersion(ctx, scope); err == nil && found {
|
|
if raw, ok := current.Evidence["index_mapping_sha256"].(string); ok && raw != "" {
|
|
return raw
|
|
}
|
|
}
|
|
versions, err := store.LibraryVersions(ctx, scope, 50)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
for _, item := range versions {
|
|
if raw, ok := item.Evidence["index_mapping_sha256"].(string); ok && raw != "" {
|
|
return raw
|
|
}
|
|
}
|
|
return ""
|
|
}
|