[backend/core] Fix note & user counters (ISH-109)

This commit is contained in:
Laura Hausmann 2024-02-29 20:04:56 +01:00
parent c87fa00864
commit 52519f3b69
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 73 additions and 33 deletions

View file

@ -139,6 +139,7 @@ public class StatusController(
throw GracefulException.BadRequest("Renote visibility must be one of: public, unlisted, private"); throw GracefulException.BadRequest("Renote visibility must be one of: public, unlisted, private");
await noteSvc.CreateNoteAsync(user, renoteVisibility, renote: note); await noteSvc.CreateNoteAsync(user, renoteVisibility, renote: note);
note.RenoteCount++; // we do not want to call save changes after this point
} }
return await GetNote(id); return await GetNote(id);
@ -158,14 +159,10 @@ public class StatusController(
.IncludeCommonProperties() .IncludeCommonProperties()
.ToListAsync(); .ToListAsync();
if (renotes.Count > 0)
{
renotes[0].Renote!.RenoteCount--;
await db.SaveChangesAsync();
}
foreach (var renote in renotes) await noteSvc.DeleteNoteAsync(renote); foreach (var renote in renotes) await noteSvc.DeleteNoteAsync(renote);
renotes[0].Renote!.RenoteCount--; // we do not want to call save changes after this point
return await GetNote(id); return await GetNote(id);
} }

View file

@ -332,8 +332,10 @@ public class ActivityHandlerService(
FollowerSharedInbox = follower.SharedInbox FollowerSharedInbox = follower.SharedInbox
}; };
follower.FollowingCount++; await db.Users.Where(p => p.Id == follower.Id)
followee.FollowersCount++; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount, i => i.FollowingCount + 1));
await db.Users.Where(p => p.Id == followee.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount, i => i.FollowersCount + 1));
_ = followupTaskSvc.ExecuteTask("IncrementInstanceIncomingFollowsCounter", async provider => _ = followupTaskSvc.ExecuteTask("IncrementInstanceIncomingFollowsCounter", async provider =>
{ {
@ -361,8 +363,12 @@ public class ActivityHandlerService(
var followings = await db.Followings.Where(p => p.Follower == follower && p.Followee == followee).ToListAsync(); var followings = await db.Followings.Where(p => p.Follower == follower && p.Followee == followee).ToListAsync();
if (followings.Count > 0) if (followings.Count > 0)
{ {
followee.FollowersCount -= followings.Count; await db.Users.Where(p => p.Id == follower.Id)
follower.FollowingCount -= followings.Count; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount,
i => i.FollowingCount - followings.Count));
await db.Users.Where(p => p.Id == followee.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount,
i => i.FollowersCount - followings.Count));
db.RemoveRange(followings); db.RemoveRange(followings);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
@ -417,8 +423,10 @@ public class ActivityHandlerService(
FolloweeSharedInbox = request.FolloweeSharedInbox FolloweeSharedInbox = request.FolloweeSharedInbox
}; };
actor.FollowersCount++; await db.Users.Where(p => p.Id == request.Follower.Id)
request.Follower.FollowingCount++; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount, i => i.FollowingCount + 1));
await db.Users.Where(p => p.Id == actor.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount, i => i.FollowersCount + 1));
_ = followupTaskSvc.ExecuteTask("IncrementInstanceOutgoingFollowsCounter", async provider => _ = followupTaskSvc.ExecuteTask("IncrementInstanceOutgoingFollowsCounter", async provider =>
{ {
@ -450,8 +458,10 @@ public class ActivityHandlerService(
.ExecuteDeleteAsync(); .ExecuteDeleteAsync();
if (count > 0) if (count > 0)
{ {
actor.FollowersCount -= count; await db.Users.Where(p => p.Id == resolvedFollower.Id)
resolvedFollower.FollowingCount -= count; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount, i => i.FollowingCount - count));
await db.Users.Where(p => p.Id == actor.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount, i => i.FollowersCount - count));
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }

View file

@ -87,7 +87,7 @@ public class NoteService(
Renote = renote, Renote = renote,
RenoteUserId = renote?.UserId, RenoteUserId = renote?.UserId,
RenoteUserHost = renote?.UserHost, RenoteUserHost = renote?.UserHost,
UserId = user.Id, User = user,
CreatedAt = DateTime.UtcNow, CreatedAt = DateTime.UtcNow,
UserHost = null, UserHost = null,
Visibility = visibility, Visibility = visibility,
@ -99,11 +99,7 @@ public class NoteService(
ThreadId = reply?.ThreadId ?? reply?.Id ThreadId = reply?.ThreadId ?? reply?.Id
}; };
if (!note.IsPureRenote) user.NotesCount++; await UpdateNoteCountersAsync(note, true);
if (reply != null) reply.RepliesCount++;
if (renote != null && !note.IsQuote)
if (!db.Notes.Any(p => p.UserId == user.Id && p.RenoteId == renote.Id && p.IsPureRenote))
renote.RenoteCount++;
await db.AddAsync(note); await db.AddAsync(note);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
@ -151,6 +147,34 @@ public class NoteService(
return note; return note;
} }
/// <remarks>
/// This needs to be called before SaveChangesAsync on create & after on delete
/// </remarks>
private async Task UpdateNoteCountersAsync(Note note, bool create)
{
var diff = create ? 1 : -1;
if (note is { Renote.Id: not null, IsPureRenote: true })
{
if (!db.Notes.Any(p => p.UserId == note.User.Id && p.RenoteId == note.Renote.Id && p.IsPureRenote))
{
await db.Notes.Where(p => p.Id == note.Renote.Id)
.ExecuteUpdateAsync(p => p.SetProperty(n => n.RenoteCount, n => n.RenoteCount + diff));
}
}
else
{
await db.Users.Where(p => p.Id == note.User.Id)
.ExecuteUpdateAsync(p => p.SetProperty(u => u.NotesCount, u => u.NotesCount + diff));
}
if (note.Reply != null)
{
await db.Notes.Where(p => p.Id == note.Reply.Id)
.ExecuteUpdateAsync(p => p.SetProperty(n => n.RepliesCount, n => n.RepliesCount + diff));
}
}
public async Task<Note> UpdateNoteAsync( public async Task<Note> UpdateNoteAsync(
Note note, string? text = null, string? cw = null, IReadOnlyCollection<DriveFile>? attachments = null Note note, string? text = null, string? cw = null, IReadOnlyCollection<DriveFile>? attachments = null
) )
@ -238,11 +262,11 @@ public class NoteService(
public async Task DeleteNoteAsync(Note note) public async Task DeleteNoteAsync(Note note)
{ {
note.User.NotesCount--;
db.Update(note.User); db.Update(note.User);
db.Remove(note); db.Remove(note);
eventSvc.RaiseNoteDeleted(this, note); eventSvc.RaiseNoteDeleted(this, note);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
await UpdateNoteCountersAsync(note, false);
if (note.UserHost != null) if (note.UserHost != null)
{ {
@ -301,11 +325,10 @@ public class NoteService(
logger.LogDebug("Deleting note '{id}' owned by {userId}", note.Id, actor.Id); logger.LogDebug("Deleting note '{id}' owned by {userId}", note.Id, actor.Id);
actor.NotesCount--;
if (dbNote.IsPureRenote) dbNote.RenoteCount--;
db.Remove(dbNote); db.Remove(dbNote);
eventSvc.RaiseNoteDeleted(this, dbNote); eventSvc.RaiseNoteDeleted(this, dbNote);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
await UpdateNoteCountersAsync(dbNote, false);
// ReSharper disable once EntityFramework.NPlusOne.IncompleteDataUsage (same reason as above) // ReSharper disable once EntityFramework.NPlusOne.IncompleteDataUsage (same reason as above)
if (dbNote.User.Uri != null && dbNote.UserHost != null) if (dbNote.User.Uri != null && dbNote.UserHost != null)
@ -331,9 +354,10 @@ public class NoteService(
.ToListAsync(); .ToListAsync();
if (notes.Count == 0) return; if (notes.Count == 0) return;
renote.RenoteCount--;
db.RemoveRange(notes); db.RemoveRange(notes);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
await db.Notes.Where(p => p.Id == note.Id)
.ExecuteUpdateAsync(p => p.SetProperty(n => n.RenoteCount, n => n.RenoteCount - 1));
foreach (var hit in notes) foreach (var hit in notes)
eventSvc.RaiseNoteDeleted(this, hit); eventSvc.RaiseNoteDeleted(this, hit);
@ -388,7 +412,7 @@ public class NoteService(
Url = note.Url?.Id, //FIXME: this doesn't seem to work yet Url = note.Url?.Id, //FIXME: this doesn't seem to work yet
Text = note.MkContent ?? await MfmConverter.FromHtmlAsync(note.Content, mentions), Text = note.MkContent ?? await MfmConverter.FromHtmlAsync(note.Content, mentions),
Cw = note.Summary, Cw = note.Summary,
UserId = actor.Id, User = actor,
CreatedAt = createdAt, CreatedAt = createdAt,
UserHost = actor.Host, UserHost = actor.Host,
Visibility = note.GetVisibility(actor), Visibility = note.GetVisibility(actor),
@ -449,8 +473,7 @@ public class NoteService(
var emoji = await emojiSvc.ProcessEmojiAsync(note.Tags?.OfType<ASEmoji>().ToList(), actor.Host); var emoji = await emojiSvc.ProcessEmojiAsync(note.Tags?.OfType<ASEmoji>().ToList(), actor.Host);
dbNote.Emojis = emoji.Select(p => p.Id).ToList(); dbNote.Emojis = emoji.Select(p => p.Id).ToList();
actor.NotesCount++; await UpdateNoteCountersAsync(dbNote, true);
if (dbNote.Reply != null) dbNote.Reply.RepliesCount++;
await db.Notes.AddAsync(dbNote); await db.Notes.AddAsync(dbNote);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
eventSvc.RaiseNotePublished(this, dbNote); eventSvc.RaiseNotePublished(this, dbNote);

View file

@ -521,8 +521,10 @@ public class UserService(
FolloweeSharedInbox = request.FolloweeSharedInbox FolloweeSharedInbox = request.FolloweeSharedInbox
}; };
request.Followee.FollowersCount++; await db.Users.Where(p => p.Id == request.Follower.Id)
request.Follower.FollowingCount++; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount, i => i.FollowingCount + 1));
await db.Users.Where(p => p.Id == request.Followee.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount, i => i.FollowersCount + 1));
db.Remove(request); db.Remove(request);
await db.AddAsync(following); await db.AddAsync(following);
@ -624,8 +626,10 @@ public class UserService(
// otherwise we'll do it when receiving the Accept activity / the local followee accepts the request // otherwise we'll do it when receiving the Accept activity / the local followee accepts the request
if (followee.Host == null && !followee.IsLocked) if (followee.Host == null && !followee.IsLocked)
{ {
followee.FollowersCount++; await db.Users.Where(p => p.Id == user.Id)
user.FollowingCount++; .ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount, i => i.FollowingCount + 1));
await db.Users.Where(p => p.Id == followee.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount, i => i.FollowersCount + 1));
} }
await db.SaveChangesAsync(); await db.SaveChangesAsync();
@ -651,8 +655,14 @@ public class UserService(
if (followee.PrecomputedIsFollowedBy ?? false) if (followee.PrecomputedIsFollowedBy ?? false)
{ {
var followings = await db.Followings.Where(p => p.Follower == user && p.Followee == followee).ToListAsync(); var followings = await db.Followings.Where(p => p.Follower == user && p.Followee == followee).ToListAsync();
user.FollowingCount -= followings.Count;
followee.FollowersCount -= followings.Count; await db.Users.Where(p => p.Id == user.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowingCount,
i => i.FollowingCount - followings.Count));
await db.Users.Where(p => p.Id == followee.Id)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.FollowersCount,
i => i.FollowersCount - followings.Count));
db.RemoveRange(followings); db.RemoveRange(followings);
await db.SaveChangesAsync(); await db.SaveChangesAsync();