`IdempotencyRequestCache`: Remove request timeout from the cache implementation and move it outside
MODIFY
WalletWasabi.Backend/Controllers/WabiSabiController.cs
WalletWasabi.Backend/Controllers/WabiSabiController.cs
+using Microsoft.AspNetCore.Mvc;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
using WalletWasabi.Backend.Filters;
using WalletWasabi.Cache;
using WalletWasabi.WabiSabi.Backend.PostRequests;
@@ -18,6 +18,8 @@ namespace WalletWasabi.Backend.Controllers;
[Produces("application/json")]
public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
{
+ private static TimeSpan RequestTimeout { get; } = TimeSpan.FromMinutes(5);
+
public WabiSabiController(IdempotencyRequestCache idempotencyRequestCache, Arena arena, CoinJoinFeeRateStatStore coinJoinFeeRateStatStore)
{
IdempotencyRequestCache = idempotencyRequestCache;
@@ -45,8 +47,11 @@ public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
[HttpPost("connection-confirmation")]
public async Task<ConnectionConfirmationResponse> ConfirmConnectionAsync(ConnectionConfirmationRequest request, CancellationToken cancellationToken)
{
+ using CancellationTokenSource timeoutCts = new(RequestTimeout);
+ using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
+
var before = DateTimeOffset.UtcNow;
- var ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.ConfirmConnectionAsync, cancellationToken);
+ var ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.ConfirmConnectionAsync, linkedCts.Token);
var duration = DateTimeOffset.UtcNow - before;
RequestTimeStatista.Instance.Add("connection-confirmation", duration);
@@ -56,8 +61,11 @@ public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
[HttpPost("input-registration")]
public async Task<InputRegistrationResponse> RegisterInputAsync(InputRegistrationRequest request, CancellationToken cancellationToken)
{
+ using CancellationTokenSource timeoutCts = new(RequestTimeout);
+ using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
+
var before = DateTimeOffset.UtcNow;
- var ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.RegisterInputAsync, cancellationToken);
+ InputRegistrationResponse ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, Arena.RegisterInputAsync, linkedCts.Token);
var duration = DateTimeOffset.UtcNow - before;
RequestTimeStatista.Instance.Add("input-registration", duration);
@@ -67,8 +75,11 @@ public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
[HttpPost("output-registration")]
public async Task RegisterOutputAsync(OutputRegistrationRequest request, CancellationToken cancellationToken)
{
+ using CancellationTokenSource timeoutCts = new(RequestTimeout);
+ using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
+
var before = DateTimeOffset.UtcNow;
- await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.RegisterOutputCoreAsync, cancellationToken);
+ await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.RegisterOutputCoreAsync, linkedCts.Token);
var duration = DateTimeOffset.UtcNow - before;
RequestTimeStatista.Instance.Add("output-registration", duration);
@@ -77,8 +88,11 @@ public class WabiSabiController : ControllerBase, IWabiSabiApiRequestHandler
[HttpPost("credential-issuance")]
public async Task<ReissueCredentialResponse> ReissuanceAsync(ReissueCredentialRequest request, CancellationToken cancellationToken)
{
+ using CancellationTokenSource timeoutCts = new(RequestTimeout);
+ using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
+
var before = DateTimeOffset.UtcNow;
- var ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.ReissuanceAsync, cancellationToken);
+ var ret = await IdempotencyRequestCache.GetCachedResponseAsync(request, action: Arena.ReissuanceAsync, linkedCts.Token);
var duration = DateTimeOffset.UtcNow - before;
RequestTimeStatista.Instance.Add("credential-issuance", duration);

MODIFY
WalletWasabi/Cache/IdempotencyRequestCache.cs
WalletWasabi/Cache/IdempotencyRequestCache.cs
@@ -19,8 +19,6 @@ public class IdempotencyRequestCache
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
};
- private static TimeSpan RequestTimeout { get; } = TimeSpan.FromMinutes(5);
-
/// <summary>Guards <see cref="ResponseCache"/>.</summary>
/// <remarks>Unfortunately, <see cref="CacheExtensions.GetOrCreate{TItem}(IMemoryCache, object, Func{ICacheEntry, TItem})"/> is not atomic.</remarks>
/// <seealso href="https://github.com/dotnet/runtime/issues/36499"/>
@@ -63,12 +61,9 @@ public class IdempotencyRequestCache
if (callAction)
{
- using CancellationTokenSource timeoutCts = new(RequestTimeout);
try
{
- using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
-
- var result = await action(request, cancellationToken).WithAwaitCancellationAsync(linkedCts.Token).ConfigureAwait(false);
+ var result = await action(request, cancellationToken).WithAwaitCancellationAsync(cancellationToken).ConfigureAwait(false);
responseTcs.SetResult(result);
return result;
}
@@ -79,9 +74,7 @@ public class IdempotencyRequestCache
ResponseCache.Remove(request);
}
- responseTcs.SetException(!timeoutCts.IsCancellationRequested
- ? e
- : new InvalidOperationException("DeadLock prevention timeout kicked in!", e));
+ responseTcs.SetException(e);
// The exception will be thrown below at 'await' to avoid unobserved exception.
}

`IdempotencyRequestCache`: Remove request timeout from the cache implementation and move it outside