Skip to content

Algorithm

The recommendation engine is a chain of six composable estimators. Each estimator wraps the previous one, processing the result before passing it along.

Chain overview

flowchart LR
    A["1. Percentile"] --> B["2. Margin"]
    B --> C["3. Burst"]
    C --> D["4. Confidence"]
    D --> E["5. Bounds"]
    E --> F["6. Change Filter"]

The chain is constructed in recommendation.NewEngine() and invoked via Recommend(profile, current).

1. Percentile Estimator

Selects the configured percentile from the usage profile. Usage data is bucketed into 24 hourly slots, and the estimator takes the maximum across all hours to ensure peak-hour coverage.

result = max(selectPercentile(overallPercentiles),
             max(selectPercentile(hourlyPercentiles[0..23])))

Supported percentiles: 50, 90, 95, 99 (default: 95 for CPU, 99 for memory).

Time-of-day awareness

The hourly bucketing provides built-in time-of-day awareness. A workload that peaks at 2 PM will have a high p95 in bucket 14, and that peak propagates through the max() to the final recommendation. This prevents under-provisioning for workloads with strong diurnal patterns.

2. Margin Estimator

Multiplies the inner result by a safety factor to provide headroom:

result = inner * factor

Typical values: 20 (20% headroom) for CPU, 30 (30%) for memory. Internally, overhead percentage is converted to a multiplier (1 + overhead/100) and applied in millicore precision, rounded up.

3. Burst Estimator

BuildProfile() flags bursts when max > 3x p95. The burst estimator uses BurstMagnitude to apply a logarithmic overhead boost after the base overhead and before the confidence adjustment. See the Burst detection section below for the full formula and sensitivity tuning.

4. Confidence Estimator

Widens the recommendation when data confidence is low. High-confidence recommendations (near 1.0) pass through with minimal adjustment. Low confidence inflates the result to be conservative.

Formula:

result = inner * (1 + multiplier / max(confidence, 0.1)) ^ exponent
Parameter Default Effect
multiplier 1.0 Controls inflation magnitude
exponent 2.0 Controls curve steepness

Confidence is floored at 0.1 to prevent division by zero.

Example: with confidence = 0.5, multiplier = 1.0, exponent = 2.0:

factor = (1 + 1.0/0.5)^2 = 3.0^2 = 9.0

This means a low-confidence recommendation is inflated 9x, resulting in a very conservative (high) value that avoids under-provisioning.

How confidence is computed

Confidence is derived from two components in metrics.BuildProfile():

timeComponent = timeSpanDays
dataComponent = sqrt(dataPoints / 24)
confidence    = clamp(min(timeComponent, dataComponent) / 7, 0, 1)

A full 7-day history window at the default queryStep: 5m yields confidence near 1.0.

5. Bounds Estimator

Clamps the result to user-defined minimum and maximum values:

result = clamp(inner, min, max)

This ensures recommendations never drop below a safe floor (e.g. 50m CPU) or exceed a known capacity ceiling (e.g. 4000m CPU).

6. Change Filter

Prevents thrashing from tiny adjustments and dangerous large swings:

changePct = abs(recommended - current) / current * 100

if changePct < MinChangePercent:
    return current            # suppress noise

if changePct > MaxChangePercent:
    return current +/- (current * MaxChangePercent / 100)  # cap
Parameter Default Purpose
MinChangePercent 10% Ignore changes below this threshold
MaxChangePercent 50% (CPU) / 30% (memory) Cap changes above this threshold

Burst detection

BuildProfile() flags bursts when max > 3x p95. The recommendation engine uses BurstMagnitude to apply a logarithmic overhead boost:

burstFactor = 1 + sensitivity * log2(BurstMagnitude)

The sensitivity defaults to 0.1 and can be configured per resource via spec.cpu.burstSensitivity / spec.memory.burstSensitivity. Set to "0" to disable burst boost entirely (useful for batch jobs).

Burst magnitude Boost (sensitivity=0.1) Boost (sensitivity=0.2)
4x +20% +40%
8x +30% +60%
16x +40% +80%
100x +66% +133%

This step runs after the base overhead and before the confidence adjustment. When no burst is detected (or magnitude <= 1), the factor is 1.0 (no change). The burst factor is visible in kubectl attune explain output via the burstFactor and afterBurst fields, and as the attune_burst_factor Prometheus metric.

Full pipeline example

Given: p95 CPU = 200m, overhead = 20%, confidence = 0.8, bounds = [1m, 4000m], current = 500m, max change = 50%.

Stage Calculation Result
Percentile max across hourly p95 200m
Overhead 200m * (1 + 20/100) = 200m * 1.2 240m
Confidence 240m * (1 + 1/0.8)^2 = 240m * 5.0625 1215m
Bounds clamp(1215m, 1m, 4000m) 1215m
Change Filter change = abs(1215-500)/500 = 143% > 50%, cap 750m

Final recommendation: 750m (capped at 50% increase from 500m).