up
Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
This commit is contained in:
@@ -4,8 +4,9 @@ using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Policy.Engine.Options;
|
||||
using StellaOps.Policy.Engine.Storage.Mongo.Repositories;
|
||||
using StellaOps.Policy.Engine.Telemetry;
|
||||
using StellaOps.Policy.Storage.Postgres.Models;
|
||||
using StellaOps.Policy.Storage.Postgres.Repositories;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace StellaOps.Policy.Engine.ExceptionCache;
|
||||
@@ -347,73 +348,37 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
|
||||
try
|
||||
{
|
||||
// Get all active exceptions from repository
|
||||
var exceptions = await _repository.ListExceptionsAsync(
|
||||
var exceptions = await _repository.GetAllAsync(
|
||||
tenantId,
|
||||
new ExceptionQueryOptions
|
||||
{
|
||||
Statuses = ImmutableArray.Create("active"),
|
||||
IncludeExpired = false,
|
||||
Limit = _options.MaxEntriesPerTenant,
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
ExceptionStatus.Active,
|
||||
limit: _options.MaxEntriesPerTenant,
|
||||
offset: 0,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (exceptions.Length == 0)
|
||||
if (exceptions.Count == 0)
|
||||
{
|
||||
_logger.LogDebug("No active exceptions to warm for tenant {TenantId}", tenantId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get bindings for all exceptions
|
||||
var entries = new List<ExceptionCacheEntry>();
|
||||
|
||||
foreach (var exception in exceptions)
|
||||
{
|
||||
var bindings = await _repository.GetBindingsForExceptionAsync(
|
||||
tenantId, exception.Id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
foreach (var binding in bindings.Where(b => b.Status == "active"))
|
||||
entries.Add(new ExceptionCacheEntry
|
||||
{
|
||||
entries.Add(new ExceptionCacheEntry
|
||||
{
|
||||
ExceptionId = exception.Id,
|
||||
AssetId = binding.AssetId,
|
||||
AdvisoryId = binding.AdvisoryId,
|
||||
CveId = binding.CveId,
|
||||
DecisionOverride = binding.DecisionOverride,
|
||||
ExceptionType = exception.ExceptionType,
|
||||
Priority = exception.Priority,
|
||||
EffectiveFrom = binding.EffectiveFrom,
|
||||
ExpiresAt = binding.ExpiresAt ?? exception.ExpiresAt,
|
||||
CachedAt = now,
|
||||
ExceptionName = exception.Name,
|
||||
});
|
||||
}
|
||||
|
||||
// Also add entries for scope-based exceptions without explicit bindings
|
||||
if (exception.Scope.ApplyToAll || exception.Scope.AssetIds.Count > 0)
|
||||
{
|
||||
foreach (var assetId in exception.Scope.AssetIds)
|
||||
{
|
||||
foreach (var advisoryId in exception.Scope.AdvisoryIds.DefaultIfEmpty(null!))
|
||||
{
|
||||
entries.Add(new ExceptionCacheEntry
|
||||
{
|
||||
ExceptionId = exception.Id,
|
||||
AssetId = assetId,
|
||||
AdvisoryId = advisoryId,
|
||||
CveId = null,
|
||||
DecisionOverride = "allow",
|
||||
ExceptionType = exception.ExceptionType,
|
||||
Priority = exception.Priority,
|
||||
EffectiveFrom = exception.EffectiveFrom ?? exception.CreatedAt,
|
||||
ExpiresAt = exception.ExpiresAt,
|
||||
CachedAt = now,
|
||||
ExceptionName = exception.Name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ExceptionId = exception.Id.ToString(),
|
||||
AssetId = string.IsNullOrWhiteSpace(exception.ProjectId) ? "*" : exception.ProjectId!,
|
||||
AdvisoryId = null,
|
||||
CveId = null,
|
||||
DecisionOverride = "allow",
|
||||
ExceptionType = "waiver",
|
||||
Priority = 0,
|
||||
EffectiveFrom = exception.CreatedAt,
|
||||
ExpiresAt = exception.ExpiresAt,
|
||||
CachedAt = now,
|
||||
ExceptionName = exception.Name,
|
||||
});
|
||||
}
|
||||
|
||||
if (entries.Count > 0)
|
||||
@@ -430,7 +395,7 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
|
||||
_logger.LogInformation(
|
||||
"Warmed cache with {Count} entries from {ExceptionCount} exceptions for tenant {TenantId} in {Duration}ms",
|
||||
entries.Count, exceptions.Length, tenantId, sw.ElapsedMilliseconds);
|
||||
entries.Count, exceptions.Count, tenantId, sw.ElapsedMilliseconds);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -584,7 +549,6 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
switch (exceptionEvent.EventType.ToLowerInvariant())
|
||||
{
|
||||
case "activated":
|
||||
// Warm the cache with the new exception
|
||||
await WarmExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
@@ -592,13 +556,11 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
case "expired":
|
||||
case "revoked":
|
||||
case "deleted":
|
||||
// Invalidate cache entries for this exception
|
||||
await InvalidateExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case "updated":
|
||||
// Invalidate and re-warm
|
||||
await InvalidateExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await WarmExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
@@ -606,14 +568,8 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
break;
|
||||
|
||||
case "created":
|
||||
// Only warm if already active
|
||||
var exception = await _repository.GetExceptionAsync(
|
||||
exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken).ConfigureAwait(false);
|
||||
if (exception?.Status == "active")
|
||||
{
|
||||
await WarmExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
await WarmExceptionAsync(exceptionEvent.TenantId, exceptionEvent.ExceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -626,10 +582,16 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
|
||||
private async Task WarmExceptionAsync(string tenantId, string exceptionId, CancellationToken cancellationToken)
|
||||
{
|
||||
var exception = await _repository.GetExceptionAsync(tenantId, exceptionId, cancellationToken)
|
||||
if (!Guid.TryParse(exceptionId, out var exceptionGuid))
|
||||
{
|
||||
_logger.LogWarning("Unable to parse exception id {ExceptionId} for tenant {TenantId}", exceptionId, tenantId);
|
||||
return;
|
||||
}
|
||||
|
||||
var exception = await _repository.GetByIdAsync(tenantId, exceptionGuid, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (exception is null || exception.Status != "active")
|
||||
if (exception is null || exception.Status != ExceptionStatus.Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -637,31 +599,22 @@ internal sealed class RedisExceptionEffectiveCache : IExceptionEffectiveCache
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var entries = new List<ExceptionCacheEntry>();
|
||||
|
||||
var bindings = await _repository.GetBindingsForExceptionAsync(tenantId, exceptionId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var binding in bindings.Where(b => b.Status == "active"))
|
||||
entries.Add(new ExceptionCacheEntry
|
||||
{
|
||||
entries.Add(new ExceptionCacheEntry
|
||||
{
|
||||
ExceptionId = exception.Id,
|
||||
AssetId = binding.AssetId,
|
||||
AdvisoryId = binding.AdvisoryId,
|
||||
CveId = binding.CveId,
|
||||
DecisionOverride = binding.DecisionOverride,
|
||||
ExceptionType = exception.ExceptionType,
|
||||
Priority = exception.Priority,
|
||||
EffectiveFrom = binding.EffectiveFrom,
|
||||
ExpiresAt = binding.ExpiresAt ?? exception.ExpiresAt,
|
||||
CachedAt = now,
|
||||
ExceptionName = exception.Name,
|
||||
});
|
||||
}
|
||||
ExceptionId = exception.Id.ToString(),
|
||||
AssetId = string.IsNullOrWhiteSpace(exception.ProjectId) ? "*" : exception.ProjectId!,
|
||||
AdvisoryId = null,
|
||||
CveId = null,
|
||||
DecisionOverride = "allow",
|
||||
ExceptionType = "waiver",
|
||||
Priority = 0,
|
||||
EffectiveFrom = exception.CreatedAt,
|
||||
ExpiresAt = exception.ExpiresAt,
|
||||
CachedAt = now,
|
||||
ExceptionName = exception.Name,
|
||||
});
|
||||
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
await SetBatchAsync(tenantId, entries, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await SetBatchAsync(tenantId, entries, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
_logger.LogDebug(
|
||||
"Warmed cache with {Count} entries for exception {ExceptionId}",
|
||||
|
||||
Reference in New Issue
Block a user