fix: sync follow counts on local and remote follows, #14105

This commit is contained in:
Julian Lam
2026-03-18 12:13:50 -04:00
parent cc606677cb
commit 44e78e4775
4 changed files with 39 additions and 25 deletions

View File

@@ -473,7 +473,7 @@ Helpers.generateCollection = async ({ set, method, count, page, perPage, url })
} else if (set) {
method = method.bind(null, set);
}
count = count || await db.sortedSetCard(set);
count = count ?? await db.sortedSetCard(set);
const pageCount = Math.max(1, Math.ceil(count / perPage));
let items = [];
let paginate = true;

View File

@@ -517,11 +517,12 @@ inbox.follow = async (req) => {
}
const now = Date.now();
await db.sortedSetAdd(`followersRemote:${id}`, now, actor);
await db.sortedSetAdd(`followingRemote:${actor}`, now, id); // for following backreference (actor pruning)
const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`);
await user.setUserField(id, 'followerRemoteCount', followerRemoteCount);
await Promise.all([
db.sortedSetAdd(`followersRemote:${id}`, now, actor),
db.sortedSetAdd(`followingRemote:${actor}`, now, id), // for following backreference (actor pruning)
user.syncFollowCounts(id, false, true),
user.syncFollowCounts(actor, true, false),
]);
activitypub.actors._followerCache.del(id);
await user.onFollow(actor, id);
@@ -600,8 +601,8 @@ inbox.accept = async (req) => {
db.sortedSetAdd(`followingRemote:${id}`, timestamp, actor),
db.sortedSetAdd(`followersRemote:${actor}`, timestamp, id), // for followers backreference and notes assertion checking
]);
const followingRemoteCount = await db.sortedSetCard(`followingRemote:${id}`);
await user.setUserField(id, 'followingRemoteCount', followingRemoteCount);
await user.syncFollowCounts(id, true, false);
await user.syncFollowCounts(actor, false, true);
} else if (localType === 'category') {
if (!await db.isSortedSetMember(`followRequests:cid.${id}`, actor)) {
if (await db.isSortedSetMember(`cid:${id}:following`, actor)) return; // already following
@@ -649,9 +650,9 @@ inbox.undo = async (req) => {
await Promise.all([
db.sortedSetRemove(`followersRemote:${id}`, actor),
db.sortedSetRemove(`followingRemote:${actor}`, id),
user.syncFollowCounts(id, false, true),
user.syncFollowCounts(actor, true, false),
]);
const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`);
await user.setUserField(id, 'followerRemoteCount', followerRemoteCount);
notifications.rescind(`follow:${id}:uid:${actor}`);
activitypub.actors._followerCache.del(id);
break;

View File

@@ -66,10 +66,8 @@ Controller.fetch = async (req, res, next) => {
};
Controller.getFollowing = async (req, res) => {
const { followingCount, followingRemoteCount } = await user.getUserFields(req.params.uid, ['followingCount', 'followingRemoteCount']);
const totalItems = parseInt(followingCount || 0, 10) + parseInt(followingRemoteCount || 0, 10);
const count = totalItems;
const followingCount = await user.getUserField(req.params.uid, 'followingCount');
const count = parseInt(followingCount, 10);
const collection = await activitypub.helpers.generateCollection({
method: user.getFollowing.bind(null, req.params.uid),
count,
@@ -92,10 +90,8 @@ Controller.getFollowing = async (req, res) => {
};
Controller.getFollowers = async (req, res) => {
const { followerCount, followerRemoteCount } = await user.getUserFields(req.params.uid, ['followerCount', 'followerRemoteCount']);
const totalItems = parseInt(followerCount || 0, 10) + parseInt(followerRemoteCount || 0, 10);
const count = totalItems;
const followerCount = await user.getUserField(req.params.uid, 'followerCount');
const count = parseInt(followerCount, 10);
const collection = await activitypub.helpers.generateCollection({
method: user.getFollowers.bind(null, req.params.uid),
count,

View File

@@ -58,15 +58,32 @@ module.exports = function (User) {
]);
}
const [followingCount, followingRemoteCount, followerCount, followerRemoteCount] = await db.sortedSetsCard([
`following:${uid}`, `followingRemote:${uid}`, `followers:${theiruid}`, `followersRemote:${theiruid}`,
]);
await Promise.all([
User.setUserField(uid, 'followingCount', followingCount + followingRemoteCount),
User.setUserField(theiruid, 'followerCount', followerCount + followerRemoteCount),
]);
User.syncFollowCounts(uid, true, false);
User.syncFollowCounts(theiruid, false, true);
}
User.syncFollowCounts = async function (uid, following, followers) {
const sets = [];
const property = [];
if (following) {
property.push('followingCount');
sets.push(`following:${uid}`, `followingRemote:${uid}`);
}
if (followers) {
property.push('followerCount');
sets.push(`followers:${uid}`, `followersRemote:${uid}`);
};
const values = await db.sortedSetsCard(sets);
const payload = property.reduce((payload, cur, idx) => {
const sum = values[idx * 2] + values[(idx * 2) + 1];
payload[cur] = sum;
return payload;
}, {});
await User.setUserFields(uid, payload);
};
User.getFollowing = async function (uid, start, stop) {
return await getFollow(uid, 'following', start, stop);
};