Cum am îmbunătățit performanța Moove cu 59%
Cei mai mulți utilizatori Moove sunt departe de servere. Cum am redus cu 59% timpul până la primul răspuns fără să mutăm vreun server și ce a fost mai greu.
Distanța era cea mai lentă dependență a noastră
Cei mai mulți oameni care deschid Moove app sunt conectați la rețele mobile din Nigeria și Indonezia, la mii de kilometri de serverele care le răspund. Luna aceasta am schimbat felul în care cererile lor ajung la noi, iar timpul până la primul octet (TTFB), timpul dintre momentul în care un telefon trimite o cerere și momentul în care sosește prima parte a răspunsului, a scăzut cu 59%, de la 0.74 secunde la 0.31 secunde. Nu am mutat niciun server ca să ajungem aici.
Această postare explică de ce distanța ne-a costat mai mult decât ne așteptam, ce am schimbat, partea care s-a dovedit mai grea decât schimbarea în sine și ce le-am spune altor echipe care sunt pe cale să facă la fel.
De ce o călătorie lungă devine trei
Inginerii numesc latență întârzierea dintre trimiterea unui mesaj și sosirea lui, iar timp dus-întors (RTT) timpul în care un mesaj ajunge la un server și răspunsul se întoarce. De la Lagos până la serverele noastre, RTT-ul este între 200 și 300 de milisecunde. Singur, acesta este suportabil. Problema este că o cerere călătorește rareori singură.
Înainte ca un telefon să poată cere ceva printr-o conexiune securizată, trebuie să parcurgă două handshake-uri, schimburile scurte de mesaje care stabilesc o conexiune. Handshake-ul TCP deschide conexiunea, iar handshake-ul TLS convine apoi asupra cheilor de criptare care o păstrează privată. Fiecare costă o călătorie dus-întors completă. Abia apoi pleacă cererea propriu-zisă, adică a treia călătorie. La 250 de milisecunde pe călătorie, un utilizator așteaptă aproape trei sferturi de secundă înainte ca serverele noastre să fi făcut vreo muncă.
În rețelele mobile, acest cost se plătește mult mai des decât ai spera. Telefoanele trec de la un turn de telefonie la altul, radiourile adorm ca să economisească bateria, iar aplicațiile sunt trimise în fundal. Oricare dintre aceste evenimente poate întrerupe conexiunea, iar următoarea cerere trebuie să pornească de la o conexiune rece, una la care nu s-a făcut încă niciun handshake, și să plătească din nou tot costul de stabilire. O conexiune caldă, deja deschisă și securizată, trece direct la cerere.
Nu puteam să aducem Lagosul mai aproape de serverele noastre. Puteam însă să aducem stabilirea conexiunii mai aproape de Lagos.
Am mutat handshake-ul, nu serverele
O rețea de livrare a conținutului (CDN) este descrisă de obicei ca un cache: un set de locații din lume care păstrează copii ale fișierelor pentru a le servi de aproape. Noi am folosit una din alt motiv. Pentru API-ul nostru, interfața cu care aplicația comunică pentru tot ce face, memorarea în cache este dezactivată complet. Fiecare cerere ajunge în continuare la origine, numele pe care CDN-ul îl dă serverelor din spatele lui.
Se schimbă locul în care începe conexiunea. Un telefon își deschide acum conexiunea către cea mai apropiată locație edge, numită și punct de prezență (PoP): unul dintre amplasamentele CDN-ului din lume, adesea la zeci de milisecunde distanță, nu la sute. Edge-ul execută terminarea TLS, adică conexiunea securizată dinspre telefon se încheie acolo. Apoi refolosește un set de conexiuni calde către originea noastră, menținute deschise prin keep-alive, o setare care lasă o conexiune deschisă între cereri în loc să o închidă. Cele două călătorii costisitoare de stabilire se desfășoară pe saltul scurt, iar doar cererea propriu-zisă traversează oceanul, pe o conexiune deja caldă.
Trei călătorii lungi devin o călătorie lungă și două scurte. Asta e toată ideea.
Restul l-am păstrat intenționat banal:
- Clienții nu au observat nimic. Aplicația și site-ul au păstrat aceleași adrese, deci nu a fost nevoie de o versiune nouă a aplicației și nimeni nu a trebuit să actualizeze nimic.
- Comportamentul la erori a rămas același. Timpii de expirare sunt aceiași ca înainte, așa că tot ce eșua înainte eșuează în același fel. Nu am vrut niciun tip nou de eroare.
- Edge-ul închide primul conexiunile inactive. Fiecare conexiune are un timp maxim de inactivitate (idle timeout), cât poate sta nefolosită înainte de a fi închisă. Dacă serverele noastre ar închide o conexiune liniștită în timp ce edge-ul o crede încă deschisă, următoarea cerere ar eșua. Setând timpul edge-ului mai scurt, acesta renunță întotdeauna primul și deschide o conexiune nouă.
- Protocoale mai noi pentru telefoane. Telefoanele se pot conecta prin HTTP/3, cea mai recentă versiune a protocolului de cereri al web-ului. Rulează peste QUIC, care îmbină ambele handshake-uri într-o singură călătorie dus-întors și se descurcă mai bine cu pierderea de pachete frecventă în rețelele mobile.
- Nicio regiune lăsată pe dinafară. Nivelurile mai ieftine ale unei rețele edge omit adesea locații tocmai în zonele lumii în care se află utilizatorii noștri, așa că am ales acoperire completă.
Ce am măsurat
Am măsurat din Sydney, care se află aproximativ la aceeași distanță de serverele noastre ca Lagosul, comparând calea veche cu cea nouă. Pentru fiecare, am deschis cinci conexiuni reci proaspete, cazul cel mai important în rețelele mobile, și am luat mediana, notată și p50: rezultatul de la mijloc, pe care jumătate din cereri îl bat, iar cealaltă jumătate nu. Același proiect a activat și compresia pentru scripturile site-ului nostru, care fuseseră trimise la dimensiunea completă. Compresia micșorează un fișier înainte de trimitere, iar browserul îl extinde la loc la sosire; am folosit Brotli, un format de compresie creat pentru web.
| Ce am măsurat | Înainte | După | Îmbunătățire |
|---|---|---|---|
| Handshake-uri TCP și TLS finalizate (p50) | 490 ms | 59 ms | cu 88% mai rapid |
| Timpul până la primul octet (p50) | 743 ms | 307 ms | cu 59% mai rapid |
| Dimensiunea scripturilor paginii de start | 1.76 MB | 560 KB | cu 68% mai mică |
De aici vine cifra de 59%: timpul până la primul octet. O conexiune care îi lua o jumătate de secundă să se stabilească este acum gata în mai puțin de o zecime de secundă, iar o primă vizită pe site descarcă acum cu două treimi mai puțin, pe conexiuni la care mulți oameni plătesc fiecare megaoctet.
Aceste cifre provin dintr-un singur oraș, nu dintr-un test de referință global. Calculul se aplică însă și la Lagos și Jakarta, iar acolo se află utilizatorii noștri.
Partea grea a fost să știm cine ne apelează
Să pui ceva în fața serverelor tale este ușor. Să menții protecțiile funcționale în spatele lui a fost locul în care a mers cea mai mare parte a efortului.
Ca majoritatea platformelor de plăți, Moove folosește limitarea ratei (rate limiting), un plafon pentru numărul de cereri pe care le poate face un apelant într-un interval dat, iar aceste limite sunt legate de adresa IP a apelantului, numărul care identifică un dispozitiv pe internet. În spatele unei rețele edge, fiecare cerere sosește de la adresa unei locații edge, nu de la cea a utilizatorului. Dacă nu s-ar fi schimbat nimic altceva, toți cei din Lagos ar fi împărțit același buget, iar o după-amiază aglomerată ar fi început să refuze oameni care nu făcuseră nimic greșit.
Soluția se bazează pe două idei.
Ai încredere exact în straturile pe care le ai. Un CDN este un fel de proxy invers, un server aflat în fața serverului tău, care transmite mai departe cererile. Fiecare proxy adaugă adresa de la care a primit o cerere într-un antet standard numit X-Forwarded-For, așa că apelantul real se află la un număr fix de pași, sau salturi, înapoi în acea listă. Un număr greșit este o eroare în ambele direcții. Dacă numeri prea puține straturi, fiecare utilizator arată ca locația sa edge. Dacă numeri prea multe, începi să ai încredere într-o intrare scrisă chiar de apelant. De aceea am făcut din acest număr o proprietate a infrastructurii, nu a codului. El se schimbă exact în momentul în care se schimbă rețeaua, iar serviciul nostru refuză să pornească dacă valoarea nu are sens, în loc să ghicească.
Lasă edge-ul să garanteze pentru apelant și crede doar edge-ul. Edge-ul scrie acum adresa reală a apelantului pe fiecare cerere și suprascrie orice a trimis apelantul în locul ei. Protecțiile noastre cred această valoare doar atunci când pot verifica faptul că cererea a trecut cu adevărat prin edge-ul nostru. Fiecare limită care se baza pe adresa de rețea se bazează acum, pentru traficul prin edge, pe apelantul real. Am adăugat aceste reguli pe lângă cele vechi, nu în locul lor, astfel încât fiecare schimbare din lansare a fost o simplă adăugare, pe care oricine o putea verifica dintr-o singură citire.
O eroare prinsă înainte de lansare
Moove Profiles pot funcționa pe domeniul propriu al unui comerciant. Cererile pentru acele domenii erau transmise prin site-ul nostru împreună cu domeniul comerciantului, ca site-ul să știe ce profil să afișeze.
Noul edge din fața site-ului elimină acest gen de informații din cererile primite, ceea ce este exact corect, pentru că un vizitator nu ar trebui să le poată seta niciodată. Dar a însemnat și că, odată ce site-ul ar fi trecut în spatele edge-ului, fiecare domeniu de comerciant ar fi afișat discret pagina de start Moove în loc de profilul comerciantului.
Am prins problema la revizuire, am mutat mai întâi calea domeniilor comercianților pe propria rută și am făcut ca infrastructura noastră să refuze aplicarea schimbării pentru site oriunde acea mutare nu avusese loc. O greșeală care trăia doar în mintea unui revizor este acum ceva pe care build-ul îl oprește.
Lecția generală: când adaugi un strat care curăță intrările, caută tot ce depindea de intrările pe care urmează să le cureți.
Cum am făcut lansarea
La infrastructura de plăți nu se face „lansează și vezi”, așa că am modificat întâi staging-ul și apoi producția, iar câteva alegeri au făcut trecerea liniștită. Am redus TTL (time to live) pentru înregistrările noastre DNS, directorul internetului care transformă un nume precum moove.xyz într-o adresă de server. TTL este cât timp are voie restul internetului să țină în cache acel răspuns, iar noi l-am redus de la o oră la cinci minute, cu o oră întreagă înainte să se mute ceva, astfel încât răspunsul vechi să fi expirat peste tot și o revenire să aibă efect în câteva minute. Am păstrat înregistrările de adrese într-o formă care putea fi comutată pe loc, pentru că ștergerea și recrearea lor ar fi lăsat un gol, iar un gol este o întrerupere. Fiecare pas riscant a fost o schimbare mică, de sine stătătoare, cu pașii de anulare scriși în ordine.
Ce le-am spune altor echipe
Dacă utilizatorii tăi sunt departe de servere, merită să ai o rețea edge chiar dacă nu memorează nimic în cache. Înainte de trecere:
- Listează tot ce citește adresa apelantului, inclusiv limitele de rată, listele de adrese permise, semnalele de fraudă, jurnalele de audit și regulile de securitate. Fiecare dintre ele vede adresa edge-ului dacă nu o schimbi.
- Fă din numărul de straturi de încredere o parte a infrastructurii tale și refuză pornirea atunci când este greșit.
- Lasă edge-ul să înregistreze apelantul real și ai încredere în acea înregistrare doar pe traficul pe care îl poți verifica ca venind de la edge-ul tău.
- Asigură-te că serverele tale acceptă trafic doar prin edge și urmărește înainte să impui regula.
- Dacă memorezi pagini în cache, construiește cheia de cache, setul de detalii ale cererii pe care un cache îl folosește pentru a decide dacă două cereri pot împărți același răspuns stocat, din tot ceea ce depinde cu adevărat răspunsul. Serverele listează aceste detalii într-un antet Vary, așa că citește-l în loc să te bazezi pe memorie.
- Măsoară înainte și după și urmărește separat timpul handshake-ului și timpul până la primul octet, ca să poți deosebi stabilirea conexiunii de timpul pe care serverele tale îl petrec lucrând.
Ce urmează
Cele mai multe dintre paginile noastre sunt personalizate, așa că deocamdată cache-ul edge al site-ului stă mai mult nefolosit. Îmbunătățirea conexiunii se aplică tuturor paginilor, indiferent de situație, iar paginile individuale pot acum să opteze pentru cache atunci când este sigur să fie partajate. Memorarea în cache a răspunsurilor API ar necesita propriul design și propria revizuire, și este următorul lucru pe care vrem să-l analizăm.
Banii ar trebui să se miște cu viteza rețelei, nu cu viteza luminii peste un ocean.
Dacă construiești pe Moove, citește introducerea noastră în API-urile și SDK-urile de plăți Web3 și Moove Developer Program.
Scris de echipa de ingineri moove.xyz.
Despre moove.xyz
moove.xyz este o platformă fintech Web3 globală, creată pentru mișcarea fără permisiuni și fără efort a valorii. Oferim companiilor și consumatorilor de oriunde posibilitatea de a trimite, primi, face staking și swap cu orice criptomonedă, pe orice blockchain, totul într-o singură platformă.
Suntem una dintre primele companii fintech Web3 din lume care inovează și construiesc o infrastructură completă de plăți crypto și finanțe descentralizate, oferind o acoperire integrată și cuprinzătoare: acces la portofele multi-chain, handle-uri de portofel personalizate, swap-uri de tokenuri cross-chain, tranzacții cross-chain integrate și o rețea financiară socială descentralizată. Printre produsele noastre principale se numără Moove Profile, Moove Send, Moove Receive, Moove Stake, Moove Swap, Moove Rewards, Moove Discover și altele.
Misiunea noastră e simplă: să creăm și să distribuim tehnologie financiară fără permisiuni și fără efort pentru următoarea miliardă de utilizatori Web3. Credem din toată inima că viitorul mișcării banilor și al valorii va fi fără costuri, fără frontiere, fără permisiuni, fără efort și construit pentru toată lumea, iar noi construim cea mai bună platformă fintech Web3 pentru a face acest viitor realitate.
Banii tăi. Decizia ta.






