using Microsoft.Extensions.Logging; using StackExchange.Redis; using System.Diagnostics; using System.Text.Json; namespace StellaOps.Provcache.Valkey; public sealed partial class ValkeyProvcacheStore { public async ValueTask GetAsync(string veriKey, CancellationToken cancellationToken = default) { var sw = Stopwatch.StartNew(); cancellationToken.ThrowIfCancellationRequested(); try { var db = await GetDatabaseAsync(cancellationToken).ConfigureAwait(false); var redisKey = BuildKey(veriKey); var value = await db.StringGetAsync(redisKey).ConfigureAwait(false); sw.Stop(); if (value.IsNullOrEmpty) { _logger.LogDebug("Cache miss for VeriKey {VeriKey} in {ElapsedMs}ms", veriKey, sw.Elapsed.TotalMilliseconds); return ProvcacheLookupResult.Miss(sw.Elapsed.TotalMilliseconds); } var entry = JsonSerializer.Deserialize((string)value!, _jsonOptions); if (entry is null) { _logger.LogWarning("Failed to deserialize cache entry for VeriKey {VeriKey}", veriKey); return ProvcacheLookupResult.Miss(sw.Elapsed.TotalMilliseconds); } if (_options.SlidingExpiration) { var ttl = entry.ExpiresAt - _timeProvider.GetUtcNow(); if (ttl > TimeSpan.Zero) { await db.KeyExpireAsync(redisKey, ttl).ConfigureAwait(false); } } _logger.LogDebug("Cache hit for VeriKey {VeriKey} in {ElapsedMs}ms", veriKey, sw.Elapsed.TotalMilliseconds); return ProvcacheLookupResult.Hit(entry, ProviderName, sw.Elapsed.TotalMilliseconds); } catch (Exception ex) { sw.Stop(); _logger.LogError(ex, "Error getting cache entry for VeriKey {VeriKey}", veriKey); return ProvcacheLookupResult.Miss(sw.Elapsed.TotalMilliseconds); } } }