feat: throttling configuration

This commit is contained in:
Florent Chehab 2020-05-11 14:12:20 +02:00
parent 409681b217
commit ca47c41c69
No known key found for this signature in database
GPG Key ID: 9A0CE018889EA246
8 changed files with 134 additions and 40 deletions

View File

@ -4,7 +4,7 @@ backend:
# TODO
webdav: false
performance:
# Whiteboard information broadcasting frequency (in /s)
# Whiteboard information broadcasting frequency (in Hz i.e. /s)
# => diminishing this will result in more latency
whiteboardInfoBroadcastFreq: 1
@ -15,7 +15,8 @@ frontend:
# Show smallest screen indicator
showSmallestScreenIndicator: true
performance:
pointerEventsThreshold:
minDistDelta: 1
minTimeDelta: 33
pointerEventsThrottling:
- fromNbUser: 0
minDistDelta: 1
maxFreq: 30
refreshInfoFreq: 5

View File

@ -41,20 +41,28 @@
"performance": {
"type": "object",
"additionalProperties": false,
"required": ["pointerEventsThreshold", "refreshInfoFreq"],
"required": ["pointerEventsThrottling", "refreshInfoFreq"],
"properties": {
"pointerEventsThreshold": {
"type": "object",
"additionalProperties": false,
"required": ["minDistDelta", "minTimeDelta"],
"properties": {
"minDistDelta": {
"type": "number",
"minimum": 0
},
"minTimeDelta": {
"type": "number",
"minimum": 0
"pointerEventsThrottling": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["fromNbUser", "minDistDelta", "maxFreq"],
"properties": {
"fromNbUser": {
"type": "number",
"minimum": 0
},
"minDistDelta": {
"type": "number",
"minimum": 0
},
"maxFreq": {
"type": "number",
"minimum": 0
}
}
}
},

View File

@ -44,11 +44,28 @@ function getConfig(path) {
*/
function isConfigValid(config, warn = true) {
const validate = ajv.compile(configSchema);
const isValid = validate(config);
const isValidAgainstSchema = validate(config);
if (!isValid && warn) console.warn(validate.errors);
if (!isValidAgainstSchema && warn) console.warn(validate.errors);
return isValid;
let structureIsValid = false;
try {
structureIsValid = config.frontend.performance.pointerEventsThrottling.some(
(item) => item.fromNbUser === 0
);
} catch (e) {
if (!e instanceof TypeError) {
throw e;
}
}
if (!structureIsValid && warn)
console.warn(
"At least one item under frontend.performance.pointerEventsThrottling" +
"must have fromNbUser set to 0"
);
return isValidAgainstSchema && structureIsValid;
}
/**

View File

@ -1,4 +1,12 @@
import { getThrottling } from "./ConfigService.utils";
class ConfigService {
/**
* @type {object}
* @private
*/
_configFromServer = {};
/**
* @readonly
* @type {boolean}
@ -13,15 +21,9 @@ class ConfigService {
/**
* @readonly
* @type {number}
* @type {{minDistDelta: number, minTimeDelta: number}}
*/
pointerEventsThresholdMinDistDelta = 0;
/**
* @readonly
* @type {number}
*/
pointerEventsThresholdMinTimeDelta = 0;
pointerEventsThrottling = { minDistDelta: 0, minTimeDelta: 0 };
/**
* @readonly
@ -32,19 +34,31 @@ class ConfigService {
/**
* Init the service from the config sent by the server
*
* @param {object} serverResponse
* @param {object} configFromServer
*/
initFromServer(serverResponse) {
const { common } = serverResponse;
initFromServer(configFromServer) {
this._configFromServer = configFromServer;
const { common } = configFromServer;
const { readOnlyOnWhiteboardLoad, showSmallestScreenIndicator, performance } = common;
this.readOnlyOnWhiteboardLoad = readOnlyOnWhiteboardLoad;
this.showSmallestScreenIndicator = showSmallestScreenIndicator;
this.pointerEventsThresholdMinDistDelta = performance.pointerEventsThreshold.minDistDelta;
this.pointerEventsThresholdMinTimeDelta = performance.pointerEventsThreshold.minTimeDelta;
this.refreshInfoInterval = 1000 / performance.refreshInfoFreq;
console.log("Whiteboard config from server:", serverResponse, "parsed:", this);
console.log("Whiteboard config from server:", configFromServer, "parsed:", this);
}
/**
* TODO
*/
refreshNbUserDependant(nbUser) {
const { _configFromServer } = this;
const { common } = _configFromServer;
const { performance } = common;
const { pointerEventsThrottling } = performance;
this.pointerEventsThrottling = getThrottling(pointerEventsThrottling, nbUser);
}
}

View File

@ -0,0 +1,21 @@
/**
* Helper to extract the correct correct throttling values based on the config and the number of user
*
* @param {Array.<{fromNbUser: number, minDistDelta: number, maxFreq: number}>} pointerEventsThrottling
* @param {number} nbUser
* @return {{minDistDelta: number, minTimeDelta: number}}
*/
export function getThrottling(pointerEventsThrottling, nbUser) {
let tmpOut = pointerEventsThrottling[0];
let lastDistToNbUser = nbUser - tmpOut.fromNbUser;
if (lastDistToNbUser < 0) lastDistToNbUser = Number.MAX_VALUE;
for (const el of pointerEventsThrottling) {
const distToNbUser = nbUser - el.fromNbUser;
if (el.fromNbUser <= nbUser && distToNbUser <= lastDistToNbUser) {
tmpOut = el;
lastDistToNbUser = distToNbUser;
}
}
return { minDistDelta: tmpOut.minDistDelta, minTimeDelta: 1000 * (1 / tmpOut.maxFreq) };
}

View File

@ -0,0 +1,29 @@
import { getThrottling } from "./ConfigService.utils";
test("Simple throttling config", () => {
const throttling = [{ fromNbUser: 0, minDistDelta: 1, maxFreq: 1 }];
const target0 = { minDistDelta: 1, minTimeDelta: 1000 };
expect(getThrottling(throttling, 0)).toEqual(target0);
const target100 = { minDistDelta: 1, minTimeDelta: 1000 };
expect(getThrottling(throttling, 100)).toEqual(target100);
});
test("Complex throttling config", () => {
// mix ordering
const throttling = [
{ fromNbUser: 100, minDistDelta: 100, maxFreq: 1 },
{ fromNbUser: 0, minDistDelta: 1, maxFreq: 1 },
{ fromNbUser: 50, minDistDelta: 50, maxFreq: 1 },
];
const target0 = { minDistDelta: 1, minTimeDelta: 1000 };
expect(getThrottling(throttling, 0)).toEqual(target0);
const target50 = { minDistDelta: 50, minTimeDelta: 1000 };
expect(getThrottling(throttling, 50)).toEqual(target50);
const target100 = { minDistDelta: 100, minTimeDelta: 1000 };
expect(getThrottling(throttling, 100)).toEqual(target100);
});

View File

@ -14,9 +14,9 @@ class InfoService {
* Holds the number of user connected to the server
*
* @type {number}
* @private
* @readonly
*/
_nbConnectedUsers = 0;
nbConnectedUsers = -1;
/**
*
@ -49,7 +49,11 @@ class InfoService {
* @param {{w: number, h: number}} smallestScreenResolution
*/
updateInfoFromServer({ nbConnectedUsers, smallestScreenResolution = undefined }) {
this._nbConnectedUsers = nbConnectedUsers;
if (this.nbConnectedUsers !== nbConnectedUsers) {
// Refresh config service parameters on nb connected user change
ConfigService.refreshNbUserDependant(nbConnectedUsers);
}
this.nbConnectedUsers = nbConnectedUsers;
if (smallestScreenResolution) {
this._smallestScreenResolution = smallestScreenResolution;
}
@ -73,7 +77,7 @@ class InfoService {
refreshDisplayedInfo() {
$("#messageReceivedCount")[0].innerText = String(this._nbMessagesReceived);
$("#messageSentCount")[0].innerText = String(this._nbMessagesSent);
$("#connectedUsersCount")[0].innerText = String(this._nbConnectedUsers);
$("#connectedUsersCount")[0].innerText = String(this.nbConnectedUsers);
const { _smallestScreenResolution: ssr } = this;
$("#smallestScreenResolution")[0].innerText = ssr ? `(${ssr.w}, ${ssr.h})` : "Unknown";
}

View File

@ -25,10 +25,10 @@ class ThrottlingService {
throttle(newPosition, onSuccess) {
const newTime = getCurrentTimeMs();
const { _lastPointPosition, _lastSuccessTime } = this;
if (newTime - _lastSuccessTime > ConfigService.pointerEventsThresholdMinTimeDelta) {
if (newTime - _lastSuccessTime > ConfigService.pointerEventsThrottling.minTimeDelta) {
if (
_lastPointPosition.distTo(newPosition) >
ConfigService.pointerEventsThresholdMinDistDelta
ConfigService.pointerEventsThrottling.minDistDelta
) {
onSuccess();
this._lastPointPosition = newPosition;