-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
More responsive to network outages (#360)
This PR introduces a number of improvements that make `YSweetProvider` more responsive to network conditions. The main issue this attempts to address is that browsers are surprisingly inconsistent in how long it takes to terminate a WebSocket connection when the network is lost. I have observed the timeout taking anywhere from 10s (Firefox) to 95s (Chrome) when using a VPN. (Without the VPN it is a bit better, presumably because the VPN makes the network status more opaque to the browser.) ## Passive / active connection monitoring This PR introduces passive connection monitoring that switches into an active heartbeat when message volume is low. When the server is sending the client lots of messages (e.g. frequent awareness updates), we know the connection is alive, and don't have to send any sort of heartbeat to confirm it. When we haven't received a message for a certain time threshold (`MAX_TIMEOUT_BETWEEN_HEARTBEATS`), we don't know whether it's because the connection was lost or just a lull in events, so we start to send an active heartbeat. This uses the same mechanism on the server introduced for detecting local unsaved changes: we simply re-send the latest `localVersion` (formerly `lastSyncSent`) instead of incrementing it (since the underlying data hasn't changed). ## Browser connection status monitoring Browsers also provide a mechanism for being notified about online/offline state. It's not entirely reliable, but we can use it as an additional signal to accelerate reconnection. The way it works is: - When we receive and `offline` event, we immediately send a heartbeat, which means we only have to wait `MAX_TIMEOUT_WITHOUT_RECEIVING_HEARTBEAT` instead of up to `MAX_TIMEOUT_BETWEEN_HEARTBEATS + MAX_TIMEOUT_WITHOUT_RECEIVING_HEARTBEAT` before showing as offline. - When we receive an `online` event, if we are currently waiting to retry a network operation (either obtaining credentials or connecting to a websocket), we bypass the wait and go directly to making the request. --------- Co-authored-by: Jake Lazaroff <[email protected]>
- Loading branch information
1 parent
b49add3
commit 80fec8d
Showing
2 changed files
with
160 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* A timeout that can be woken up prematurely by calling `wake()`. | ||
*/ | ||
export class Sleeper { | ||
resolve?: () => void | ||
reject?: () => void | ||
promise: Promise<void> | ||
|
||
constructor(timeout: number) { | ||
this.promise = new Promise<void>((resolve, reject) => { | ||
setTimeout(() => { | ||
resolve() | ||
}, timeout) | ||
|
||
this.resolve = resolve | ||
this.reject = reject | ||
}) | ||
} | ||
|
||
/** | ||
* Sleeps until the timeout has completed (or has been woken). | ||
*/ | ||
async sleep() { | ||
await this.promise | ||
} | ||
|
||
/** | ||
* Wakes up the timeout if it has not completed yet. | ||
*/ | ||
wake() { | ||
this.resolve && this.resolve() | ||
} | ||
} |