From af2a27c730fb0e0a4112803f7e01969ec3c72c0e Mon Sep 17 00:00:00 2001
From: Rodrigo Nascimento <rodrigoknascimento@gmail.com>
Date: Mon, 21 Mar 2016 20:45:40 -0300
Subject: [PATCH] Improve sendMessage performance by 300%

---
 packages/rocketchat-lib/lib/callbacks.coffee  | 22 ++++--
 .../server/lib/notifyUsersOnMessage.js        |  4 +-
 .../server/lib/sendNotificationsOnMessage.js  | 72 ++++++++++---------
 .../rocketchat-lib/server/models/Users.coffee |  8 +++
 .../server/models/Subscriptions.js            | 67 ++++++++++-------
 5 files changed, 108 insertions(+), 65 deletions(-)

diff --git a/packages/rocketchat-lib/lib/callbacks.coffee b/packages/rocketchat-lib/lib/callbacks.coffee
index e9f8ffc8662..ebbe30ad649 100644
--- a/packages/rocketchat-lib/lib/callbacks.coffee
+++ b/packages/rocketchat-lib/lib/callbacks.coffee
@@ -6,6 +6,8 @@
 ###
 RocketChat.callbacks = {}
 
+RocketChat.callbacks.showTime = false
+
 ###
 # Callback priorities
 ###
@@ -19,7 +21,6 @@ RocketChat.callbacks.priority =
 # @param {String} hook - The name of the hook
 # @param {Function} callback - The callback function
 ###
-
 RocketChat.callbacks.add = (hook, callback, priority, id) ->
 	# if callback array doesn't exist yet, initialize it
 	priority ?= RocketChat.callbacks.priority.MEDIUM
@@ -28,12 +29,16 @@ RocketChat.callbacks.add = (hook, callback, priority, id) ->
 	callback.priority = priority
 	callback.id = id or Random.id()
 	RocketChat.callbacks[hook] ?= []
-	
+
+	if RocketChat.callbacks.showTime is true
+		err = new Error
+		callback.stack = err.stack
+
 	# Avoid adding the same callback twice
 	for cb in RocketChat.callbacks[hook]
 		if cb.id is callback.id
 			return
-			
+
 	RocketChat.callbacks[hook].push callback
 	return
 
@@ -62,7 +67,16 @@ RocketChat.callbacks.run = (hook, item, constant) ->
 		# if the hook exists, and contains callbacks to run
 		_.sortBy(callbacks, (callback) -> return callback.priority or RocketChat.callbacks.priority.MEDIUM).reduce (result, callback) ->
 			# console.log(callback.name);
-			callback result, constant
+			if RocketChat.callbacks.showTime is true
+				time = Date.now()
+
+			callbackResult = callback result, constant
+
+			if RocketChat.callbacks.showTime is true
+				console.log hook, Date.now() - time
+				console.log callback.stack.split('\n')[2]
+
+			return callbackResult
 		, item
 	else
 		# else, just return the item unchanged
diff --git a/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js b/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
index 59de2faa8ec..9a7547219b5 100644
--- a/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
+++ b/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
@@ -13,7 +13,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 	 * @returns {boolean}
      */
 	function messageContainsHighlight(message, highlights) {
-		if (! highlights || highlights.length == 0) { return false; }
+		if (! highlights || highlights.length === 0) { return false; }
 
 		var has = false;
 		highlights.some(function (highlight) {
@@ -36,7 +36,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 		mentionIds = [];
 		highlightsIds = [];
 		toAll = false;
-		highlights = RocketChat.models.Users.findUsersByUsernames(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch();
+		highlights = RocketChat.models.Users.findUsersByUsernamesWithHighlights(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch();
 
 		if (message.mentions != null) {
 			message.mentions.forEach(function(mention) {
diff --git a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
index f03ac3e2b69..1cd04f8fc10 100644
--- a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
+++ b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
@@ -1,3 +1,5 @@
+/* globals Push */
+
 RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 	// skips this callback if the message was edited
 	if (message.editedAt) {
@@ -9,7 +11,14 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 	/*
 	Increment unread couter if direct messages
 	 */
-	var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+	var indexOf = [].indexOf || function(item) {
+		for (var i = 0, l = this.length; i < l; i++) {
+			if (i in this && this[i] === item) {
+				return i;
+			}
+		}
+		return -1;
+	};
 
 	var settings, desktopMentionIds, i, j, len, len1, highlights, mentionIds, highlightsIds, usersWithHighlights, mobileMentionIds, ref, ref1, toAll, userIdsToNotify, userIdsToPushNotify, userOfMention, userOfMentionId, usersOfDesktopMentions, usersOfMentionId, usersOfMentionItem, usersOfMobileMentions;
 
@@ -39,7 +48,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 	 * @returns {boolean}
 	 */
 	function messageContainsHighlight(message, highlights) {
-		if (! highlights || highlights.length == 0) { return false; }
+		if (! highlights || highlights.length === 0) { return false; }
 
 		var has = false;
 		highlights.some(function (highlight) {
@@ -55,30 +64,26 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 
 	settings = {};
 
-	settings.alwaysNotifyDesktopUsers = _.compact(_.map(RocketChat.models.Subscriptions.findAlwaysNotifyDesktopUsersByRoomId(room._id).fetch(), function(subscription) {
-		var ref;
-		return subscription != null ? (ref = subscription.u) != null ? ref._id : void 0 : void 0;
-	}));
-
-	settings.dontNotifyDesktopUsers = _.compact(_.map(RocketChat.models.Subscriptions.findDontNotifyDesktopUsersByRoomId(room._id).fetch(), function(subscription) {
-		var ref;
-		return subscription != null ? (ref = subscription.u) != null ? ref._id : void 0 : void 0;
-	}));
-
-	settings.alwaysNotifyMobileUsers = _.compact(_.map(RocketChat.models.Subscriptions.findAlwaysNotifyMobileUsersByRoomId(room._id).fetch(), function(subscription) {
-		var ref;
-		return subscription != null ? (ref = subscription.u) != null ? ref._id : void 0 : void 0;
-	}));
-
-	settings.dontNotifyMobileUsers = _.compact(_.map(RocketChat.models.Subscriptions.findDontNotifyMobileUsersByRoomId(room._id).fetch(), function(subscription) {
-		var ref;
-		return subscription != null ? (ref = subscription.u) != null ? ref._id : void 0 : void 0;
-	}));
+	settings.alwaysNotifyDesktopUsers = [];
+	settings.dontNotifyDesktopUsers = [];
+	settings.alwaysNotifyMobileUsers = [];
+	settings.dontNotifyMobileUsers = [];
+	RocketChat.models.Subscriptions.findNotificationPreferencesByRoom(room._id).forEach(function(subscription) {
+		if (subscription.desktopNotifications === 'all') {
+			settings.alwaysNotifyDesktopUsers.push(subscription.u._id);
+		} else if (subscription.desktopNotifications === 'nothing') {
+			settings.dontNotifyDesktopUsers.push(subscription.u._id);
+		} else if (subscription.mobilePushNotifications === 'all') {
+			settings.alwaysNotifyMobileUsers.push(subscription.u._id);
+		} else if (subscription.mobilePushNotifications === 'nothing') {
+			settings.dontNotifyMobileUsers.push(subscription.u._id);
+		}
+	});
 
 	userIdsToNotify = [];
 	userIdsToPushNotify = [];
 	usersWithHighlights = [];
-	highlights = RocketChat.models.Users.findUsersByUsernames(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch();
+	highlights = RocketChat.models.Users.findUsersByUsernamesWithHighlights(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch();
 
 	highlights.forEach(function (user) {
 		if (user && user.settings && user.settings.preferences && messageContainsHighlight(message, user.settings.preferences.highlights)) {
@@ -86,19 +91,22 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 		}
 	});
 
+	let push_message;
 	//Set variables depending on Push Notification settings
 	if (RocketChat.settings.get('Push_show_message')) {
-		push_message = message.msg
+		push_message = message.msg;
 	} else {
-		push_message = ' '
+		push_message = ' ';
 	}
 
+	let push_username;
+	let push_room;
 	if (RocketChat.settings.get('Push_show_username_room')) {
-		push_username = "@" + user.username
-		push_room = "#" + room.name + " "
+		push_username = '@' + user.username;
+		push_room = '#' + room.name + ' ';
 	} else {
-		push_username = ' '
-		push_room = ' '
+		push_username = ' ';
+		push_room = ' ';
 	}
 
 	if ((room.t == null) || room.t === 'd') {
@@ -113,7 +121,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 		});
 		if ((userOfMention != null) && canBeNotified(userOfMentionId, 'mobile')) {
 			RocketChat.Notifications.notifyUser(userOfMention._id, 'notification', {
-				title: "@" + user.username,
+				title: '@' + user.username,
 				text: message.msg,
 				payload: {
 					rid: message.rid,
@@ -131,7 +139,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 					text: push_message,
 					apn: {
 						// ternary operator
-						text: push_username + ((push_username != ' ' && push_message != ' ') ? ":\n" : '') + push_message
+						text: push_username + ((push_username !== ' ' && push_message !== ' ') ? ':\n' : '') + push_message
 					},
 					badge: 1,
 					sound: 'chime',
@@ -241,7 +249,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 			for (j = 0, len1 = userIdsToNotify.length; j < len1; j++) {
 				usersOfMentionId = userIdsToNotify[j];
 				RocketChat.Notifications.notifyUser(usersOfMentionId, 'notification', {
-					title: "@" + user.username + " @ #" + room.name,
+					title: '@' + user.username + ' @ #' + room.name,
 					text: message.msg,
 					payload: {
 						rid: message.rid,
@@ -261,7 +269,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
 					text: push_message,
 					apn: {
 						// ternary operator
-						text: push_room + push_username + ((push_username != ' ' && push_room != ' ' && push_message != ' ') ? ":\n" : '') + push_message
+						text: push_room + push_username + ((push_username !== ' ' && push_room !== ' ' && push_message !== ' ') ? ':\n' : '') + push_message
 					},
 					badge: 1,
 					sound: 'chime',
diff --git a/packages/rocketchat-lib/server/models/Users.coffee b/packages/rocketchat-lib/server/models/Users.coffee
index 012f91aaab7..0186d5a8756 100644
--- a/packages/rocketchat-lib/server/models/Users.coffee
+++ b/packages/rocketchat-lib/server/models/Users.coffee
@@ -62,6 +62,14 @@ RocketChat.models.Users = new class extends RocketChat.models._Base
 
 		return @find query, options
 
+	findUsersByUsernamesWithHighlights: (username, options) ->
+		query =
+			username: username
+			'settings.preferences.highlights':
+				$exists: true
+
+		return @find query, options
+
 	findActiveByUsernameRegexWithExceptions: (username, exceptions = [], options = {}) ->
 		if not _.isArray exceptions
 			exceptions = [ exceptions ]
diff --git a/packages/rocketchat-push-notifications/server/models/Subscriptions.js b/packages/rocketchat-push-notifications/server/models/Subscriptions.js
index c421bcd634f..2a15a3be81b 100644
--- a/packages/rocketchat-push-notifications/server/models/Subscriptions.js
+++ b/packages/rocketchat-push-notifications/server/models/Subscriptions.js
@@ -1,80 +1,93 @@
 RocketChat.models.Subscriptions.updateDesktopNotificationsById = function(_id, desktopNotifications) {
-	query = {
+	const query = {
 		_id: _id
-	}
+	};
 
-	update = {
+	const update = {
 		$set: {
 			desktopNotifications: desktopNotifications
 		}
-	}
+	};
 
 	return this.update(query, update);
-}
+};
 
 RocketChat.models.Subscriptions.updateMobilePushNotificationsById = function(_id, mobilePushNotifications) {
-	query = {
+	const query = {
 		_id: _id
-	}
+	};
 
-	update = {
+	const update = {
 		$set: {
 			mobilePushNotifications: mobilePushNotifications
 		}
-	}
+	};
 
 	return this.update(query, update);
-}
+};
 
 RocketChat.models.Subscriptions.updateEmailNotificationsById = function(_id, emailNotifications) {
-	query = {
+	const query = {
 		_id: _id
-	}
+	};
 
-	update = {
+	const update = {
 		$set: {
 			emailNotifications: emailNotifications
 		}
-	}
+	};
 
 	return this.update(query, update);
-}
+};
 
 RocketChat.models.Subscriptions.findAlwaysNotifyDesktopUsersByRoomId = function(roomId) {
-	query = {
+	const query = {
 		rid: roomId,
 		desktopNotifications: 'all'
-	}
+	};
 
 	return this.find(query);
-}
+};
 
 RocketChat.models.Subscriptions.findDontNotifyDesktopUsersByRoomId = function(roomId) {
-	query = {
+	const query = {
 		rid: roomId,
 		desktopNotifications: 'nothing'
-	}
+	};
 
 	return this.find(query);
-}
+};
 
 RocketChat.models.Subscriptions.findAlwaysNotifyMobileUsersByRoomId = function(roomId) {
-	query = {
+	const query = {
 		rid: roomId,
 		mobilePushNotifications: 'all'
-	}
+	};
 
 	return this.find(query);
-}
+};
 
 RocketChat.models.Subscriptions.findDontNotifyMobileUsersByRoomId = function(roomId) {
-	query = {
+	const query = {
 		rid: roomId,
 		mobilePushNotifications: 'nothing'
-	}
+	};
 
 	return this.find(query);
-}
+};
+
+RocketChat.models.Subscriptions.findNotificationPreferencesByRoom = function(roomId) {
+	const query = {
+		rid: roomId,
+		'u._id': {$exists: true},
+		$or: [
+			{desktopNotifications: {$exists: true}},
+			{mobilePushNotifications: {$exists: true}}
+		]
+	};
+
+	return this.find(query);
+};
 
 RocketChat.models.Subscriptions.findWithSendEmailByRoomId = function(roomId) {
 	var query = {
-- 
GitLab