fix(fr007-cache): 直查命中失效快照+去冗余排序+重载失败保旧快照防重试风暴

- EodPriceQueryService.TryGetPrice: FR007 快照 miss 直查库命中后 Invalidate,
  下次访问整表重载回 O(1),否则该键 TTL 窗口内每次读取都退化为点查
- 移除点查多余的 OrderByDescending(主键等值查询取首行即可)
- Fr007FixingCache.EnsureFresh: 重载失败(DB抖动)保留旧快照不抛并重置TTL时钟
  ——历史定盘不可变旧值依旧正确,同时防重试风暴;版本失效路径不受TTL掩盖
- 补两测试: 重载失败保快照窗口内不重试 / 首次装载失败miss不抛窗口内不重试
This commit is contained in:
hjhan
2026-08-18 09:33:28 +08:00
parent e219bdae18
commit 7a5089555d
3 changed files with 56 additions and 6 deletions
@@ -101,5 +101,37 @@ namespace YLErp.Modules.DataProviderModule
Assert.AreEqual(1, _loadCount, "稳态窗口内多次读取零重载(零查询)");
}
[TestMethod]
public void TTL过期重载失败_保留旧快照不抛_且窗口内不重试()
{
Fr007FixingCache.NowTicks = () => 1_000_000L;
Assert.IsTrue(Fr007FixingCache.TryGet(D1, out _), "先成功装载一次");
Assert.AreEqual(1, _loadCount);
Fr007FixingCache.LoadSnapshot = () => { _loadCount++; throw new InvalidOperationException("db down"); };
Fr007FixingCache.NowTicks = () => 1_000_000L + TimeSpan.FromMinutes(1).Ticks + 1; // TTL 过期 → 触发重载 → 失败
// 不抛:历史定盘不可变,命中继续走旧快照(异常会直接 fail 本用例)
Assert.IsTrue(Fr007FixingCache.TryGet(D1, out var p), "重载失败应保留旧快照继续命中");
Assert.AreEqual(0.0142, p, 1e-12, "旧快照值不变");
Assert.AreEqual(2, _loadCount, "失败的重载尝试恰好一次");
Fr007FixingCache.TryGet(D2, out _); // TTL 时钟已被重置:窗口内不再重试(防重试风暴)
Assert.AreEqual(2, _loadCount, "失败后TTL窗口内不得反复重试全表SELECT");
}
[TestMethod]
public void _miss不抛_窗口内不重试()
{
Fr007FixingCache.LoadSnapshot = () => { _loadCount++; throw new InvalidOperationException("db down"); };
Fr007FixingCache.NowTicks = () => 1_000_000L;
Assert.IsFalse(Fr007FixingCache.TryGet(D1, out _), "装载失败=空快照miss(调用方直查库兜底,新数据该响仍响)");
Assert.AreEqual(1, _loadCount);
Fr007FixingCache.TryGet(D1, out _);
Assert.AreEqual(1, _loadCount, "失败后TTL窗口内不重试");
}
}
}