Skip to content

Commit d1995a9

Browse files
committed
Adds backoff to minimum interval between successfull connects
1 parent bd0b695 commit d1995a9

File tree

5 files changed

+42
-19
lines changed

5 files changed

+42
-19
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ wsOpts = {
103103
awareness: new awarenessProtocol.Awareness(ydoc),
104104
// Specify the maximum amount to wait between reconnects (we use exponential backoff).
105105
maxBackoffTime: 2500
106-
// Specify the minimum amount to wait between reconnects if the previous connection succeeded but got closed right after.
107-
minRetryInterval: 2500
106+
// Specify the maximum amount to wait between reconnects if previous connection succeeded (we use exponential backoff).
107+
// This prevents connections that close right after their successful creation from being retried immediately.
108+
// Backoff only resets when a connection has been open for `maxBackoffIntervalOnSuccessfulConnects` ms
109+
maxBackoffIntervalOnSuccessfulConnects: 2500
108110
}
109111
```
110112

dist/src/y-websocket.d.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export class WebsocketProvider extends ObservableV2<{
3535
* @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
3636
* @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
3737
* @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
38-
* @param {number} [opts.minRetryInterval] Minimum mount of time to wait between reconnects if the previous connection succeeded but got closed right after.
38+
* @param {number} [opts.maxBackoffIntervalOnSuccessfulConnects] Maximum amount of time to wait before trying to reconnect if previous connection succeeded (we try to reconnect using exponential backoff). Backoff only resets after a connection has been open for `maxBackoffIntervalOnSuccessfulConnects` ms
3939
* @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
4040
*/
41-
constructor(serverUrl: string, roomname: string, doc: Y.Doc, { connect, awareness, params, protocols, WebSocketPolyfill, resyncInterval, maxBackoffTime, minRetryInterval, disableBc }?: {
41+
constructor(serverUrl: string, roomname: string, doc: Y.Doc, { connect, awareness, params, protocols, WebSocketPolyfill, resyncInterval, maxBackoffTime, maxBackoffIntervalOnSuccessfulConnects, disableBc }?: {
4242
connect?: boolean | undefined;
4343
awareness?: awarenessProtocol.Awareness | undefined;
4444
params?: {
@@ -55,13 +55,13 @@ export class WebsocketProvider extends ObservableV2<{
5555
} | undefined;
5656
resyncInterval?: number | undefined;
5757
maxBackoffTime?: number | undefined;
58-
minRetryInterval?: number | undefined;
58+
maxBackoffIntervalOnSuccessfulConnects?: number | undefined;
5959
disableBc?: boolean | undefined;
6060
});
6161
serverUrl: string;
6262
bcChannel: string;
6363
maxBackoffTime: number;
64-
minRetryInterval: number;
64+
maxBackoffIntervalOnSuccessfulConnects: number;
6565
/**
6666
* The specified url parameters. This can be safely updated. The changed parameters will be used
6767
* when a new connection is established.
@@ -88,6 +88,7 @@ export class WebsocketProvider extends ObservableV2<{
8888
disableBc: boolean;
8989
wsUnsuccessfulReconnects: number;
9090
lastConnectAt: any;
91+
wsSubsequentConnects: number;
9192
messageHandlers: ((arg0: encoding.Encoder, arg1: decoding.Decoder, arg2: WebsocketProvider, arg3: boolean, arg4: number) => void)[];
9293
/**
9394
* @type {boolean}

dist/y-websocket.cjs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,21 @@ const closeWebsocketConnection = (provider, ws, event) => {
201201
* @param {WebsocketProvider} provider
202202
*/
203203
const setupWS = (provider) => {
204-
const timeout = provider.lastConnectAt ? Date.now() - provider.lastConnectAt.getTime() : provider.minRetryInterval;
205-
if (timeout < provider.minRetryInterval && provider.wsUnsuccessfulReconnects === 0) {
204+
// Start with 100ms interval between successful connects and increase with exponential backoff
205+
const backoff = math__namespace.min(math__namespace.pow(2, provider.wsSubsequentConnects) * 50, provider.maxBackoffIntervalOnSuccessfulConnects);
206+
const timeout = provider.lastConnectAt ? Date.now() - provider.lastConnectAt.getTime() : backoff;
207+
if (timeout < backoff) {
208+
console.log(`Backoff: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`);
206209
setTimeout(setupWS, timeout, provider);
207-
return
210+
return;
211+
} else if (timeout > provider.maxBackoffIntervalOnSuccessfulConnects) {
212+
console.log(`Reset: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt, max: provider.maxBackoffIntervalOnSuccessfulConnects }, null, 2)}`);
213+
provider.wsSubsequentConnects = 0;
208214
}
209215

210216
if (provider.shouldConnect && provider.ws === null) {
211217
const websocket = new provider._WS(provider.url, provider.protocols);
218+
console.log(`Connect: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`);
212219
websocket.binaryType = 'arraybuffer';
213220
provider.ws = websocket;
214221
provider.wsconnecting = true;
@@ -230,6 +237,8 @@ const setupWS = (provider) => {
230237
};
231238
websocket.onopen = () => {
232239
provider.lastConnectAt = new Date();
240+
provider.wsSubsequentConnects++;
241+
console.log(`Open: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`);
233242
provider.wsLastMessageReceived = time__namespace.getUnixTime();
234243
provider.wsconnecting = false;
235244
provider.wsconnected = true;
@@ -301,7 +310,7 @@ class WebsocketProvider extends observable.ObservableV2 {
301310
* @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
302311
* @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
303312
* @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
304-
* @param {number} [opts.minRetryInterval] Minimum mount of time to wait between reconnects if the previous connection succeeded but got closed right after.
313+
* @param {number} [opts.maxBackoffIntervalOnSuccessfulConnects] Maximum amount of time to wait before trying to reconnect if previous connection succeeded (we try to reconnect using exponential backoff). Backoff only resets after a connection has been open for `maxBackoffIntervalOnSuccessfulConnects` ms
305314
* @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
306315
*/
307316
constructor (serverUrl, roomname, doc, {
@@ -312,7 +321,7 @@ class WebsocketProvider extends observable.ObservableV2 {
312321
WebSocketPolyfill = WebSocket,
313322
resyncInterval = -1,
314323
maxBackoffTime = 2500,
315-
minRetryInterval = 2500,
324+
maxBackoffIntervalOnSuccessfulConnects = 2500,
316325
disableBc = false
317326
} = {}) {
318327
super();
@@ -323,7 +332,7 @@ class WebsocketProvider extends observable.ObservableV2 {
323332
this.serverUrl = serverUrl;
324333
this.bcChannel = serverUrl + '/' + roomname;
325334
this.maxBackoffTime = maxBackoffTime;
326-
this.minRetryInterval = minRetryInterval;
335+
this.maxBackoffIntervalOnSuccessfulConnects = maxBackoffIntervalOnSuccessfulConnects;
327336
/**
328337
* The specified url parameters. This can be safely updated. The changed parameters will be used
329338
* when a new connection is established.
@@ -341,6 +350,7 @@ class WebsocketProvider extends observable.ObservableV2 {
341350
this.disableBc = disableBc;
342351
this.wsUnsuccessfulReconnects = 0;
343352
this.lastConnectAt = null;
353+
this.wsSubsequentConnects = 0;
344354
this.messageHandlers = messageHandlers.slice();
345355
/**
346356
* @type {boolean}

dist/y-websocket.cjs.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/y-websocket.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,21 @@ const closeWebsocketConnection = (provider, ws, event) => {
172172
* @param {WebsocketProvider} provider
173173
*/
174174
const setupWS = (provider) => {
175-
const timeout = provider.lastConnectAt ? Date.now() - provider.lastConnectAt.getTime() : provider.minRetryInterval
176-
if (timeout < provider.minRetryInterval && provider.wsUnsuccessfulReconnects === 0) {
175+
// Start with 100ms interval between successful connects and increase with exponential backoff
176+
const backoff = math.min(math.pow(2, provider.wsSubsequentConnects) * 50, provider.maxBackoffIntervalOnSuccessfulConnects)
177+
const timeout = provider.lastConnectAt ? Date.now() - provider.lastConnectAt.getTime() : backoff
178+
if (timeout < backoff) {
179+
console.log(`Backoff: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`)
177180
setTimeout(setupWS, timeout, provider)
178-
return
181+
return;
182+
} else if (timeout > provider.maxBackoffIntervalOnSuccessfulConnects) {
183+
console.log(`Reset: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt, max: provider.maxBackoffIntervalOnSuccessfulConnects }, null, 2)}`)
184+
provider.wsSubsequentConnects = 0
179185
}
180186

181187
if (provider.shouldConnect && provider.ws === null) {
182188
const websocket = new provider._WS(provider.url, provider.protocols)
189+
console.log(`Connect: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`)
183190
websocket.binaryType = 'arraybuffer'
184191
provider.ws = websocket
185192
provider.wsconnecting = true
@@ -201,6 +208,8 @@ const setupWS = (provider) => {
201208
}
202209
websocket.onopen = () => {
203210
provider.lastConnectAt = new Date()
211+
provider.wsSubsequentConnects++
212+
console.log(`Open: ${JSON.stringify({ backoff, timeout, wsSubsequentConnects: provider.wsSubsequentConnects, lastConnectAt: provider.lastConnectAt }, null, 2)}`)
204213
provider.wsLastMessageReceived = time.getUnixTime()
205214
provider.wsconnecting = false
206215
provider.wsconnected = true
@@ -272,7 +281,7 @@ export class WebsocketProvider extends ObservableV2 {
272281
* @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
273282
* @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
274283
* @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
275-
* @param {number} [opts.minRetryInterval] Minimum mount of time to wait between reconnects if the previous connection succeeded but got closed right after.
284+
* @param {number} [opts.maxBackoffIntervalOnSuccessfulConnects] Maximum amount of time to wait before trying to reconnect if previous connection succeeded (we try to reconnect using exponential backoff). Backoff only resets after a connection has been open for `maxBackoffIntervalOnSuccessfulConnects` ms
276285
* @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
277286
*/
278287
constructor (serverUrl, roomname, doc, {
@@ -283,7 +292,7 @@ export class WebsocketProvider extends ObservableV2 {
283292
WebSocketPolyfill = WebSocket,
284293
resyncInterval = -1,
285294
maxBackoffTime = 2500,
286-
minRetryInterval = 2500,
295+
maxBackoffIntervalOnSuccessfulConnects = 2500,
287296
disableBc = false
288297
} = {}) {
289298
super()
@@ -294,7 +303,7 @@ export class WebsocketProvider extends ObservableV2 {
294303
this.serverUrl = serverUrl
295304
this.bcChannel = serverUrl + '/' + roomname
296305
this.maxBackoffTime = maxBackoffTime
297-
this.minRetryInterval = minRetryInterval
306+
this.maxBackoffIntervalOnSuccessfulConnects = maxBackoffIntervalOnSuccessfulConnects
298307
/**
299308
* The specified url parameters. This can be safely updated. The changed parameters will be used
300309
* when a new connection is established.
@@ -312,6 +321,7 @@ export class WebsocketProvider extends ObservableV2 {
312321
this.disableBc = disableBc
313322
this.wsUnsuccessfulReconnects = 0
314323
this.lastConnectAt = null
324+
this.wsSubsequentConnects = 0
315325
this.messageHandlers = messageHandlers.slice()
316326
/**
317327
* @type {boolean}

0 commit comments

Comments
 (0)