Commit 5b490bd9 authored by 郭阔's avatar 郭阔

fix(panel): prefer the freshest phase signal when heartbeat lags

The agent occasionally forgets to flip the .phase heartbeat on transition;
when the objective disk evidence (registry/.summaries/.manifest mtime) is
newer than the heartbeat file, trust the disk. Seen live: heartbeat stuck
at 采集中 while manifest had landed 3 minutes earlier.
parent d179c21f
...@@ -561,34 +561,11 @@ def _harvest_phase(source): ...@@ -561,34 +561,11 @@ def _harvest_phase(source):
"""返回正在运行任务的 (开始时间, 阶段名)。 """返回正在运行任务的 (开始时间, 阶段名)。
阶段以真实信号为准,不做推测: 阶段以真实信号为准,不做推测:
1. <source>.phase 文件 —— harvest.sh 写初始"采集中",采集会话每进入新阶段 1. <source>.phase 心跳文件 —— harvest.sh 写初始"采集中",采集会话每进入
即时更新(采集中/摘要生成/推送收尾),任务退出时删除; 新阶段即时更新;仅当它不比磁盘证据旧时采信(会话偶尔忘翻牌);
2. 兜底用当日目录磁盘信号(registry/.summaries/.manifest 的 mtime); 2. 当日目录磁盘信号(registry/.summaries/.manifest 的 mtime)是客观事实;
3. 都没有 → 中性"运行中"。 3. 都没有 → 中性"运行中"。
""" """
port = _site_cdp_port(source.split("-")[0])
lockdir = WORK_DIR / "runlocks"
if not lockdir.is_dir():
return None, None
locks = list(lockdir.glob(f"port-{port}.*.lock"))
if not locks:
return None, None
try:
start = datetime.fromtimestamp(locks[0].stat().st_mtime)
except OSError:
return None, None
# 1) 真实阶段文件(进程自己写的)
phase_file = LOG_DIR / f"{source}.phase"
if phase_file.exists():
try:
txt = phase_file.read_text(encoding="utf-8").strip()
except OSError:
txt = ""
if txt:
return start, txt
# 2) 磁盘信号兜底(仍是事实,非推测)
def mtime(p): def mtime(p):
try: try:
return datetime.fromtimestamp(p.stat().st_mtime) return datetime.fromtimestamp(p.stat().st_mtime)
...@@ -603,16 +580,49 @@ def _harvest_phase(source): ...@@ -603,16 +580,49 @@ def _harvest_phase(source):
src.get("vaultFolder", "") / datetime.now().strftime("%Y%m%d") src.get("vaultFolder", "") / datetime.now().strftime("%Y%m%d")
except (StopIteration, ValueError, OSError): except (StopIteration, ValueError, OSError):
day_dir = None day_dir = None
if day_dir and day_dir.is_dir(): port = _site_cdp_port(source.split("-")[0])
lockdir = WORK_DIR / "runlocks"
if not lockdir.is_dir():
return None, None
locks = list(lockdir.glob(f"port-{port}.*.lock"))
if not locks:
return None, None
try:
start = datetime.fromtimestamp(locks[0].stat().st_mtime)
except OSError:
return None, None
# 1) 真实阶段文件(进程自己写的)——但仅当它比磁盘证据新时才采信
# (会话偶尔忘记翻牌,磁盘信号(manifest/summaries/registry)是客观事实)
def disk_phase_and_mtime():
if not (day_dir and day_dir.is_dir()):
return None, None
reg = mtime(day_dir / "registry.json") reg = mtime(day_dir / "registry.json")
if reg and reg > start: if reg and reg > start:
return start, "推送/收尾" return "推送/收尾", reg
summ = mtime(day_dir / ".summaries.json") summ = mtime(day_dir / ".summaries.json")
if summ and summ > start: if summ and summ > start:
return start, "摘要生成" return "摘要生成", summ
mani = mtime(day_dir / ".manifest.json") mani = mtime(day_dir / ".manifest.json")
if mani and mani > start: if mani and mani > start:
return start, "摘要生成" return "摘要生成", mani
return None, None
phase_file = LOG_DIR / f"{source}.phase"
hb_txt, hb_mtime = "", None
if phase_file.exists():
try:
hb_txt = phase_file.read_text(encoding="utf-8").strip()
hb_mtime = mtime(phase_file)
except OSError:
hb_txt, hb_mtime = "", None
dp, dp_mtime = disk_phase_and_mtime()
if hb_txt and hb_mtime:
if not dp or hb_mtime >= dp_mtime:
return start, hb_txt
return start, dp # 心跳比磁盘证据旧:信磁盘
if dp:
return start, dp
# 3) 无信号:中性显示,不猜 # 3) 无信号:中性显示,不猜
return start, "运行中" return start, "运行中"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment