gramjs
gramjs copied to clipboard
Unresolved promises after disconnect resulting in race condition.
After calling the client disconnect()
command, I've found that a pending promise can resolve after the the disconnect resulting in the [ERROR] - [Error: Cannot send requests while disconnected. You need to call .connect()]
error.
This line sleeps for 9 seconds https://github.com/gram-js/gramjs/blob/master/gramjs/client/updates.ts#L172
before calling the timeout()
callback:
https://github.com/gram-js/gramjs/blob/master/gramjs/client/updates.ts#L180
By the time the callback occurs the client may have already disconnected. client._sender.send()
then results in the error mentioned above.
It also appears that the property _destroyed
is never set, so that this while
statement is stuck in an infinite loop.
https://github.com/gram-js/gramjs/blob/master/gramjs/client/updates.ts#L171
I'm not sure what the best solution is to fix the async race condition here. Somehow these sleep
and timeout
promises need to be canceled when the client disconnect()
is called. Or perhaps the disconnect()
method needs to wait for the pending timeout()
to resolve before resolving itself.
As a temporary workaround I'm using:
myClient._destroyed = true;
await sleep(9000);
In this way I can force the while
loop to stop and then wait the PING_INTERVAL
period to make sure the pending promises resolve.
we need to keep pinging telegram or otherwise they will close the connection so there is a background task that pings once in a while.
JS doesn't allow you to cancel a promise so we can't really kill it after disconnect.
Usually when someone calls .disconnect they are planning to reconnect again so it doesn't make sense to completely stop the loop (also a lot of internal functions call .disconnect()) The loop should only stop once you call .destroy .destroy() should be setting ._destroyed to true but currently it's not so that's a bug./
Ah, yes, that makes sense.
Maybe the timeout
callback should check the connection status before calling client._sender.send()
? At least that would avoid the error being thrown.
Does destroy
require that the Telegram session be completely re-authenticated with phone-number, etc?
it shouldn't, it just tries to kill all senders/event listeners
I've pushed a small fix to 2.4.5. it might fix it.
This is working great. Many thanks!
JS doesn't allow you to cancel a promise so we can't really kill it after disconnect.
You can reject the promises. Promises never rejected or resolved are a really big problem to app, because can generate memory leaks.
For example I have situations when I try download two files at same time, Gramjs disconnects one connection while getting the data, and the promise never resolve/reject.