emilhe / dash-extensions

The dash-extensions package is a collection of utility functions, syntax extensions, and Dash components that aim to improve the Dash development experience
https://www.dash-extensions.com/
MIT License
417 stars 59 forks source link

websocket message not received when transmitting fast #214

Open ytsai-d1 opened 2 years ago

ytsai-d1 commented 2 years ago

Hello,

When sending consecutive websocket messages from server quickly, I have noticed client websocket callbacks not firing. How do I ensure there are no packet loss sent from server? I have tested this with Javascript websocket client and don't see this issue. Any thoughts?

Thanks!

Server code

import asyncio
from quart import websocket, Quart

app = Quart(__name__)

@app.websocket("/ws")
async def ws():
    count = 0
    await websocket.accept()
    while count < 100:
        await websocket.send( str(count) )
        count += 1
        # Only with sleep> 0.1, then messages appear on client
        await asyncio.sleep(0.1)

app.run(port=5000)

Client code


from dash_extensions.enrich import DashProxy, html, dcc, Input, Output
from dash_extensions import WebSocket

# Create example app.
app = DashProxy(prevent_initial_callbacks=True)
app.layout = html.Div([
    html.Div(id="message"),
    WebSocket(url="ws://127.0.0.1:5000/ws", id="ws")
])

@app.callback(Output("message", "children"), [Input("ws", "message")])
def message(e):
    print(f"{e['data']}")
    return f"{e['data']}"

app.run_server()

Output - without sleep, output missing consecutive numbers. (With sleep numbers are all there 0-99).

127.0.0.1 - - [07/Oct/2022 11:09:00] "GET / HTTP/1.1" 200 - .... 0 127.0.0.1 - - [07/Oct/2022 11:09:00] "POST /_dash-update-component HTTP/1.1" 200 - 89 127.0.0.1 - - [07/Oct/2022 11:09:01] "POST /_dash-update-component HTTP/1.1" 200 - 99 127.0.0.1 - - [07/Oct/2022 11:09:01] "POST /_dash-update-component HTTP/1.1" 200 -

quecksilberio commented 8 months ago

I'm facing the same issue. Did you ever find a solution?

emer-bestseller commented 8 months ago

If you are steaming data faster than they can be processed, they will be lost. If you use a client side callback, which executes faster, the effect should be less significant.

quecksilberio commented 8 months ago

Is there any way to force the callback to be called for each websocket message, irregardless of the previous websocket messages having been processed or not?

emilhe commented 8 months ago

Not with the current implementaion - it simply relays the messages. It sounds like you need queuing and/or buffering of the data.

petoncle commented 5 months ago

This is what I've tried: I use a Dash clientside_callback to process the received websocket messages in the browser. Whenever a websocket message is received by the browser, the following callback is supposed to be called:

function (msg) {
        console.log("Received message: " + msg);
}

But even then, most messages are lost, i.e. the console.log call only happens for ~10% of the messages depending on how fast they're being sent. Sometimes, the callback function is called out of order.
Now, what's surprising me is that when I look at the websocket connection in the browser's network tab, I see all messages being received fine, in order. Yet, the client side callback is not called for most messages (and sometimes the order is lost). Is that a bug in Dash's client side callback implementation? Is there a way to work around that problem?

Not with the current implementaion - it simply relays the messages. It sounds like you need queuing and/or buffering of the data.

@emilhe Could you explain what you meant by buffering the data: where should the buffering be implemented?