[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.
|
/// The created date of the DriveFile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Column("createdAt")]
|
[Column("createdAt")]
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The owner ID.
|
/// The owner ID.
|
||||||
|
@ -188,7 +188,7 @@ public class DriveFile : IEntity
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
public string Id { get; set; } = IdHelpers.GenerateSlowflakeId();
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
public class FileProperties
|
public class FileProperties
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,9 +18,7 @@ public class ActivityDeliverService(ILogger<ActivityDeliverService> logger, Queu
|
||||||
{
|
{
|
||||||
ActorId = actor.Id,
|
ActorId = actor.Id,
|
||||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||||
SerializedActivity =
|
SerializedActivity = JsonConvert.SerializeObject(activity, LdHelpers.JsonSerializerSettings),
|
||||||
JsonConvert.SerializeObject(activity,
|
|
||||||
LdHelpers.JsonSerializerSettings),
|
|
||||||
DeliverToFollowers = true
|
DeliverToFollowers = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34,9 +32,7 @@ public class ActivityDeliverService(ILogger<ActivityDeliverService> logger, Queu
|
||||||
{
|
{
|
||||||
ActorId = actor.Id,
|
ActorId = actor.Id,
|
||||||
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
RecipientIds = recipients.Select(p => p.Id).ToList(),
|
||||||
SerializedActivity =
|
SerializedActivity = JsonConvert.SerializeObject(activity, LdHelpers.JsonSerializerSettings),
|
||||||
JsonConvert.SerializeObject(activity,
|
|
||||||
LdHelpers.JsonSerializerSettings),
|
|
||||||
DeliverToFollowers = false
|
DeliverToFollowers = false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,13 +112,18 @@ public class UserResolver(
|
||||||
private async Task<User> GetUpdatedUser(User user)
|
private async Task<User> GetUpdatedUser(User user)
|
||||||
{
|
{
|
||||||
if (!user.NeedsUpdate) return user;
|
if (!user.NeedsUpdate) return user;
|
||||||
|
user.LastFetchedAt = DateTime.UtcNow; // Prevent multiple background tasks from being started
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var task = followupTaskSvc.ExecuteTask("UpdateUserAsync", async provider =>
|
var task = followupTaskSvc.ExecuteTask("UpdateUserAsync", async provider =>
|
||||||
{
|
{
|
||||||
|
// Get a fresh UserService instance in a new scope
|
||||||
var bgUserSvc = provider.GetRequiredService<UserService>();
|
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
|
// 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;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Federation.Cryptography;
|
using Iceshrimp.Backend.Core.Federation.Cryptography;
|
||||||
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Queues;
|
using Iceshrimp.Backend.Core.Queues;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -240,6 +241,8 @@ public class DriveService(
|
||||||
|
|
||||||
file = new DriveFile
|
file = new DriveFile
|
||||||
{
|
{
|
||||||
|
Id = IdHelpers.GenerateSlowflakeId(),
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
User = user,
|
User = user,
|
||||||
UserHost = user.Host,
|
UserHost = user.Host,
|
||||||
Sha256 = digest,
|
Sha256 = digest,
|
||||||
|
@ -332,6 +335,8 @@ file static class DriveFileExtensions
|
||||||
|
|
||||||
return new DriveFile
|
return new DriveFile
|
||||||
{
|
{
|
||||||
|
Id = IdHelpers.GenerateSlowflakeId(),
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
User = user,
|
User = user,
|
||||||
Blurhash = file.Blurhash,
|
Blurhash = file.Blurhash,
|
||||||
Type = file.Type,
|
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 })
|
if (actor is { IsUnresolved: true } or { Username: null })
|
||||||
actor = null; // This will trigger a fetch a couple lines down
|
actor = null; // This will trigger a fetch a couple lines down
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue