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)