Commit 268daf1
committed
fix(adapter): clear _pending_enqueued_at on teardown + cancellation (#33)
The Kimi adapter maintains _pending_enqueued_at as a parallel TTL dict
alongside the base class's _pending_messages and _active_sessions
(gateway/platforms/base.py). The base clears the latter two during
cancel_background_tasks (base.py:2553-2554), but our subclass's
parallel dict is untouched — entries leak across reconnects since
the gateway reuses the same adapter instance (gateway/run.py:2725-2729
calls cancel_background_tasks then disconnect on the same instance,
and the adapter is then re-used for the next connect).
Three layered guarantees close the leak:
1. cancel_background_tasks override mirrors the base's clear() —
this is the primary fix. super() runs the drain (await
asyncio.gather over _background_tasks) and clears base state;
our override then clears _pending_enqueued_at as a final sweep.
Order matters: super() first, our clear() last. Reversed, an
in-flight handler's finally block could re-insert a key after
our clear during the gather. With this order, every handler's
finally has already run and self-popped via the
"if session_key not in _pending_messages" guard.
2. disconnect() also clears as defense-in-depth for direct-
disconnect call sites that bypass cancel_background_tasks
(gateway/run.py:_safe_adapter_disconnect, the error-recovery
branch in connect()). Documented limitation: those direct paths
don't clear base _pending_messages or _active_sessions either —
pre-existing behaviour, out of scope.
3. handle_message wraps the post-super cleanup in try/finally so
any unexpected exception from super (most relevantly a
CancelledError propagating up from base.handle_message) doesn't
skip the cleanup. The cleanup guard is deterministic under
cancellation because base's _pending_messages writes happen
synchronously with no await between the write and the function
return — so the guard observes the slot in one of two known
states: empty (pop) or owned by a follow-up (preserve fresh ts).
Plus connect() clears _pending_enqueued_at at session start as
belt-and-braces against any future code path that bypasses the
teardown machinery entirely (e.g. partial-init reuse). Cheap and
idempotent.
Three-way review feedback applied:
- Codex caught that my original docstring overstated the consequence
(a stale-only timestamp can't actually evict a fresh message — the
TTL guard at line ~1247 first does _pending_messages.get and bails
if None). Reworded as "memory hygiene, not correctness".
- Claude caught that the try/finally docstring rationale was
misdirected (cancel_background_tasks cancels _background_tasks,
not direct handle_message callers). Reframed as "any unexpected
super() exception".
- Kimi audited other parallel dicts (_last_message_id_per_room,
_probe_msg_id_room_counts) and confirmed they don't need clearing
— the first persists by design as a replay-dedup anchor; the
second is debug-only and gated behind isEnabledFor(DEBUG).
Regression tests (tests/test_kimi.py PendingEnqueuedAtCleanupTests):
- test_cancel_background_tasks_clears_pending_enqueued_at: seeds the
dict, calls the override, asserts empty.
- test_disconnect_clears_pending_enqueued_at: seeds the dict, calls
disconnect with WS/HTTP/lock teardown patched, asserts empty.
- test_connect_clears_pending_enqueued_at: seeds a "stale" entry,
calls connect with GetMe forced to fail, asserts the entry was
cleared at session start (before the GetMe failure).
- test_handle_message_cleanup_runs_on_cancellation: patches
super().handle_message to raise CancelledError, asserts the try/
finally pops the timestamp via the guard.
No production behavior change for healthy connect/disconnect cycles
— this only matters for reconnects, error-recovery, and the rare
per-task cancellation outside of full adapter teardown.1 parent 6f3bccb commit 268daf1
2 files changed
Lines changed: 194 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1095 | 1095 | | |
1096 | 1096 | | |
1097 | 1097 | | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
1098 | 1107 | | |
1099 | 1108 | | |
1100 | 1109 | | |
| |||
1161 | 1170 | | |
1162 | 1171 | | |
1163 | 1172 | | |
| 1173 | + | |
| 1174 | + | |
| 1175 | + | |
| 1176 | + | |
| 1177 | + | |
| 1178 | + | |
| 1179 | + | |
| 1180 | + | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
1164 | 1230 | | |
1165 | 1231 | | |
1166 | 1232 | | |
| |||
1247 | 1313 | | |
1248 | 1314 | | |
1249 | 1315 | | |
1250 | | - | |
1251 | | - | |
1252 | | - | |
1253 | | - | |
1254 | | - | |
1255 | | - | |
1256 | | - | |
| 1316 | + | |
| 1317 | + | |
| 1318 | + | |
| 1319 | + | |
| 1320 | + | |
| 1321 | + | |
| 1322 | + | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
| 1326 | + | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
| 1330 | + | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
1257 | 1342 | | |
1258 | 1343 | | |
1259 | 1344 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3187 | 3187 | | |
3188 | 3188 | | |
3189 | 3189 | | |
| 3190 | + | |
| 3191 | + | |
| 3192 | + | |
| 3193 | + | |
| 3194 | + | |
| 3195 | + | |
| 3196 | + | |
| 3197 | + | |
| 3198 | + | |
| 3199 | + | |
| 3200 | + | |
| 3201 | + | |
| 3202 | + | |
| 3203 | + | |
| 3204 | + | |
| 3205 | + | |
| 3206 | + | |
| 3207 | + | |
| 3208 | + | |
| 3209 | + | |
| 3210 | + | |
| 3211 | + | |
| 3212 | + | |
| 3213 | + | |
| 3214 | + | |
| 3215 | + | |
| 3216 | + | |
| 3217 | + | |
| 3218 | + | |
| 3219 | + | |
| 3220 | + | |
| 3221 | + | |
| 3222 | + | |
| 3223 | + | |
| 3224 | + | |
| 3225 | + | |
| 3226 | + | |
| 3227 | + | |
| 3228 | + | |
| 3229 | + | |
| 3230 | + | |
| 3231 | + | |
| 3232 | + | |
| 3233 | + | |
| 3234 | + | |
| 3235 | + | |
| 3236 | + | |
| 3237 | + | |
| 3238 | + | |
| 3239 | + | |
| 3240 | + | |
| 3241 | + | |
| 3242 | + | |
| 3243 | + | |
| 3244 | + | |
| 3245 | + | |
| 3246 | + | |
| 3247 | + | |
| 3248 | + | |
| 3249 | + | |
| 3250 | + | |
| 3251 | + | |
| 3252 | + | |
| 3253 | + | |
| 3254 | + | |
| 3255 | + | |
| 3256 | + | |
| 3257 | + | |
| 3258 | + | |
| 3259 | + | |
| 3260 | + | |
| 3261 | + | |
| 3262 | + | |
| 3263 | + | |
| 3264 | + | |
| 3265 | + | |
| 3266 | + | |
| 3267 | + | |
| 3268 | + | |
| 3269 | + | |
| 3270 | + | |
| 3271 | + | |
| 3272 | + | |
| 3273 | + | |
| 3274 | + | |
| 3275 | + | |
| 3276 | + | |
| 3277 | + | |
| 3278 | + | |
| 3279 | + | |
| 3280 | + | |
| 3281 | + | |
| 3282 | + | |
| 3283 | + | |
| 3284 | + | |
| 3285 | + | |
| 3286 | + | |
| 3287 | + | |
| 3288 | + | |
| 3289 | + | |
| 3290 | + | |
| 3291 | + | |
3190 | 3292 | | |
3191 | 3293 | | |
3192 | 3294 | | |
| |||
0 commit comments