feat(faultguard): 单维度 tick 工作耗时连续超 300ms 自动关细粒度

- FaultGuard 新增 onTickSlow(double workMs):连续 3 次 > SLOW_TICK_MS(300ms)
  关闭细粒度并行(保留维度并行),复用 fineDegraded 通道;恢复靠手动
  /dimensionalripper fine on(setFine 内 FaultGuard.reset 会清计数)。
- LevelTickLoop.runIfCurrent finally:成功完成后上报本次维度纯工作耗时
  (workNanos[0],不含收口等待)到 FaultGuard.onTickSlow。
- reset() 增加 slowTicks 清零。

背景:MSPT 爆高时细粒度并行放大单 tick 内调度/锁竞争开销,先关它把 tick
压回可控范围,避免拖成硬超时/假死。关闭安全:子任务在独立池内,不影响
维度 tick 收口,保留维度所有权。
This commit is contained in:
Xiao-no-love committed 2026-10-13 21:51:59 +08:00
1 parent 1702d11d8f
commit c24b35640b
2 files changed
+51

No files matched your search

@@ -26,6 +26,10 @@ public final class FaultGuard {
private static final int AWAIT_TIMEOUT_THRESHOLD = 3;
/** 看门狗:维度 tick 最晚完成时限(主线程等待上限)。 */
public static final long AWAIT_TIMEOUT_MS = 30_000L;
/** 单维度 tick 工作耗时超过该值(毫秒)视为一次"慢 tick"。 */
public static final double SLOW_TICK_MS = 300.0;
/** 连续慢 tick 达到该次数,自动关闭细粒度并行(保留维度并行)。 */
private static final int SLOW_TICK_THRESHOLD = 3;
/** 细粒度是否已被自动关闭。 */
private static volatile boolean fineDegraded = false;
@@ -37,6 +41,7 @@ public final class FaultGuard {
private static int fineFailures;
private static int dimFailures;
private static int awaitTimeouts;
private static int slowTicks;
private FaultGuard() {
}
@@ -94,6 +99,47 @@ public final class FaultGuard {
FineGrainScheduler.setEnabled(false);
}
/**
* 上报一次维度 tick 的工作耗时(毫秒)。连续 {@link #SLOW_TICK_THRESHOLD} 次超过
* {@link #SLOW_TICK_MS} 时,自动关闭细粒度并行(保留维度并行)。
* <p>
* 与异常/超时通道的区别:这是"慢但没崩"的软故障。细粒度并行会放大单 tick 内的
* 调度/锁竞争开销,MSPT 爆高时先关它能把 tick 耗时压回可控范围,避免拖成硬超时。
* 关闭是安全的:细粒度子任务在独立池内,不影响维度 tick 收口,保留维度所有权。
* <p>
* 计数器为连续语义:一次正常(<= 阈值)会清零,容忍偶发尖峰。
*
* @param workMs 本次维度 tick 的纯工作耗时(不含收口等待);< 0 或非有限值忽略
*/
public static synchronized void onTickSlow(double workMs) {
if (fineDegraded) {
return;
}
if (!Double.isFinite(workMs) || workMs < 0) {
return;
}
if (workMs <= SLOW_TICK_MS) {
// 恢复正常:清零连续慢 tick 计数
slowTicks = 0;
return;
}
slowTicks++;
ModZuozhi.LOGGER.warn("[FaultGuard] 维度 tick 工作耗时 {} ms 超过 {} ms(第 {} 次)",
String.format(java.util.Locale.ROOT, "%.1f", workMs), SLOW_TICK_MS, slowTicks);
if (slowTicks >= SLOW_TICK_THRESHOLD) {
fineDegraded = true;
reason = "维度 tick 连续 " + slowTicks + " 次超过 " + (long) SLOW_TICK_MS
+ " ms,已自动关闭细粒度并行(保留维度并行),手动 /dimensionalripper fine on 可恢复";
ModZuozhi.LOGGER.error("[FaultGuard] {}", reason);
FineGrainScheduler.setEnabled(false);
}
}
/** 当前连续慢 tick 计数(status 诊断用)。 */
public static synchronized int slowTickCount() {
return slowTicks;
}
/**
* 看门狗:主线程等待维度 tick 完成后上报结果。
*
@@ -148,6 +194,7 @@ public final class FaultGuard {
fineFailures = 0;
dimFailures = 0;
awaitTimeouts = 0;
slowTicks = 0;
ModZuozhi.LOGGER.info("[FaultGuard] 降级状态已重置");
}
}
@@ -229,6 +229,10 @@ public final class LevelTickLoop implements Runnable, java.util.concurrent.Execu
} finally {
ThreadOwnershipDiagnostics.phase(this.level, "idle");
progress.finish(System.nanoTime(), workNanos[0], completed[0]);
// 慢 tick 保底:单维度工作耗时连续超阈 → FaultGuard 自动关细粒度(保留维度并行)。
if (completed[0]) {
FaultGuard.onTickSlow(workNanos[0] / 1_000_000.0);
}
synchronized (this) {
this.ticking = false;
if (this.running && !this.paused && !this.mainThreadHeld) {