Dagbog-bloggen

Uge 2: Need for Speed

Oleg, vores ukrainske udvikler i memit, havde julelys i øjnene da han forleden fortalte om resultatet af vores seneste performanceoptimeringer. Få hundrede milisekunders loadtid kunne en af de helt centrale – og komplekse! – sider på memit.com nu prale med. Og det var ikke kun ham. Både hos mig og hele vejen rundt i vores lille workshop-rum spredte euforien sig. Sejt!

Det havde ellers holdt hårdt. Faktisk havde vi været ude og røre performanceproblemer af den slags, der får det til at klø i klikke-fingeren på selv ellers meget tålmodige brugere. 3,27 sekunder havde vi målt loadtiden på siden til. Av. Alle havde råbt og skreget: ”hurtigere, hurtigere. Vi har need for speed”.

Den langsommere svartid var gradvist kommet snigende i takt med flere brugere og mere data i systemet. Vi havde lavet et trade-off der gik på at det som startup var ultravigtigt at komme i søen og vise, at vi kunne lave en tjeneste med et featuresæt så tæt, at det virkeliggjorde vores drøm: at lave en tjeneste, der rent faktisk kunne hjælpe brugerne med at blive klogere. Afskærme dem mod det information overload der hærger nettet. Gøre det muligt at gemme – eller som vi kalder det: ”memme” - fed viden når de stødte på den, og kunne arbejde videre med den i ro og mag. Helt for sig selv, eller i et fagligt socialt fællesskab med andre. Og selvom featuresættet stadigvæk har mangler af alle mulige slags, har vi faktisk sådan en tjeneste i dag. Og en del af prisen herfor var altså performance. De mandemåneder vi havde sparet i starten sved nu. Men alligevel: hellere det, end aldrig at have fået en service med glade brugere i luften.

Inden jeg dykker ned i hvad vi fandt af flaskehalse, da vi gik i kødet på problemet, og løsningerne herpå, er her indledningsvist en kort introduktion til arkitekturen og datagrundlaget i den hidtidige løsning. Arkitekturen er baseret på PHP og MySQL hosted i Amazon Cloud. Tidligere benyttede vi et cachelag memcached, til bl.a. at cache SQL forespørgsler. Datagrundlaget i memit er centreret omkring streams mems, de artikler, pdf’er, billeder mm som brugerne gemmer med vores service, samt de meta-data omkring dem, vi bruger i forbindelse med selve visningen (hvem har memmet den, hvor mange har memmet den, osv.).

I forbindelse med performanceoptimeringerne analyserede Oleg og jeg os gradvist frem til flaskehalsene i systemet, som viste sig at være relateret til flere ting. Dels så havde vi ikke fået cachet samtlige af de væsentlige kald til databasen (80% ramte cachen). En optimering af dette gav en indledende bedre effekt, men stadig på den forkerte side af de 2 sekunder for den omtalte aktivitetsside. Dernæst kunne vi se at mængden af kald mod databasen spillede ind, for selvom kaldene nu næsten udelukkende ramte cachen, så skulle forespørgslerne stadig over netværket til cache serveren, dvs. at netværkslatency spillede ind.

Vi kunne også konstatere, at for hver mem i en stream af mems, blev der foretaget en hel stribe af kald mod databasen til de forskellige normaliserede tabeller for at samle al den data der skulle til at præsentere denne. Samtidig blev der også foretaget dyre kald for at hente selve streamen. Sammenlagt, altså en hel del forespørgsler. Det, blev det klart for os, blev vi nødt til at reorganisere.

Ideen var at samle al den data der relaterede sig til hver mem og cache hele det samlede mem-objekt, i stedet for blot at cache SQL forespørgslerne der skulle til at opbygge det. Samtidig ønskede vi at cache selve streamen eller listen af mems, så der ikke skulle bruges dyre kald for at generere denne.

Løsningen blev således en NoSQL implementation ved brug af Redis, som udmærker sig ved at være meget effektiv i håndtering af lister, samt holde objekter i hukommelsen – netop de ting, som vi havde behov for i memit. Hvert mem og al dens data er nu samlet i et objekt, således at der alene skal laves 1 forespørgsel per mem. Dvs. når memit viser listen af en brugers mems, så laves basalt set 1 kald til Redis for at udtrække listen af mems, samt 1 kald per mem for at hente dets data.

Resultatet var som nævnt fedt. 0,2-3 sekund pr. side virker i praksis for brugeren som om siden flyver ind på skærmen. Men bedst af alt: Den nye arkitektur skalerer, således at performance nu ikke degraderes når der kommer nye brugere på. Vi har i øvrigt også bygget sharding ind allerede, således at der nemt kan introduceres flere server-instanser og vi kan sprede data og load imellem disse i takt med at behovet vokser.

Vi er dog på ingen måde i hus endnu. Både fordi den nye struktur har vist sig at give en del ekstra bogholderi med at holde listerne opdateret, så de altid er korrekt repræsenteret i hukommelsen, og kan leveres lynhurtigt. Hvilket igen kræver en grundig QA proces. Vores tester, Elmedina, knokler derfor løs med både manuelle og automatiske testværktøjer for at sikre at disse større ændringer ikke tilfører fejl.

Og så er det i skrivende stund kun de to mest centrale aktivitetssider vi har fået has på: Forsiden, der viser streams af mems både fra brugeren selv, og fra dem som brugeren følger på memit. Og brugerens ”my mem”-side, der samler alle brugerens egne mems. En trejde central side – collections-siden, der viser de samlinger af mems brugeren har adgang til – er den næste vi arbejder på.

Som opstart med begrænsede ressourcer skal du hurtigt udvikle de kernefeatures som bringer værdi og beviser berettigelsen af servicen, også selvom det koster på arkitektur. Men du skal også være klar til at gribe ind, når det begynder at gå galt.

I dag håber jeg, at vores kommende feature, som giver vores brugere mulighed for at uploade filer når at blive færdig til test som planlagt.

Det bedste i dag var, at vi releasede vores nye forbedrede top bar redesign, som øger læsevenligheden på sitet og strukturerer valgmulighederne bedre. Og at vi kunne meddele det til de brugere som havde efterlyst dette.

Det værste i dag var, at vi opdagede en fejl i memming af pdf'er i Firefox, som ser ud til at blive tricky at løse.

Gårsdagen bragte nyheden om en skolelærer som var begyndt at bruge memit til vidensdeling med hans klasse. Det var fedt.

Min aftenbøn til i morgen er at se den fortsatte vækst i brugere.

Kommentarer (18)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
#1 Allan Ebdrup Blogger

Det lyder som en fuldstændig klassisk optimering: Reducer antallet af databaseforespørgsler og så lyder det også som om i denormaliserer samtidig.

Tre spørgsmål hvis du læser med:

1) Har I overvejet at skifte MySQL ud med et datastore der er født til at gemme data struktureret som i skal bruge det. Så i evt. kan slippe for at vedligeholde en separat Redis "cache" (som jeg tolker Redis brugt som)? Og så i også kunne undgå denormalisering og dobbelt (trippelt?) bogholderi osv?

2) Hvilke værktøjer brugte i til at analysere problemet?

3) Gemmer i pdf'er og andet binært data i MySQL? (det håber jeg ikke)

  • 2
  • 0
#2 Rune Alblas

Hej Allan,

Tak for dine spørgsmål. Ja, det er en nok en helt klassisk optimeringsopgave og en naturlig iteration at tage på vejen.

  1. Vores plan er faktisk at bringe Redis endnu mere i spil :-) Jeg kan ikke skjule vores begejstring for Redis, og da alt i Memit nærmest består af lister, som Redis er fantastisk god og effektiv til at håndtere, så går vi den vej. Så svaret er at vi pt. går efter Redis + MySQL som bl.a. også Tumblr har valgt.

  2. Vi har lavet en egenudviklet controller som kan give databasestatistik på de forskellige sider.

  3. Nej, binært data gemmer vi i Amazon S3.

  • 1
  • 0
#4 Lars Tørnes Hansen

Meget interessant, jeg har for nyligt også haft fat i Redis (der ikke er en cache) fra hhtp://redis.io/ - den udmærker sig vedat være meget hurtigt.

Jeg vil lige komme et gratis tip - for det tilfælde at i skulle få brug for mere vidtgående performance forbedringer. Tag et kig på Go programmeringssproget: Det bruges af Google, naturligvis, da det er dem der har lavet det, og stadig udvikler på det. Det bruges på blandt andet dl.google.com og youtube.com

Her er et ekstremt eksempel på en performance forbedring; http://blog.iron.io/2013/03/how-we-went-from-30-servers-to-2-go.html Opfølger artikel, 2 år senere: http://blog.iron.io/2013/08/go-after-2-years-in-production.html

Der er også en lille video på ca 45 minutter, hvor 4 firmaer fortæller om Go i et produktionsmiljø: "Google I/O 2012 - Go in Production" http://www.youtube.com/watch?v=kKQLhGZVN4A

Seleve Go sproget har web siden: http://golang.org/ og der er også en meget aktiv Google+ gruppe, der hedder "Go+": https://plus.google.com/u/0/communities/114112804251407510571

  • 2
  • 0
#6 Allan Ebdrup Blogger

Meget interessant, jeg har for nyligt også haft fat i Redis (der ikke er en cache) fra hhtp://redis.io/ - den udmærker sig vedat være meget hurtigt.

Jeg kender godt lidt til Redis. Den kan fungere som en cache, afhængigt af hvordan man bruger den. Grunden til at jeg skrev cache, var at det lyder nu som om den bliver brugt som sådan i løsningen. (med MySQL som stedet hvor "sandheden" opbevares) Fra artiklen:

Ideen var at samle al den data der relaterede sig til hver mem og cache hele det samlede mem-objekt, i stedet for blot at cache SQL forespørgslerne der skulle til at opbygge det. Samtidig ønskede vi at cache selve streamen eller listen af mems, så der ikke skulle bruges dyre kald for at generere denne.

Men det er muligt at jeg tager fejl - Rune korrigerede mig dog ikke (i første omgang)

  • 0
  • 0
#7 Tommy Bell

Hej Rune

Tillykke med den fine optimering.

Nu synes jeg sådan set det lyder om et rigtigt spændende projekt det her, og jeg ville være inklineret til at benytte mig af det. Hvis det ikke var fordi det minder utroligt meget om andre sociale tjenester hvor, så snart jeg har bidraget til noget, så er det ikke længere mit. Men jeres.

Ud fra jeres terms, der står der.

You grant Memit and its users a non-exclusive, royalty-free, transferable, sublicensable, worldwide license to use, store, display, reproduce, re-mem, modify, create derivative works, perform, and distribute your User Content on Memit solely for the purposes of operating, developing, providing, and using the Memit Products. Nothing in these Terms shall restrict other legal rights Memit may have to User Content, for example under other licenses. We reserve the right to remove or modify User Content for any reason, including User Content that we believe violates these Terms or our policies.

Det læser jeg som at jeg mister rettigheden til det jeg har lagt ind i tjenesten. Så jeg kan bestemt ikke bruge det til at gøre noget som helst, udover at dele information med andre, hvilket selvfølgelig også virker som et af de primære formål. Men jeg er noget ked af at miste rettigheden til information jeg selv producere, det kan jeg i hvert fald ikke ligge i memit, så vidt jeg har forstået det.

Hvis det er forkert, må du meget gerne rette mig.

  • 2
  • 0
#9 Jesper Louis Andersen

Mere generelt, så handler det om et sprog der supporterer samtidighed (concurrency) vil kunne køre databasequeries samtidigt. Det betyder ikke at de nødvendigvis er parallelle på klientmaskinen, men det giver databasen mulighed for at udføre queries i parallel, hvor det betyder noget.

Men når du har en effektiv cache-struktur på plads, så er det ikke sikkert det vil batte så meget på et typisk side-load. Og det er ihvertfald en masse arbejde i de fleste sprog. Det giver mere mening at regne ud hvordan Redis kan opdateres løbende med persistente data i baggrunden, så du slipper for at skulle refreshe et query fra backend. Et andet godt trick er at få styr på query-coalescing, så det samme query ikke foretages flere gange inden for samme tidsrum mod backenden.

  • 1
  • 1
#11 Allan Ebdrup Blogger
  • 0
  • 0
#12 Adam Tulinius

Trådning/samtidighed er ikke nødvendig - trådning er bare en abstraktion lagt oven på et lilleantal cores. Jeg laver massa parallele DB-queries i Node.js der ikke har trådning.

Non-blocking io / futures? Det koster også en tråd, men man slipper selvfølgelig for selv at gøre noget særligt for det.

Jeg er dog glad for din pointe; det behøver slet ikke være svært.

  • 0
  • 0
#13 Allan Ebdrup Blogger

Non-blocking io / futures? Det koster også en tråd, men man slipper selvfølgelig for selv at gøre noget særligt for det.

Hej Adam Hvis du læser det jeg svarer på, så skriver begge at programmeringssproget skal have trådning/samtidighed, det er der netop ikke i sproget i Node.js.

Det er muligt at non-blocking IO, kan koste lidt. Jeg skal blankt indrømme at jeg ikke kender detaljerne. Men jeg vil blive meget overrasket hvis hver non-blocking I/O operation bliver håndteret nøjagtig som en seperat tråd (Så ville man jo ikke vinde noget som helst med non-blocking I/O).

Jeg mindes I/O styret af interrupts og lignende fra min tid på uni. Altså drevet af events.

  • 1
  • 0
#14 Adam Tulinius
  • 0
  • 0
#15 Jonas Høgh

Det er helt korrekt Allan, non-blocking IO koster bestemt ikke en tråd hvis det er lavet rigtigt, hvilket gør det meget mere skalerbart. Nu er jeg ikke den store kernenørd, men der er mange måder at gøre det på, fx mener jeg man kan få kernen til at kalde en callback når IO er afsluttet.

Den slags detaljer er heldigvis pænt abstraheret væk i asynkrone højniveau værktøjer som node.js og .Net 4.5

  • 0
  • 0
#16 Adam Tulinius

Node.js modellen er, forøvrigt, langt fra perfekt. Lad mig citere:

"The whole point of node.js is that it's event-driven. All the code runs in event handlers in a single thread. There are no concurrency issues because the code doesn't run concurrently. The downside is that each event handler must exit quickly because it blocks the other events.

In your example, the code will start the disk IO and exit immediately. The node.js infrastructure will notify the program that the IO operation was completed by running an event handler. The timer event will be called before or after the IO event, but never concurrently." - http://stackoverflow.com/questions/4209139/javascript-event-driven-and-c...

Det betyder også at hvis man laver noget dumt i node.js, som en eller anden tung beregning i.f.m. en sidevisning, så ender alle andre events altså med at måtte vente. Sammenlign det med platforme som Erlang, eller Haskell, hvor man altså sagtens kan have millioner af "tråde" (som altså ikke er rigtige tråde) kørende "samtidigt", hvor en scheduler så fordeler cpu-tid på en passende måde: Pludselig er det i det store og hele irrelevant om ens IO er non-blocking.

  • 0
  • 0
#17 Allan Ebdrup Blogger

Det betyder også at hvis man laver noget dumt i node.js, som en eller anden tung beregning i.f.m. en sidevisning, så ender alle andre events altså med at måtte vente.

Helt korrekt. I min bog er dette en fordel. Jeg bruger nemlig Node.js til webservere, og det er alligevel dårlig arkitektur at sætte din webserver til at lave noget CPU-tungt.

Det er bedre at offloade de CPU-tunge beregninger til nogle dedikerede applikationsservere, bag en kø. Så webserveren kan bruge sine kræfter på webtraffik.

Men man skal selvfølgelig vælge det rigtige værktøj til opgaven.

  • 1
  • 0
#18 Jan Kjaer

Re: Ulv i fåreklæder

Tommy - bemærk dette er det samme svar som jeg har givet på din kommentar under mit indlæg. Det var blot vigtigt for mig at den ikke var efterladt i vinden for de der har kommenteret i dette spor :-)

Hej Tommy, Først tak for dit kritiske spørgsmål – Stor respekt til den der læser det med småt!!

Sådan en mandag aften efter putning og fingeropsamling af 53 rulleærter fra under den lilles højstol (jeg har en dreng på 1 år) – så rammer dit indlæg lige i solo-plexus for at sige det som det er! Jeg har lyst til at give dig et kort og præcist svar. Det vil jeg gøre – men jeg håber du har lyst til at give mig tiden til også at læse den lange version.

Korte version: Det er ikke godt nok det der står – og det skal vi have lavet om! memit har ikke et ønske om at tage dit eller andres brugeres indhold gidsel – og som du selv nævner så kan dele af det der står i ”terms of use” godt give det indtryk. Derfor skal det naturligvis laves på en måde så det afspejler det der reelt set handler om – nemlig at sikre at vi ikke foruretter indholdets ophavshavere! Vi skal have kigget på det så hurtigt det er os muligt (læs tid og penge). På den korte bane vil jeg love dig at vi ikke gør noget med dit indhold som du ikke selv er i kontrol over – plus give dig mulighed for at hente dit indhold ned til dig selv og slette din konto (inkl. indhold) på memit hvis det er det du ønsker.

Lange version: Hvis jeg selv skulle vælge en løsning (som jeg jo i mange andre sammenhænge gør en del) – så har jeg selv samme holdning til den der ”legal cover my ass jargon” / attitude som lægges for dagen især fra store it / tech selskaber. (Har du eksempelvis givet apples terms en gennemlæsning ;-)

Og hvad sker der så lige for os … en lille spiller der prøver at blomstre op igennem asfalten af gamle og nye spillere på markedet. Så kommer du og vil gerne bruge memit (tak for det :-) – men rammes så af vores ”cover our memit ass story” – hvorfor nu det?

Her er et par vinkler på hvorfor vi er havnet der for nu.

1) Copyright er et felt der netop på vores domæne er en rigtig stor ”issue”. Især i USA hvor ca. 1/3 af vores brugere kommer fra – og derfor har vi helt banalt set ladet os inspirere af den amerikanske legal jargon på området for at sikre at vi ikke træder nogen over tærene ifht. denne brugergruppe. Det er på ingen måde den holdning vi gerne vil arbejde med i memit (faktisk lige det modsatte – se pkt. 2) – men har simpelthen ikke prioriteret ressourcerne til at arbejde med nuancerne i formuleringen af vores ”terms of use” i relation til forskellige lande, formatter mv. – derfor har vi taget ”den laveste fællesnævner” (Det tør jeg godt sige også til en amerikaner da det er min erfaring at de godt selv er klar over at den er "gone too far") – for at sikre at vi ikke stillede os i en situation hvor vi havde kørt projektet ned i en blindgyde i relation til fremtiden.

2) Det vi gerne vil er netop at angribe dette (ophavsretten) fra den helt anden side – nemlig at alt det indhold der produceres derude – og ikke mindst alt det gode indhold der er værd at memme. Det skal memit netop være en platform der kan håndtere at binde sammen. På tværs af formater og ejerskab – således at ejermand og ophavsrats-manden ikke mister styring med sit indhold. Dette gælder for så vidt det kommercielle medie, den ihærdige blogger, webmaster eller den sporadiske indholdsproducent og almene memit bruger!

3) Forholdene omkring ”terms of use” som vi skal have formuleret på rigtig (og forhåbentlig rimelig forståelig juridisk vis) handler helt centralt om følgende; nemlig at memit som platform redegøre for den dynamik der skaber relationer mellem indholdet på tværs af brugere – altså den ”sociale medie del”. Ligeledes er det centralt at brugere der anvender systemet vedkender sig et ansvar for ikke at gøre noget ulovligt med de muligheder de får ved at anvende systemet. Dette svare i princippet til at du, når du køber en bil, ikke kan give din bilforhandler skylden for at du ikke overholder de lokale hastighedsbestemmelser.

4) Hvad kan jeg så love dig – så du måske stadig har lyst til at give memit et spin. a) du vil altid vide hvad der sker med dit indhold – inden det er sket (lige nu har vi ikke nogle ønsker om og slet ikke nogen forretningsmodel der arbejder i at udnytte selve indholdet hvor vi har behov for at have ejerskab til det enkelte indhold b) skulle vi mod alt forventning bukke under vil du få muligheden for at hente dit indhold ned i en fil 3) den måde formuleringen er lavet på vil jeg indenfor 6 mdr. arbejde hårdt på at prioritere at vi får omformuleret jf. ovenstående men naturligvis stadig indenfor rammerne af de krav der bliver stillet set med juridiske briller. Den endelige model bliver ikke entydig på tværs af hele portalen og alt indhold – da vi er nødt til at lave forskellige parametre ifht. de forskellige formater, kilder og ejerforhold.

Tommy … håber ovenstående giver mening .. håber du nåede hele vejen igennem … men det ligger mig virkelig på sinde at - make this right!

Jeg glæder mig til at høre fra dig. helt konkret ville jeg spørge dig om du set nogle der har nailet den .. som vi kunne kigge i retning af?

Igen tak for din tid og dit feedback – tag fat i mig når som helts her eller jfk@memit.com / 20159544

Mv Jan :-) www.memit.com/janfuttrup

  • 0
  • 0
Log ind eller Opret konto for at kommentere