namespace StellaOps.Provcache; /// /// High-level service interface for Provcache operations. /// Orchestrates cache store and repository with metrics and invalidation logic. /// public interface IProvcacheService { /// /// Gets a cached decision by VeriKey. /// /// The cache key. /// If true, skip cache and force re-evaluation. /// Cancellation token. /// The cache result with decision if found. Task GetAsync( string veriKey, bool bypassCache = false, CancellationToken cancellationToken = default); /// /// Stores a decision in the cache. /// /// The cache entry to store. /// Cancellation token. /// True if the entry was stored successfully. Task SetAsync(ProvcacheEntry entry, CancellationToken cancellationToken = default); /// /// Gets or computes a decision using a factory function for cache misses. /// /// The cache key. /// Factory function to create the entry on cache miss. /// If true, skip cache and force re-computation. /// Cancellation token. /// The cached or newly computed entry. Task GetOrComputeAsync( string veriKey, Func> factory, bool bypassCache = false, CancellationToken cancellationToken = default); /// /// Invalidates a cache entry by VeriKey. /// /// The cache key. /// Reason for invalidation (for audit log). /// Cancellation token. /// True if the entry existed and was invalidated. Task InvalidateAsync( string veriKey, string? reason = null, CancellationToken cancellationToken = default); /// /// Invalidates entries by invalidation criteria. /// /// The invalidation request. /// Cancellation token. /// Invalidation result with count of affected entries. Task InvalidateByAsync( InvalidationRequest request, CancellationToken cancellationToken = default); /// /// Gets cache metrics for monitoring. /// /// Cancellation token. /// Cache metrics. Task GetMetricsAsync(CancellationToken cancellationToken = default); /// /// Prunes expired entries from the cache. /// /// Cancellation token. /// Number of entries pruned. Task PruneExpiredAsync(CancellationToken cancellationToken = default); } /// /// Result of a cache service lookup. /// public sealed record ProvcacheServiceResult { /// /// The cache result status. /// public required ProvcacheResultStatus Status { get; init; } /// /// The cache entry if found. /// public ProvcacheEntry? Entry { get; init; } /// /// Whether the result came from cache (true) or needs computation (false). /// public bool WasCached => Status == ProvcacheResultStatus.CacheHit; /// /// Source of the cache hit for diagnostics. /// public string? Source { get; init; } /// /// Time taken for the lookup in milliseconds. /// public double ElapsedMs { get; init; } /// /// Creates a cache hit result. /// public static ProvcacheServiceResult Hit(ProvcacheEntry entry, string source, double elapsedMs) => new() { Status = ProvcacheResultStatus.CacheHit, Entry = entry, Source = source, ElapsedMs = elapsedMs }; /// /// Creates a cache miss result. /// public static ProvcacheServiceResult Miss(double elapsedMs) => new() { Status = ProvcacheResultStatus.CacheMiss, Entry = null, Source = null, ElapsedMs = elapsedMs }; /// /// Creates a bypassed result (cache was skipped). /// public static ProvcacheServiceResult Bypassed() => new() { Status = ProvcacheResultStatus.Bypassed, Entry = null, Source = null, ElapsedMs = 0 }; /// /// Creates an expired result. /// public static ProvcacheServiceResult Expired(ProvcacheEntry entry, double elapsedMs) => new() { Status = ProvcacheResultStatus.Expired, Entry = entry, Source = "expired", ElapsedMs = elapsedMs }; } /// /// Cache result status. /// public enum ProvcacheResultStatus { /// /// Entry was found in cache and is valid. /// CacheHit, /// /// Entry was not found in cache. /// CacheMiss, /// /// Cache was bypassed (force re-computation). /// Bypassed, /// /// Entry was found but has expired. /// Expired } /// /// Request for cache invalidation by criteria. /// public sealed record InvalidationRequest { /// /// The invalidation type. /// public required InvalidationType Type { get; init; } /// /// The value to match for invalidation. /// public required string Value { get; init; } /// /// Reason for invalidation (for audit log). /// public string? Reason { get; init; } /// /// Actor who initiated the invalidation. /// public string? Actor { get; init; } /// /// Creates an invalidation request by policy hash. /// public static InvalidationRequest ByPolicyHash(string policyHash, string? reason = null) => new() { Type = InvalidationType.PolicyHash, Value = policyHash, Reason = reason ?? "policy-update" }; /// /// Creates an invalidation request by signer set hash. /// public static InvalidationRequest BySignerSetHash(string signerSetHash, string? reason = null) => new() { Type = InvalidationType.SignerSetHash, Value = signerSetHash, Reason = reason ?? "signer-revocation" }; /// /// Creates an invalidation request by feed epoch. /// public static InvalidationRequest ByFeedEpochOlderThan(string feedEpoch, string? reason = null) => new() { Type = InvalidationType.FeedEpochOlderThan, Value = feedEpoch, Reason = reason ?? "feed-update" }; /// /// Creates an invalidation request by key pattern. /// public static InvalidationRequest ByPattern(string pattern, string? reason = null) => new() { Type = InvalidationType.Pattern, Value = pattern, Reason = reason ?? "pattern-invalidation" }; } /// /// Type of invalidation criteria. /// public enum InvalidationType { /// /// Invalidate by policy hash. /// PolicyHash, /// /// Invalidate by signer set hash. /// SignerSetHash, /// /// Invalidate entries with feed epoch older than specified. /// FeedEpochOlderThan, /// /// Invalidate by key pattern. /// Pattern, /// /// Invalidate expired entries. /// Expired } /// /// Result of an invalidation operation. /// public sealed record InvalidationResult { /// /// Number of entries invalidated. /// public required long EntriesAffected { get; init; } /// /// The invalidation request that was executed. /// public required InvalidationRequest Request { get; init; } /// /// Timestamp of the invalidation. /// public required DateTimeOffset Timestamp { get; init; } /// /// Whether the invalidation was logged for audit. /// public bool WasLogged { get; init; } } /// /// Cache metrics for monitoring and observability. /// public sealed record ProvcacheMetrics { /// /// Total cache requests since startup. /// public long TotalRequests { get; init; } /// /// Total cache hits since startup. /// public long TotalHits { get; init; } /// /// Total cache misses since startup. /// public long TotalMisses { get; init; } /// /// Cache hit rate (0.0 - 1.0). /// public double HitRate => TotalRequests == 0 ? 0.0 : (double)TotalHits / TotalRequests; /// /// Average lookup latency in milliseconds. /// public double AvgLatencyMs { get; init; } /// /// P99 lookup latency in milliseconds. /// public double P99LatencyMs { get; init; } /// /// Current number of entries in cache. /// public long CurrentEntryCount { get; init; } /// /// Total invalidations since startup. /// public long TotalInvalidations { get; init; } /// /// Valkey cache health status. /// public bool ValkeyCacheHealthy { get; init; } /// /// Postgres repository health status. /// public bool PostgresRepositoryHealthy { get; init; } /// /// Timestamp when metrics were collected. /// public DateTimeOffset CollectedAt { get; init; } }