feat(audit): Apply TreatWarningsAsErrors=true to 160+ production csproj files
Sprint: SPRINT_20251229_049_BE_csproj_audit_maint_tests Tasks: AUDIT-0001 through AUDIT-0147 APPLY tasks (approved decisions 1-9) Changes: - Set TreatWarningsAsErrors=true for all production .NET projects - Fixed nullable warnings in Scanner.EntryTrace, Scanner.Evidence, Scheduler.Worker, Concelier connectors, and other modules - Injected TimeProvider/IGuidProvider for deterministic time/ID generation - Added path traversal validation in AirGap.Bundle - Fixed NULL handling in various cursor classes - Third-party GostCryptography retains TreatWarningsAsErrors=false (preserves original) - Test projects excluded per user decision (rejected decision 10) Note: All 17 ACSC connector tests pass after snapshot fixture sync
This commit is contained in:
@@ -12,6 +12,10 @@ namespace StellaOps.AdvisoryAI.Worker.Services;
|
||||
|
||||
internal sealed class AdvisoryTaskWorker : BackgroundService
|
||||
{
|
||||
private const int MaxRetryDelaySeconds = 60;
|
||||
private const int BaseRetryDelaySeconds = 2;
|
||||
private const double JitterFactor = 0.2;
|
||||
|
||||
private readonly IAdvisoryTaskQueue _queue;
|
||||
private readonly IAdvisoryPlanCache _cache;
|
||||
private readonly IAdvisoryPipelineOrchestrator _orchestrator;
|
||||
@@ -19,6 +23,7 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
||||
private readonly IAdvisoryPipelineExecutor _executor;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<AdvisoryTaskWorker> _logger;
|
||||
private int _consecutiveErrors;
|
||||
|
||||
public AdvisoryTaskWorker(
|
||||
IAdvisoryTaskQueue queue,
|
||||
@@ -61,11 +66,28 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
||||
var fromCache = plan is not null && !message.Request.ForceRefresh;
|
||||
activity?.SetTag("advisory.plan_cache_hit", fromCache);
|
||||
|
||||
// When cache miss occurs, preserve the original plan cache key by storing
|
||||
// under the message's key as an alias
|
||||
string effectiveCacheKey = message.PlanCacheKey;
|
||||
if (!fromCache)
|
||||
{
|
||||
var start = _timeProvider.GetTimestamp();
|
||||
plan = await _orchestrator.CreatePlanAsync(message.Request, stoppingToken).ConfigureAwait(false);
|
||||
|
||||
// Store under both the new cache key and the original message key
|
||||
await _cache.SetAsync(plan.CacheKey, plan, stoppingToken).ConfigureAwait(false);
|
||||
|
||||
// If the new plan's cache key differs from the original request,
|
||||
// also store under the original key as an alias
|
||||
if (!string.Equals(plan.CacheKey, message.PlanCacheKey, StringComparison.Ordinal))
|
||||
{
|
||||
await _cache.SetAsync(message.PlanCacheKey, plan, stoppingToken).ConfigureAwait(false);
|
||||
_logger.LogDebug(
|
||||
"Plan cache key changed from {OriginalKey} to {NewKey}; stored alias",
|
||||
message.PlanCacheKey,
|
||||
plan.CacheKey);
|
||||
}
|
||||
|
||||
var elapsed = _timeProvider.GetElapsedTime(start);
|
||||
_metrics.RecordPlanCreated(elapsed.TotalSeconds, message.Request.TaskType);
|
||||
}
|
||||
@@ -85,18 +107,48 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
||||
var totalElapsed = _timeProvider.GetElapsedTime(processStart);
|
||||
_metrics.RecordPipelineLatency(message.Request.TaskType, totalElapsed.TotalSeconds, fromCache);
|
||||
activity?.SetTag("advisory.pipeline_latency_seconds", totalElapsed.TotalSeconds);
|
||||
|
||||
// Reset consecutive error count on success
|
||||
_consecutiveErrors = 0;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
// graceful shutdown
|
||||
// Graceful shutdown - exit the loop cleanly
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing advisory task queue message");
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken).ConfigureAwait(false);
|
||||
_consecutiveErrors++;
|
||||
|
||||
// Apply exponential backoff with jitter
|
||||
var delaySeconds = ComputeRetryDelay(_consecutiveErrors);
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(delaySeconds), stoppingToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
// Graceful shutdown during delay - exit cleanly
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Advisory pipeline worker stopping");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes retry delay with exponential backoff and jitter.
|
||||
/// </summary>
|
||||
private double ComputeRetryDelay(int errorCount)
|
||||
{
|
||||
// Exponential backoff: base * 2^(errorCount-1), capped at max
|
||||
var backoff = Math.Min(BaseRetryDelaySeconds * Math.Pow(2, errorCount - 1), MaxRetryDelaySeconds);
|
||||
|
||||
// Add jitter (+/- JitterFactor percent)
|
||||
var jitter = backoff * JitterFactor * (2 * Random.Shared.NextDouble() - 1);
|
||||
|
||||
return Math.Max(BaseRetryDelaySeconds, backoff + jitter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
|
||||
Reference in New Issue
Block a user