Skip to content

Commit 730cdfc

Browse files
committed
fix(webui): Matcher 耗时改用 matcher 属性记录起始时间
ContextVar 在 NoneBot task group 子任务内无法回写父任务,导致单次耗时统计失效。
1 parent d2de4c3 commit 730cdfc

2 files changed

Lines changed: 66 additions & 9 deletions

File tree

src/plugins/pallas_webui/extended_api.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
import asyncio
6-
import contextvars
76
import copy
87
import json
98
import os
@@ -149,10 +148,8 @@ def _is_console_stats_excluded_plugin(plugin: str) -> bool:
149148
return bool(key) and key in resolve_console_stats_excluded_plugin_names()
150149

151150

152-
_MATCHER_RUN_STARTED: contextvars.ContextVar[float | None] = contextvars.ContextVar(
153-
"matcher_run_started",
154-
default=None,
155-
)
151+
# NoneBot run_preprocessor 在 task group 子任务内执行,ContextVar 无法回写到父任务。
152+
_MATCHER_RUN_STARTED_ATTR = "_pallas_matcher_run_started_pc"
156153
_MATCHER_ERROR_LOG_CAP = 80
157154
_MATCHER_DURATION_LOG_CAP = 150
158155
_MATCHER_DURATION_LOG_PER_PLUGIN_CAP = 30
@@ -2110,6 +2107,19 @@ def _duration_ms_float(value: object) -> float:
21102107
return 0.0
21112108

21122109

2110+
def mark_matcher_run_started(matcher: object) -> None:
2111+
setattr(matcher, _MATCHER_RUN_STARTED_ATTR, time.perf_counter())
2112+
2113+
2114+
def take_matcher_run_started(matcher: object) -> float | None:
2115+
started = getattr(matcher, _MATCHER_RUN_STARTED_ATTR, None)
2116+
if hasattr(matcher, _MATCHER_RUN_STARTED_ATTR):
2117+
delattr(matcher, _MATCHER_RUN_STARTED_ATTR)
2118+
if isinstance(started, (int, float)):
2119+
return float(started)
2120+
return None
2121+
2122+
21132123
def _matcher_elapsed_ms(started: float | None) -> float:
21142124
"""Matcher 墙钟耗时(毫秒,保留 _MATCHER_DURATION_MS_DECIMALS 位小数)。"""
21152125
if started is None:
@@ -2813,7 +2823,7 @@ async def _mark_plugin_matcher_run_start(
28132823
sid = str(getattr(bot, "self_id", "") or "").strip()
28142824
if not sid:
28152825
return
2816-
_MATCHER_RUN_STARTED.set(time.perf_counter())
2826+
mark_matcher_run_started(matcher)
28172827

28182828
@run_postprocessor
28192829
async def _count_plugin_matcher_run(
@@ -2828,7 +2838,7 @@ async def _count_plugin_matcher_run(
28282838
sid = str(getattr(bot, "self_id", "") or "").strip()
28292839
if not sid:
28302840
return
2831-
started = _MATCHER_RUN_STARTED.get()
2841+
started = take_matcher_run_started(matcher)
28322842
elapsed_ms = _matcher_elapsed_ms(started)
28332843
try:
28342844
row = _plugin_run_plugin_row(sid, plugin)
@@ -2853,8 +2863,6 @@ async def _count_plugin_matcher_run(
28532863
)
28542864
except Exception: # noqa: BLE001
28552865
pass
2856-
finally:
2857-
_MATCHER_RUN_STARTED.set(None)
28582866

28592867

28602868
def _plugin_run_stats_bot_row(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import asyncio
2+
import time
3+
4+
import anyio
5+
6+
from src.plugins.pallas_webui.extended_api import (
7+
_matcher_elapsed_ms,
8+
mark_matcher_run_started,
9+
take_matcher_run_started,
10+
)
11+
12+
13+
class FakeMatcher:
14+
pass
15+
16+
17+
def test_matcher_run_started_survives_anyio_task_group_child():
18+
"""NoneBot 在 task group 子任务里跑 preprocessor,计时须挂在 matcher 实例上。"""
19+
20+
async def run() -> float | None:
21+
matcher = FakeMatcher()
22+
23+
async def child_mark() -> None:
24+
mark_matcher_run_started(matcher)
25+
26+
async with anyio.create_task_group() as tg:
27+
tg.start_soon(child_mark)
28+
await asyncio.sleep(0.002)
29+
return take_matcher_run_started(matcher)
30+
31+
started = asyncio.run(run())
32+
assert started is not None
33+
assert _matcher_elapsed_ms(started) >= 1.0
34+
35+
36+
def test_take_matcher_run_started_clears_attr():
37+
matcher = FakeMatcher()
38+
mark_matcher_run_started(matcher)
39+
assert take_matcher_run_started(matcher) is not None
40+
assert take_matcher_run_started(matcher) is None
41+
42+
43+
def test_matcher_elapsed_ms_none_is_zero():
44+
assert _matcher_elapsed_ms(None) == 0.0
45+
46+
47+
def test_matcher_elapsed_ms_positive():
48+
started = time.perf_counter() - 0.01
49+
assert _matcher_elapsed_ms(started) >= 9.0

0 commit comments

Comments
 (0)