[backend/core] Add keyed async locks in UserResolver to prevent insertion conflicts
This commit is contained in:
parent
99a1ff43f6
commit
0928c19b06
3 changed files with 52 additions and 37 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using AsyncKeyedLock;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
using Iceshrimp.Backend.Core.Federation.WebFinger;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
|
@ -12,6 +13,12 @@ public class UserResolver(
|
||||||
FollowupTaskService followupTaskSvc
|
FollowupTaskService followupTaskSvc
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
private static readonly AsyncKeyedLocker<string> KeyedLocker = new(o =>
|
||||||
|
{
|
||||||
|
o.PoolSize = 100;
|
||||||
|
o.PoolInitialFill = 5;
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The full web finger algorithm:
|
* The full web finger algorithm:
|
||||||
*
|
*
|
||||||
|
@ -119,9 +126,12 @@ public class UserResolver(
|
||||||
if (user != null)
|
if (user != null)
|
||||||
return await GetUpdatedUser(user);
|
return await GetUpdatedUser(user);
|
||||||
|
|
||||||
|
using (await KeyedLocker.LockAsync(uri))
|
||||||
|
{
|
||||||
// Pass the job on to userSvc, which will create the user
|
// Pass the job on to userSvc, which will create the user
|
||||||
return await userSvc.CreateUserAsync(uri, acct);
|
return await userSvc.CreateUserAsync(uri, acct);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<User> GetUpdatedUser(User user)
|
private async Task<User> GetUpdatedUser(User user)
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,17 +66,10 @@ public class UserService(
|
||||||
public async Task<User> CreateUserAsync(string uri, string acct)
|
public async Task<User> CreateUserAsync(string uri, string acct)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Creating user {acct} with uri {uri}", acct, uri);
|
logger.LogDebug("Creating user {acct} with uri {uri}", acct, uri);
|
||||||
var actor = await fetchSvc.FetchActorAsync(uri);
|
|
||||||
logger.LogDebug("Got actor: {url}", actor.Url);
|
|
||||||
|
|
||||||
actor.Normalize(uri, acct);
|
|
||||||
|
|
||||||
if (actor.PublicKey?.Id == null || actor.PublicKey?.PublicKey == null)
|
|
||||||
throw new GracefulException(HttpStatusCode.UnprocessableEntity, "Actor has no valid public key");
|
|
||||||
|
|
||||||
var user = await db.Users
|
var user = await db.Users
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.FirstOrDefaultAsync(p => p.Uri != null && p.Uri == actor.Id);
|
.FirstOrDefaultAsync(p => p.Uri != null && p.Uri == uri);
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
|
@ -85,6 +78,17 @@ public class UserService(
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var actor = await fetchSvc.FetchActorAsync(uri);
|
||||||
|
logger.LogDebug("Got actor: {url}", actor.Url);
|
||||||
|
|
||||||
|
actor.Normalize(uri, acct);
|
||||||
|
|
||||||
|
if (actor.Id != uri)
|
||||||
|
throw GracefulException.UnprocessableEntity("Uri doesn't match id of fetched actor");
|
||||||
|
|
||||||
|
if (actor.PublicKey?.Id == null || actor.PublicKey?.PublicKey == null)
|
||||||
|
throw GracefulException.UnprocessableEntity("Actor has no valid public key");
|
||||||
|
|
||||||
user = new User
|
user = new User
|
||||||
{
|
{
|
||||||
Id = IdHelpers.GenerateSlowflakeId(),
|
Id = IdHelpers.GenerateSlowflakeId(),
|
||||||
|
|
|
@ -15,46 +15,47 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Amazon.S3" Version="0.31.2"/>
|
<PackageReference Include="Amazon.S3" Version="0.31.2" />
|
||||||
<PackageReference Include="AngleSharp" Version="1.1.0"/>
|
<PackageReference Include="AngleSharp" Version="1.1.0" />
|
||||||
<PackageReference Include="Asp.Versioning.Http" Version="8.0.0"/>
|
<PackageReference Include="Asp.Versioning.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0"/>
|
<PackageReference Include="AsyncKeyedLock" Version="6.3.4" />
|
||||||
<PackageReference Include="cuid.net" Version="5.0.2"/>
|
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0" />
|
||||||
<PackageReference Include="dotNetRdf.Core" Version="3.2.1-dev"/>
|
<PackageReference Include="cuid.net" Version="5.0.2" />
|
||||||
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.0.0"/>
|
<PackageReference Include="dotNetRdf.Core" Version="3.2.1-dev" />
|
||||||
<PackageReference Include="EntityFrameworkCore.Projectables" Version="3.0.4"/>
|
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.0.0" />
|
||||||
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0"/>
|
<PackageReference Include="EntityFrameworkCore.Projectables" Version="3.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.1"/>
|
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0"/>
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0"/>
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1"/>
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0"/>
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||||
<PackageReference Include="protobuf-net" Version="3.2.30"/>
|
<PackageReference Include="protobuf-net" Version="3.2.30" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.1"/>
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.1" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.7.17"/>
|
<PackageReference Include="StackExchange.Redis" Version="2.7.17" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="System.IO.Hashing" Version="8.0.0"/>
|
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageReference Include="Vite.AspNetCore" Version="1.11.0"/>
|
<PackageReference Include="Vite.AspNetCore" Version="1.11.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Pages\Error.cshtml"/>
|
<AdditionalFiles Include="Pages\Error.cshtml" />
|
||||||
<AdditionalFiles Include="Pages\Shared\_Layout.cshtml"/>
|
<AdditionalFiles Include="Pages\Shared\_Layout.cshtml" />
|
||||||
<AdditionalFiles Include="Pages\_ViewImports.cshtml"/>
|
<AdditionalFiles Include="Pages\_ViewImports.cshtml" />
|
||||||
<AdditionalFiles Include="Pages\_ViewStart.cshtml"/>
|
<AdditionalFiles Include="Pages\_ViewStart.cshtml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="wwwroot\.vite\manifest.json" CopyToPublishDirectory="PreserveNewest"/>
|
<Content Include="wwwroot\.vite\manifest.json" CopyToPublishDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="migrate.sql"/>
|
<None Remove="migrate.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Add table
Reference in a new issue