fix(qqbot): stop 100% CPU spin when WebSocket is closed but not None (#31193, #31771) (#40574)

_read_events() returned normally when self._ws was closed-but-non-None
(the while-condition is false on entry). _listen_loop treats a normal
return as a clean read, resets backoff to 0, and immediately retries —
a tight busy-loop pinning CPU. Raising on entry routes it through the
reconnect/backoff path instead.

Co-authored-by: xushibo <xushibo@users.noreply.github.com>
Co-authored-by: cnfi <cnfi@users.noreply.github.com>
This commit is contained in:
Teknium
2026-06-06 18:44:44 -07:00
committed by GitHub
co-authored by xushibo cnfi
parent 5b55f4fe8e
commit 3eeca4613d
2 changed files with 30 additions and 0 deletions
+24
View File
@@ -2196,3 +2196,27 @@ class TestCloseCodeClassification:
assert 4014 in fatal_codes
assert 4001 in fatal_codes
assert 4915 in fatal_codes
class TestReadEventsClosedWsGuard:
"""Regression: a closed-but-non-None ws must raise on entry, not return
normally, so _listen_loop goes through reconnect/backoff instead of
busy-looping at 100% CPU (issues #31193 / #31771)."""
def _make_adapter(self, **extra):
from gateway.platforms.qqbot import QQAdapter
return QQAdapter(_make_config(app_id="a", client_secret="b", **extra))
def test_read_events_raises_when_ws_closed_on_entry(self):
adapter = self._make_adapter()
adapter._running = True
adapter._ws = SimpleNamespace(closed=True)
with pytest.raises(RuntimeError):
asyncio.run(adapter._read_events())
def test_read_events_raises_when_ws_none(self):
adapter = self._make_adapter()
adapter._running = True
adapter._ws = None
with pytest.raises(RuntimeError):
asyncio.run(adapter._read_events())