101 lines
4.1 KiB
C#
101 lines
4.1 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace StellaOps.Provcache;
|
|
|
|
public sealed partial class ProvcacheService
|
|
{
|
|
/// <inheritdoc />
|
|
public async Task<ProvcacheServiceResult> GetAsync(
|
|
string veriKey,
|
|
bool bypassCache = false,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(veriKey);
|
|
|
|
if (bypassCache && _options.AllowCacheBypass)
|
|
{
|
|
_logger.LogDebug("Cache bypass requested for VeriKey {VeriKey}", veriKey);
|
|
return ProvcacheServiceResult.Bypassed();
|
|
}
|
|
var sw = Stopwatch.StartNew();
|
|
using var activity = ProvcacheTelemetry.StartGetActivity(veriKey);
|
|
|
|
try
|
|
{
|
|
var result = await _store.GetAsync(veriKey, cancellationToken).ConfigureAwait(false);
|
|
sw.Stop();
|
|
|
|
RecordMetrics(result.IsHit, sw.Elapsed.TotalMilliseconds);
|
|
|
|
if (result.IsHit && result.Entry is not null)
|
|
{
|
|
if (result.Entry.ExpiresAt <= _timeProvider.GetUtcNow())
|
|
{
|
|
_logger.LogDebug("Cache entry for VeriKey {VeriKey} is expired", veriKey);
|
|
ProvcacheTelemetry.RecordRequest("get", "expired");
|
|
return ProvcacheServiceResult.Expired(result.Entry, sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
_logger.LogDebug(
|
|
"Cache hit for VeriKey {VeriKey} from {Source} in {ElapsedMs}ms",
|
|
veriKey,
|
|
result.Source,
|
|
sw.Elapsed.TotalMilliseconds);
|
|
|
|
ProvcacheTelemetry.MarkCacheHit(activity, result.Source ?? "valkey");
|
|
ProvcacheTelemetry.RecordHit(result.Source ?? "valkey");
|
|
ProvcacheTelemetry.RecordRequest("get", ProvcacheTelemetry.ResultHit);
|
|
ProvcacheTelemetry.RecordLatency("get", sw.Elapsed);
|
|
|
|
return ProvcacheServiceResult.Hit(result.Entry, result.Source!, sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
var dbEntry = await _repository.GetAsync(veriKey, cancellationToken).ConfigureAwait(false);
|
|
sw.Stop();
|
|
|
|
if (dbEntry is not null)
|
|
{
|
|
if (dbEntry.ExpiresAt <= _timeProvider.GetUtcNow())
|
|
{
|
|
_logger.LogDebug("Database entry for VeriKey {VeriKey} is expired", veriKey);
|
|
ProvcacheTelemetry.RecordRequest("get", "expired");
|
|
return ProvcacheServiceResult.Expired(dbEntry, sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
await _store.SetAsync(dbEntry, cancellationToken).ConfigureAwait(false);
|
|
|
|
_logger.LogDebug(
|
|
"Cache backfill for VeriKey {VeriKey} from postgres in {ElapsedMs}ms",
|
|
veriKey,
|
|
sw.Elapsed.TotalMilliseconds);
|
|
|
|
ProvcacheTelemetry.MarkCacheHit(activity, "postgres");
|
|
ProvcacheTelemetry.RecordHit("postgres");
|
|
ProvcacheTelemetry.RecordRequest("get", ProvcacheTelemetry.ResultHit);
|
|
ProvcacheTelemetry.RecordLatency("get", sw.Elapsed);
|
|
|
|
return ProvcacheServiceResult.Hit(dbEntry, "postgres", sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
ProvcacheTelemetry.MarkCacheMiss(activity);
|
|
ProvcacheTelemetry.RecordMiss();
|
|
ProvcacheTelemetry.RecordRequest("get", ProvcacheTelemetry.ResultMiss);
|
|
ProvcacheTelemetry.RecordLatency("get", sw.Elapsed);
|
|
|
|
_logger.LogDebug("Cache miss for VeriKey {VeriKey} in {ElapsedMs}ms", veriKey, sw.Elapsed.TotalMilliseconds);
|
|
return ProvcacheServiceResult.Miss(sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ProvcacheTelemetry.MarkError(activity, ex.Message);
|
|
ProvcacheTelemetry.RecordRequest("get", ProvcacheTelemetry.ResultError);
|
|
_logger.LogError(ex, "Error getting cache entry for VeriKey {VeriKey}", veriKey);
|
|
return ProvcacheServiceResult.Miss(sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
}
|
|
}
|