Class LoadScenarioOrchestrator

java.lang.Object
org.mockserver.mock.action.http.LoadScenarioOrchestrator

public class LoadScenarioOrchestrator extends Object
Orchestrates an in-process API-driven load scenario. A scenario drives an ordered list of templated request LoadSteps through a sequence of LoadStages described by a LoadProfile, producing latency/error samples for the SLO verdict feature.

Copies the ChaosExperimentOrchestrator shape: a process-wide singleton with a single-thread daemon ScheduledExecutorService ("load-scenario-scheduler"). The scheduler thread does no I/O — it advances stages and computes setpoints on a fixed control tick and hands each request to an injected sender that returns a CompletableFuture immediately. Step pacing is scheduled (never Thread.sleep-ed), so a slow target never blocks a worker thread.

Two load models, run in sequence:

  • LoadStageType.VU — closed model. The tick maintains a pool of looping virtual users sized to targetVusAt(elapsedInStage) (hold or ramp); each VU loops the steps back-to-back.
  • LoadStageType.RATE — open model (arrival rate). The tick computes the target rate r(t) in iterations/second and starts new one-shot iterations so the cumulative number started tracks the integral of r(t) (deficit accounting), auto-scaling a VU pool up to the stage maxVus (or the global cap). When the cap blocks the rate, the shortfall is counted as a rate_limit throttle.
  • LoadStageType.PAUSE — drives no load; VUs drain for the duration.

Decoupling: core must not depend on the Netty HTTP client, so the actual request sender is injected via setSender(Function) (mirrors HttpState.setReplayHandler). The Netty runtime wires it from HttpActionHandler.getHttpClient(); unit tests pass a deterministic synchronous fake sender to start(LoadScenario, Function).

Self-load guard: off by default (loadGenerationEnabled=false → the PUT endpoint returns 403). Even when enabled, validate(org.mockserver.load.LoadScenario) enforces hard caps on VUs, rate, stages, duration and step count, and dispatch is bounded live by an in-flight Semaphore and an RPS token bucket, so a forgotten scenario cannot self-DoS the server.

Time is read via a pluggable LongSupplier clock (defaults to TimeService.currentTimeMillis()) so tests can drive progression deterministically via tickNow() / advanceNow(long) without wall-clock sleeps.

  • Method Details

    • getInstance

      public static LoadScenarioOrchestrator getInstance()
    • setSender

      public void setSender(Function<HttpRequest,CompletableFuture<HttpResponse>> sender)
      Install the request sender that re-issues a HttpRequest to its target and returns the upstream response. Called by the Netty runtime, wiring the existing HTTP client so the core never depends on it directly (mirrors HttpState.setReplayHandler).
    • setConfiguration

      public void setConfiguration(Configuration configuration)
      Install the configuration used for caps and template engines. Called by the runtime.
    • start

      Trigger a scenario to start (run it). Returns a validation error message if the definition is invalid, the caps are exceeded, or the concurrent-scenario cap would be exceeded; or null on success. Each trigger gets a fresh run_id. Re-triggering a name that is already active replaces that run (and evicts its prior metric series). A scenario with startDelayMillis > 0 enters PENDING and begins after the delay; otherwise it begins RUNNING immediately. When sender is null the installed runtime sender is used; unit tests pass a deterministic synchronous sender here.
    • stop

      public void stop()
      Stop the most-recently-triggered run (single-run convenience). Idempotent. For multi-run use stop(String) or stopAll().
    • stop

      public void stop(String name)
      Stop a specific scenario's active run by name. Idempotent; no-op if not active.
    • stopAll

      public void stopAll()
      Stop every active run.
    • reset

      public void reset()
      Reset: stop all active runs and clear all terminal status. Called on server reset.
    • getStatus

      Status of the most-recently-triggered run (active or its retained terminal status), or null if none has ever run. Single-run convenience; see getStatuses() for all runs.
    • statusFor

      Status for a specific scenario name (active run snapshot or retained terminal status), or null.
    • isActive

      public boolean isActive(String name)
      True if the named scenario currently has an active (PENDING or RUNNING) run.
    • getStatuses

      Snapshots of every currently-active run (PENDING/RUNNING), keyed by scenario name. The registry (not the orchestrator) is the source of truth for the full scenario list; this only reports the live ones. Includes the retained terminal status for names with no active run is the caller's job via statusFor(String).
    • evictTerminalSeries

      public void evictTerminalSeries(String name)
      Evict the retained metric series for a scenario removed from the registry (its run is no longer referenceable). Called by HttpState when a DELETE removes a registered scenario.
    • validate

      public String validate(LoadScenario scenario)