Skip to content

feat(ws): per-instance event loop, graceful stop, and recv timeout - #144

Open
hotwater9 wants to merge 1 commit into
larksuite:v2_mainfrom
hotwater9:feat/per-instance-event-loop
Open

feat(ws): per-instance event loop, graceful stop, and recv timeout#144
hotwater9 wants to merge 1 commit into
larksuite:v2_mainfrom
hotwater9:feat/per-instance-event-loop

Conversation

@hotwater9

Copy link
Copy Markdown

Problem

The ws.Client uses a module-level loop variable that is shared across
all instances. In multi-bot deployments (multiple Client objects in separate
threads), each thread overwrites the same global, causing:

  1. RuntimeError: Task got Future attached to a different loop on reconnect (ws.Client: module-level loop variable prevents multiple instances (multi-bot race condition) #119)
  2. Silent death — _receive_message_loop crashes but _select() keeps sleeping,
    making the client appear alive while receiving nothing
  3. No way to gracefully stop a running client (feat(ws): add public Client.stop() for graceful shutdown #128)
  4. Cannot use inside async frameworks ([Python lark-oapi 1.6.7] ws/client.py module-level event loop causes RuntimeError when first imported inside a running loop #133)

Solution

Issue Fix
Loop collision self._loop per instance; each start() creates its own loop
Silent death (TCP black hole) asyncio.wait_for(conn.recv(), timeout=300)
Silent death (task exception swallowed) _receive_message_loop sets stop_event on unrecoverable failure
No graceful stop stop()call_soon_threadsafe(stop_event.set)
Async framework compat start_async() uses get_running_loop()
Uninterruptible sleeps All asyncio.sleep()wait_for(stop_event.wait(), timeout=...)

Breaking Changes

None. start() signature is unchanged. stop() and start_async() are additive.
Module-level loop is retained (set to the last started instance's loop) for
backward compatibility.

Related

Testing

  • test_multi_instance.py: independent loops, stop() terminates start(),
    stop() interrupts reconnect, start_async() reuses caller loop, backward compat
  • Updated test_websockets_compat.py for per-instance loop

@CLAassistant

CLAassistant commented Jul 22, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@hotwater9
hotwater9 force-pushed the feat/per-instance-event-loop branch from dd82e46 to d1c279a Compare July 22, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants