|
|
@@ -469,6 +469,22 @@ public class S8WatchSchedulerService : ITransient
|
|
|
|
|
|
foreach (var rule in rules.OrderBy(x => x.Id))
|
|
|
{
|
|
|
+ // S8-RUN-ONCE-LEASE-AWARENESS-1:debug run-once 不持 lease,但若该 rule 已被 Scheduler Job
|
|
|
+ // 通过 PickReadyRulesAsync 抢锁(lock_token 非空且 lock_until > now),run-once 跳过该 rule,
|
|
|
+ // 不写 last_*、不写 detection_log、不动锁,以避免与 Job tick 并发评估同一 rule 而互相覆盖运行态。
|
|
|
+ // 锁状态用 ToListAsync 的初始快照判定;run-once 与 Job 的微秒级竞速无法在不抢 lease 的前提下
|
|
|
+ // 完全消除(已登记为 follow-up 风险)。
|
|
|
+ if (!string.IsNullOrEmpty(rule.LockToken)
|
|
|
+ && rule.LockUntil.HasValue
|
|
|
+ && rule.LockUntil.Value > DateTime.Now)
|
|
|
+ {
|
|
|
+ _logger.LogInformation(
|
|
|
+ "run_once_skip_locked ruleId={RuleId} ruleCode={RuleCode} ruleType={RuleType} lockToken={Token} lockedBy={By} lockUntil={Until}",
|
|
|
+ rule.Id, rule.RuleCode, ruleType, rule.LockToken, rule.LockedBy, rule.LockUntil);
|
|
|
+ aggregate.Add(BuildSkipResult(rule, "rule_locked_by_scheduler", null));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
|
|
S8RuleRunResult completion;
|
|
|
try
|