namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.Health; /// /// Authority 健康状态。 /// /// 只有三个状态,且 HEALTHY_EMPTY 不在其中: /// 「Authority 可信但本轮确实一行都没有」是 HEALTHYrows.Count == 0 的 /// 组合观测,不是一个独立状态。把它设成状态,就必须在状态机里读行数 —— /// 而本门禁存在的全部意义,正是让恢复判定与行数解耦 /// public static class S8AuthorityHealthState { /// 本轮取数可信:生产者最近一次成功、够新、无更新的失败/在途运行、快照不变量成立。与返回了几行无关。 public const string Healthy = "HEALTHY"; /// 已知不可信:生产失败 / 在途 / 超期 / 快照不变量被破坏。运维动作 = 去修管线。 public const string Degraded = "DEGRADED"; /// 无法建立可信性:从未成功、无 AuthoritySpec、发布无法确认、探测自身失败。运维动作 = 去查这条链是否跑起来过。 public const string Unknown = "UNKNOWN"; } /// 健康判定的原因码。回执与 detection_log 用它解释「为什么本轮没做恢复」。 public static class S8AuthorityHealthReason { public const string Healthy = "HEALTHY"; /// 该数据集没有声明 AuthoritySpec —— 无从判断,保守拦截。 public const string NoAuthoritySpec = "NO_AUTHORITY_SPEC"; /// 回溯窗口内没有任何一次成功的生产运行。含「从未同步」与「停摆超过回溯窗口」两种。 public const string NoSuccessfulRun = "NO_SUCCESSFUL_RUN"; /// /// 回溯窗口内有成功运行,但没有一次带着发布证据summary_json.publish)。 /// /// 只对 RequiresPublicationEvidence 的数据集适用。它与 /// 是两件事:跑批成功了,但运行日志证明不了 /// 「当前快照」是哪一批 —— 这时任何按时间兜底挑出来的快照都可能是上一轮的。 /// 保守判 UNKNOWN,让 Wave2 闸门拦下恢复。 /// public const string NoPublication = "NO_PUBLICATION"; /// 最近一次成功距今超过 StaleWindow。 public const string Stale = "STALE"; /// 存在比最近一次成功更新、且尚未终态的生产运行,年龄仍在 StaleWindow 内(正常跑批中)。 public const string InFlight = "IN_FLIGHT"; /// 同上,但年龄已超过 StaleWindow —— 大概率是永不回收的孤儿 RUNNING 行。 public const string StuckRun = "STUCK_RUN"; /// 最近一次成功之后出现了终态失败。 public const string LatestFailed = "LATEST_FAILED"; /// 最近一次成功之后出现了终态 PARTIAL。 public const string LatestPartial = "LATEST_PARTIAL"; /// Authority 的快照不变量被破坏(如同时存在多个当前批次、当前快照里有必填列为空)。 public const string SnapshotInvariantFailed = "SNAPSHOT_INVARIANT_FAILED"; /// /// 快照型 Authority 无法确认「本轮发布是否成功」。 /// 这是 Rule03 的现状:合法空快照与发布失败在库里字面等同(都表现为 0 个当前批次), /// 生产侧不留任何 batch marker。保守判 UNKNOWN,不猜。 /// public const string SnapshotPublicationUnconfirmed = "SNAPSHOT_PUBLICATION_UNCONFIRMED"; /// Authority 存在多个写入方且部分不留运行日志,run-log 无法证明最终 provenance(Rule01)。 public const string Rule01MultiWriterUntrusted = "RULE01_MULTI_WRITER_UNTRUSTED"; /// 健康探测自身抛错。fail-safe:判 UNKNOWN、拦截恢复,绝不当成 HEALTHY。 public const string ResolverFailed = "RESOLVER_FAILED"; } /// 健康判定结果。刻意不是 bool —— 回执必须能回答「为什么」。 public sealed class S8AuthorityHealthResult { public string DatasetCode { get; init; } = string.Empty; public long TenantId { get; init; } /// public string State { get; init; } = S8AuthorityHealthState.Unknown; /// public string ReasonCode { get; init; } = S8AuthorityHealthReason.NoAuthoritySpec; /// 人类可读说明,带上判定用到的实际数值。 public string Reason { get; init; } = string.Empty; public DateTime ObservedAt { get; init; } /// 最近一次成功生产运行的结束时刻;无则为 null。 public DateTime? LastSuccessAt { get; init; } /// 超过该时刻即判 STALE(= LastSuccessAt + StaleWindow);LastSuccessAt 为 null 时为 null。 public DateTime? StaleAfter { get; init; } /// /// 是否允许执行恢复判定。只有 HEALTHY 放行。 /// 注意这里没有、也不允许有任何 rows.Count 分支: /// 「Authority 可信 + 本轮 0 行」必须放行恢复,那是合法的业务全退场。 /// public bool AllowsRecovery => string.Equals(State, S8AuthorityHealthState.Healthy, StringComparison.Ordinal); }