Oeps, jodiBooks is traaaaag

2018 June 2411 min read
Joep van de Laarschot

Deel 1: de simpele uitleg

Wat was er aan de hand

Heel simpel gezegd: jodiBooks was ontzettend traag. Om een bonnetje in te voeren moet je door 5 schermen heen. Het laden van ieder scherm duurde soms gewoon zo lang (meer dan een minuut), dat de website een foutmelding (timeout) gaf. Een foutmelding is natuurlijk niet de bedoeling, maar lang wachten ook absoluut niet. Je moet juist snel van je administratie af zijn.

Eerst dachten we dat het aan onze webhoster lag. Een webhoster zorgt er grofweg voor dat de website op internet staat. Dat kan hij op verschillende manieren doen. Hij kan 1 website op 1 computer (server) zetten. Of hij kan 1 server delen met meerdere websites. Dat laatste is goedkoper, maar je moet ook snelheid delen. We hebben er voor gekozen om te beginnen met "shared hosting", dat laatste dus. Ook wij zijn mensen en zochten dus eerst de fout bij hun: "jullie hebben veel te veel andere websites op onze server staan". Er werden dan ook al plannen gemaakt om naar een "fatsoenlijke" te gaan, zoals Azure (Microsoft) of AWS (Amazon).

imgix klWUhr wPJ8 unsplash
Foto door imgix op Unsplash

Maar, jodiBooks is een spiksplinternieuwe website, die we vanaf de grond aan het opbouwen zijn. Tijdens dit proces maken we verschillende keuzes over hoe we de website opbouwen en welke (software)technieken we gebruiken. Zonder verder op de details in te gaan (dat doet Diana in deel 2), moeten we toegeven dat we daar, achteraf gezien, verkeerde keuzes gemaakt hebben:

  1. We hebben de website gebouwd als een wiskundig model in plaats van een bruikbare website. Prachtig kloppend en elegant, maar daardoor ook traag.
  2. We hebben een prachtige database gebouwd met nergens dubbele data. Ook heel mooi, maar daarom moet de website wel iedere keer de hele database doorzoeken en doorrekenen.
  3. We hebben standaard code gebruikt, zodat we meer konden doen in de beperkte tijd die we hebben. Deze code is alleen heel inefficiënt. Hiervoor waren we al gewaarschuwd, maar we hadden niet verwacht dat het zo erg zou zijn.

Waarom hebben jullie dat dan niet eerder gezien?

Diana heeft een uitgebreid testplan geschreven dat ze, iedere keer als ze een nieuwe versie "released", helemaal naloopt. Daarin klikt ze op alle links en knoppen om te kijken of alles werkt zoals bedoelt.

Wat we daarin vergeten zijn is testen wat er gebeurt als je heel veel bonnetjes erin zet. We hadden veel te weinig testdata, waardoor jodiBooks bij ons heel snel was. Daarnaast test Diana ook alles op haar laptop en onze eigen testserver, die veel sneller zijn dan de "echte" jodiBooks server, die moeten we namelijk delen.


Metingen

Hoe traag is het

We weten dat de website traag is, maar hoe traag? En waar is hij precies traag? Om zeker te weten dat we aan de juiste oplossingen werken hebben we 17 uur aan metingen gedaan. Met deze data konden we zien waarom de website traag was en dat het niet aan onze hoster lag (dat klagen bij hun was dus niet terecht) en we blijven nog wel even waar we zijn.

Wat willen we meten

Om goed te kunnen meten, moet je eerst weten wat je wilt leren van de metingen. Ons gekibbel hierover geeft wel aan dat de data die een software engineer wilt hebben duidelijk anders is dan wat een systeembeheerder of product manager denkt dat belangrijk is.

Diana wil als engineer in detail weten op welke pagina en in welke databasetabel het fout gaat en hoeveel milliseconden ieder database request duurt. Ik wil de gemiddelde tijd weten die de gebruiker zit te wachten. En of we het kunnen oplossen door een server te huren met meer geheugen en meer processorkracht.

We zijn er uiteindelijk uitgekomen en Diana heeft een prachtig programma (Gatling) gevonden waarmee je een websitepagina automatisch meerdere keren kunt openen. Dit programma opent bijvoorbeeld de homepage 20 keer en meet hoe lang dat duurt. Door 20 keer te meten, is het statistisch verantwoord (niet helemaal correct, maar goed genoeg) en door de tijd te middelen, kon ik mijn overzicht maken. Gatling meet nog veel meer, maar dat zijn de details die Diana gebruikt om haar code te verbeteren (deel 2).

Screenshot van Gatling output voor het openen van de homepage van jodiBooks v1.1
Screenshot van Gatling output voor het openen van de homepage van jodiBooks v1.1

Datasets

We hadden ook een sterk vermoeden dat de traagheid erger wordt naarmate er meer bonnetjes, klanten, leveranciers, etc. worden ingevoerd. jodiBooks leek namelijk steeds trager te worden bij onze testgebruikers. We hebben daarom 3 datasets gemaakt: van alles 10, 100 en 1000. Dus 1000 inkoopfacturen, 1000 klanten, 1000 verkoopbonnen, etc.

Het volgende stukje kun je overslaan als je niet technisch bent. We zullen hier uitleggen wat we precies gemeten hebben en hoe. Dat is allemaal vrij technisch, dus als dat niet je ding is, lees dan verder bij "Hoe hebben we het opgelost". Daar vertellen we wat we gedaan hebben om het sneller te maken en wat wij ervan geleerd hebben.

Hoe hebben we gemeten

jodiBooks wordt gemaakt op een laptop, dit noemen wij de dev-omgeving (dev staat voor developer). Hier kan Diana lekker rommelen, nieuwe dingen uitproberen, toevoegen en verbeteren. Deze laptop is lekker snel, dus Diana merkt niet snel dat iets traag is.

Als Diana denkt dat ze klaar is voor een release, zet ze de website op onze testserver (de _test-_omgeving). Dit is een computer waarop we de website kunnen testen alsof hij op internet staat. Het is echter geen "echte" volwaardige computer, maar een virtual machine. Het voordeel daarvan voor jodiBooks is, dat we de hardware heel makkelijk kunnen aanpassen. En dat alle jodiBooks data apart staat.

Als alles goed werkt op de testserver, zetten we jodiBooks online op de _live-_omgeving (jodibooks.com). Ik wilde weten of het uitmaakt of jodiBooks op een server staat met 1, 2, 4 of 8 cores en of het uitmaakt hoeveel geheugen die server heeft. Daarvoor heb ik ons testscript gedraaid (zie volgende paragraaf) met 1, 2, 4 en 8 GB geheugen en met 1, 2, 4, en 8 cores. In totaal heb ik zo 21 tests gedraaid.

Voor de geeks onder ons: Onze server heeft eigenlijk maar 4 echte cores (Intel Xeon E3–1230v2). De 8 krijgen we door hyperthreading te gebruiken. Zo hebben we meteen getest of dat voordeel oplevert. De harde schijf waar de web- en sqlserver op draaien is een SSD.

Virtual machine instellingen.
Virtual machine instellingen.

Meetresultaten

Diana heeft dus met Gatling een script gemaakt die iedere pagina op onze website 20x opent en meet hoelang dat duurt. Dit script heb ik op de testomgeving (eigen server) gedraaid met 10, 100 en 1000 records in iedere database tabel. Hierbij heb ik het geheugen en het aantal cores gevarieerd.

Geheugen

Het viel meteen op dat we best veel geheugen nodig hebben, 1–1,5 GB. Zoveel is meestal niet beschikbaar op een gedeelde server. Maakt dat uit? Procentueel is het verschil tussen te weinig (1 a 2 GB) en genoeg (4+ GB) geheugen ongeveer 27–29%. Dit komt neer op 13 seconden bij 10 records, 23 seconden bij 100 en 69 seconden bij 1000.

2 GB is wel het absolute minimum. Dit kan ook liggen aan het gebruikte besturingssysteem (Windows 10 Pro). Op Linux of Windows Server kan 1 GB misschien wel genoeg zijn. Nu moest er een hoop "geswapt" worden.

Grafiek geheugengebruik
Snelheid van jodiBooks v1.1 bij verschillende hoeveelheden geheugen

Processor

De volgende interessante uitkomst was het processor gebruik. Het verschil tussen 1 of 4 cores is ongeveer 9–22%. Dit komt neer op 7 seconden bij 10 records, 8 seconden bij 100 en 49 seconden bij 1000. Heel grappig om te zien is dat 8 cores gemiddeld precies even snel zijn als 4 (op simpele taken zelfs iets langzamer, op moeilijke net iets sneller). Onze website heeft dus niks aan hyperthreading.

Meer geheugen levert ons dus meer op dan meer processoren, maar bij grotere datasets (en waarschijnlijk meer gebruikers) kan de processor wel meer invloed gaan hebben.

Grafiek processorgebruik
Snelheid van jodiBooks v1.1 bij verschillende aantallen cores

Hoe hebben we het opgelost

jodiBooks is dus traag en dat komt door de manier waarop we jodiBooks geprogrammeerd hebben. Het gebruikt te veel geheugen en er moeten teveel berekeningen gedaan worden om een pagina te tonen. Daarom hebben we verbeteringen gedaan aan de software die ervoor zorgt dat jodiBooks werkt.

Zonder in detail te gaan hebben we de volgende dingen gedaan:

  • Paginering: niet meer al je bonnetjes in een keer inladen, maar 20 per pagina. Je moet nu dus wel doorklikken. Later gaan we hier een zoekfunctie voor toevoegen, zodat je weer snel het juiste (oude) bonnetje kunt vinden.
  • Versimpelde tabellen (views) in de database. De software hoeft nu niet eerst meerdere tabellen aan elkaar te plakken, maar kan meteen zoeken in 1 tabel. Dit scheelt echt heel veel.
  • Diana heeft eigen code (queries) geschreven voor het zoeken in de database. We gebruiken dus niet meer de inefficiënte standaard code, die heel dom zijn werk deed.
  • Conceptbonnen niet meer bewaren. We weten zelf niet meer waarom we ze bewaarden, maar het scheelt de helft aan data die doorzocht moet worden, de tabellen zijn nu kleiner.

Nieuwe metingen

Heeft het ook echt geholpen? We hebben niet alle 21 metingen opnieuw gedaan, maar de 3 belangrijkste (4 cores, 8 GB geheugen, 10, 100 en 1000 records). En zoals je kunt zien in de grafiek is jodiBooks nu zeker sneller geworden. 71% sneller bij 10 bonnetjes, 17% bij 100 en 44% bij 1000. Ook is het geheugengebruik gedaald van 1,5 GB naar <1 GB.

Voorlopig hebben we (in v1.2) de verbeteringen alleen voor het inkomstendeel helemaal verwerkt. Dit lijkt heel goed te werken en we gaan het de komende weken uitrollen naar alle tabellen, dus ook uitgaven, klanten, leveranciers, etc. We verwachten daarmee dat v1.3 nog een keer 2% voor 10, 54% voor 100 en zelfs 73% voor 1000 records sneller wordt.

Daarna willen we proberen om iedere pagina binnen een seconde te kunnen tonen. Nu is dat nog 1.1–1.6 seconde. Dat willen later gaan doen, omdat het niet zoveel meer oplevert. Op dit moment vinden we 1.3 seconde acceptabel en focussen we ons liever op de nieuwe functies (jodiBooks S).

Grafiek gerealiseerde verbetering
Dalende laadtijd na doorvoeren verbeteringen

*v1.3: verwachte verbetering. Wijzigingen moeten nog doorgevoerd worden, gevolgd door metingen.

**v1.4: snelheidsdoel. We weten nog niet wat we moeten doen om dit te halen.

Wat hebben we ervan geleerd

Testen, testen, testen

We beginnen met het gebruiken van veel grotere testdatasets in onze tests. De '1000 records' test is nu opgenomen in ons testplan, zodat we meteen kunnen zien of het fout gaat. In de toekomst gaan we deze test uitbreiden door hem te draaien als meerdere gebruikers. We weten nu namelijk nog niet wat er gebeurt als 100 mensen tegelijk jodiBooks gebruiken.

Ook gaan we bepaalde doelen stellen. Bijvoorbeeld: een pagina moet binnen 0,8 seconden geladen zijn. We weten nog niet wat een reële waarde is, maar ter vergelijking: de trage jodiBooks deed er 1,1 tot 60! seconden over.

In de toekomst willen we eigen servers gaan gebruiken. Met de test en de testdata die we nu hebben, kunnen we beter inschatten wat we dan nodig hebben en kunnen we realistischer testen. In ieder geval zullen we nu eerst naar onze eigen code kijken voordat we de hoster ervan langs geven.

Focus op ons doel: de gebruiker

Code hoeft niet perfect kloppend te zijn. Een website hoeft geen model te zijn waar een wiskundige van gaat kwijlen. Een website moet vooral bruikbaar zijn. Dat betekent dat je af en toe dingen doet die niet helemaal "correct" zijn. Zolang je deze dingen maar goed documenteert, is een "shortcut" niet zo erg.

Hetzelfde geldt voor de manier waarop de website naar de database kijkt. Een perfecte database heeft geen dubbele data, maar dat betekent dat de website op heel veel plekken moet zoeken. We gaan de data nu makkelijker en sneller doorzoekbaar maken. Dat geeft later misschien weer problemen, maar dat lossen we dan weer op.

Ook moeten we ons focussen om de basis voor jodiBooks efficiënter te maken. Dat betekent dat de website veel sneller moet worden en de database compacter (het geheugengebruik moet omlaag). Hoe weten we nog niet, dat moeten we gaan onderzoeken.

Advies serieus nemen

Je moet altijd voorzichtig zijn met geloven wat mensen roepen op internet. Maar als meerdere verstandige mensen waarschuwen dat code inefficiënt is, gaan we er toch maar naar luisteren. Ook al lijkt het niet direct een probleem te zijn voor ons. Je kunt nooit alles overzien, zeker niet als het nieuw is.

Lees hier verder voor "Deel 2: de moeilijke uitleg" door Diana. Hierin gaan we dieper in op hoe we de snelheid van de site willen gaan verbeteren. Dit stuk is geschreven voor software ontwikkelaars, vandaar "moeilijk".