[FIX] web_editor: retry peer to peer connection with exponential backoff

When there is a problem in the peer to peer network where we try to
recover a connection, retry with exponential backoff instead of
potentially spamming the server indefinitely.

task-3164011

closes odoo/odoo#111802

X-original-commit: fd97295c9cf8b1b0c800f407bb5f538f78cc2665
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
Nicolas Bayet
2023-02-03 10:36:01 +01:00
committed by David Monjoie
parent 5cb4175e9c
commit 071ca078b5
@@ -379,6 +379,7 @@ export class PeerToPeer {
this.clientsInfos[clientId] = {
makingOffer: false,
iceCandidateBuffer: [],
backoffFactor: 0,
};
const pc = new RTCPeerConnection(this.options.peerConnectionConfig);
@@ -429,6 +430,9 @@ export class PeerToPeer {
reason: 'ice connection disconnected',
});
break;
case 'connected':
this.clientsInfos[clientId].backoffFactor = 0;
break;
}
};
// This event does not work in FF. Let's try with oniceconnectionstatechange if it is sufficient.
@@ -446,6 +450,10 @@ export class PeerToPeer {
reason: 'connection disconnected',
});
break;
case 'connected':
case 'completed':
this.clientsInfos[clientId].backoffFactor = 0;
break;
}
};
pc.onicecandidateerror = async error => {
@@ -505,7 +513,7 @@ export class PeerToPeer {
if (!dataChannel || dataChannel.readyState !== 'open') {
if (clientInfo && !clientInfo.zombieTimeout) {
if (debugShowLog) console.warn(
`Impossible to communicate with client ${clientId}. The connection be killed in 10 seconds if the datachannel state has not changed.`,
`Impossible to communicate with client ${clientId}. The connection will be killed in 10 seconds if the datachannel state has not changed.`,
);
this._killPotentialZombie(clientId);
}
@@ -563,6 +571,18 @@ export class PeerToPeer {
}
const clientInfos = this.clientsInfos[clientId];
if (!clientInfos || clientInfos.fallbackTimeout) return;
const backoffFactor = this.clientsInfos[clientId].backoffFactor;
const backoffDelay = delay * Math.pow(2, backoffFactor);
// Stop trying to recover the connection after 10 attempts.
if (backoffFactor > 10) {
if (debugShowLog) {
console.log(
`%c STOP RTC RECOVERY: impossible to connect to client ${clientId}: ${reason}`,
'background: darkred; color: white;',
);
}
return;
}
clientInfos.fallbackTimeout = setTimeout(async () => {
clientInfos.fallbackTimeout = undefined;
@@ -576,12 +596,13 @@ export class PeerToPeer {
// hard reset: recreating a RTCPeerConnection
if (debugShowLog)
console.log(
`%c RTC RECOVERY: calling back client ${clientId} to salvage the connection ${pc.iceConnectionState}, reason: ${reason}`,
`%c RTC RECOVERY: calling back client ${clientId} to salvage the connection ${pc.iceConnectionState} after ${backoffDelay}ms, reason: ${reason}`,
'background: darkorange; color: white;',
);
this.removeClient(clientId);
await this._createClient(clientId);
}, delay);
const newClientInfos = this._createClient(clientId);
newClientInfos.backoffFactor = backoffFactor + 1;
}, backoffDelay);
}
// todo: do we try to salvage the connection after killing the zombie ?
// Maybe the salvage should be done when the connection is dropped.