Any way to signify to users that the websocket connection has been lost?

Hi!

This is basically the same topic as this: Bokeh 2.0 websockets, which has since been closed.

In short, we’ve found its common for our users to leave a Bokeh application idling, come back to it later, and find a dead page. The page is dead because the websocket as been lost, and bokeh fails to reconnect. Yet there is no indication to users that the connection lost, so it’s not immediately clear to them that they should just reload the page.

In the previous topic, @p-himik mentions:

I would probably intercept all Bokeh log messages and look out for the messages that start with “Lost websocket”.

But It’s not clear to me how to intercept these log messages (a caveat - I’m not a javascript expert).
Here is the log message I think I need to intercept: https://github.com/bokeh/bokeh/blob/branch-3.0/bokehjs/src/lib/client/connection.ts#L213
But from what I can tell, the Logger class stores a reference to logger.info (I’d add a link, but new users can only include 2 links in posts…)

So I can’t simply patch logger.info in a custom javascript callback, since the Logger class already referenced the original logger.info.

Does that make sense?

Is there some other way to intercept these messages? Or perhaps there’s a new, better way to handle lost connections since that topic?

Thanks in advance,
Barak

Pretty sure you’re wrong here. Have you actually tried overriding logger.info?

I’ve tried using Panel’s “lifecycle callbacks”: Custom Components — Panel 0.12.5 documentation

Here’s my application:

from panel.reactive import ReactiveHTML

JAVASCRIPT_RENDER_CALLBACK = """
console.log("before patching");
var info = console.info;
console.info = function() {
  console.log("inside the wrapper");
  info.apply(console, arguments);
}
"""


class WebSocketMonitor(ReactiveHTML):
    _template = "<div></div>"
    _scripts = {
        "render": JAVASCRIPT_RENDER_CALLBACK
    }


WebSocketMonitor().servable()

Here’s the console output:

You can see the callback runs and successfully patches console.info. I then force the connection to close, yet there is not “inside the wrapper” message corresponding to the Connection log messages.

(Note: this is bokeh version 2.4.0)

1 Like

I’ve managed to create a very hacky solution in Panel.

Now, I know this the Bokeh discourse, not the Panel one, but I’ll show it here just in case someone has a similar need:

import param
from panel.models.reactive_html import DOMEvent
from panel.reactive import ReactiveHTML


class ConnectionMonitor(ReactiveHTML):
    _in: str = param.String(default="0")

    _template = """
    <input class="connection_monitor_in" id="connection_monitor_in" style="display:none;">${_in}</input>
    <input class="connection_monitor_out" id="connection_monitor_out" style="display:none;" value="0"></input>
    <div class="lost_connection" style="display:none;">
        Lost connection to server. <a class="refresh_button" href="">Refresh ⟳</a> 
    </div>
    <script>
        (function() {
            var timeoutId = setInterval(function() {
                var inEl = document.getElementsByClassName("connection_monitor_in")[0];
                var outEl = document.getElementsByClassName("connection_monitor_out")[0];
                
                var inVal = parseInt(inEl.textContent);
                var outVal = parseInt(outEl.value);
                
                // Allow two failed heartbeats (10 seconds)
                if (outVal > inVal + 1) {
                    var alertEl = document.getElementsByClassName("lost_connection")[0];
                    alertEl.style.display = "block";
                    clearTimeout(timeoutId);
                }
                outEl.value = (outVal + 1).toString();
                outEl.dispatchEvent(new CustomEvent("change"));
            }, 5000);
        })();
    </script>
    """
    _child_config = {"_in": "literal"}
    _dom_events = {"connection_monitor_out": ["change"]}

    def _connection_monitor_out_change(self, event: DOMEvent) -> None:
        self._in = event.data["value"]

I’m sure there’s a similar way of doing this in Bokeh, since I believe Panel is mostly a layer on top of Bokeh.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.