Skip to content

fix(ws): isolate transport event loops per client - #10

Open
wufei-png wants to merge 2 commits into
larksuite:mainfrom
wufei-png:codex/fix-ws-client-loop-ownership
Open

fix(ws): isolate transport event loops per client#10
wufei-png wants to merge 2 commits into
larksuite:mainfrom
wufei-png:codex/fix-ws-client-loop-ownership

Conversation

@wufei-png

Copy link
Copy Markdown

Summary

  • stop each WebSocket client through its own recorded event loop
  • remove the module-level event loop captured at import time
  • create and clean up a private transport loop for every ws.Client.start() invocation
  • bind fragmented-frame cache expiry to that same loop without constructor-time loop side effects
  • schedule receive and handler tasks on the loop that is actually running them

Root cause

lark_channel.ws.client captured asyncio.get_event_loop() in a module global. If the module was first imported from an already-running asyncio application, FeishuChannel.start_background() later ran Client.start() in an executor thread, but Client.start() still called run_until_complete() on the caller's running loop. This raised RuntimeError: This event loop is already running.

The same module global was shared by every client, so independently started clients could also race on one loop. ExpiringCache separately captured the ambient loop during construction, and the high-level Channel shutdown path looked the loop up from the module instead of the client instance.

Related reports against the legacy Python SDK describe the same shared-loop failure modes:

Commit structure

  1. fix(channel): stop websocket clients on their own loop
  2. fix(ws): isolate event loops per client

The first commit is independently valid with the existing client because it already records its selected loop on the instance. The second commit removes the shared loop and completes per-client ownership.

Validation

  • Python 3.8 focused WebSocket/lifecycle suite: 62 passed
  • Python 3.11 focused WebSocket/lifecycle suite: 62 passed
  • Full Python 3.11 suite excluding one unrelated baseline failure: 1101 passed, 1 deselected
  • The deselected local-file error-path assertion also fails unchanged on upstream/main when the pytest temp path contains a backslash
  • python -m build: sdist and wheel built successfully
  • git diff --check: passed

Non-goals

This PR does not change WebSocket data-frame message-type routing or the existing MessageType.CARD behavior.

@CLAassistant

CLAassistant commented Aug 14, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@wufei-png
wufei-png marked this pull request as ready for review August 14, 2026 10:18
@wufei-png
wufei-png force-pushed the codex/fix-ws-client-loop-ownership branch from 704ed7d to e32f304 Compare August 14, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants