Implement manual network/channel cloning for better performance
This commit is contained in:
parent
41e121d270
commit
552f3da67e
@ -196,7 +196,7 @@ Client.prototype.connect = function(args) {
|
||||
|
||||
client.networks.push(network);
|
||||
client.emit("network", {
|
||||
networks: [network],
|
||||
networks: [network.getFilteredClone(this.lastActiveChannel, -1)],
|
||||
});
|
||||
|
||||
if (config.lockNetwork) {
|
||||
|
@ -136,11 +136,39 @@ Chan.prototype.removeUser = function(user) {
|
||||
this.users.delete(user.nick.toLowerCase());
|
||||
};
|
||||
|
||||
Chan.prototype.toJSON = function() {
|
||||
var clone = _.clone(this);
|
||||
clone.users = []; // Do not send user list, the client will explicitly request it when needed
|
||||
clone.messages = clone.messages.slice(-1);
|
||||
return clone;
|
||||
/**
|
||||
* Get a clean clone of this channel that will be sent to the client.
|
||||
* This function performs manual cloning of channel object for
|
||||
* better control of performance and memory usage.
|
||||
*
|
||||
* @param {(int|bool)} lastActiveChannel - Last known active user channel id (needed to control how many messages are sent)
|
||||
* If true, channel is assumed active.
|
||||
* @param {int} lastMessage - Last message id seen by active client to avoid sending duplicates.
|
||||
*/
|
||||
Chan.prototype.getFilteredClone = function(lastActiveChannel, lastMessage) {
|
||||
return Object.keys(this).reduce((newChannel, prop) => {
|
||||
if (prop === "users") {
|
||||
// Do not send users, client requests updated user list whenever needed
|
||||
newChannel[prop] = [];
|
||||
} else if (prop === "messages") {
|
||||
// If channel is active, send up to 100 last messages, for all others send just 1
|
||||
// Client will automatically load more messages whenever needed based on last seen messages
|
||||
const messagesToSend = lastActiveChannel === true || this.id === lastActiveChannel ? -100 : -1;
|
||||
|
||||
// If client is reconnecting, only send new messages that client has not seen yet
|
||||
if (lastMessage > -1) {
|
||||
newChannel[prop] = this[prop]
|
||||
.filter((m) => m.id > lastMessage)
|
||||
.slice(messagesToSend);
|
||||
} else {
|
||||
newChannel[prop] = this[prop].slice(messagesToSend);
|
||||
}
|
||||
} else {
|
||||
newChannel[prop] = this[prop];
|
||||
}
|
||||
|
||||
return newChannel;
|
||||
}, {});
|
||||
};
|
||||
|
||||
function writeUserLog(client, msg) {
|
||||
|
@ -7,6 +7,17 @@ module.exports = Network;
|
||||
|
||||
let id = 1;
|
||||
|
||||
/**
|
||||
* @type {Object} List of keys which should not be sent to the client.
|
||||
*/
|
||||
const filteredFromClient = {
|
||||
awayMessage: true,
|
||||
chanCache: true,
|
||||
highlightRegex: true,
|
||||
irc: true,
|
||||
password: true,
|
||||
};
|
||||
|
||||
function Network(attr) {
|
||||
_.defaults(this, attr, {
|
||||
name: "",
|
||||
@ -63,14 +74,27 @@ Network.prototype.setNick = function(nick) {
|
||||
);
|
||||
};
|
||||
|
||||
Network.prototype.toJSON = function() {
|
||||
return _.omit(this, [
|
||||
"awayMessage",
|
||||
"chanCache",
|
||||
"highlightRegex",
|
||||
"irc",
|
||||
"password",
|
||||
]);
|
||||
/**
|
||||
* Get a clean clone of this network that will be sent to the client.
|
||||
* This function performs manual cloning of network object for
|
||||
* better control of performance and memory usage.
|
||||
*
|
||||
* Both of the parameters that are accepted by this function are passed into channels' getFilteredClone call.
|
||||
*
|
||||
* @see {@link Chan#getFilteredClone}
|
||||
*/
|
||||
Network.prototype.getFilteredClone = function(lastActiveChannel, lastMessage) {
|
||||
return Object.keys(this).reduce((newNetwork, prop) => {
|
||||
if (prop === "channels") {
|
||||
// Channels objects perform their own cloning
|
||||
newNetwork[prop] = this[prop].map((channel) => channel.getFilteredClone(lastActiveChannel, lastMessage));
|
||||
} else if (!filteredFromClient[prop]) {
|
||||
// Some properties that are not useful for the client are skipped
|
||||
newNetwork[prop] = this[prop];
|
||||
}
|
||||
|
||||
return newNetwork;
|
||||
}, {});
|
||||
};
|
||||
|
||||
Network.prototype.export = function() {
|
||||
|
@ -420,24 +420,11 @@ function initializeClient(socket, client, token, lastMessage) {
|
||||
socket.join(client.id);
|
||||
|
||||
const sendInitEvent = (tokenToSend) => {
|
||||
let networks = client.networks;
|
||||
|
||||
if (lastMessage > -1) {
|
||||
// We need a deep cloned object because we are going to remove unneeded messages
|
||||
networks = _.cloneDeep(networks);
|
||||
|
||||
networks.forEach((network) => {
|
||||
network.channels.forEach((channel) => {
|
||||
channel.messages = channel.messages.filter((m) => m.id > lastMessage);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
socket.emit("init", {
|
||||
applicationServerKey: manager.webPush.vapidKeys.publicKey,
|
||||
pushSubscription: client.config.sessions[token],
|
||||
active: client.lastActiveChannel,
|
||||
networks: networks,
|
||||
networks: client.networks.map((network) => network.getFilteredClone(client.lastActiveChannel, lastMessage)),
|
||||
token: tokenToSend,
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user