Socket request timeout handling, with retries.

auto_join_3.3
Håvar Aambø Fosstveit 2020-05-19 14:09:27 +02:00
parent b0e57756cb
commit 0f184a3a29
6 changed files with 109 additions and 12 deletions

View File

@ -37,7 +37,8 @@ var config =
'opera' 'opera'
], ],
// Socket.io request timeout // Socket.io request timeout
requestTimeout : 10000, requestTimeout : 20000,
requestRetries : 3,
transportOptions : transportOptions :
{ {
tcp : true tcp : true

View File

@ -1,6 +1,7 @@
import Logger from './Logger'; import Logger from './Logger';
import hark from 'hark'; import hark from 'hark';
import { getSignalingUrl } from './urlFactory'; import { getSignalingUrl } from './urlFactory';
import { SocketTimeoutError } from './utils';
import * as requestActions from './actions/requestActions'; import * as requestActions from './actions/requestActions';
import * as meActions from './actions/meActions'; import * as meActions from './actions/meActions';
import * as roomActions from './actions/roomActions'; import * as roomActions from './actions/roomActions';
@ -574,7 +575,7 @@ export default class RoomClient
if (called) if (called)
return; return;
called = true; called = true;
callback(new Error('Request timeout.')); callback(new SocketTimeoutError('Request timed out'));
}, },
ROOM_OPTIONS.requestTimeout ROOM_OPTIONS.requestTimeout
); );
@ -590,13 +591,13 @@ export default class RoomClient
}; };
} }
sendRequest(method, data) _sendRequest(method, data)
{ {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
if (!this._signalingSocket) if (!this._signalingSocket)
{ {
reject('No socket connection.'); reject('No socket connection');
} }
else else
{ {
@ -606,19 +607,42 @@ export default class RoomClient
this.timeoutCallback((err, response) => this.timeoutCallback((err, response) =>
{ {
if (err) if (err)
{
reject(err); reject(err);
}
else else
{
resolve(response); resolve(response);
}
}) })
); );
} }
}); });
} }
async sendRequest(method, data)
{
logger.debug('sendRequest() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = window.config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('sendRequest() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
async changeDisplayName(displayName) async changeDisplayName(displayName)
{ {
logger.debug('changeDisplayName() [displayName:"%s"]', displayName); logger.debug('changeDisplayName() [displayName:"%s"]', displayName);

View File

@ -16,4 +16,22 @@ export const idle = (callback, delay) =>
handle = setTimeout(callback, delay); handle = setTimeout(callback, delay);
}; };
}; };
/**
* Error produced when a socket request has a timeout.
*/
export class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}

View File

@ -276,6 +276,10 @@ module.exports =
// maxUsersPerRoom : 20, // maxUsersPerRoom : 20,
// Room size before spreading to new router // Room size before spreading to new router
routerScaleSize : 40, routerScaleSize : 40,
// Socket timout value
requestTimeout : 20000,
// Socket retries when timeout
requestRetries : 3,
// Mediasoup settings // Mediasoup settings
mediasoup : mediasoup :
{ {

View File

@ -3,6 +3,7 @@ const AwaitQueue = require('awaitqueue');
const axios = require('axios'); const axios = require('axios');
const Logger = require('./Logger'); const Logger = require('./Logger');
const Lobby = require('./Lobby'); const Lobby = require('./Lobby');
const { SocketTimeoutError } = require('./errors');
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const userRoles = require('../userRoles'); const userRoles = require('../userRoles');
@ -1759,9 +1760,9 @@ class Room extends EventEmitter
if (called) if (called)
return; return;
called = true; called = true;
callback(new Error('Request timeout.')); callback(new SocketTimeoutError('Request timed out'));
}, },
10000 config.requestTimeout || 20000
); );
return (...args) => return (...args) =>
@ -1775,7 +1776,7 @@ class Room extends EventEmitter
}; };
} }
_request(socket, method, data = {}) _sendRequest(socket, method, data = {})
{ {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
@ -1797,6 +1798,33 @@ class Room extends EventEmitter
}); });
} }
async _request(socket, method, data)
{
logger.debug('_request() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(socket, method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('_request() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
_notification(socket, method, data = {}, broadcast = false, includeSender = false) _notification(socket, method, data = {}, broadcast = false, includeSender = false)
{ {
if (broadcast) if (broadcast)

View File

@ -0,0 +1,22 @@
/**
* Error produced when a socket request has a timeout.
*/
class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}
module.exports =
{
SocketTimeoutError
};