perf: cache groups:createtime

when deleting users, each user.delete() was loading groups:createtime
This commit is contained in:
Barış Soner Uşaklı
2026-03-09 17:30:20 -04:00
parent c4d7002647
commit c4e9e1cbf0
6 changed files with 24 additions and 8 deletions

View File

@@ -15,9 +15,8 @@ groupsController.get = async function (req, res) {
const payload = res.locals.userData; const payload = res.locals.userData;
let groupsData = await groups.getUserGroups([res.locals.uid]); const groupsData = payload.groups.filter(Boolean);
groupsData = groupsData[0]; const groupNames = groupsData.map(group => group.name);
const groupNames = groupsData.filter(Boolean).map(group => group.name);
const members = await groups.getMemberUsers(groupNames, 0, 3); const members = await groups.getMemberUsers(groupNames, 0, 3);
groupsData.forEach((group, index) => { groupsData.forEach((group, index) => {
group.members = members[index]; group.members = members[index];

View File

@@ -4,6 +4,7 @@ const meta = require('../meta');
const plugins = require('../plugins'); const plugins = require('../plugins');
const slugify = require('../slugify'); const slugify = require('../slugify');
const db = require('../database'); const db = require('../database');
const cache = require('../cache');
module.exports = function (Groups) { module.exports = function (Groups) {
Groups.create = async function (data) { Groups.create = async function (data) {
@@ -47,7 +48,7 @@ module.exports = function (Groups) {
await db.sortedSetAdd('groups:createtime', groupData.createtime, groupData.name); await db.sortedSetAdd('groups:createtime', groupData.createtime, groupData.name);
await db.setObject(`group:${groupData.name}`, groupData); await db.setObject(`group:${groupData.name}`, groupData);
cache.del(`zset:groups:createtime`);
if (data.hasOwnProperty('ownerUid')) { if (data.hasOwnProperty('ownerUid')) {
await db.setAdd(`group:${groupData.name}:owners`, data.ownerUid); await db.setAdd(`group:${groupData.name}:owners`, data.ownerUid);
await db.sortedSetAdd(`group:${groupData.name}:members`, timestamp, data.ownerUid); await db.sortedSetAdd(`group:${groupData.name}:members`, timestamp, data.ownerUid);

View File

@@ -4,6 +4,7 @@ const plugins = require('../plugins');
const slugify = require('../slugify'); const slugify = require('../slugify');
const db = require('../database'); const db = require('../database');
const batch = require('../batch'); const batch = require('../batch');
const cache = require('../cache');
module.exports = function (Groups) { module.exports = function (Groups) {
Groups.destroy = async function (groupNames) { Groups.destroy = async function (groupNames) {
@@ -44,6 +45,7 @@ module.exports = function (Groups) {
removeGroupsFromPrivilegeGroups(groupNames), removeGroupsFromPrivilegeGroups(groupNames),
]); ]);
Groups.cache.reset(); Groups.cache.reset();
cache.del(`zset:groups:createtime`);
plugins.hooks.fire('action:groups.destroy', { groups: groupsData }); plugins.hooks.fire('action:groups.destroy', { groups: groupsData });
}; };

View File

@@ -5,6 +5,7 @@ const db = require('../database');
const plugins = require('../plugins'); const plugins = require('../plugins');
const privileges = require('../privileges'); const privileges = require('../privileges');
const slugify = require('../slugify'); const slugify = require('../slugify');
const cache = require('../cache');
const Groups = module.exports; const Groups = module.exports;
@@ -99,8 +100,10 @@ Groups.getNonPrivilegeGroups = async function (set, start, stop, flags) {
ephemeral: true, ephemeral: true,
}; };
} }
const useCache = set === 'groups:createtime' && parseInt(start, 10) === 0 && parseInt(stop, 10) === -1;
let groupNames = await db.getSortedSetRevRange(set, start, stop); let groupNames = useCache ?
await Groups.getAllGroupNames('groups:createtime') :
await db.getSortedSetRevRange(set, start, stop);
groupNames = groupNames.filter(groupName => !Groups.isPrivilegeGroup(groupName)); groupNames = groupNames.filter(groupName => !Groups.isPrivilegeGroup(groupName));
if (flags.ephemeral) { if (flags.ephemeral) {
groupNames = groupNames.concat(Groups.ephemeralGroups); groupNames = groupNames.concat(Groups.ephemeralGroups);
@@ -110,6 +113,17 @@ Groups.getNonPrivilegeGroups = async function (set, start, stop, flags) {
return groupsData.filter(Boolean); return groupsData.filter(Boolean);
}; };
Groups.getAllGroupNames = async function (set) {
const cacheKey = `zset:${set}`;
let names = cache.get(cacheKey);
if (names !== undefined) {
return [...names];
}
names = await db.getSortedSetRevRange(set, 0, -1);
cache.set(names);
return [...names];
};
Groups.getGroups = async function (set, start, stop) { Groups.getGroups = async function (set, start, stop) {
return await db.getSortedSetRevRange(set, start, stop); return await db.getSortedSetRevRange(set, start, stop);
}; };

View File

@@ -105,7 +105,7 @@ module.exports = function (Groups) {
} }
Groups.leaveAllGroups = async function (uid) { Groups.leaveAllGroups = async function (uid) {
const groups = await db.getSortedSetRange('groups:createtime', 0, -1); const groups = await Groups.getAllGroupNames('groups:createtime');
await Promise.all([ await Promise.all([
Groups.leave(groups, uid), Groups.leave(groups, uid),
Groups.rejectMembership( Groups.rejectMembership(

View File

@@ -158,7 +158,7 @@ helpers.getUserPrivileges = async function (cid, userPrivileges) {
helpers.getGroupPrivileges = async function (cid, groupPrivileges) { helpers.getGroupPrivileges = async function (cid, groupPrivileges) {
const [memberSets, allGroupNames] = await Promise.all([ const [memberSets, allGroupNames] = await Promise.all([
groups.getMembersOfGroups(groupPrivileges.map(privilege => `cid:${cid}:privileges:${privilege}`)), groups.getMembersOfGroups(groupPrivileges.map(privilege => `cid:${cid}:privileges:${privilege}`)),
groups.getGroups('groups:createtime', 0, -1), groups.getAllGroupNames('groups:createtime'),
]); ]);
const uniqueGroups = _.uniq(_.flatten(memberSets)); const uniqueGroups = _.uniq(_.flatten(memberSets));