From f98de3e98591b50fb1a649e5a3a0bf0bc6cd1bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 25 Jan 2026 19:45:23 -0500 Subject: [PATCH] fix: closes #13899 --- src/posts/votes.js | 13 +++--- src/topics/posts.js | 24 +++++++---- .../4.8.2/clean_ap_tids_from_topic_zsets.js | 42 +++++++++++++++++++ 3 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 src/upgrades/4.8.2/clean_ap_tids_from_topic_zsets.js diff --git a/src/posts/votes.js b/src/posts/votes.js index d9487b1dc2..64724778c1 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -271,27 +271,30 @@ module.exports = function (Posts) { async function updateTopicVoteCount(postData) { const topicData = await topics.getTopicFields(postData.tid, ['mainPid', 'cid', 'pinned']); - + const { cid } = topicData; if (postData.uid) { if (postData.votes !== 0) { - await db.sortedSetAdd(`cid:${topicData.cid}:uid:${postData.uid}:pids:votes`, postData.votes, postData.pid); + await db.sortedSetAdd(`cid:${cid}:uid:${postData.uid}:pids:votes`, postData.votes, postData.pid); } else { - await db.sortedSetRemove(`cid:${topicData.cid}:uid:${postData.uid}:pids:votes`, postData.pid); + await db.sortedSetRemove(`cid:${cid}:uid:${postData.uid}:pids:votes`, postData.pid); } } if (String(topicData.mainPid) !== String(postData.pid)) { return await db.sortedSetAdd(`tid:${postData.tid}:posts:votes`, postData.votes, postData.pid); } + const isRemoteCid = !utils.isNumber(cid) || cid === -1; const promises = [ topics.setTopicFields(postData.tid, { upvotes: postData.upvotes, downvotes: postData.downvotes, }), - db.sortedSetAdd('topics:votes', postData.votes, postData.tid), + isRemoteCid ? + Promise.resolve() : + db.sortedSetAdd('topics:votes', postData.votes, postData.tid), ]; if (!topicData.pinned) { - promises.push(db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, postData.votes, postData.tid)); + promises.push(db.sortedSetAdd(`cid:${cid}:tids:votes`, postData.votes, postData.tid)); } await Promise.all(promises); } diff --git a/src/topics/posts.js b/src/topics/posts.js index a8535939e9..67d9d4ed62 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -312,13 +312,22 @@ module.exports = function (Topics) { }; Topics.increasePostCount = async function (tid) { - incrementFieldAndUpdateSortedSet(tid, 'postcount', 1, 'topics:posts'); + await incrementFieldAndUpdateSortedSet(tid, 'postcount', 1, 'topics:posts'); }; Topics.decreasePostCount = async function (tid) { - incrementFieldAndUpdateSortedSet(tid, 'postcount', -1, 'topics:posts'); + await incrementFieldAndUpdateSortedSet(tid, 'postcount', -1, 'topics:posts'); }; + async function incrementFieldAndUpdateSortedSet(tid, field, by, set) { + const cid = await Topics.getTopicField(tid, 'cid'); + const value = await db.incrObjectFieldBy(`topic:${tid}`, field, by); + const isRemoteCid = !utils.isNumber(cid) || cid === -1; + if (!isRemoteCid) { + await db.sortedSetAdd(set, value, tid); + } + } + Topics.increaseViewCount = async function (req, tid) { const allow = req.uid > 0 || (meta.config.guestsIncrementTopicViews && req.uid === 0); if (allow) { @@ -327,17 +336,16 @@ module.exports = function (Topics) { const interval = meta.config.incrementTopicViewsInterval * 60000; if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < now - interval) { const cid = await Topics.getTopicField(tid, 'cid'); - incrementFieldAndUpdateSortedSet(tid, 'viewcount', 1, ['topics:views', `cid:${cid}:tids:views`]); + const isRemoteCid = !utils.isNumber(cid) || cid === -1; + const value = await db.incrObjectFieldBy(`topic:${tid}`, 'viewcount', 1); + await db.sortedSetsAdd( + isRemoteCid ? [`cid:${cid}:tids:views`] : ['topics:views', `cid:${cid}:tids:views`], value, tid + ); req.session.tids_viewed[tid] = now; } } }; - async function incrementFieldAndUpdateSortedSet(tid, field, by, set) { - const value = await db.incrObjectFieldBy(`topic:${tid}`, field, by); - await db[Array.isArray(set) ? 'sortedSetsAdd' : 'sortedSetAdd'](set, value, tid); - } - Topics.getTitleByPid = async function (pid) { return await Topics.getTopicFieldByPid('title', pid); }; diff --git a/src/upgrades/4.8.2/clean_ap_tids_from_topic_zsets.js b/src/upgrades/4.8.2/clean_ap_tids_from_topic_zsets.js new file mode 100644 index 0000000000..d5ce541eb9 --- /dev/null +++ b/src/upgrades/4.8.2/clean_ap_tids_from_topic_zsets.js @@ -0,0 +1,42 @@ +'use strict'; + +const db = require('../../database'); +const batch = require('../../batch'); +const utils = require('../../utils'); + +module.exports = { + name: 'Remote AP tids from topics:recent, topics:views, topics:posts, topics:votes zsets', + timestamp: Date.UTC(2026, 0, 25), + method: async function () { + const { progress } = this; + const [recent, views, posts, votes] = await db.sortedSetsCard([ + 'topics:recent', 'topics:views', 'topics:posts', 'topics:votes', + ]); + progress.total = recent + views + posts + votes; + + async function cleanupSet(setName, count) { + const tidsToRemove = []; + await batch.processSortedSet(setName, async (tids) => { + const topicData = await db.getObjectsFields(tids.map(tid => `topic:${tid}`), ['tid', 'cid']); + const batchTids = topicData.filter( + t => t && (!t.cid || !utils.isNumber(t.cid) || t.cid === -1) + ).map(t => t.tid); + tidsToRemove.push(...batchTids); + progress.incr(batchTids.length); + }, { + batch: 500, + }); + + await batch.processArray(tidsToRemove, async (batchTids) => { + await db.sortedSetRemove(setName, batchTids); + }, { + batch: 500, + }); + progress.incr(count); + } + await cleanupSet('topics:recent', recent); + await cleanupSet('topics:views', views); + await cleanupSet('topics:posts', posts); + await cleanupSet('topics:votes', votes); + }, +};