[Bug] WebSocket Streams: after a connection drops, resubscribing to the same stream on the same client sends nothing
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 82/100
Hướng nghiên cứu
Đọc common/src/binance_common/websocket.py, đặc biệt là _discard_connection() và _forget_connection_streams(), sau đó chạy cách tái hiện được mô tả trong issue với một máy chủ WebSocket cục bộ. Kiểm tra rằng sau khi kết nối bị ngắt, việc đăng ký lại bằng cùng một client sẽ gửi SUBSCRIBE và chuyển giao bản cập nhật khôi phục. Issue đề xuất quên các stream của kết nối đã chết ở cuối _discard_connection().
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Follow-up to #570, which common v4.6.0 fixed for two clients in one process. With a single client, one case from the same stream map remains: after a stream connection drops unexpectedly, subscribing to the same stream again on a new connection does nothing. No SUBSCRIBE is sent, nothing is raised, nothing is logged about the skipped stream, and no messages arrive, so the stream looks like a quiet market. Recovering through a new Spot client works in 4.6.0.
When the server closes the connection or the TCP connection drops, the receive loop fires close and calls _discard_connection() (lines 414, 421 and 428), which removes the connection from the pool (line 497). The client's stream_connections still maps the stream to the dead connection, because _forget_connection_streams() runs only after a failed planned reconnect (line 907) and on a user-initiated close (line 1138). On the next subscribe, subscribe() filters the stream out as already registered (lines 1288-1292), and on() attaches the callback to the dead connection (lines 1361-1367). Spot.websocket_streams is cached, and create_connection() on it returns the same instance, so the new connection shares the stale map. Line numbers are from common/src/binance_common/websocket.py at a54ec0b, which matches the 4.6.0 release.
Reproduce
binance-sdk-spot==13.2.0, binance-common==4.6.0, aiohttp==3.14.5, Python 3.14.8. A local aiohttp server drops the first connection's TCP without a close frame. The client then opens a new connection on the same Spot client and subscribes to the same stream again. A close frame (1001) in place of the TCP drop gives the same result.
Reproduction script and output
import asyncio, json, logging
from aiohttp import web
from binance_common.configuration import ConfigurationWebSocketStreams
from binance_sdk_spot.spot import Spot
logging.disable(logging.CRITICAL)
conns, subs = [], []
async def handler(request):
ws = web.WebSocketResponse(); await ws.prepare(request)
conns.append(request); conn = len(conns)
async for msg in ws:
d = json.loads(msg.data)
if d["method"] == "SUBSCRIBE":
subs.append((conn, d["params"]))
await ws.send_json({"id": d["id"], "result": None})
for s in d["params"]:
await ws.send_json({"stream": s, "data": {"e": "depthUpdate", "E": 1, "s": "BNBUSDT", "U": conn, "u": conn, "b": [], "a": []}})
if conn == 1:
await asyncio.sleep(0.2)
request.transport.abort() # the TCP connection drops, no close frame
return ws
async def main():
app = web.Application(); app.router.add_get("/stream", handler)
r = web.AppRunner(app); await r.setup(); await web.TCPSite(r, "127.0.0.1", 18767).start()
client = Spot(config_ws_streams=ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18767/stream"))
got, events = [], []
ws = await client.websocket_streams.create_connection()
ws.on_connection("close", lambda *a: events.append("close"))
(await ws.diff_book_depth(symbol="bnbusdt")).on("message", lambda m: got.append(("first", m.U)))
await asyncio.sleep(0.5)
# recover: a new connection on the same client, same stream
ws2 = await client.websocket_streams.create_connection()
(await ws2.diff_book_depth(symbol="bnbusdt")).on("message", lambda m: got.append(("recovery", m.U)))
await asyncio.sleep(0.5)
print("same instance:", ws2 is ws)
print("connections accepted by the server:", len(conns))
print("connection events:", events)
print("SUBSCRIBE frames received (connection, params):", subs)
print("messages delivered:", got)
await ws2.close_connection(close_session=True); await r.cleanup()
asyncio.run(main())
Output:
same instance: True
connections accepted by the server: 2
connection events: ['close']
SUBSCRIBE frames received (connection, params): [(1, ['bnbusdt@depth'])]
messages delivered: [('first', 1)]
Expected
The second connection sends SUBSCRIBE for bnbusdt@depth, and the recovery callback receives the second connection's update.
Suggested fix
Call self._forget_connection_streams(connection) at the end of _discard_connection(), after its early return for planned reconnects. Its docstring already describes this case: "A connection that is gone for good must not be left in the stream maps, or a later subscribe would treat its streams as still live and silently skip them." With that call patched in, the script above shows one SUBSCRIBE per connection and delivers ('recovery', 2).
- Ngôn ngữ chính
- Python
- Star
- 2.9k
- Fork
- 708
- Merge trung bình
- 1 ngày 5 giờ
- Pull request đã merge (30 ngày)
- 4
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của binance/binance-connector-python
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
binance/binance-connector-python#570 · 2 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
binance/binance-connector-python#514 · 2 bình luận ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 15/100
-
[Bug] Response models declare numeric fields as strict `str`, causing silent fallback of `.data()` to raw dict (simple-earn & staking)Có thể đã có người làm @Tyagiquamar đã nhận 30 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 68/100
binance/binance-connector-python#553 · 2 bình luận ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 20/100
Tất cả issue của binance/binance-connector-python
Issue tương tự
-
documentation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
ianktoo/project-monitor#14 ·
-
A wrong descriptor checksum is refused with the right one, also for an xprv or WIF descriptorĐang mởsecurity
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
btclib-org/btclib-wallet#478 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
security
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
btclib-org/btclib#2727 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue lead variant:container
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
radialmonster/orf-cleanroom#10 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
gdsa opencode: keep-alive cleanup trap fails with 'keepalive_pid: unbound variable'Có thể đã có người làm @darribas đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100