audit, advisories and doctors/setup work
This commit is contained in:
@@ -18,6 +18,8 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
private readonly SemaphoreSlim _connectionLock = new(1, 1);
|
||||
private IDatabase? _database;
|
||||
private const int DefaultScanPageSize = 200;
|
||||
private const int DefaultDeleteBatchSize = 500;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "valkey";
|
||||
@@ -43,10 +45,11 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
public async ValueTask<ProvcacheLookupResult> GetAsync(string veriKey, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
var redisKey = BuildKey(veriKey);
|
||||
|
||||
var value = await db.StringGetAsync(redisKey).ConfigureAwait(false);
|
||||
@@ -92,6 +95,7 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var keyList = veriKeys.ToList();
|
||||
|
||||
if (keyList.Count == 0)
|
||||
@@ -106,7 +110,7 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
var redisKeys = keyList.Select(k => (RedisKey)BuildKey(k)).ToArray();
|
||||
|
||||
var values = await db.StringGetAsync(redisKeys).ConfigureAwait(false);
|
||||
@@ -168,7 +172,8 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
var redisKey = BuildKey(entry.VeriKey);
|
||||
var value = JsonSerializer.Serialize(entry, _jsonOptions);
|
||||
|
||||
@@ -200,18 +205,20 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
public async ValueTask SetManyAsync(IEnumerable<ProvcacheEntry> entries, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var entryList = entries.ToList();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (entryList.Count == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
var batch = db.CreateBatch();
|
||||
var tasks = new List<Task>();
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
|
||||
foreach (var entry in entryList)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var redisKey = BuildKey(entry.VeriKey);
|
||||
var value = JsonSerializer.Serialize(entry, _jsonOptions);
|
||||
|
||||
@@ -242,7 +249,8 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
var redisKey = BuildKey(veriKey);
|
||||
|
||||
var deleted = await db.KeyDeleteAsync(redisKey).ConfigureAwait(false);
|
||||
@@ -262,16 +270,44 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var db = await GetDatabaseAsync().ConfigureAwait(false);
|
||||
var server = _connectionMultiplexer.GetServer(_connectionMultiplexer.GetEndPoints().First());
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var endpoints = _connectionMultiplexer.GetEndPoints();
|
||||
if (endpoints.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var fullPattern = $"{_options.ValkeyKeyPrefix}{pattern}";
|
||||
var keys = server.Keys(pattern: fullPattern).ToArray();
|
||||
long deleted = 0;
|
||||
|
||||
if (keys.Length == 0)
|
||||
return 0;
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var server = _connectionMultiplexer.GetServer(endpoint);
|
||||
if (server is null || !server.IsConnected)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var deleted = await db.KeyDeleteAsync(keys).ConfigureAwait(false);
|
||||
var batchKeys = new List<RedisKey>(DefaultDeleteBatchSize);
|
||||
foreach (var key in server.Keys(pattern: fullPattern, pageSize: DefaultScanPageSize))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
batchKeys.Add(key);
|
||||
if (batchKeys.Count >= DefaultDeleteBatchSize)
|
||||
{
|
||||
deleted += await db.KeyDeleteAsync(batchKeys.ToArray()).ConfigureAwait(false);
|
||||
batchKeys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (batchKeys.Count > 0)
|
||||
{
|
||||
deleted += await db.KeyDeleteAsync(batchKeys.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Invalidated {Count} cache entries matching pattern {Pattern}", deleted, pattern);
|
||||
return deleted;
|
||||
@@ -289,6 +325,7 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
Func<CancellationToken, ValueTask<ProvcacheEntry>> factory,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var result = await GetAsync(veriKey, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsHit && result.Entry is not null)
|
||||
{
|
||||
@@ -303,12 +340,14 @@ public sealed class ValkeyProvcacheStore : IProvcacheStore, IAsyncDisposable
|
||||
|
||||
private string BuildKey(string veriKey) => $"{_options.ValkeyKeyPrefix}{veriKey}";
|
||||
|
||||
private async Task<IDatabase> GetDatabaseAsync()
|
||||
private async Task<IDatabase> GetDatabaseAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (_database is not null)
|
||||
return _database;
|
||||
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
await _connectionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_database ??= _connectionMultiplexer.GetDatabase();
|
||||
|
||||
Reference in New Issue
Block a user