| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | ) |
| 7 | |
| 8 | // faultRecovery splits a run by whether the meter actually failed it. With a |
| 9 | // cadence, short tasks never reach a fault and form an in-run control group, |
| 10 | // so the cost of failure is measured against the same suite and model rather |
| 11 | // than against a separate arm run at a different time. |
| 12 | type faultRecovery struct { |
| 13 | faulted, faultedSolved int |
| 14 | unfaulted, unfaultSolved int |
| 15 | keptGoing int // faulted runs that issued another request |
| 16 | injected int |
| 17 | } |
| 18 | |
| 19 | func gatherFaultRecovery(results []result) faultRecovery { |
| 20 | var f faultRecovery |
| 21 | for _, r := range results { |
| 22 | if r.Skipped || r.Meter == nil || r.Attempt > 1 { |
| 23 | continue |
| 24 | } |
| 25 | if r.Meter.Injected == 0 { |
| 26 | f.unfaulted++ |
| 27 | if r.Passed { |
| 28 | f.unfaultSolved++ |
| 29 | } |
| 30 | continue |
| 31 | } |
| 32 | f.faulted++ |
| 33 | f.injected += r.Meter.Injected |
| 34 | if r.Meter.RequestsAfterFault > 0 { |
| 35 | f.keptGoing++ |
| 36 | } |
| 37 | if r.Passed { |
| 38 | f.faultedSolved++ |
| 39 | } |
| 40 | } |
| 41 | return f |
| 42 | } |
| 43 | |
| 44 | // renderFaultRecovery prices injected failure. Two different things are worth |
| 45 | // separating: whether the harness kept talking after a failure at all, and |
| 46 | // whether the task still landed. A harness can retry forever and still never |
| 47 | // finish, and that is not recovery. |
| 48 | func renderFaultRecovery(results []result) string { |
| 49 | f := gatherFaultRecovery(results) |
| 50 | if f.faulted == 0 { |
| 51 | return "" |
| 52 | } |
| 53 | var b strings.Builder |
| 54 | fmt.Fprintf(&b, "**Fault recovery** (%d runs failed on purpose, %d injections): **retried** %s (%d) · **still solved** %s (%d/%d)", |
| 55 | f.faulted, f.injected, pct(f.keptGoing, f.faulted), f.keptGoing, |
| 56 | pct(f.faultedSolved, f.faulted), f.faultedSolved, f.faulted) |
| 57 | if f.unfaulted > 0 { |
| 58 | fmt.Fprintf(&b, " · in-run control %s (%d/%d never hit a fault)", |
| 59 | pct(f.unfaultSolved, f.unfaulted), f.unfaultSolved, f.unfaulted) |
| 60 | } |
| 61 | return b.String() + "\n\n" |
| 62 | } |
| 63 |