refactor(discord): state the dispatch-liveness rationale once
The #109521 explanation (socket_event_type fires for every parsed DISPATCH frame and is not debug-gated unlike on_socket_raw_receive; op-11 ACKs carry no event type) was written out four times: knob init, stamp init, the on_socket_event_type handler, and _read_websocket_health. Keep the authoritative paragraph on the handler that actually stamps, and point the other three sites at it so a future edit has one place to go. Drop the math.isfinite(event_silence) half of the silence guard. Both operands are our own perf_counter floats, so their difference cannot be non-finite; the ack_age guard is different because _last_ack comes from discord.py. math stays imported for the remaining finiteness checks.
This commit is contained in:
@@ -1071,16 +1071,11 @@ class DiscordAdapter(DiscordMediaMixin, BasePlatformAdapter):
|
||||
self._max_latency_seconds = self._finite_positive_config_float(
|
||||
"websocket_max_latency_seconds", 30.0,
|
||||
)
|
||||
# Dispatch-side liveness (#109521 incident 2): an ESTAB socket can keep ACKing
|
||||
# heartbeats (op 11, no event type) while zero DISPATCH events are parsed, so every
|
||||
# transport-side sample reads healthy for hours. ``socket_event_type`` fires for every
|
||||
# parsed DISPATCH frame and is NOT gated behind ``enable_debug_events`` (unlike
|
||||
# ``on_socket_raw_receive`` — verified against discord.py 2.7.1 ``gateway.py``:
|
||||
# ``received_message`` calls ``self._dispatch('socket_event_type', event)`` before the
|
||||
# op-code switch). 0 disables this dimension alone; ack-age/latency still guard.
|
||||
# Default 4h mirrors the field-proven operator bound from the incident report; quiet
|
||||
# guilds can go hours without a single DISPATCH event (typing/reaction/presence), so
|
||||
# a short bound would force reconnect loops on healthy-but-idle installs (#109782).
|
||||
# Dispatch-side liveness bound (#109521; rationale on ``on_socket_event_type``).
|
||||
# 0 disables this dimension alone; ack-age/latency still guard. Default 4h mirrors the
|
||||
# field-proven operator bound from the incident report; quiet guilds can go hours
|
||||
# without a single DISPATCH event, so a short bound would force reconnect loops on
|
||||
# healthy-but-idle installs (#109782).
|
||||
self._event_max_silence_seconds = self._finite_positive_config_float(
|
||||
"websocket_event_max_silence_seconds", 14400.0,
|
||||
)
|
||||
@@ -1088,10 +1083,9 @@ class DiscordAdapter(DiscordMediaMixin, BasePlatformAdapter):
|
||||
self._liveness_notification_task: Optional[asyncio.Task] = None
|
||||
# True while disconnect() intentionally closes discord.py (done callback: shutdown vs crash).
|
||||
self._disconnecting = False
|
||||
# Last DISPATCH frame's monotonic stamp (#109521): ticked by ``on_socket_event_type``
|
||||
# (fires for every parsed DISPATCH event, not debug-gated) and read by the liveness
|
||||
# probe's ``event_silence`` dimension. ``None`` means "no event yet on this connection"
|
||||
# and is not treated as silence (on_ready often arrives in bursts).
|
||||
# Last DISPATCH frame's monotonic stamp, ticked by ``on_socket_event_type`` (see its
|
||||
# rationale) and read by ``_read_websocket_health``. ``None`` = no event yet on this
|
||||
# connection, which is not silence.
|
||||
self._last_dispatched_event_monotonic: Optional[float] = None
|
||||
self._missed_message_backfill_task: Optional[asyncio.Task] = None
|
||||
from hermes_constants import get_hermes_home
|
||||
@@ -1287,11 +1281,15 @@ class DiscordAdapter(DiscordMediaMixin, BasePlatformAdapter):
|
||||
|
||||
@self._client.event
|
||||
async def on_socket_event_type(event_type: str):
|
||||
# Dispatch-side liveness stamp (#109521): discord.py dispatches this for every
|
||||
# parsed DISPATCH frame on every connection — no ``enable_debug_events`` needed
|
||||
# (unlike ``on_socket_raw_receive``). Heartbeat ACKs (op 11) return before the
|
||||
# dispatch, so an ACKing-but-deaf socket leaves this stamp frozen while every
|
||||
# transport-side check reads healthy.
|
||||
# Dispatch-side liveness stamp (#109521 incident 2): an ESTAB socket can keep
|
||||
# ACKing heartbeats (op 11, no event type) while zero DISPATCH events are parsed,
|
||||
# so every transport-side sample reads healthy for hours. discord.py dispatches
|
||||
# ``socket_event_type`` for every parsed DISPATCH frame on every connection and it
|
||||
# is NOT gated behind ``enable_debug_events`` (unlike ``on_socket_raw_receive`` —
|
||||
# verified against discord.py 2.7.1 ``gateway.py``: ``received_message`` calls
|
||||
# ``self._dispatch('socket_event_type', event)`` before the op-code switch).
|
||||
# Heartbeat ACKs return before that dispatch, so an ACKing-but-deaf socket leaves
|
||||
# this stamp frozen while every transport-side check reads healthy.
|
||||
adapter_self._last_dispatched_event_monotonic = time.perf_counter()
|
||||
|
||||
@self._client.event
|
||||
@@ -1693,12 +1691,14 @@ class DiscordAdapter(DiscordMediaMixin, BasePlatformAdapter):
|
||||
# explicit 0 disables this dimension alone and ack-age/latency keep guarding (the #109782
|
||||
# regression put the knob in the probe's all-or-nothing startup guard, killing the whole
|
||||
# watchdog). ``None`` = no DISPATCH event yet on this connection: not silence (the
|
||||
# not_ready check above still covers the pre-ready window).
|
||||
# not_ready check above still covers the pre-ready window). Why the stamp is trustworthy:
|
||||
# see ``on_socket_event_type``. No finiteness guard here: both operands are our own
|
||||
# perf_counter floats (``ack_age`` differs — ``_last_ack`` is discord.py's).
|
||||
if self._event_max_silence_seconds > 0:
|
||||
last_event = self._last_dispatched_event_monotonic
|
||||
if last_event is not None:
|
||||
event_silence = time.perf_counter() - last_event
|
||||
if not math.isfinite(event_silence) or event_silence > self._event_max_silence_seconds:
|
||||
if event_silence > self._event_max_silence_seconds:
|
||||
return False, "event_silence"
|
||||
return True, "healthy"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user