PadMessageHandler: Replace `channels` package with async-friendly class
parent
5c1177a3d9
commit
85919ff914
|
@ -34,11 +34,9 @@ const messageLogger = log4js.getLogger('message');
|
||||||
const accessLogger = log4js.getLogger('access');
|
const accessLogger = log4js.getLogger('access');
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks.js');
|
||||||
const channels = require('channels');
|
|
||||||
const stats = require('../stats');
|
const stats = require('../stats');
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const {RateLimiterMemory} = require('rate-limiter-flexible');
|
const {RateLimiterMemory} = require('rate-limiter-flexible');
|
||||||
const util = require('util');
|
|
||||||
const webaccess = require('../hooks/express/webaccess');
|
const webaccess = require('../hooks/express/webaccess');
|
||||||
|
|
||||||
let rateLimiter;
|
let rateLimiter;
|
||||||
|
@ -84,11 +82,44 @@ stats.gauge('activePads', () => {
|
||||||
return padIds.size;
|
return padIds.size;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes one task at a time per channel.
|
||||||
|
*/
|
||||||
|
class Channels {
|
||||||
|
/**
|
||||||
|
* @param {(ch, task) => any} [exec] - Task executor. If omitted, tasks are assumed to be
|
||||||
|
* functions that will be executed with the channel as the only argument.
|
||||||
|
*/
|
||||||
|
constructor(exec = (ch, task) => task(ch)) {
|
||||||
|
this._exec = exec;
|
||||||
|
this._promiseChains = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a task for execution. The task will be executed once all previously enqueued tasks
|
||||||
|
* for the named channel have completed.
|
||||||
|
*
|
||||||
|
* @param {any} ch - Identifies the channel.
|
||||||
|
* @param {any} task - The task to give to the executor.
|
||||||
|
* @returns {Promise<any>} The value returned by the executor.
|
||||||
|
*/
|
||||||
|
async enqueue(ch, task) {
|
||||||
|
const p = (this._promiseChains.get(ch) || Promise.resolve()).then(() => this._exec(ch, task));
|
||||||
|
const pc = p
|
||||||
|
.catch(() => {}) // Prevent rejections from halting the queue.
|
||||||
|
.then(() => {
|
||||||
|
// Clean up this._promiseChains if there are no more tasks for the channel.
|
||||||
|
if (this._promiseChains.get(ch) === pc) this._promiseChains.delete(ch);
|
||||||
|
});
|
||||||
|
this._promiseChains.set(ch, pc);
|
||||||
|
return await p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A changeset queue per pad that is processed by handleUserChanges()
|
* A changeset queue per pad that is processed by handleUserChanges()
|
||||||
*/
|
*/
|
||||||
const padChannels =
|
const padChannels = new Channels((ch, {socket, message}) => handleUserChanges(socket, message));
|
||||||
new channels.channels(({socket, message}, cb) => handleUserChangesCb(socket, message, cb));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Method is called by server.js to tell the message handler on which socket it should send
|
* This Method is called by server.js to tell the message handler on which socket it should send
|
||||||
|
@ -265,7 +296,7 @@ exports.handleMessage = async (socket, message) => {
|
||||||
messageLogger.warn('Dropped message, COLLABROOM for readonly pad');
|
messageLogger.warn('Dropped message, COLLABROOM for readonly pad');
|
||||||
} else if (message.data.type === 'USER_CHANGES') {
|
} else if (message.data.type === 'USER_CHANGES') {
|
||||||
stats.counter('pendingEdits').inc();
|
stats.counter('pendingEdits').inc();
|
||||||
padChannels.emit(message.padId, {socket, message}); // add to pad queue
|
await padChannels.enqueue(message.padId, {socket, message});
|
||||||
} else if (message.data.type === 'USERINFO_UPDATE') {
|
} else if (message.data.type === 'USERINFO_UPDATE') {
|
||||||
await handleUserInfoUpdate(socket, message);
|
await handleUserInfoUpdate(socket, message);
|
||||||
} else if (message.data.type === 'CHAT_MESSAGE') {
|
} else if (message.data.type === 'CHAT_MESSAGE') {
|
||||||
|
@ -690,7 +721,6 @@ const handleUserChanges = async (socket, message) => {
|
||||||
|
|
||||||
stopWatch.end();
|
stopWatch.end();
|
||||||
};
|
};
|
||||||
const handleUserChangesCb = util.callbackify(handleUserChanges);
|
|
||||||
|
|
||||||
exports.updatePadClients = async (pad) => {
|
exports.updatePadClients = async (pad) => {
|
||||||
// skip this if no-one is on this pad
|
// skip this if no-one is on this pad
|
||||||
|
|
|
@ -1233,11 +1233,6 @@
|
||||||
"supports-color": "^5.3.0"
|
"supports-color": "^5.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"channels": {
|
|
||||||
"version": "0.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz",
|
|
||||||
"integrity": "sha1-G+4yPt6hUrue8E9BvG5rD1lIqUE="
|
|
||||||
},
|
|
||||||
"character-entities-html4": {
|
"character-entities-html4": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^3.2.1",
|
"async": "^3.2.1",
|
||||||
"channels": "0.0.4",
|
|
||||||
"clean-css": "^5.2.1",
|
"clean-css": "^5.2.1",
|
||||||
"cookie-parser": "1.4.5",
|
"cookie-parser": "1.4.5",
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.3",
|
||||||
|
|
Loading…
Reference in New Issue