[Bug] WebSocket Streams: stream registry is global, two clients with the same stream name get each other's data
还没有人认领这个 Issue。
评估
调研方向
从 common/src/binance_common/websocket.py 中的 WebSocketStreamBase 开始:找到模块级映射 global_stream_connections 和 global_user_stream_connections,然后找出所有读取或写入它们的位置——订阅过滤器、on、取消订阅以及重连清理——并将它们移到在 __init__ 中初始化的实例属性上。先运行 issue 正文中的自包含 aiohttp 复现脚本以确认错误输出,然后重新运行以检查每个服务器都获得自己的 SUBSCRIBE 且 fut_cb 收到 {'venue': 'futures'}。检查仓库现有的 websocket 测试仍然通过,因为池化和去重逻辑依赖于这个注册表。
由索引模型根据 Issue 内容生成。
描述
Summary
WebSocketStreamBase keeps its stream-to-connection registry in two module-level objects (global_stream_connections, global_user_stream_connections in common/src/binance_common/websocket.py). Every client in the process shares them, keyed only by stream name.
Spot and USDⓈ-M Futures use the same stream names (btcusdt@aggTrade, btcusdt@depth, ...), and so do mainnet and testnet. When two stream clients in one process subscribe to the same name:
- the second client never sends
SUBSCRIBE, because the name is already registered; - its callback is attached to the first client's connection, so it receives the other venue's data, with no error.
Reproduce
Self-contained: one local aiohttp server with two endpoints standing in for two venues; each tags its messages with its name. binance-common==4.5.1 (same as master at 43f19a9), Python 3.13.
import asyncio, json, logging
from aiohttp import web
from binance_common.configuration import ConfigurationWebSocketStreams
from binance_common.websocket import WebSocketStreamBase, RequestStream
logging.disable(logging.CRITICAL)
subs = {"spot": [], "futures": []}
def mk(name):
async def handler(request):
ws = web.WebSocketResponse(); await ws.prepare(request)
async for msg in ws:
d = json.loads(msg.data)
if d["method"] == "SUBSCRIBE":
subs[name] += d["params"]
await ws.send_json({"id": d["id"], "result": None})
for s in d["params"]:
await ws.send_json({"stream": s, "data": {"venue": name}})
return ws
return handler
async def main():
app = web.Application()
app.router.add_get("/spot/stream", mk("spot")); app.router.add_get("/fut/stream", mk("futures"))
r = web.AppRunner(app); await r.setup(); await web.TCPSite(r, "127.0.0.1", 18766).start()
spot = WebSocketStreamBase(ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18766/spot/stream"))
fut = WebSocketStreamBase(ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18766/fut/stream"))
await spot.create_connection(); await fut.create_connection()
got = {"spot_cb": [], "fut_cb": []}
h1 = await RequestStream(spot, "btcusdt@aggTrade"); h1.on("message", lambda d: got["spot_cb"].append(d))
h2 = await RequestStream(fut, "btcusdt@aggTrade"); h2.on("message", lambda d: got["fut_cb"].append(d))
await asyncio.sleep(0.5)
print("SUBSCRIBE frames received by servers:", subs)
print("callback payloads:", got)
await spot.close_connection(); await fut.close_connection(); await r.cleanup()
asyncio.run(main())
Output:
SUBSCRIBE frames received by servers: {'spot': ['btcusdt@aggTrade'], 'futures': []}
callback payloads: {'spot_cb': [{'stream': 'btcusdt@aggTrade', 'data': {'venue': 'spot'}}], 'fut_cb': [{'stream': 'btcusdt@aggTrade', 'data': {'venue': 'spot'}}]}
Expected
Each server receives its own SUBSCRIBE, and fut_cb receives {'venue': 'futures'}.
Impact
A process that follows the same symbol on Spot and Futures (basis or arbitrage monitoring, for example) gets Spot trades delivered to the Futures handler. Nothing is logged, and the payload shapes are close enough that it is easy to miss.
Suggested fix
Make both maps instance attributes created in the client's __init__ and use them wherever the globals are referenced today (subscribe filter, on, unsubscribe and the reconnect cleanup). Streams would then be deduplicated per client, which is what the pooling logic needs.
- 主要语言
- Python
- 星标
- 2.9k
- 派生
- 708
- 平均合并
- 2 小时 17 分钟
- 30 天内合并 PR
- 2
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
binance/binance-connector-python 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 72/100
binance/binance-connector-python#514 · 1 条评论 ·
-
[Bug] WebSocket API: session re-logon after reconnect is sent without apiKey and signature可能已有人在做 @Amadeus-22 于 4 天前认领。 未关闭
难度 3/5 1-3 小时 新手友好度 74/100
-
[Bug] Response models declare numeric fields as strict `str`, causing silent fallback of `.data()` to raw dict (simple-earn & staking)可能已有人在做 @Tyagiquamar 于 26 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 68/100
binance/binance-connector-python#553 · 2 条评论 ·
-
难度 5/5 一周以上 新手友好度 20/100
-
难度 5/5 一周以上 新手友好度 30/100
binance/binance-connector-python#464 · 1 条评论 · 5 个 reaction ·
查看 binance/binance-connector-python 的全部 Issue
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 72/100
-
[Bug]: Importing weave changes the process-wide locale可能已有人在做 @himanshu748 今天认领。 未关闭bug
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
-
bug
难度 2/5 1-3 小时 新手友好度 74/100
-
UX
难度 1/5 1 小时以内 新手友好度 72/100
mediajunkie/piper-morgan-product#2008 · 2 条评论 ·