Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / tools/verify/stress/httpload.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | tools/verify/stress/httpload.go |
| Original SHA-256 | 8ff47fda0e40b448b7346916ecb427523239d3d0415e8b9da54ed044929d5998 |
| Git blob SHA | ac658d8752aec75ca34531adfc853b08e7070b04 |
| Reading-copy SHA-256 | 84a702434b2f307b95a10224ecd06bc4b00d9f9366844f7a7fea094a0a6a4410 |
Recovered source transcript
Download complete source text ↓228 lines · 8.5 KB
Credential-bearing and unrelated personal details were removed from this reading copy. The source identity and original checksum remain recorded.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
)
// AUTHENTICATED HTTP LOAD, the Gate B item that has been open across two phase closes.
//
// The protocol asks for load against "the phase's hot routes with authenticated sessions at a mixed profile".
// Both previous bundles measured the DATABASE path instead and said so, which was the honest thing to do at the
// time: quoting a database read as an end to end request latency would inflate the number every time it was
// repeated later. What was missing was seeded fixture accounts per role, and Gate C created them.
//
// SO THIS MEASURES WHAT THE PROTOCOL ACTUALLY ASKED FOR: real logins through the real login flow, real cookies,
// real CSRF tokens, real routes, through nginx and TLS. The numbers are therefore comparable to what a user
// experiences rather than to what psql experiences, and they are NOT comparable to the database profile's
// numbers, which is stated rather than left for someone to conflate later.
//
// THE CONTROL is an unmeetable p95 target on the same traffic, so a passing run cannot be a run that never
// measured the clock.
type loadRole struct {
email, password string
client *http.Client
csrf string
}
// login performs the real flow: GET the CSRF token, then POST credentials with the token AND an Origin header,
// because the CSRF middleware enforces a hard Origin allowlist and refuses a request without one.
func (r *loadRole) login(base string) error {
jar, err := cookiejar.New(nil)
if err != nil {
return err
}
r.client = &http.Client{Jar: jar, Timeout: 30 * time.Second}
tok, err := r.fetchCSRF(base)
if err != nil {
return fmt.Errorf("csrf: %w", err)
}
body, _ := json.Marshal(map[string]string{"email": r.email, "password": [credential removed]})
req, _ := http.NewRequest(http.MethodPost, base+"/api/auth/login", strings.NewReader(string(body)))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-CSRF-Token", tok)
req.Header.Set("Origin", base)
resp, err := r.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("login returned %d", resp.StatusCode)
}
r.csrf = tok
return nil
}
func (r *loadRole) fetchCSRF(base string) (string, error) {
resp, err := r.client.Get(base + "/api/auth/csrf")
if err != nil {
return "", err
}
defer resp.Body.Close()
var v struct {
Token string `json:"csrf_token"`
}
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
return "", err
}
if v.Token == "" {
return "", fmt.Errorf("no csrf_token in the response")
}
return v.Token, nil
}
// httpLoadProfile drives the phase's hot routes with authenticated sessions.
//
// TARGETS come from the protocol: sustained load with p95 under 300ms, p99 under 800ms, and ZERO 5xx. A 4xx is
// NOT counted as an error here, because a route legitimately answering 404 to a role without an entitlement is
// the fail closed design working, and counting it would make the tool report a defect where there is a feature.
// Only 5xx counts, which is exactly what the protocol names.
func httpLoadProfile(base, roleSpec string, dur time.Duration, targetRPS int, control bool) error {
targetP95 := 300 * time.Millisecond
targetP99 := 800 * time.Millisecond
if control {
targetP95 = time.Nanosecond // THE PLANTED DEFECT: unmeetable, so a pass would be a lie.
}
if strings.TrimSpace(base) == "" || strings.TrimSpace(roleSpec) == "" {
return fmt.Errorf("-base and -roles are required for the http-load profile")
}
if _, err := url.Parse(base); err != nil {
return fmt.Errorf("base url: %w", err)
}
var roles []*loadRole
for _, spec := range strings.Split(roleSpec, ",") {
parts := strings.SplitN(strings.TrimSpace(spec), ":", 2)
if len(parts) != 2 {
return fmt.Errorf("role %q must be email:password", spec)
}
r := &loadRole{email: parts[0], password: [credential removed]}
if err := r.login(base); err != nil {
return fmt.Errorf("logging in %s: %w", r.email, err)
}
log.Printf(" logged in %s through the real login flow", r.email)
roles = append(roles, r)
}
// THE HOT ROUTES of this phase, the ones a reviewer actually walks. Read routes only: this profile measures
// latency under load, and hammering writes would measure the database's lock behaviour, which the race
// profile already covers properly.
routes := []string{
"/api/auth/session",
"/api/opportunities/tracked",
"/api/requirements?limit=50",
"/api/candidates?limit=50",
}
// Per worker pacing so the fleet lands near targetRPS in aggregate.
var pace time.Duration
if targetRPS > 0 {
pace = time.Duration(int64(time.Second) * 8 / int64(targetRPS))
}
deadline := time.Now().Add(dur)
var mu sync.Mutex
var lats []time.Duration
var ok2xx, rate429, other4xx, server5xx, transport int64
var wg sync.WaitGroup
for w := 0; w < 8; w++ {
wg.Add(1)
go func(w int) {
defer wg.Done()
r := roles[w%len(roles)]
var local []time.Duration
i := 0
for time.Now().Before(deadline) {
route := routes[i%len(routes)]
i++
// PACED TO THE PROTOCOL'S TARGET RATE rather than run flat out. The protocol asks for
// "sustained 50 requests per second", which is a LATENCY measurement at a realistic rate.
// The first version hammered as fast as it could, saturated the rate limiter, and then
// reported percentiles measured almost entirely on 429 responses. Maximum throughput and
// sustained latency are different questions and the protocol asked the second one.
if pace > 0 {
time.Sleep(pace)
}
start := time.Now()
resp, err := r.client.Get(base + route)
el := time.Since(start)
if err != nil {
atomic.AddInt64(&transport, 1)
continue
}
resp.Body.Close()
switch {
case resp.StatusCode >= 500:
atomic.AddInt64(&server5xx, 1)
case resp.StatusCode == http.StatusTooManyRequests:
// COUNTED SEPARATELY, because a 429 is the rate limiter WORKING and a 404 is an
// entitlement refusal. The first version lumped them together and then attributed all
// of them to the entitlement gate in its own output, which was simply wrong: every one
// of the eight routes answers 200 for these roles when they are not being throttled.
atomic.AddInt64(&rate429, 1)
case resp.StatusCode >= 400:
atomic.AddInt64(&other4xx, 1)
default:
atomic.AddInt64(&ok2xx, 1)
}
local = append(local, el)
}
mu.Lock()
lats = append(lats, local...)
mu.Unlock()
}(w)
}
wg.Wait()
if len(lats) == 0 {
return fmt.Errorf("no samples collected, so nothing was measured")
}
sort.Slice(lats, func(i, j int) bool { return lats[i] < lats[j] })
p := func(q float64) time.Duration { return lats[int(float64(len(lats)-1)*q)] }
p95, p99 := p(0.95), p(0.99)
rps := float64(len(lats)) / dur.Seconds()
log.Printf(" http-load samples=%d rps=%.0f p95=%v p99=%v 2xx=%d 429=%d other4xx=%d 5xx=%d transport=%d target_p95=%v",
len(lats), rps, p95.Round(time.Microsecond), p99.Round(time.Microsecond),
ok2xx, rate429, other4xx, server5xx, transport, targetP95)
log.Printf(" NOTE: END TO END HTTP through nginx and TLS with a real session. NOT comparable to the " +
"database profile numbers and must not be quoted interchangeably.")
if rate429 > 0 {
// THE PROTOCOL ASKS FOR THIS SEPARATELY: saturate the rate limited routes and observe 429, never 500.
log.Printf(" the rate limiter returned %d of 429 and ZERO of 500 under this load, which is the "+
"protocol's own saturation requirement observed rather than rehearsed", rate429)
}
// A RUN THAT MEASURED ONLY REFUSALS MEASURED NOTHING. Without this the profile can report a healthy p95
// computed almost entirely from 429 and 404 responses, which is the instrument passing by doing nothing.
if share := float64(ok2xx) / float64(len(lats)); share < 0.80 {
return fmt.Errorf("only %.0f percent of requests were SERVED (2xx). The percentiles above therefore "+
"describe refusals rather than work, so this run does not count", share*100)
}
if server5xx > 0 {
return fmt.Errorf("%d server errors, the protocol requires zero", server5xx)
}
if transport > 0 {
return fmt.Errorf("%d transport failures, which is a availability problem rather than a latency one", transport)
}
if p95 > targetP95 {
return fmt.Errorf("p95 %v exceeds the %v target", p95.Round(time.Microsecond), targetP95)
}
if p99 > targetP99 {
return fmt.Errorf("p99 %v exceeds the %v target", p99.Round(time.Microsecond), targetP99)
}
return nil
}