feat: throttling configuration
This commit is contained in:
parent
409681b217
commit
ca47c41c69
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
21
src/js/services/ConfigService.utils.js
Normal file
21
src/js/services/ConfigService.utils.js
Normal 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) };
|
||||
}
|
29
src/js/services/ConfigService.utils.test.js
Normal file
29
src/js/services/ConfigService.utils.test.js
Normal 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);
|
||||
});
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user