@venn-lang/load
v0.9.0
Published
The load namespace: describe a load profile, run it, assert on the metrics.
Readme
@venn-lang/load
The
loadnamespace: describe a load profile, run it, assert on the metrics.
Three builders turn a sentence about traffic into a profile value, and a fourth verb hands that
profile to the LoadRunner port and gives you back latency percentiles and an error rate. The
profile is plain data, so a flow can hold it, pass it and read it before anything is driven.
Install
Nothing to install. @venn-lang/load is part of the standard library, and the CLI and the language
server both load @venn-lang/stdlib, which lists every stdlib plugin. A file reaches the
namespace with one line.
import { load } from "venn/load"Usage
module demo.load
import { load } from "venn/load"
import { assert } from "venn/assert"
flow "Checkout under load" {
step "ramp to 200 VUs" {
const profile = load.ramp 0 200 { over: "30s", hold: "5m" }
const metrics = load.run profile
expect metrics.p95 < 800
expect metrics.errorRate < 0.05
}
}Verbs
| Verb | Call | Result |
| --- | --- | --- |
| load.ramp | load.ramp 0 200 { over: "30s", hold: "5m" } | load.Ramp |
| load.constant | load.constant 50 { over: "10s" } | load.Constant |
| load.spike | load.spike 500 { at: "5s" } | load.Spike |
| load.run | load.run profile | load.Metrics |
The three builders are pure: they compute a descriptor and touch nothing. Only load.run reaches
the port.
load.ramp takes two positional arguments, the virtual users it starts from and the number it ends
at. The spec's 0 -> 200 arrow sugar is not in the grammar.
Durations may be written any of three ways. over, hold and at are read by the SDK's
Duration schema, which accepts the language's own 30s literal, the text "30s", or a plain
count of milliseconds, and yields milliseconds in every case. Write { over: 30s },
{ over: "30s" } or { over: 30000 }.
This paragraph used to say the opposite: that 30s was "refused before the run" with
"over" is not a valid option. It was refused, by a schema that took only a string or a number,
and the sentence was wrong twice over, since the option was declared and it was the value that was
turned away. Neither is true now.
The types it publishes
| Type | Shape |
| --- | --- |
| load.Ramp | { kind: "ramp", from, to, over?, hold? } |
| load.Constant | { kind: "constant", vus, over? } |
| load.Spike | { kind: "spike", peak, at? } |
| load.Profile | Whichever of the three a builder produced. This is what load.run takes. |
| load.Metrics | { vus, rps, p50, p95, p99, errorRate } |
The durations inside a profile are numbers, not durations: Duration already turned "30s" into
30000 on the way in, so what a profile holds is a count of milliseconds.
The LoadRunner port
| | |
| --- | --- |
| id | venn.port.load-runner |
| version | 1 |
| requires | net |
| methods | run |
Two implementations ship together, which is what makes this a port rather than a module with a good interface:
createFakeLoadRunner()derives canned metrics from the profile's peak VUs, with no real traffic. This is the one@venn-lang/stdlibbinds, so a load flow is runnable offline.createRealLoadRunner()is a stub. Every call throws aVennErrorwith codeVN8090, because this repository is the language and drives no real load.
Both run the same conformance suite, loadRunnerConformance in src/runner/load-runner.suite.ts,
which holds them to two invariants: p50 <= p95 <= p99, and the reported vus is the profile's
peak.
API
| Export | What it is |
| --- | --- |
| loadPlugin | The plugin definition: namespace load, requires: ["net"]. Also the default export. |
| rampProfile({ from, to, over?, hold? }) | Builds a RampProfile. |
| constantProfile({ vus, over? }) | Builds a ConstantProfile. |
| spikeProfile({ peak, at? }) | Builds a SpikeProfile. |
| peakVus(profile) | The peak concurrent VUs a profile reaches, which is what a runner drives toward. |
| RampProfile, ConstantProfile, SpikeProfile | The three descriptor types. |
| LoadProfile | Their union: what the runner consumes. |
| LoadRunnerPort | The port descriptor. |
| LoadRunner | The port interface: run(profile). |
| createFakeLoadRunner() | The fake runner. |
| createRealLoadRunner() | The real runner, stubbed to throw VN8090. |
| LoadMetrics | The metrics type. |
Binding a different runner means one entry in the runner's port list:
import { createFakeLoadRunner, LoadRunnerPort } from "@venn-lang/load";
import { createRunner } from "@venn-lang/runtime";
const runner = createRunner({
host,
plugins,
sink,
uri,
ports: [{ port: LoadRunnerPort, impl: createFakeLoadRunner() }],
});See also
@venn-lang/sdkfordefinePlugin,defineActionand theDurationschema.@venn-lang/httpfor the requests a load profile is usually pointed at.@venn-lang/artifactsfor filing what a run produced.
