In the Daml Finance Reference App we make use of useStreamQueries a lot. I realized that the websocket connections don’t get released / closed when I eg. navigate away from a page that calls useStreamQueries. This leads to a lot of ununsed connections staying open, and when it reaches ~500 of them (as evidenced in the sandbox logs) the whole app crashes and burns.
Is there a way to explicitly release those connections, or if not, what is the “policy” of when these get closed?
On the json-api side, it’s determined by the websocket-config.max-duration setting. The default is a very long 2 hours. You can turn this way down, because the @daml/ledger library should handle reconnecting for you if the page is still active.
However, you must also consider the reconnectThreshold setting in your new Ledger invocations. The default is 30000 (30 seconds); ensure that your json-api isn’t set up to disconnect faster than @daml/ledger can detect a healthy connection. (Some further discussion of proposed reconnect semantics here.)
First off, what do you mean by “when I navigate away from a page”? As far as I understand the browser model, everything JavaScript, including open WS connections, should be terminated when you navigate away from a page.
Assuming you mean “when the React components get unmounted”, I’m not sure what to suggest here. The underlying @daml/ledger functions seem to return explicit streams on which you can call .close, but it looks like those streams are not exposed by the @daml/react library. Looking through what I believe to be the relevant code, it looks like we plug the stream directly into a React “state” behind the scenes, in a sort-of separate JS “thread”, and that’s the only place where the stream object itself is accessible.
I suppose this works well enough to get started quickly on a prototype (which is the main goal of the @daml/react library), or if you’re in a situation where you know all of the streams you need to open from the very first render of the page (i.e. you don’t expect to dynamically change the set of open streams based on user behaviour), but, as you’ve found out, it’s an approach that definitely has its limits.
My best recommendation at this point would be to use the @daml/ledger library more directly. The useLedger function from the @daml/react library returns a Ledger object from the @daml/ledger library, so you may operate on that more explicitly. You’ll need to add the “close stream” operation yourself, probably on componentWillUnmount or similar - my React is very rusty at this point, I think it’s been about five to six years since I last looked into React in depth.
Thanks both for the answers. Setting the max-duration down to 1 minute seems to have remediated the situation sufficiently, it’s now recycling the connections regularily. Maybe consider changing the default for this setting.
For reference, here’s the full JSON API config I’m using now:
{
server {
address = "0.0.0.0"
port = 7575
}
ledger-api {
address = "localhost"
port = 6865
}
websocket-config {
max-duration = 1m
}
}
And I use this setting in daml.yaml to load the config when running daml start: