S8AuthorityHealth.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.Health;
  2. /// <summary>
  3. /// Authority 健康状态。
  4. ///
  5. /// <para><b>只有三个状态,且 HEALTHY_EMPTY 不在其中</b>:
  6. /// 「Authority 可信但本轮确实一行都没有」是 <c>HEALTHY</c> 与 <c>rows.Count == 0</c> 的
  7. /// 组合观测,不是一个独立状态。把它设成状态,就必须在状态机里读行数 ——
  8. /// 而本门禁存在的全部意义,正是让恢复判定<b>与行数解耦</b>。</para>
  9. /// </summary>
  10. public static class S8AuthorityHealthState
  11. {
  12. /// <summary>本轮取数可信:生产者最近一次成功、够新、无更新的失败/在途运行、快照不变量成立。与返回了几行无关。</summary>
  13. public const string Healthy = "HEALTHY";
  14. /// <summary>已知不可信:生产失败 / 在途 / 超期 / 快照不变量被破坏。运维动作 = 去修管线。</summary>
  15. public const string Degraded = "DEGRADED";
  16. /// <summary>无法建立可信性:从未成功、无 AuthoritySpec、发布无法确认、探测自身失败。运维动作 = 去查这条链是否跑起来过。</summary>
  17. public const string Unknown = "UNKNOWN";
  18. }
  19. /// <summary>健康判定的原因码。回执与 detection_log 用它解释「为什么本轮没做恢复」。</summary>
  20. public static class S8AuthorityHealthReason
  21. {
  22. public const string Healthy = "HEALTHY";
  23. /// <summary>该数据集没有声明 AuthoritySpec —— 无从判断,保守拦截。</summary>
  24. public const string NoAuthoritySpec = "NO_AUTHORITY_SPEC";
  25. /// <summary>回溯窗口内没有任何一次成功的生产运行。含「从未同步」与「停摆超过回溯窗口」两种。</summary>
  26. public const string NoSuccessfulRun = "NO_SUCCESSFUL_RUN";
  27. /// <summary>
  28. /// 回溯窗口内有成功运行,但没有一次带着<b>发布证据</b>(<c>summary_json.publish</c>)。
  29. ///
  30. /// <para>只对 <c>RequiresPublicationEvidence</c> 的数据集适用。它与
  31. /// <see cref="NoSuccessfulRun"/> 是两件事:跑批成功了,但运行日志证明不了
  32. /// 「当前快照」是哪一批 —— 这时任何按时间兜底挑出来的快照都可能是上一轮的。
  33. /// 保守判 UNKNOWN,让 Wave2 闸门拦下恢复。</para>
  34. /// </summary>
  35. public const string NoPublication = "NO_PUBLICATION";
  36. /// <summary>最近一次成功距今超过 StaleWindow。</summary>
  37. public const string Stale = "STALE";
  38. /// <summary>存在比最近一次成功更新、且尚未终态的生产运行,年龄仍在 StaleWindow 内(正常跑批中)。</summary>
  39. public const string InFlight = "IN_FLIGHT";
  40. /// <summary>同上,但年龄已超过 StaleWindow —— 大概率是永不回收的孤儿 RUNNING 行。</summary>
  41. public const string StuckRun = "STUCK_RUN";
  42. /// <summary>最近一次成功之后出现了终态失败。</summary>
  43. public const string LatestFailed = "LATEST_FAILED";
  44. /// <summary>最近一次成功之后出现了终态 PARTIAL。</summary>
  45. public const string LatestPartial = "LATEST_PARTIAL";
  46. /// <summary>Authority 的快照不变量被破坏(如同时存在多个当前批次、当前快照里有必填列为空)。</summary>
  47. public const string SnapshotInvariantFailed = "SNAPSHOT_INVARIANT_FAILED";
  48. /// <summary>
  49. /// 快照型 Authority 无法确认「本轮发布是否成功」。
  50. /// <para>这是 Rule03 的现状:合法空快照与发布失败在库里字面等同(都表现为 0 个当前批次),
  51. /// 生产侧不留任何 batch marker。保守判 UNKNOWN,不猜。</para>
  52. /// </summary>
  53. public const string SnapshotPublicationUnconfirmed = "SNAPSHOT_PUBLICATION_UNCONFIRMED";
  54. /// <summary>Authority 存在多个写入方且部分不留运行日志,run-log 无法证明最终 provenance(Rule01)。</summary>
  55. public const string Rule01MultiWriterUntrusted = "RULE01_MULTI_WRITER_UNTRUSTED";
  56. /// <summary>健康探测自身抛错。fail-safe:判 UNKNOWN、拦截恢复,绝不当成 HEALTHY。</summary>
  57. public const string ResolverFailed = "RESOLVER_FAILED";
  58. }
  59. /// <summary>健康判定结果。<b>刻意不是 bool</b> —— 回执必须能回答「为什么」。</summary>
  60. public sealed class S8AuthorityHealthResult
  61. {
  62. public string DatasetCode { get; init; } = string.Empty;
  63. public long TenantId { get; init; }
  64. /// <summary>见 <see cref="S8AuthorityHealthState"/>。</summary>
  65. public string State { get; init; } = S8AuthorityHealthState.Unknown;
  66. /// <summary>见 <see cref="S8AuthorityHealthReason"/>。</summary>
  67. public string ReasonCode { get; init; } = S8AuthorityHealthReason.NoAuthoritySpec;
  68. /// <summary>人类可读说明,带上判定用到的实际数值。</summary>
  69. public string Reason { get; init; } = string.Empty;
  70. public DateTime ObservedAt { get; init; }
  71. /// <summary>最近一次成功生产运行的结束时刻;无则为 null。</summary>
  72. public DateTime? LastSuccessAt { get; init; }
  73. /// <summary>超过该时刻即判 STALE(= LastSuccessAt + StaleWindow);LastSuccessAt 为 null 时为 null。</summary>
  74. public DateTime? StaleAfter { get; init; }
  75. /// <summary>
  76. /// 是否允许执行恢复判定。<b>只有 HEALTHY 放行</b>。
  77. /// <para>注意这里没有、也不允许有任何 rows.Count 分支:
  78. /// 「Authority 可信 + 本轮 0 行」必须放行恢复,那是合法的业务全退场。</para>
  79. /// </summary>
  80. public bool AllowsRecovery => string.Equals(State, S8AuthorityHealthState.Healthy, StringComparison.Ordinal);
  81. }