diff --git a/ECMJobRunner.WebCron/ProfileWorker/ProfileWorker.cs b/ECMJobRunner.WebCron/ProfileWorker/ProfileWorker.cs index 13a9e48..f6b2d0f 100644 --- a/ECMJobRunner.WebCron/ProfileWorker/ProfileWorker.cs +++ b/ECMJobRunner.WebCron/ProfileWorker/ProfileWorker.cs @@ -18,12 +18,14 @@ namespace ECMJobRunner.WebCron.ProfileWorker; /// Tracks health status based on success/failure patterns /// Gracefully handles cancellation during application shutdown /// -/// Health states: +/// Health states (with adaptive thresholds): /// -/// Healthy: Last successful run within 3x interval -/// Degraded: 1-2 consecutive failures but within time limit -/// Unhealthy: No success for 3x interval or 3+ consecutive failures +/// Healthy: Recent successful run with no failures +/// Degraded: 1+ consecutive failures but within time threshold +/// Unhealthy: No success within adaptive threshold (3x interval for fast intervals <10s, 1.5x for slower intervals) /// +/// The adaptive multiplier ensures fast problem detection when using longer intervals (e.g., 60s interval = 90s timeout) +/// while maintaining tolerance for network jitter on short intervals (e.g., 1s interval = 3s timeout). /// public class ProfileWorker( ILogger Logger, @@ -112,20 +114,24 @@ public class ProfileWorker( /// /// Health determination logic: /// - /// Unhealthy: Time since last success exceeds 3x the configured interval + /// Unhealthy: Time since last success exceeds adaptive threshold (3x interval for fast intervals <10s, 1.5x for slower intervals) /// Degraded: 1+ consecutive failures within time threshold /// Healthy: Recent successful run with no failures /// - /// Includes diagnostic data in the result for monitoring and alerting. + /// Adaptive multiplier ensures fast problem detection for longer intervals while maintaining tolerance for short intervals. /// public Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) { var timeSinceLastSuccess = DateTime.UtcNow - _lastSuccessfulRun; - var maxAllowedDelay = TimeSpan.FromMilliseconds(_options.IntervalMS * 3); + + // Adaptive multiplier: 3x for fast intervals (<10s), 1.5x for slower intervals + // This ensures faster problem detection when using longer intervals (e.g., 60s) + var multiplier = _options.IntervalMS < 10000 ? 3.0 : 1.5; + var maxAllowedDelay = TimeSpan.FromMilliseconds(_options.IntervalMS * multiplier); - // Unhealthy: No successful run for 3x interval + // Unhealthy: No successful run within adaptive threshold if (timeSinceLastSuccess > maxAllowedDelay) { var data = new Dictionary @@ -133,7 +139,9 @@ public class ProfileWorker( ["LastSuccessfulRun"] = _lastSuccessfulRun, ["TimeSinceLastSuccess"] = timeSinceLastSuccess, ["ConsecutiveFailures"] = _consecutiveFailures, - ["IntervalMS"] = _options.IntervalMS + ["IntervalMS"] = _options.IntervalMS, + ["Multiplier"] = multiplier, + ["MaxAllowedDelay"] = maxAllowedDelay }; if (_lastException != null)