[backend/federation] Fix race condition when updating a user during a request
This commit is contained in:
parent
0884f462c0
commit
f073018e95
5 changed files with 29 additions and 16 deletions
|
@ -26,7 +26,7 @@ public class DriveFile : IEntity
|
|||
/// The created date of the DriveFile.
|
||||
/// </summary>
|
||||
[Column("createdAt")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The owner ID.
|
||||
|
@ -188,7 +188,7 @@ public class DriveFile : IEntity
|
|||
[Key]
|
||||
[Column("id")]
|
||||
[StringLength(32)]
|
||||
public string Id { get; set; } = IdHelpers.GenerateSlowflakeId();
|
||||
public string Id { get; set; } = null!;
|
||||
|
||||
public class FileProperties
|
||||
{
|
||||
|
|
|
@ -16,11 +16,9 @@ public class ActivityDeliverService(ILogger<ActivityDeliverService> logger, Queu
|
|||
|
||||
await queueService.PreDeliverQueue.EnqueueAsync(new PreDeliverJob
|
||||
{
|
||||
ActorId = actor.Id,
|
||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||
SerializedActivity =
|
||||
JsonConvert.SerializeObject(activity,
|
||||
LdHelpers.JsonSerializerSettings),
|
||||
ActorId = actor.Id,
|
||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||
SerializedActivity = JsonConvert.SerializeObject(activity, LdHelpers.JsonSerializerSettings),
|
||||
DeliverToFollowers = true
|
||||
});
|
||||
}
|
||||
|
@ -32,11 +30,9 @@ public class ActivityDeliverService(ILogger<ActivityDeliverService> logger, Queu
|
|||
|
||||
await queueService.PreDeliverQueue.EnqueueAsync(new PreDeliverJob
|
||||
{
|
||||
ActorId = actor.Id,
|
||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||
SerializedActivity =
|
||||
JsonConvert.SerializeObject(activity,
|
||||
LdHelpers.JsonSerializerSettings),
|
||||
ActorId = actor.Id,
|
||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||
SerializedActivity = JsonConvert.SerializeObject(activity, LdHelpers.JsonSerializerSettings),
|
||||
DeliverToFollowers = false
|
||||
});
|
||||
}
|
||||
|
|
|
@ -112,13 +112,18 @@ public class UserResolver(
|
|||
private async Task<User> GetUpdatedUser(User user)
|
||||
{
|
||||
if (!user.NeedsUpdate) return user;
|
||||
user.LastFetchedAt = DateTime.UtcNow; // Prevent multiple background tasks from being started
|
||||
|
||||
try
|
||||
{
|
||||
var task = followupTaskSvc.ExecuteTask("UpdateUserAsync", async provider =>
|
||||
{
|
||||
// Get a fresh UserService instance in a new scope
|
||||
var bgUserSvc = provider.GetRequiredService<UserService>();
|
||||
await bgUserSvc.UpdateUserAsync(user);
|
||||
|
||||
// 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;
|
||||
});
|
||||
|
||||
// Return early, but continue execution in background
|
||||
|
|
|
@ -4,6 +4,7 @@ using Iceshrimp.Backend.Core.Configuration;
|
|||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Federation.Cryptography;
|
||||
using Iceshrimp.Backend.Core.Helpers;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Iceshrimp.Backend.Core.Queues;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -240,6 +241,8 @@ public class DriveService(
|
|||
|
||||
file = new DriveFile
|
||||
{
|
||||
Id = IdHelpers.GenerateSlowflakeId(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
User = user,
|
||||
UserHost = user.Host,
|
||||
Sha256 = digest,
|
||||
|
@ -332,6 +335,8 @@ file static class DriveFileExtensions
|
|||
|
||||
return new DriveFile
|
||||
{
|
||||
Id = IdHelpers.GenerateSlowflakeId(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
User = user,
|
||||
Blurhash = file.Blurhash,
|
||||
Type = file.Type,
|
||||
|
|
|
@ -158,9 +158,16 @@ public class UserService(
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<User> UpdateUserAsync(User user, ASActor? actor = null)
|
||||
public async Task<User> UpdateUserAsync(string id)
|
||||
{
|
||||
if (!user.NeedsUpdate && actor == null) return user;
|
||||
var user = await db.Users.IncludeCommonProperties().FirstOrDefaultAsync(p => p.Id == id) ??
|
||||
throw new Exception("Cannot update nonexistent user");
|
||||
return await UpdateUserAsync(user, force: true);
|
||||
}
|
||||
|
||||
public async Task<User> UpdateUserAsync(User user, ASActor? actor = null, bool force = false)
|
||||
{
|
||||
if (!user.NeedsUpdate && actor == null && !force) return user;
|
||||
if (actor is { IsUnresolved: true } or { Username: null })
|
||||
actor = null; // This will trigger a fetch a couple lines down
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue