Skip to content

Load Profiles

Scale Sentry's loadgen runs an ordered list of phases. Each phase carries its own arrival pattern, rate, and duration. The default (single-phase, constant-rate) is what older ScaleValidation CRs land on, but spec.load.profile unlocks open-loop and multi-phase shapes that exercise the autoscaler more realistically.

Why open-loop matters

Closed-loop benchmarks (one request, wait for response, send the next) hide the moments when the target stalls: a 200ms backend pause just means each worker sends fewer requests, and the latency histogram still looks healthy. Open-loop generators emit requests on a schedule regardless of how fast the target replies, so a stall shows up as growing queue depth and elevated tail latency. That is the failure mode an HPA needs to react to.

The scale-sentry loadgen also applies the scheduled-arrival fix for coordinated omission: every request's latency is measured from the slot's scheduled time, not from the moment the worker actually dispatched it. A stalled target turns into honest p99 growth instead of silently-absorbed queue wait.

Patterns

spec.load.profile.pattern selects the arrival shape. Pattern-specific knobs apply only to their named pattern; the rest are ignored.

Pattern Shape Use when
Constant Fixed rate (legacy default) Smoke testing; comparing against pre-loadprofile baselines
Poisson Exponential inter-arrival, mean 1 / baseRps SLA-accurate verdicts; matches real user traffic
Ramp Linear interpolation baseRpsendRps over rampDuration Measuring HPA reaction latency under monotonic growth
Step Climbs stepRps every stepDuration, holding each plateau Stressing HPA stabilization windows and downscaling hysteresis
Spike Decomposed into Constant base / spike / base / ... by the controller Reproducing diurnal traffic spikes and noisy-neighbour patterns
spec:
  load:
    baseRps: 200
    profile:
      pattern: Ramp
      endRps: 800
      rampDuration: 3m

Warmup phase

spec.load.warmupDuration runs traffic against the target before the SLA window opens. Requests are dispatched (so TCP/TLS handshakes settle, JIT runs, page caches warm) but their latencies and counters are excluded from the verdict. The warmup phase always runs at baseRps with the Constant pattern.

spec:
  load:
    baseRps: 200
    warmupDuration: 30s   # 30s warmup then the measurement window
    profile:
      pattern: Poisson

Skip the warmup (omit warmupDuration) when comparing against the older single-shot defaults; otherwise enable it for any verdict that needs to survive code review.

Spike profile

The Spike pattern stitches one or more elevated-rate windows into a baseRps Constant background. The controller decomposes the spike into atomic phases before sending them to the loadgen, so the SLA verdict reflects each slice individually.

spec:
  load:
    baseRps: 100
    profile:
      pattern: Spike
      spikes:
        - at: 30s        # offset from measurement start
          duration: 10s
          rps: 600
        - at: 90s
          duration: 5s
          rps: 1200

at is measured from the start of the measurement phase (post-warmup). Spike RPS must exceed baseRps; otherwise it would be a dip, which is rejected by the CRD.

Phase summaries in the result

Every executed phase contributes a PhaseSummary to the run output:

{
  "phases": [
    { "name": "warmup",  "pattern": "Constant", "recordStats": false, "started": "...", "ended": "..." },
    { "name": "measure", "pattern": "Poisson",  "recordStats": true,  "started": "...", "ended": "..." }
  ],
  "warmupDuration":      "30s",
  "measurementDuration": "3m0s"
}

WarmupDuration and MeasurementDuration sum the wall-clock spent in each kind of phase. Use them to confirm a long run actually entered the measurement window before drawing verdicts.