Switch `CachedRpcClient` to use `IdempotencyRequestCache`.
MODIFY
WalletWasabi/BitcoinCore/Rpc/CachedRpcClient.cs
WalletWasabi/BitcoinCore/Rpc/CachedRpcClient.cs
@@ -5,6 +5,7 @@ using NBitcoin.RPC;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore.Rpc.Models;
+using WalletWasabi.Cache;
namespace WalletWasabi.BitcoinCore.Rpc;
@@ -15,13 +16,12 @@ public class CachedRpcClient : RpcClientBase
public CachedRpcClient(RPCClient rpc, IMemoryCache cache)
: base(rpc)
{
- Cache = cache;
+ IdempotencyRequestCache = new(cache);
}
- private object CancellationTokenSourceLock { get; } = new object();
+ private object CancellationTokenSourceLock { get; } = new();
- // SizeLimit is not set, so cache size set on the entry will be ignored.
- public IMemoryCache Cache { get; }
+ private IdempotencyRequestCache IdempotencyRequestCache { get; }
private CancellationTokenSource TipChangeCancellationTokenSource
{
@@ -43,144 +43,154 @@ public class CachedRpcClient : RpcClientBase
{
string cacheKey = nameof(GetBestBlockHashAsync);
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(1, 4, true),
- () => base.GetBestBlockHashAsync(cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetBestBlockHashAsync(cancellationToken),
+ options: CacheOptions(size: 1, expireInSeconds: 4, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<Block> GetBlockAsync(uint256 blockHash, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetBlockAsync)}:{blockHash}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(10, 300),
- () => base.GetBlockAsync(blockHash, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetBlockAsync(blockHash, cancellationToken),
+ options: CacheOptions(size: 10, expireInSeconds: 300),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<Block> GetBlockAsync(uint blockHeight, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetBlockAsync)}:{blockHeight}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(10, 300),
- () => base.GetBlockAsync(blockHeight, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetBlockAsync(blockHeight, cancellationToken),
+ options: CacheOptions(size: 10, expireInSeconds: 300),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<VerboseBlockInfo> GetVerboseBlockAsync(uint256 blockId, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetVerboseBlockAsync)}:{blockId}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(20, 300),
- () => base.GetVerboseBlockAsync(blockId, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetVerboseBlockAsync(blockId, cancellationToken),
+ options: CacheOptions(size: 20, expireInSeconds: 300),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<BlockHeader> GetBlockHeaderAsync(uint256 blockHash, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetBlockHeaderAsync)}:{blockHash}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(2, 300),
- () => base.GetBlockHeaderAsync(blockHash, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetBlockHeaderAsync(blockHash, cancellationToken),
+ options: CacheOptions(size: 2, expireInSeconds: 300),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<int> GetBlockCountAsync(CancellationToken cancellationToken = default)
{
string cacheKey = nameof(GetBlockCountAsync);
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(1, 2, true),
- () => base.GetBlockCountAsync(cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetBlockCountAsync(cancellationToken),
+ options: CacheOptions(size: 1, expireInSeconds: 2, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<PeerInfo[]> GetPeersInfoAsync(CancellationToken cancellationToken = default)
{
string cacheKey = nameof(GetPeersInfoAsync);
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(2, 0.5),
- () => base.GetPeersInfoAsync(cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetPeersInfoAsync(cancellationToken),
+ options: CacheOptions(size: 2, expireInSeconds: 0.5),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<MempoolEntry> GetMempoolEntryAsync(uint256 txid, bool throwIfNotFound = true, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetMempoolEntryAsync)}:{txid}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(20, 2, true),
- () => base.GetMempoolEntryAsync(txid, throwIfNotFound, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetMempoolEntryAsync(txid, throwIfNotFound, cancellationToken),
+ options: CacheOptions(size: 20, expireInSeconds: 2, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
- public override async Task<MemPoolInfo> GetMempoolInfoAsync(CancellationToken cancel = default)
+ public override async Task<MemPoolInfo> GetMempoolInfoAsync(CancellationToken cancellationToken = default)
{
string cacheKey = nameof(GetMempoolInfoAsync);
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(1, 10),
- () => base.GetMempoolInfoAsync(cancel)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetMempoolInfoAsync(cancellationToken),
+ options: CacheOptions(size: 1, expireInSeconds: 10),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<EstimateSmartFeeResponse> EstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(EstimateSmartFeeAsync)}:{confirmationTarget}:{estimateMode}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(1, 60, true),
- () => base.EstimateSmartFeeAsync(confirmationTarget, estimateMode, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.EstimateSmartFeeAsync(confirmationTarget, estimateMode, cancellationToken),
+ options: CacheOptions(size: 1, expireInSeconds: 60, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<uint256[]> GetRawMempoolAsync(CancellationToken cancellationToken = default)
{
string cacheKey = nameof(GetRawMempoolAsync);
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(20, 2, true),
- () => base.GetRawMempoolAsync(cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetRawMempoolAsync(cancellationToken),
+ options: CacheOptions(size: 20, expireInSeconds: 2, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
public override async Task<GetTxOutResponse?> GetTxOutAsync(uint256 txid, int index, bool includeMempool = true, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(GetTxOutAsync)}:{txid}:{index}:{includeMempool}";
- return await Cache.AtomicGetOrCreateAsync(
+ return await IdempotencyRequestCache.GetCachedResponseAsync(
cacheKey,
- CacheOptions(2, 2, true),
- () => base.GetTxOutAsync(txid, index, includeMempool, cancellationToken)).ConfigureAwait(false);
+ action: (string request, CancellationToken cancellationToken) => base.GetTxOutAsync(txid, index, includeMempool, cancellationToken),
+ options: CacheOptions(size: 2, expireInSeconds: 2, addExpirationToken: true),
+ cancellationToken).ConfigureAwait(false);
}
- // Only used in Regtest mode or in tests.
public override async Task<uint256[]> GenerateAsync(int blockCount, CancellationToken cancellationToken = default)
{
TipChangeCancellationTokenSource.Cancel();
return await base.GenerateAsync(blockCount, cancellationToken).ConfigureAwait(false);
}
- // Only used in Regtest mode or in tests.
public override async Task InvalidateBlockAsync(uint256 blockHash, CancellationToken cancellationToken = default)
{
TipChangeCancellationTokenSource.Cancel();
await base.InvalidateBlockAsync(blockHash, cancellationToken).ConfigureAwait(false);
}
- private MemoryCacheEntryOptions CacheOptions(int size, double expireInSeconds, bool addExpitationToken = false)
+ private MemoryCacheEntryOptions CacheOptions(int size, double expireInSeconds, bool addExpirationToken = false)
{
var cacheOptions = new MemoryCacheEntryOptions
{
Size = size,
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(expireInSeconds)
};
- if (addExpitationToken)
+ if (addExpirationToken)
{
cacheOptions.AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
}

Switch `CachedRpcClient` to use `IdempotencyRequestCache`.