# Latenstid

## Hvad det er

Latenstid, ofte bare latens, er den tid, et system er om at begynde at svare:
fra en forespørgsel forlader klienten, til den første byte af svaret er tilbage.
Man taler også om forsinkelse, om rundturstid (round-trip time, RTT), når en
netværksstrækning måles frem og tilbage, og i webperformance om time to first
byte (TTFB). Svartid er den større storebror, der løber helt til svarets sidste
byte.

Latenstid forveksles let med gennemløb og båndbredde, men de tre besvarer hvert
sit spørgsmål. Båndbredde er, hvor mange bit en forbindelse kan bære i sekundet.
Gennemløb er, hvor meget arbejde et system gør færdigt i sekundet. Latenstid er,
hvor længe ét stykke arbejde venter. Et natligt batchjob kan have glimrende
gennemløb og elendig latenstid, og en chatserver omvendt.

Hvert millisekund stammer fra et af få steder:

- **udbredelse**, signalets vej over afstanden: lys i optisk fiber tilbagelægger
  omkring 200 km pr. millisekund, så en strækning på 1.000 km koster mindst 10 ms
  frem og tilbage, før noget software overhovedet kører;
- **transmission**, at få bytes ud på ledningen, hvilket betyder noget for store
  datamængder på langsomme forbindelser;
- **kø**, ventetid bag andet arbejde i en routerbuffer, en trådpulje, en
  forbindelsespulje eller en lås, og den eneste del, der vokser med belastningen;
- **behandling**, selve arbejdet: forespørgslen, skabelonen, krypteringen;
- **protokolrundture**, samtalen før den første nyttige byte. En ny
  HTTPS-forbindelse kræver først et TCP-håndtryk og et TLS-håndtryk, og TLS 1.3
  skar det fulde håndtryk ned til én rundtur.

IETF's arbejdsgruppe for IP Performance Metrics definerede netværksudgaven
præcist i RFC 2681: rundturstiden løber fra den første bit af en pakke sendes,
til den sidste bit af svaret er modtaget. To af valgene i den gælder for enhver
tjeneste. Et svar, der aldrig kommer, tælles som udefineret, uformelt uendeligt,
i stedet for at forsvinde stille ud af stikprøven. Og de statistikker, RFC'en
beskriver, er percentiler, medianen og minimum. Den bemærker også, at minimum
afspejler udbredelse og transmission alene, mens alt over minimum skyldes
trængsel, en opdeling der holder lige så godt for et API som for en vej mellem
routere.

For en enkelt webforespørgsel kan curl dele ventetiden op i faser:

```sh
curl -s -o /dev/null "$URL" -w '
dns        %{time_namelookup}
connect    %{time_connect}
tls        %{time_appconnect}
first byte %{time_starttransfer}
total      %{time_total}
'
```

Hvert tal er sekunder siden start, så afstanden mellem dem er prisen for hvert
trin: navneopslaget, forbindelsen, TLS-håndtrykket og til sidst serverens egen
betænkningstid frem til den første byte.

## Hvorfor det betyder noget

Latenstid er det, brugeren mærker; gennemløb er det, serverregningen afspejler.
Når en side er langsom, er beslutningen, hvor pengene skal bruges, og de fire
årsager ovenfor har fire forskellige løsninger, der ikke kan erstatte hinanden.
Afstand løses ved at flytte tættere på: en cache, et CDN, en region nær
brugerne. Rundture løses ved at få færre af dem: genbrugte forbindelser,
samlede forespørgsler, parallelle kald. Behandling løses ved at lave mindre
arbejde: et indeks, en bedre forespørgselsplan. Kun kø løses med kapacitet. En
forespørgsel, der tager to sekunder på en ledig server, tager også to sekunder
på en server, der er ti gange større, og derfor er mere hardware kun svaret på
én af de fire.

Gennemsnit får problemet til at se mindre ud, end det er, for brugerne møder
halen. En side, der kalder ti backend-tjenester parallelt, venter på den
langsomste. Er hvert kald langsomt 1 % af tiden, rammer 1 − 0,99¹⁰, omkring
9,6 % af sidevisningerne, mindst ét langsomt kald. En sjælden forsinkelse i
delene bliver en almindelig forsinkelse i helheden.

Derfor skrives seriøse mål i percentiler, for eksempel at 99 % af
forespørgslerne besvares inden for 300 ms over 30 dage, og derfor er den 99.
percentil som regel det tal, der er værd at holde øje med. Medianen fortæller,
hvordan tjenesten føles på en god dag; de høje percentiler fortæller, hvordan
den føles for de kunder, der er på vej væk.

## Sådan ser det ud i praksis

I en geografisk kortapplikation gik latens ved rendering af kortlag og
forespørgsler på lokationsdata ud over både brugerne og backendens pålidelighed.
Databasen nedenunder, Microsoft SQL Server, manglede native geospatial
indeksering, så den langsomme del var behandling og ikke afstand.
[Skiftet til PostgreSQL med PostGIS gav en gennemsnitlig svartid på under 250 millisekunder](/da/portfolio/improved-geographical-map-application-performance-by-10x-through-6/),
ned fra 2,5 sekunder, og dermed en tidobling af ydeevnen for spatiale
forespørgsler. Arbejdet bag var GiST- og SP-GiST-indeks på de spatiale
kolonner, over 50 millioner records migreret og en konfiguration tunet til
samtidig adgang; backendens CPU-belastning faldt med 65 %.

Latenstid sætter også loft over gennemløbet, hvor én række er omstridt. En note
om
[flash-salg, hvor alle købere skriver til den samme række](/da/notes/selling-limited-stock-without-overselling/),
sætter loftet for en enkelt vare til omtrent de salg, én lås bærer, divideret
med den tid, låsen holdes. Alt langsomt inde i låsen sænker loftet: et
betalingskald, mens låsen holdes, bringer det ned på omkring to salg i sekundet.
Selv en sikkerhedsforanstaltning har en pris her. En synkron standby i
PostgreSQL lægger latens på hver commit, og det forlænger præcis den holdetid,
loftet divideres med.

Og for at finde ud af, hvor tiden går, registrerer
[den hjemmebyggede overvågning på én platform](/da/portfolio/built-first-party-error-monitoring-and-tracing-98/)
API-latenstid i platformens egen database og sporer forespørgsler over
OpenTelemetry, så en langsom forespørgsel kan følges gennem frontend, API og
database i stedet for at blive gættet på.

## Hvor det går galt

**Gennemsnittet skjuler halen.** Et gennemsnit på 100 ms er foreneligt med, at
hver halvtredsindstyvende forespørgsel tager tre sekunder. Registrér latenstid
som et histogram, og læs percentilerne af det. Pas så på, hvordan de lægges
sammen: den 99. percentil fra fem servere kan ikke gennemsnitsberegnes til hele
flådens 99. percentil. Prometheus siger det selv i sin dokumentation: kvantiler
beregnet af en summary kan ikke aggregeres, mens histogrammets spande kan
lægges sammen først, hvorefter kvantilen beregnes.

**Uret starter det forkerte sted.** En server, der måler fra det øjeblik, dens
handler begynder, overser den tid, forespørgslen brugte i load balanceren, i
accept-køen eller på at vente på en forbindelse fra puljen. Belastningsværktøjer,
der venter på hvert svar, før de sender den næste forespørgsel, sender færre
forespørgsler, netop når systemet går i stå, så stoppet næsten ikke ses i
resultaterne. Mål også fra klienten, og lad serveren melde sin andel i en
standardheader:

```http
Server-Timing: db;dur=53, app;dur=47.2
```

**Samtalen er for snakkesalig.** En side, der kører én forespørgsel pr. række i
en liste, betaler én rundtur pr. række. Står databasen i en anden region med
30 ms pr. rundtur, koster hundrede rækker tre sekunder, før nogen forespørgsel
laver rigtigt arbejde. Én forespørgsel med en join, eller ét samlet kald,
betaler afstanden én gang.

**Genforsøg ganger belastningen op.** En klient, der giver op efter et sekund og
prøver igen tre gange, gør én langsom server til fire gange trafikken, hvilket
gør den endnu langsommere. Giv hver forespørgsel en samlet tidsfrist, vent
eksponentielt længere med tilfældig spredning mellem forsøgene, og sæt et loft
over antallet af genforsøg.

## Læs mere

- [RFC 2681: A Round-trip Delay Metric for IPPM](https://www.rfc-editor.org/rfc/rfc2681)
  — rundturstid defineret ned til bitten, med tabte svar og percentiler
  behandlet eksplicit.
- [RFC 7679: A One-Way Delay Metric for IP Performance Metrics](https://www.rfc-editor.org/rfc/rfc7679)
  — modstykket for envejsforsinkelse, udgivet som Internet Standard.
- [Server Timing](https://www.w3.org/TR/server-timing/) — W3C-headeren, der
  lader en server fortælle browseren, hvor tiden gik.
- [Histograms and summaries](https://prometheus.io/docs/practices/histograms/)
  — hvorfor kvantiler fra en summary ikke kan kombineres, og histogrammets
  spande kan.
- [RFC 8446: TLS 1.3](https://www.rfc-editor.org/rfc/rfc8446) — håndtrykkets
  design, herunder den rundtur, det sparer.

<https://engineer.company/da/glossary/latency/>
