mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-26 00:21:16 +01:00
refactor: dont store ap pids in posts:pid
new zset postsRemote:pid in topic purge only make 1 db call to reduce counters
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
"new-users": "New Users",
|
||||
"posts": "Posts",
|
||||
"topics": "Topics",
|
||||
"remote-posts": "Remote Posts",
|
||||
"remote-topics": "Remote Topics",
|
||||
"page-views-seven": "Last 7 Days",
|
||||
"page-views-thirty": "Last 30 Days",
|
||||
"page-views-last-day": "Last 24 hours",
|
||||
|
||||
@@ -130,13 +130,15 @@ async function getStats() {
|
||||
return cachedStats;
|
||||
}
|
||||
|
||||
let results = await Promise.all([
|
||||
let results = (await Promise.all([
|
||||
getStatsFromAnalytics('uniquevisitors', ''),
|
||||
getStatsFromAnalytics('logins', 'loginCount'),
|
||||
getStatsForSet('users:joindate', 'userCount'),
|
||||
getStatsForSet('posts:pid', 'postCount'),
|
||||
getStatsForSet('topics:tid', 'topicCount'),
|
||||
]);
|
||||
meta.config.activitypubEnabled ? getStatsForSet('postsRemote:pid', '') : null,
|
||||
meta.config.activitypubEnabled ? getStatsForSet('topicsRemote:tid', '') : null,
|
||||
])).filter(Boolean);
|
||||
|
||||
results[0].name = '[[admin/dashboard:unique-visitors]]';
|
||||
|
||||
@@ -151,6 +153,13 @@ async function getStats() {
|
||||
results[4].name = '[[admin/dashboard:topics]]';
|
||||
results[4].href = `${nconf.get('relative_path')}/admin/dashboard/topics`;
|
||||
|
||||
if (results[5]) {
|
||||
results[5].name = '[[admin/dashboard:remote-posts]]';
|
||||
}
|
||||
if (results[6]) {
|
||||
results[6].name = '[[admin/dashboard:remote-topics]]';
|
||||
}
|
||||
|
||||
({ results } = await plugins.hooks.fire('filter:admin.getStats', {
|
||||
results,
|
||||
helpers: { getStatsForSet, getStatsFromAnalytics },
|
||||
|
||||
@@ -59,10 +59,10 @@ module.exports = function (Posts) {
|
||||
|
||||
const topicData = await topics.getTopicFields(tid, ['cid', 'pinned']);
|
||||
postData.cid = topicData.cid;
|
||||
|
||||
const isRemote = !utils.isNumber(pid);
|
||||
await Promise.all([
|
||||
db.sortedSetAdd('posts:pid', timestamp, postData.pid),
|
||||
utils.isNumber(pid) ? db.incrObjectField('global', 'postCount') : null,
|
||||
db.sortedSetAdd(!isRemote ? 'posts:pid' : 'postsRemote:pid', timestamp, postData.pid),
|
||||
!isRemote ? db.incrObjectField('global', 'postCount') : null,
|
||||
user.onNewPostMade(postData),
|
||||
topics.onNewPostMade(postData),
|
||||
categories.onNewPostMade(topicData.cid, topicData.pinned, postData),
|
||||
|
||||
@@ -79,7 +79,9 @@ module.exports = function (Posts) {
|
||||
deleteFromGroups(pids),
|
||||
deleteDiffs(pids),
|
||||
deleteFromUploads(pids),
|
||||
db.sortedSetsRemove(['posts:pid', 'posts:votes', 'posts:flagged'], pids),
|
||||
db.sortedSetsRemove([
|
||||
'posts:pid', 'posts:votes', 'posts:flagged', 'postsRemote:pid',
|
||||
], pids),
|
||||
Posts.attachments.empty(pids),
|
||||
activitypub.notes.delete(pids),
|
||||
db.deleteAll(pids.map(pid => `pid:${pid}:editors`)),
|
||||
|
||||
@@ -260,7 +260,9 @@ module.exports = function (Posts) {
|
||||
}
|
||||
await Promise.all([
|
||||
updateTopicVoteCount(postData),
|
||||
db.sortedSetAdd('posts:votes', postData.votes, postData.pid),
|
||||
utils.isNumber(postData.pid) ?
|
||||
db.sortedSetAdd('posts:votes', postData.votes, postData.pid) :
|
||||
null,
|
||||
Posts.setPostFields(postData.pid, {
|
||||
upvotes: postData.upvotes,
|
||||
downvotes: postData.downvotes,
|
||||
|
||||
@@ -138,15 +138,15 @@ module.exports = function (Topics) {
|
||||
|
||||
async function reduceCounters(tid) {
|
||||
const incr = -1;
|
||||
const { cid, postcount } = await Topics.getTopicFields(tid, ['cid', 'postcount']);
|
||||
const postCountChange = incr * postcount;
|
||||
const categoryKey = `${utils.isNumber(cid) ? 'category' : 'categoryRemote'}:${cid}`;
|
||||
const bulkIncr = [
|
||||
[categoryKey, { post_count: postCountChange, topic_count: incr }],
|
||||
];
|
||||
if (utils.isNumber(tid)) {
|
||||
await db.incrObjectFieldBy('global', 'topicCount', incr);
|
||||
bulkIncr.push(['global', { postCount: postCountChange, topicCount: incr }]);
|
||||
}
|
||||
const topicData = await Topics.getTopicFields(tid, ['cid', 'postcount']);
|
||||
const postCountChange = incr * topicData.postcount;
|
||||
await Promise.all([
|
||||
db.incrObjectFieldBy('global', 'postCount', postCountChange),
|
||||
db.incrObjectFieldBy(`${utils.isNumber(topicData.cid) ? 'category' : 'categoryRemote'}:${topicData.cid}`, 'post_count', postCountChange),
|
||||
db.incrObjectFieldBy(`${utils.isNumber(topicData.cid) ? 'category' : 'categoryRemote'}:${topicData.cid}`, 'topic_count', incr),
|
||||
]);
|
||||
await db.incrObjectFieldByBulk(bulkIncr);
|
||||
}
|
||||
};
|
||||
|
||||
37
src/upgrades/4.9.0/postsRemote_zset.js
Normal file
37
src/upgrades/4.9.0/postsRemote_zset.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('../../database');
|
||||
const batch = require('../../batch');
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = {
|
||||
name: 'Move ap pids from posts:pid to postsRemote:pid',
|
||||
timestamp: Date.UTC(2026, 1, 24),
|
||||
method: async function () {
|
||||
const { progress } = this;
|
||||
progress.total = await db.sortedSetCard('posts:pid');
|
||||
const removePosts = [];
|
||||
await batch.processSortedSet('posts:pid', async (postData) => {
|
||||
const apPosts = postData.filter(post => !utils.isNumber(post.value));
|
||||
removePosts.push(...apPosts.map(post => post.value));
|
||||
await db.sortedSetAdd(
|
||||
'postsRemote:pid',
|
||||
apPosts.map(p => p.score),
|
||||
apPosts.map(p => p.value)
|
||||
);
|
||||
progress.incr(postData.length);
|
||||
}, {
|
||||
batch: 500,
|
||||
withScores: true,
|
||||
});
|
||||
|
||||
await batch.processArray(removePosts, async (pids) => {
|
||||
await db.sortedSetsRemove(['posts:pid', 'posts:votes'], pids);
|
||||
}, {
|
||||
batch: 500,
|
||||
});
|
||||
|
||||
const postCount = await db.sortedSetCard('posts:pid');
|
||||
await db.setObjectField('global', 'postCount', postCount);
|
||||
},
|
||||
};
|
||||
@@ -30,5 +30,7 @@ module.exports = {
|
||||
}, {
|
||||
batch: 500,
|
||||
});
|
||||
const topicCount = await db.sortedSetCard('topics:tid');
|
||||
await db.setObjectField('global', 'topicCount', topicCount);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -782,6 +782,45 @@ describe('Topic\'s', () => {
|
||||
assert.strictEqual(false, isMember);
|
||||
});
|
||||
|
||||
it('should update global & category topic/post counters when topic is purged', async () => {
|
||||
const category = await categories.create({
|
||||
name: 'Category for purge count test',
|
||||
});
|
||||
const { topicCount, postCount } = await db.getObject('global');
|
||||
|
||||
const cid = category.cid;
|
||||
const topic1 = await topics.post({
|
||||
uid: adminUid,
|
||||
title: 'topic for purge count test',
|
||||
content: 'topic content',
|
||||
cid,
|
||||
});
|
||||
await topics.post({
|
||||
uid: adminUid,
|
||||
title: 'topic for purge count test',
|
||||
content: 'topic content',
|
||||
cid,
|
||||
});
|
||||
const tid1 = topic1.topicData.tid;
|
||||
await topics.reply({ uid: adminUid, content: 'reply 1', tid: tid1 });
|
||||
await topics.reply({ uid: adminUid, content: 'reply 2', tid: tid1 });
|
||||
await topics.reply({ uid: adminUid, content: 'reply 3', tid: tid1 });
|
||||
let categoryData = await categories.getCategoriesFields([cid], ['topic_count', 'post_count']);
|
||||
assert.strictEqual(categoryData[0].topic_count, 2);
|
||||
assert.strictEqual(categoryData[0].post_count, 5);
|
||||
|
||||
await apiTopics.purge({ uid: adminUid }, { tids: [tid1], cid: categoryObj.cid });
|
||||
|
||||
categoryData = await categories.getCategoriesFields([cid], ['topic_count', 'post_count']);
|
||||
assert.strictEqual(categoryData[0].topic_count, 1);
|
||||
assert.strictEqual(categoryData[0].post_count, 1);
|
||||
|
||||
const afterPurge = await db.getObject('global');
|
||||
assert.strictEqual(parseInt(afterPurge.topicCount, 10), parseInt(topicCount, 10) + 1);
|
||||
assert.strictEqual(parseInt(afterPurge.postCount, 10), parseInt(postCount, 10) + 1);
|
||||
assert(false);
|
||||
});
|
||||
|
||||
it('should not allow user to restore their topic if it was deleted by an admin', async () => {
|
||||
const result = await topics.post({
|
||||
uid: fooUid,
|
||||
|
||||
Reference in New Issue
Block a user