Mejlet som startade allt
I förra veckan fick jag ett mejl från Teodor:
"Blev lite intresserad av valet av överföring av datan till klienten. Det ser ut som klienten hämtar en komplett lägesbild av alla fordon från servern varje sekund. En väldig massa data som skickas. Nu kanske det är så att mycket av datan faktiskt ändras varje sekund?"
Bra fråga. Jag visste att kartan pollade varje sekund, men hade aldrig mätt exakt hur mycket data det innebar. Dags att ta reda på det.
Hur mycket data är det egentligen?
Jag skrev ett mätscript med Puppeteer som öppnar kartan och loggar all nätverkstrafik under en minut. Först mätte jag klockan tre på natten — 65 fordon ute, ~180 KB per minut. Inte så farligt.
Men 80 % av kartans besökare sitter på mobilen, och de flesta tittar under dagen — morgonrusningen sammanfaller med flest besökare. Så jag mätte igen klockan åtta, med ~1 500 fordon i trafik:
| Tid | Fordon | Per poll (gzip) | Per minut |
|---|---|---|---|
| 03:00 (natt) | 65 | 3 KB | 180 KB |
| 08:00 (rusning) | 1 500+ | 61 KB | 3,7 MB |
Nu ska det sägas att servern redan komprimerar all data med gzip innan den skickas. Den råa JSON-datan för 1 500 fordon är ungefär 500 KB, men gzip pressar ner den till ~60 KB. Det är alltså den komprimerade storleken som skickas — men med en poll varje sekund blir det ändå 3,7 MB per minut. Teodor hade rätt: det var en väldig massa data, även med komprimering.
Vad ändras egentligen?
Alla fordonspositioner kommer från Trafiklabs GTFS-RT-flöde, som SL:s operatörer matar med GPS-data. Varje fordon har 13 fält: id, position (lat/lon), bearing, speed, linje, headsign, transportmedel, med mera. Men de flesta fälten — linje, headsign, transportmedel, route_id — ändras aldrig under en tur. Det enda som rör sig är positionen och tidsstämpeln.
Jag jämförde två på varandra följande snapshots under rusningstid:
| Fält | Andel som ändrades |
|---|---|
| timestamp | 79 % |
| lon | 62 % |
| lat | 58 % |
| speed | 45 % |
| bearing | 43 % |
| line, headsign, transport_mode, ... | 0 % |
Hälften av alla fält skickas om i onödan varje sekund utan att ha ändrats.
Lösningen: delta-uppdateringar
Konceptet är inspirerat av hur multiplayer-spel och appar som Flightradar24 hanterar realtidsdata — istället för att skicka hela världen varje frame, skickar man bara deltan.
Servern håller nu koll på de senaste ögonblicksbilderna. Klienten skickar med tidsstämpeln från sin senaste uppdatering, och servern svarar bara med:
- Nya fordon som tillkommit (med alla fält)
- Fordon som försvunnit (bara id:t)
- Fordon som rört sig — men bara de fält som ändrats
Första anropet ger fortfarande en komplett bild. Allt efter det är en liten diff. Mönstret liknar hur operational transformation fungerar i Google Docs — fast enklare, eftersom det bara finns en skrivare (servern).
Testresultat
Jag körde Puppeteer-mätningen i två minuter och jämförde gamla och nya versionen sida vid sida, mot samma realtidsdata under morgonrusningen:
| Före | Efter | |
|---|---|---|
| Data per minut | 3 370 KB | 2 097 KB |
| Antal fordon (snitt) | 1 429 | 1 429 |
| Skillnad i fordon | 0 | |
38 % mindre data, exakt samma karta. Det är ~1,3 MB per minut som varje mobilanvändare slipper ladda ner. Under en kvarts pendling sparar det ~20 MB — märkbart om man sitter på en begränsad mobildataplan.
Bakåtkompatibelt och felsäkert
Om klienten inte skickar med en tidsstämpel, eller om servern inte har kvar den begärda ögonblicksbilden, faller systemet tillbaka till ett fullständigt svar — en teknik som ibland kallas graceful degradation. Samma sak händer om något går fel — klienten nollställer sin lokala data och börjar om från en komplett bild. Ingen besökare märker skillnad, förutom att det går snabbare.
Tack till Teodor för mejlet som satte igång det hela! Har du fler funderingar kring hur kartan funkar, eller idéer på förbättringar? Hör gärna av dig via formuläret nedan.