How a run is judged¶
A run is a scenario file, a set of probes sampling the service, and hypotheses about what the samples must show. This page says exactly what each of those means, so that a green run is one you can trust.
Phases¶
baseline (steady), the faults (inject), then recover. Each probe sample belongs to the phase in which the sample started. A request that starts just before the faults arm and fails because of them is counted as steady; keep probe timeouts short relative to the phases.
Probe modes¶
mode |
Samples |
|---|---|
continuous (default for probes) |
every interval, from the start of the baseline to the end of recovery |
on-chaos |
every interval, only while the faults are armed; the first sample is taken as soon as they arm, so a window shorter than the interval is still sampled |
SOT |
once, before anything is injected (steadyState probes are SOT); if it fails the run stops and no fault is started |
EOT |
once, after recovery. It is recorded in the report, and the phase summaries do not include it |
An unknown mode is an error.
What a probe checks¶
An HTTP probe (type: http, the default) sends one request per sample and is healthy when the status is the expected one (expect.status, default 200) and every body assertion holds:
probes:
- name: charge
type: http
url: http://127.0.0.1:8091/charge
expect:
status: 200
body:
contains: paid # a string, or a list: every entry must be present
notContains: ["error", "maintenance"]
matches: '"id":\s*[0-9]+' # a Go (RE2) regular expression, or a list
A gateway that answers 200 with an error page, or a cache that serves stale data, passes on status alone; the body assertions are for those. Notes:
- Only the first 1 MiB of a response is read and judged, and only when there is a body assertion.
- The status is judged first. When an assertion fails, the sample records which one (
body does not contain "paid") and never quotes the body, because a response can carry a token or personal data and the sample ends up in the report. - An assertion that cannot work is an error when the file is loaded, never a skipped check: a value that is not a string or a list of non-empty strings, a regular expression that does not compile, or a key other than
contains,notContainsandmatches. - Probes follow redirects.
A command probe (type: cmd) runs sh -c command and is healthy on exit status 0 and, if expect.contains is set, when the output contains that text. It needs sh, so it does not run on Windows. validate and run list every command a scenario runs before they run it, and validate -output json returns them as runsCommands; see SECURITY.md.
Hypotheses¶
hypotheses:
- name: checkout stays up
metric: availability # error_rate | availability | p50 | p95 | p99 | rps | recovery
operator: ">=" # < <= > >=
value: 99%
error_rate, availability, p50, p95, p99 and rps are measured over the inject window, over all probes together. recovery is measured from the end of the faults.
value is read in the unit of the metric, and a value that cannot be read is an error when the file is loaded (validate, run), never a silent 0:
| metric | value | a bare number is |
|---|---|---|
error_rate, availability |
a percentage, 5 or 5%, from 0 to 100 |
percent |
p50, p95, p99 |
a duration, 500ms, 1.5s, 2m |
milliseconds |
recovery |
a duration, 500ms, 5s, 1m |
seconds |
rps |
a number | requests per second |
No data, and not recovered¶
A hypothesis about a window with no samples is not a pass. It fails with observed: "no data" and noData: true in the report. If that is what you expect (a probe that only runs at the end, say), set allowNoData: true on the hypothesis.
recovery is the time from the end of the faults to the first of three healthy samples in a row (with fewer than three samples, all of them healthy). If the service is not healthy again by the end of the run, the hypothesis fails with observed: "not recovered", whatever its limit and whether or not allowNoData is set. A recovery window shorter than the limit never satisfies recovery <= 30s on its own.
Strict checking¶
Most keys in a scenario are read by name, so a misspelled one is ignored: dealy: 800ms injects no delay, intervall: 50ms changes nothing. litmus-lite warns about each key nothing reads, with the closest name it does read, when you validate or run (the warnings are also in the report). -strict turns the warnings into an error, before anything is injected:
Values that cannot be read (delay: 800 without a unit, percent: lots) are errors without -strict.
Report format¶
report.json has a schemaVersion (currently 2). A report without it was written by an older litmus-lite and is read as version 1. Later versions only add fields. A litmus-lite refuses to read a report with a version newer than it knows, and says so.
| version | change |
|---|---|
| 1 | no schemaVersion |
| 2 | adds schemaVersion, and noData and allowNoData on each hypothesis |