Improve performance of client presence monitor (#18645)

pull/18671/head
Rodrigo Nascimento 5 years ago committed by GitHub
parent b38e26f1af
commit 930d3879e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      app/ui-sidenav/client/userPresence.js
  2. 71
      imports/startup/client/listenActiveUsers.js

@ -1,12 +1,12 @@
import { Meteor } from 'meteor/meteor';
// import { ReactiveVar } from 'meteor/reactive-var';
import { Accounts } from 'meteor/accounts-base';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import _ from 'underscore';
import mem from 'mem';
import { APIClient } from '../../utils/client';
import { saveUser } from '../../../imports/startup/client/listenActiveUsers';
import { saveUser, interestedUserIds } from '../../../imports/startup/client/listenActiveUsers';
import './userPresence.html';
@ -45,6 +45,7 @@ const getAll = _.debounce(async function getAll() {
}, 1000);
const get = mem(function get(id) {
interestedUserIds.add(id);
const promise = pending.get(id) || new Promise((resolve, reject) => {
promises.set(id, { resolve, reject });
});
@ -71,11 +72,17 @@ const featureExists = !!window.IntersectionObserver;
const observer = featureExists && new IntersectionObserver(handleEntries, options);
let wasConnected = Meteor.status().connected;
Tracker.autorun(() => {
if (!Meteor.userId() || !Meteor.status().connected) {
return Meteor.users.update({}, { $unset: { status: '' } }, { multi: true });
// Only clear statuses on disconnect, prevent process it on reconnect again
const isConnected = Meteor.status().connected;
if (!Meteor.userId() || (wasConnected && !isConnected)) {
wasConnected = isConnected;
return Meteor.users.update({ status: { $exists: true } }, { $unset: { status: true } }, { multi: true });
}
mem.clear(get);
wasConnected = isConnected;
if (featureExists) {
for (const node of data.keys()) {
@ -85,6 +92,10 @@ Tracker.autorun(() => {
return;
}
getAll();
Accounts.onLogout(() => {
interestedUserIds.clear();
});
});
Template.userPresence.onRendered(function() {
@ -95,7 +106,4 @@ Template.userPresence.onRendered(function() {
if (featureExists) {
return observer.observe(this.firstNode);
}
get(this.data.uid);
getAll();
});

@ -1,8 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { debounce } from 'underscore';
import { Accounts } from 'meteor/accounts-base';
import { Notifications } from '../../../app/notifications/client';
import { APIClient } from '../../../app/utils/client';
// mirror of object in /imports/users-presence/server/activeUsers.js - keep updated
const STATUS_MAP = [
@ -12,13 +11,18 @@ const STATUS_MAP = [
'busy',
];
export const interestedUserIds = new Set();
export const saveUser = (user, force = false) => {
// do not update my own user, my user's status will come from a subscription
if (user._id === Meteor.userId()) {
if (user._id === Accounts.connection?._userId) {
return;
}
if (force) {
return Meteor.users.upsert({ _id: user._id }, {
const found = Meteor.users._collection._docs._map[user._id];
if (found && force) {
return Meteor.users.update({ _id: user._id }, {
$set: {
username: user.username,
// name: user.name,
@ -30,66 +34,17 @@ export const saveUser = (user, force = false) => {
});
}
const found = Meteor.users.findOne(user._id, { fields: { _id: 1 } });
if (found) {
return;
if (!found) {
Meteor.users.insert(user);
}
Meteor.users.insert(user);
};
let lastStatusChange = null;
let retry = 0;
const getUsersPresence = debounce(async (isConnected) => {
try {
const params = {};
if (lastStatusChange) {
params.from = lastStatusChange.toISOString();
}
const {
users,
full,
} = await APIClient.v1.get('users.presence', params);
// if is reconnecting, set everyone else to offline
if (full && isConnected) {
Meteor.users.update({
_id: { $ne: Meteor.userId() },
}, {
$set: {
status: 'offline',
},
}, { multi: true });
}
users.forEach((user) => saveUser(user, full));
lastStatusChange = new Date();
} catch (e) {
setTimeout(() => getUsersPresence(isConnected), retry++ * 2000);
}
}, 1000);
Meteor.startup(function() {
Notifications.onLogged('user-status', ([_id, username, status, statusText]) => {
// only set after first request completed
if (lastStatusChange) {
lastStatusChange = new Date();
if (!interestedUserIds.has(_id)) {
return;
}
saveUser({ _id, username, status: STATUS_MAP[status], statusText }, true);
});
Notifications.onLogged('Users:NameChanged', ({ _id, username }) => {
if (!username) {
return;
}
Meteor.users.upsert({ _id }, {
$set: {
username,
},
});
});
});

Loading…
Cancel
Save