[Bug] WebSocket Streams: after a connection drops, resubscribing to the same stream on the same client sends nothing
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 82/100
Direzione di ricerca
Leggi common/src/binance_common/websocket.py, in particolare _discard_connection() e _forget_connection_streams(), poi esegui la riproduzione descritta nell’issue contro un server WebSocket locale. Verifica che, dopo la caduta della connessione, una nuova sottoscrizione con lo stesso client invii SUBSCRIBE e consegni l’aggiornamento di ripristino. L’issue suggerisce di dimenticare gli stream della connessione morta alla fine di _discard_connection().
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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).
- Lingua principale
- Python
- Stelle
- 2.9k
- Fork
- 708
- Merge medio
- 1g 5h
- PR unite (30g)
- 4
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di binance/binance-connector-python
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
binance/binance-connector-python#570 · 2 commenti ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
binance/binance-connector-python#514 · 2 commenti ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 15/100
-
[Bug] Response models declare numeric fields as strict `str`, causing silent fallback of `.data()` to raw dict (simple-earn & staking)Forse già presa @Tyagiquamar l’ha presa 29 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 68/100
binance/binance-connector-python#553 · 2 commenti ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
Tutte le issue di binance/binance-connector-python
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
NousResearch/hermes-agent#140958 ·
I maintainer di solito rispondono entro 1 giorno
-
Bisect issue233186Apertabisect
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
dtcxzyw/llvm-bisect-service#548 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
sallowayma-git/IELTS-practice#248 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 65/100
anthropics/claude-plugins-official#6763 · 10 commenti · 10 reazioni ·
I maintainer di solito rispondono entro 3 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno