perf(stats): threads, better & parallel queries

This commit is contained in:
Isaac
2025-02-12 01:37:44 +00:00
parent 2ed25f7bbf
commit 6b0146e099
8 changed files with 230 additions and 106 deletions

View File

@@ -6,10 +6,7 @@ const {
ChannelType: { GuildCategory },
} = require('discord.js');
const ms = require('ms');
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../../../../../../lib/stats');
const { getAverageTimes } = require('../../../../../../lib/stats');
module.exports.get = fastify => ({
handler: async req => {
@@ -29,24 +26,40 @@ module.exports.get = fastify => ({
name: true,
requiredRoles: true,
staffRoles: true,
tickets: { where: { open: false } },
tickets: {
select: {
closedAt: true,
createdAt: true,
firstResponseAt: true,
},
where: {
firstResponseAt: { not: null },
open: false,
},
},
},
},
},
where: { id: req.params.guild },
});
categories = categories.map(c => {
const closedTickets = c.tickets.filter(t => t.firstResponseAt && t.closedAt);
c = {
...c,
stats: {
avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(getAvgResponseTime(closedTickets)),
},
};
delete c.tickets;
return c;
});
categories = await Promise.all(
categories.map(async category => {
const {
avgResolutionTime,
avgResponseTime,
} = await getAverageTimes(category.tickets);
category = {
...category,
stats: {
avgResolutionTime: ms(avgResolutionTime),
avgResponseTime: ms(avgResponseTime),
},
};
delete category.tickets;
return category;
}),
);
return categories;
},

View File

@@ -1,10 +1,7 @@
/* eslint-disable no-underscore-dangle */
const { logAdminEvent } = require('../../../../../lib/logging.js');
const { iconURL } = require('../../../../../lib/misc');
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../../../../../lib/stats');
const { getAverageTimes } = require('../../../../../lib/stats');
const ms = require('ms');
module.exports.delete = fastify => ({
@@ -42,39 +39,61 @@ module.exports.get = fastify => ({
if (!cached) {
const guild = client.guilds.cache.get(id);
const settings = await client.prisma.guild.findUnique({ where: { id } }) ??
const settings =
await client.prisma.guild.findUnique({ where: { id } }) ??
await client.prisma.guild.create({ data: { id } });
const categories = await client.prisma.category.findMany({
select: {
_count: { select: { tickets: true } },
id: true,
name: true,
},
where: { guildId: id },
});
const tickets = await client.prisma.ticket.findMany({
select: {
closedAt: true,
createdAt: true,
firstResponseAt: true,
},
where: { guildId: id },
});
const closedTickets = tickets.filter(t => t.firstResponseAt && t.closedAt);
const [
categories,
tags,
tickets,
closedTickets,
] = await Promise.all([
client.prisma.category.findMany({
select: {
_count: { select: { tickets: true } },
id: true,
name: true,
},
where: { guildId: id },
}),
client.prisma.tag.count({ where: { guildId: id } }),
client.prisma.ticket.count(),
client.prisma.ticket.findMany({
select: {
closedAt: true,
createdAt: true,
firstResponseAt: true,
},
where: {
firstResponseAt: { not: null },
guildId: id,
open: false,
},
}),
client.prisma.user.aggregate({
_count: true,
_sum: { messageCount: true },
}),
]);
const {
avgResolutionTime,
avgResponseTime,
} = await getAverageTimes(closedTickets);
cached = {
createdAt: settings.createdAt,
id: guild.id,
logo: iconURL(guild),
name: guild.name,
stats: {
avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(getAvgResponseTime(closedTickets)),
avgResolutionTime: ms(avgResolutionTime),
avgResponseTime: ms(avgResponseTime),
categories: categories.map(c => ({
id: c.id,
name: c.name,
tickets: c._count.tickets,
})),
tags: await client.prisma.tag.count({ where: { guildId: id } }),
tags,
tickets: tickets.length,
},
};

View File

@@ -1,8 +1,9 @@
const {
getAvgResolutionTime, getAvgResponseTime,
} = require('../../lib/stats');
/* eslint-disable no-underscore-dangle */
const ms = require('ms');
const pkg = require('../../../package.json');
const { getAverageTimes } = require('../../lib/stats');
const { quick } = require('../../lib/threads');
module.exports.get = () => ({
handler: async req => {
@@ -10,40 +11,61 @@ module.exports.get = () => ({
const client = req.routeOptions.config.client;
const cacheKey = 'cache/stats/client';
let cached = await client.keyv.get(cacheKey);
if (!cached) {
const tickets = await client.prisma.ticket.findMany({
select: {
closedAt: true,
createdAt: true,
firstResponseAt: true,
},
});
const closedTickets = tickets.filter(t => t.firstResponseAt && t.closedAt);
const users = await client.prisma.user.findMany({ select: { messageCount: true } });
// TODO: background
const [
categories,
members,
tags,
tickets,
closedTickets,
users,
] = await Promise.all([
client.prisma.category.count(),
quick('stats', w => w.sum(client.guilds.cache.map(g => g.memberCount))),
client.prisma.tag.count(),
client.prisma.ticket.count(),
client.prisma.ticket.findMany({
select: {
closedAt: true,
createdAt: true,
firstResponseAt: true,
},
where: {
firstResponseAt: { not: null },
open: false,
},
}),
client.prisma.user.aggregate({
_count: true,
_sum: { messageCount: true },
}),
]);
const {
avgResolutionTime,
avgResponseTime,
} = await getAverageTimes(closedTickets);
cached = {
avatar: client.user.avatarURL(),
discriminator: client.user.discriminator,
id: client.user.id,
public: (process.env.PUBLIC_BOT === 'true'),
stats: {
activatedUsers: users.length,
archivedMessages: users.reduce((total, user) => total + user.messageCount, 0), // don't count archivedMessage table rows, they can be deleted
avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(getAvgResponseTime(closedTickets)),
categories: await client.prisma.category.count(),
activatedUsers: users._count,
archivedMessages: users._sum.messageCount,
avgResolutionTime: ms(avgResolutionTime),
avgResponseTime: ms(avgResponseTime),
categories,
guilds: client.guilds.cache.size,
members: client.guilds.cache.reduce((t, g) => t + g.memberCount, 0),
tags: await client.prisma.tag.count(),
tickets: tickets.length,
members,
tags,
tickets,
},
username: client.user.username,
version: pkg.version,
};
await client.keyv.set(cacheKey, cached, ms('15m'));
}
return cached;
},
});