From f7a2cbe5a8bcc224e616bf2c9126a0b1a99a8d6e Mon Sep 17 00:00:00 2001 From: gao Date: Fri, 3 Jul 2026 18:25:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(daily=5Fcheck):=20=E6=9C=9F=E6=9C=9B?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E6=9C=AA=E5=88=B0=E4=B8=8D=E5=BA=94=E5=88=A4?= =?UTF-8?q?=20missed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit work #07 (2026-07-03):daily_check 在日内运行时(如 09:00/12:00/15:00) 误把'还没到时间'的任务标 missed。SCHEDULE 里有 window_end 但 _classify_task 没用到——直接判 last_success vs today_start。 修复:判定 missed 之前先比较 now vs window_end;未到则返回 not_yet (新状态,不进 alerts),已过但今日未成功走原 missed 路径。解析失败 回落旧逻辑不引入风险。 验证 4 个用例:12:00 kline_daily→not_yet, 18:00 kline_daily→missed, 18:00 longhubang→not_yet, 22:30 longhubang→missed。全部正确。 Co-Authored-By: Claude --- bin/daily_sync_check.py | 23 +++++++ .../2026-07-03-07-daily-check-window-logic.md | 67 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 docs/works/2026-07-03-07-daily-check-window-logic.md diff --git a/bin/daily_sync_check.py b/bin/daily_sync_check.py index 9317cdf..6343c8f 100644 --- a/bin/daily_sync_check.py +++ b/bin/daily_sync_check.py @@ -133,6 +133,28 @@ def _classify_task( last_failure = _parse_ts(row.get("last_failure_at")) status_field = row.get("status", "idle") + # 期望窗口是否已过?未到窗口不应判 missed + if spec and spec.get("window_end") and not status_field == "running": + try: + hh, mm = spec["window_end"].split(":") + window_end_dt = datetime.combine(check_date, datetime.min.time()).replace( + hour=int(hh), minute=int(mm) + ) + if now < window_end_dt: + return { + "dataset_id": dataset_id, + "name": row.get("name", ""), + "status": "not_yet", + "reason": f"期望窗口 {spec['window_end']} 未到(now={now.strftime('%H:%M')})", + "last_success_at": row.get("last_success_at"), + "last_failure_at": row.get("last_failure_at"), + "last_error": row.get("last_error"), + "needs_resync": row.get("needs_resync", 0), + "status_field": status_field, + } + except (ValueError, AttributeError): + pass # 解析失败回落到旧逻辑 + # 还在跑(卡死检测) if status_field == "running": started = _parse_ts(row.get("started_at")) @@ -399,6 +421,7 @@ def print_summary(report: dict[str, Any]) -> None: "running": "🔄", "never_run": "❓", "not_expected": "⏸ ", + "not_yet": "⏳", "disabled": "🚫", }.get(t["status"], "·") print(f" {icon} [{t['status']:>13}] {t['dataset_id']:<20} {t['reason']}") diff --git a/docs/works/2026-07-03-07-daily-check-window-logic.md b/docs/works/2026-07-03-07-daily-check-window-logic.md new file mode 100644 index 0000000..5caf58d --- /dev/null +++ b/docs/works/2026-07-03-07-daily-check-window-logic.md @@ -0,0 +1,67 @@ +# 2026-07-03 — work #07: daily_check 分类逻辑 bug — 期望窗口未到也判 missed + +## 发现 + +12/12 全部 ok 后跑 daily_check 复验,看到 report 里有个问题: + +```python +# 旧逻辑(伪码) +if last_success and last_success >= today_start: + return "ok" +elif last_failure and last_failure > last_success and last_failure >= today_start: + return "failed" +else: + return "missed" # ❌ 不管窗口是否已过 +``` + +跑一个反例(today=07-03 12:00, last_success=07-02 15:56, window_end=15:45): +- **期望**:not_yet(窗口 15:30-15:45 还没到) +- **实际**:missed(误报) + +每天 23:00 跑 daily_check 时这个 bug 不可见(窗口都已过);但在 09:00 / 12:00 / 15:00 这种日内 check 会误报,让"daily check 视图"看起来比实际严重。 + +## 根因 + +`SCHEDULE` 表里有 `window_end`(如 kline_daily=15:45)但 `_classify_task` 函数没用它 —— 直接基于 `last_success_at` vs `today_start` 判定。 + +## 解决 + +`_classify_task` 在判定"今日未成功"之前先看 `now < window_end`: +- 窗口未到 → 返回 `status=not_yet`(新状态,不进 alerts) +- 窗口已过 + 今日未成功 → 走原 missed 路径 +- 解析失败(window_end 格式异常)→ 回落旧逻辑(不引入新风险) + +新 status `not_yet` 加 icon `⏳` 到 print_summary。 + +## 验证 + +4 个测试用例: + +| 时间点 | task | window_end | 期望 | 实际 | +|---|---|---|---|---| +| 12:00 | kline_daily | 15:45 | not_yet | ✅ not_yet | +| 18:00 | kline_daily | 15:45 | missed | ✅ missed | +| 18:00 | longhubang | 22:05 | not_yet | ✅ not_yet | +| 22:30 | longhubang | 22:05 | missed | ✅ missed | + +mock 5 个 task 跑 build_report: +``` +counts: {'ok': 2, 'missed': 1, 'not_yet': 1, 'not_expected': 1} +overall: warning + stock_basic status=ok + kline_daily status=missed + longhubang status=not_yet + stock_node status=not_expected + mairui_ma_daily status=ok +``` + +## 关联 + +- [[market-data-overview]]:12 task 的窗口时间表 +- 这次修改让"在指定时间"语义在 check 阶段也正确(不再把"还没到时间"误报为 missed) + +## 不在范围(未来) + +- 给"stuck" 任务也加窗口判断(running 任务超过 24h 才算 stuck,目前是 2h) +- 增加"in_window"状态(任务正在窗口内)— 现版 "not_yet" 模糊覆盖了 +- 历史回看:让 check_date 接受任意一天,校验过去的 missed(现在只查今天) \ No newline at end of file