From 2d92d27dbf547f87fddd20a3480679ece35b7f9d Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 1 Mar 2024 02:47:53 +0100 Subject: [PATCH] [backend/core] Don't share entities between DbContext instances --- .../Core/Federation/ActivityPub/UserResolver.cs | 14 ++++++++++---- Iceshrimp.Backend/Core/Services/UserService.cs | 9 ++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs index f6df4d50..84ba55dd 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs @@ -1,9 +1,12 @@ using AsyncKeyedLock; using Iceshrimp.Backend.Core.Configuration; +using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; +using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Federation.WebFinger; using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Services; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Core.Federation.ActivityPub; @@ -13,7 +16,8 @@ public class UserResolver( UserService userSvc, WebFingerService webFingerSvc, FollowupTaskService followupTaskSvc, - IOptions config + IOptions config, + DatabaseContext db ) { private static readonly AsyncKeyedLocker KeyedLocker = new(o => @@ -207,6 +211,8 @@ public class UserResolver( if (!user.NeedsUpdate) return user; user.LastFetchedAt = DateTime.UtcNow; // Prevent multiple background tasks from being started + var success = false; + try { var task = followupTaskSvc.ExecuteTask("UpdateUserAsync", async provider => @@ -215,8 +221,8 @@ public class UserResolver( var bgUserSvc = provider.GetRequiredService(); // Use the id overload so it doesn't attempt to insert in the main thread's DbContext - var fetchedUser = await bgUserSvc.UpdateUserAsync(user.Id); - user = fetchedUser; + await bgUserSvc.UpdateUserAsync(user.Id); + success = true; }); // Return early, but continue execution in background @@ -230,6 +236,6 @@ public class UserResolver( logger.LogError("UpdateUserAsync for user {user} failed with {error}", user.Uri, e.Message); } - return user; + return success ? await db.Users.IncludeCommonProperties().FirstAsync(p => p.Id == user.Id) : user; } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Services/UserService.cs b/Iceshrimp.Backend/Core/Services/UserService.cs index 1a586bcf..fc03a8dd 100644 --- a/Iceshrimp.Backend/Core/Services/UserService.cs +++ b/Iceshrimp.Backend/Core/Services/UserService.cs @@ -402,7 +402,7 @@ public class UserService( user.AvatarUrl = avatar?.Url; user.BannerUrl = banner?.Url; - + await db.SaveChangesAsync(); return async () => @@ -731,6 +731,8 @@ public class UserService( if (followupTaskSvc.IsBackgroundWorker && !force) return user; if (KeyedLocker.IsInUse($"profileMentions:{user.Id}")) return user; + var success = false; + var task = followupTaskSvc.ExecuteTask("UpdateProfileMentionsInBackground", async provider => { using (await KeyedLocker.LockAsync($"profileMentions:{user.Id}")) @@ -773,11 +775,12 @@ public class UserService( bgUser.UserProfile.MentionsResolved = true; bgDbContext.Update(bgUser.UserProfile); await bgDbContext.SaveChangesAsync(); - user = bgUser; + success = true; } }); await task.SafeWaitAsync(TimeSpan.FromMilliseconds(500)); - return user; + + return success ? await db.Users.IncludeCommonProperties().FirstAsync(p => p.Id == user.Id) : user; } } \ No newline at end of file