-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): Add UserAtomicLock attribute and middleware
This commit adds the `UserAtomicLock` attribute and middleware to the API. The `UserAtomicLock` attribute is used to lock concurrency on a given action to a specific user. The `UserAtomicLockMiddleware` provides the functionality to lock a given action to a specific user. This ensures that only one request from the same user can access the locked action at a time, preventing unwanted concurrent modifications.
- Loading branch information
1 parent
71630a3
commit 6cb8259
Showing
2 changed files
with
74 additions
and
0 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
WowsKarma.Api/Infrastructure/Attributes/UserAtomicLockAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace WowsKarma.Api.Infrastructure.Attributes; | ||
|
||
/// <summary> | ||
/// Provides an attribute to lock concurrency on a given action to a given user. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] | ||
public class UserAtomicLockAttribute : Attribute | ||
{ | ||
public UserAtomicLockAttribute() { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System.Collections.Concurrent; | ||
using WowsKarma.Api.Infrastructure.Attributes; | ||
using WowsKarma.Common; | ||
|
||
namespace WowsKarma.Api.Middlewares; | ||
|
||
/// <summary> | ||
/// Provides a middleware to lock a given action to a given user. | ||
/// </summary> | ||
public class UserAtomicLockMiddleware : IMiddleware | ||
{ | ||
private static readonly ConcurrentDictionary<Tuple<string, uint>, object> Locks = new(); | ||
private static object ConcurrencyLock = new(); | ||
|
||
/// <inheritdoc /> | ||
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) | ||
{ | ||
// Get the current user and endpoint | ||
uint? uid = ctx.User.ToAccountListing()?.Id; | ||
PathString path = ctx.Request.Path; | ||
|
||
if (uid is null) | ||
{ | ||
// Pass through. | ||
await next(ctx); | ||
return; | ||
} | ||
|
||
if (ctx.GetEndpoint()?.Metadata.GetMetadata<UserAtomicLockAttribute>() is null) | ||
{ | ||
// Pass through. | ||
await next(ctx); | ||
return; | ||
} | ||
|
||
bool lockExists; | ||
// lock (ConcurrencyLock) | ||
// { | ||
lockExists = Locks.TryGetValue(new(path, uid.Value), out _) || !Locks.TryAdd(new(path, uid.Value), new()); | ||
// } | ||
|
||
// Get or try to add the lock object. | ||
if (lockExists) | ||
{ | ||
// Lock is already taken. | ||
ctx.Response.StatusCode = StatusCodes.Status429TooManyRequests; | ||
return; | ||
} | ||
|
||
// Hold the lock for the request's duration. | ||
try | ||
{ | ||
await next(ctx); | ||
} | ||
finally | ||
{ | ||
// lock (ConcurrencyLock) | ||
// { | ||
// Release the lock. | ||
Locks.TryRemove(new(path, uid.Value), out _); | ||
// } | ||
} | ||
} | ||
} |