O întrebare frecventă despre orice sistem distribuit din partea unui specialist non-tehnic este „Care este numărul de tps în blockchain-ul vostru?”. Totuși, cifra menționată ca răspuns este de obicei puțin relevantă pentru ceea ce ar dori să audă întrebătorul. În realitate, el a vrut să întrebe „Va fi blockchain-ul vostru potrivit pentru cerințele mele de afaceri”, iar aceste cerințe nu constau într-un singur număr, ci într-o multitudine de condiții — aici se includ atât rezistența la erori a rețelei, cât și cerințele legate de finalitate, dimensiunile, natura tranzacțiilor și multe alte parametrii. Așadar, răspunsul la întrebarea „câte tps” va fi probabil complicat și aproape niciodată complet. Un sistem distribuit cu zeci și sute de noduri care efectuează calcule destul de complexe poate exista într-un număr mare de stări diferite, legate de starea rețelei, conținutul blockchain-ului, defectele tehnice, problemele economice, atacurile asupra rețelei și multe alte motive. Etapele în care pot apărea probleme de performanță sunt diferite de serviciile tradiționale, iar serverul rețelei blockchain este un serviciu de rețea care combină funcționalitatea unei baze de date, a unui server web și a unui client torrent, ceea ce îl face extrem de complex în ceea ce privește profilul de sarcină asupra tuturor subsistemelor: procesor, memorie, rețea, stocare.
S-a întâmplat ca rețelele descentralizate și blockchain-urile să fie un software destul de specific și neobișnuit pentru dezvoltatorii de software centralizat. De aceea, aș dori să subliniez aspectele importante ale performanței și rezilienței rețelelor descentralizate, abordările de măsurare ale acestora și identificarea bottleneck-urilor. Vom examina diferitele probleme de performanță care limitează viteza de livrare a serviciilor utilizatorilor blockchain-urilor și vom observa trăsăturile caracteristice acestui tip de software.
Etapele solicitării serviciului de către clientul blockchain-ului
Pentru a discuta cu onestitate despre calitatea oricărui serviciu mai complex, trebuie să luăm în considerare nu doar valorile medii, ci și maximele/minimele, medianele și percentilele. Teoretic, putem vorbi despre 1000 tps într-un blockchain, dar dacă 900 de tranzacții s-au realizat cu o viteză uriașă, iar 100 au «înghețat» timp de câteva secunde, atunci timpul mediu obținut din toate tranzacțiile nu este o metrică tocmai corectă pentru clientul care nu a reușit să finalizeze tranzacția într-un interval de câteva secunde. „Gropile” temporale, cauzate de runde de consens pierdute sau de divizarea rețelei pot afecta semnificativ un serviciu care, pe standurile de testare, a arătat o performanță excelentă.
Pentru a identifica astfel de bottleneck-uri, trebuie să înțelegem bine etapele în care un blockchain real poate întâmpina dificultăți în servirea utilizatorilor. Să descriem ciclul de livrare și procesare a tranzacțiilor, precum și obținerea unei noi stări a blockchain-ului, din care clientul poate verifica că tranzacția sa a fost procesată și înregistrată.
- tranzacția se formează pe client
- tranzacția este semnată pe client
- clientul alege unul dintre noduri și își trimite tranzacția către acesta
- clientul se abonează la actualizările bazei de date a stării nodului, așteptând apariția rezultatelor execuției tranzacției sale
- nodul răspândește tranzacția prin rețeaua p2p
- câteva sau un singur BP (block producer) procesează tranzacțiile acumulate, actualizând baza de date a stării
- BP formează un nou bloc, procesând numărul necesar de tranzacții
- BP răspândește noul bloc prin rețeaua p2p
- noul bloc este livrat către nodul la care se adresează clientul
- nodul actualizează baza de date a stării
- nodul observă actualizarea referitoare la client și îi trimite o notificare despre tranzacție
Acum să examinăm aceste etape în detaliu și să descriem problemele potențiale de performanță la fiecare etapă. Spre deosebire de sistemele centralizate, vom analiza și execuția codului pe clienții rețelei. Destul de des, la măsurarea tps, timpul de procesare a tranzacțiilor este colectat de la noduri, nu de la client — ceea ce nu este tocmai corect. Clientului nu-i pasă cât de repede a procesat nodul tranzacția lui, cel mai important pentru el este momentul în care informația validă despre această tranzacție, inclusă în blockchain, devine disponibilă. Această metrică este, de fapt, timpul de execuție a tranzacției. Aceasta înseamnă că diferiți clienți, chiar și atunci când trimit aceeași tranzacție, pot obține timpi complet diferiți, care depind de canal, de încărcare și de proximitatea nodului, etc. Așadar, este absolut necesar să măsurăm acest timp pe clienți, deoarece aceasta este parametrul care trebuie optimizat.
Pregătirea tranzacției pe partea clientului
Să începem cu primele două puncte: tranzacția este formată și semnată de client. Ciudat, este un potențial bottleneck de performanță a blockchain-ului din perspectiva clientului. Aceasta este neobișnuit pentru serviciile centralizate, care își asumă toate calculele și operațiile cu datele, iar clientul pregătește pur și simplu o cerere scurtă, capabilă să solicite un volum mare de date sau calcule, obținând un rezultat final. În blockchain-uri, codul clientului devine din ce în ce mai puternic, iar nucleul blockchain-ului devine din ce în ce mai ușor, iar sarcinile de calcul masive sunt predată software-ului client. În blockchain-uri există clienți care pot pregăti o tranzacție destul de mult timp (vorbesc despre diverse dovezi Merkle, dovezi succincte, semnături threshold și alte operații complexe pe partea clientului). Un exemplu bun de verificare ușoară on-chain și pregătire grea a tranzacției pe client este dovada apartenenței la o listă pe baza unui Merkle-tree, iată .
De asemenea, nu trebuie uitat că codul clientului nu trimite doar tranzacții în blockchain, ci mai întâi cere starea blockchain-ului — iar această activitate poate influența încărcarea rețelei și a nodurilor blockchain. Așadar, atunci când facem măsurători, ar fi înțelept să emulăm cât mai complet comportamentul codului clientului. Chiar dacă în blockchain-ul tău există clienți obișnuiți care semnează digital o tranzacție simplă de transfer a unui anumit asset, cu fiecare an, volumul de calcul pe client devine tot mai mare, algoritmii criptografici devin mai puternici, iar această parte a procesării poate deveni un bottleneck semnificativ în viitor. De aceea, fii atent și nu rata situația în care, dintr-o tranziție care durează 3.5s, 2.5s sunt consumate pentru pregătirea și semnarea tranzacției, iar 1.0s pentru trimiterea în rețea și așteptarea răspunsului. Pentru a evalua riscurile apariției acestui bottleneck, trebuie să colectezi metrici de pe mașinile clientului, nu doar de la nodurile blockchain.
Trimiterea tranzacției și monitorizarea stării acesteia
Următorul pas este trimiterea tranzacției către nodul blockchain selectat și obținerea stării de acceptare a acesteia în pool-ul de tranzacții. Această etapă este similară cu o solicitare obișnuită către o bază de date, nodul trebuie să înregistreze tranzacția în pool și să înceapă să răspândească informația despre aceasta prin rețeaua p2p. Abordarea de evaluare a performanței aici este similară cu evaluarea funcționării microservicilor tradiționali Web API, iar tranzacțiile din blockchain-uri pot fi actualizate, schimbând activ starea. În general, actualizarea informației despre tranzacție în unele blockchain-uri poate să aibă loc de mai multe ori, de exemplu, în cazul trecerii între fork-uri ale lanțului sau atunci când BP-urile anunță intenția de a include tranzacția în bloc. Restricțiile asupra volumului acestui pool și numărul de tranzacții din acesta pot influența performanța blockchain-ului. Dacă pool-ul de tranzacții este umplut până la dimensiunea maximă posibilă sau nu încape în memoria operațională — performanța rețelei poate scădea brusc. Blockchain-urile nu au mijloace centralizate de protecție împotriva fluxului de mesaje junk, iar dacă blockchain-ul suportă tranzacții de mari dimensiuni și comisioane reduse, acesta poate duce la supraaglomerarea pool-ului de tranzacții — acesta este un alt potențial bottleneck de performanță.
În blockchain-uri, clientul trimite o tranzacție către orice nod blockchain care îi place, hash-ul tranzacției fiind de obicei cunoscut clientului încă înainte de trimitere, astfel că tot ce trebuie să facă este să stabilească o conexiune și apoi să aștepte ca blockchain-ul să își schimbe starea, includând tranzacția sa. Să observăm că măsurarea „tps” poate oferi rezultate complet diferite pentru diferitele metode de conectare la nodul blockchain. Aceasta poate fi o obișnuită RPC HTTP sau WebSocket, care permite implementarea pattern-ului „subscribe”. În al doilea caz, clientul va primi o notificare mai devreme, iar nodul va consuma mai puține resurse (în principal memorie și trafic) pentru a răspunde la starea tranzacției. Așadar, la măsurarea „tps”, este necesar să se ia în considerare metoda de conectare a clienților la noduri. Prin urmare, pentru a evalua riscurile apariției acestui bottleneck, benchmark-ul blockchain-ului trebuie să fie capabil să emuleze clienți atât cu WebSocket cât și cu cereri RPC HTTP, în proporții corespunzătoare rețelelor reale, precum și să schimbe natura tranzacțiilor și dimensiunea acestora.
Pentru a evalua riscurile apariției acestui bottleneck, este necesar să se colecteze metriki și de pe mașinile clienților, nu doar de pe nodurile blockchain.
Transmiterea tranzacțiilor și blocurilor prin rețea p2p
În blockchain-uri, pentru transferul tranzacțiilor și blocurilor între participanți se utilizează rețele peer-to-peer (p2p). Tranzacțiile se răspândesc prin rețea, începând de la unul dintre noduri, până ajung la peer-ii producătorilor de blocuri, care înglobează tranzacțiile în blocuri și, prin aceeași rețea p2p, răspândesc noile blocuri la toate nodurile din rețea. Baza majorității rețelelor p2p moderne este formată din diverse modificări ale protocolului Kademlia. o bună prezentare generală a acestui protocol, iar – un articol cu diverse măsurători în rețeaua BitTorrent, din care se poate înțelege că acest tip de rețele este mai complex și mai imprevizibil decât o rețea centralizată rigid configurată. De asemenea, un articol despre măsurarea diferitelor metrici interesante pentru nodurile Ethereum.
În esență, fiecare peer din aceste rețele își menține propria listă dinamică de alți peers, de la care solicită blocuri de informație adresate pe baza conținutului. Atunci când primește o solicitare, peer-ul fie furnizează informația solicitată, fie transmite solicitarea către următorul peer pseudo-aleator din listă, iar după ce primește un răspuns, îl trimite celui care a solicitat și îl memorează temporar, oferind acest bloc de informații mai devreme data viitoare. Astfel, informațiile populare ajung să fie în multe cache-uri ale unui număr mare de peers, în timp ce cele nepopulare sunt treptat eliminate. Peers țin evidența a câte informații au transferat unii altora, iar rețeaua încearcă să stimuleze distribuitorii activi, sporindu-le ratingul și asigurându-le un nivel mai înalt de serviciu, eliminând automat participanții inactivi din listele de peers.
Deci, acum trebuie să se răspândească tranzacția în rețea, astfel încât să fie observată de block-produceri și inclusă în bloc. Nodul distribuie activ noua tranzacție tuturor doritorilor și ascultă rețeaua, așteptând blocul în indexul căruia va apărea tranzacția dorită, pentru a notifica clientul aflat în așteptare. Timpul necesar pentru ca rețeaua să transmită informații despre noi tranzacții și blocuri în rețele p2p depinde de un număr foarte mare de factori: numărul de noduri corecte care funcționează în apropiere (din punct de vedere al rețelei), „încălzirea” cache-urilor acestor noduri, dimensiunea blocurilor, tranzacțiilor, natura modificărilor, geografia rețelei, numărul de noduri și multe alte aspecte. Măsurarea complexă a metricalor de performanță în astfel de rețele este o sarcină complicată; este necesar să se evalueze simultan timpul de procesare a solicitărilor atât pe clienți, cât și pe peers (noduri blockchain). Problemele în orice mecanism p2p, eliminarea și cache-ul incorecte ale datelor, gestionarea ineficientă a listelor de peers activi și multe alte factori pot cauza întârzieri, afectând eficiența întregii rețele, iar acest bottleneck este cel mai greu de analizat, testat și interpretat.
Procesarea lanțului de blocuri și actualizarea bazei de date de stare
Cea mai importantă parte a muncii blocului de date este algoritmul de consens, aplicarea sa la noile blocuri obținute din rețea și procesarea tranzacțiilor cu înregistrarea rezultatelor în baza de date de stat. Adăugarea unui nou bloc în lanț și alegerea următorului bloc principal trebuie să funcționeze cât mai rapid. Totuși, în viața reală, „trebuie” nu înseamnă neapărat „funcționează”, și putem, de exemplu, să ne imaginăm o situație în care două lanțuri lungi concurente se schimbă constant între ele, modificând metadatele a mii de tranzacții din pool la fiecare schimbare și efectuând constant reveniri ale stării bazei de date de stat. Această etapă, în ceea ce privește determinarea punctului critic, este mai simplă decât stratul p2p de rețea, deoarece execuția tranzacțiilor și algoritmul de consens sunt strict deterministe, și este mai ușor să măsurăm ceva aici.
Cel mai important este să nu confundăm degradarea randomizată a performanței acestei etape cu problemele rețelei - nodurile răspund mai lent blocurilor și informațiilor despre lanțul principal și pentru clientul extern, aceasta poate părea o rețea lentă, deși problema se află complet în altă parte.
Pentru optimizarea performanței în această etapă, este util să colectăm și să monitorizăm metrici de la noduri, incluzându-le pe cele care vizează actualizarea bazei de date de stat: numărul de blocuri procesate pe nod, dimensiunea acestora, numărul de tranzacții, numărul de switch-uri între fork-uri, numărul de blocuri invalide, timpul de execuție al mașinii virtuale, timpul pentru fixații de date etc. Acest lucru va permite să nu confundăm problemele de rețea cu erorile din algoritmii de procesare a lanțului.
Mașina virtuală care procesează tranzacțiile poate fi o sursă valoroasă de informații capabile să optimizeze funcționarea blocului de date. Cantitatea de alocări de memorie, numărul de instrucțiuni read/write, și alte metrici care țin de eficiența execuției codului contractelor pot oferi multe informații utile dezvoltatorilor. În același timp, contractele inteligente sunt programe, ceea ce înseamnă că, teoretic, acestea pot consuma orice dintre resurse: cpu/memory/network/storage, astfel că procesarea tranzacțiilor este o etapă destul de indefinită, care, în plus, variază semnificativ atunci când se trece între versiuni și când se modifică codul contractelor. Prin urmare, metricile care vizează procesarea tranzacțiilor sunt, de asemenea, necesare pentru o optimizare eficientă a performanței blocului de date.
Notificarea clientului cu privire la activarea tranzacției în blockchain
Aceasta este etapa finală a obținerii serviciului blockchain de către client; spre deosebire de alte etape, aici nu există cheltuieli mari, dar este important să se țină cont de posibilitatea ca clientul să primească un răspuns voluminos de la nod (de exemplu, un smart contract care returnează un array de date). În orice caz, acest moment este cel mai important pentru cel care a întrebat „cât tps are blockchain-ul vostru?”, deoarece în acest moment se fixează timpul de obținere a serviciului.
În acest loc se va trimite cu siguranță timpul total pe care clientul l-a petrecut așteptând răspunsul de la blockchain; exact acest timp va fi așteptat de utilizator pentru a obține confirmarea în aplicația sa, iar optimizarea lui este principalul obiectiv al dezvoltatorilor.
Concluzie
Ca urmare, putem descrie tipurile de operațiuni efectuate în blockchain-uri și le putem împărți în mai multe categorii:
- transformări criptografice, construirea dovezilor
- rețele peer-to-peer, replicarea tranzacțiilor și blocurilor
- procesarea tranzacțiilor, executarea smart contractelor
- aplicarea modificărilor în blockchain la baza de date de stări, actualizarea datelor despre tranzacții și blocuri
- întrebări read-only la baza de date de stări, API-ul nodului blockchain, servicii de abonare
În general, cerințele tehnice pentru nodurile blockchain-urilor moderne sunt extrem de stricte — este nevoie de CPU-uri rapide pentru criptografie, de multă memorie RAM pentru a stoca și accesa rapid baza de date de stări, de o interacțiune de rețea care utilizează un număr mare de conexiuni deschise simultan, și de un stocare semnificativă. Aceste cerințe ridicate și abundența diverselor tipuri de operațiuni duc inevitabil la lipsa de resurse pentru noduri, iar, în acel caz, orice etapă discutată mai sus poate deveni o nouă gâtleu pentru performanța generală a rețelei.
Atunci când dezvoltați și evaluați performanța blockchain-urilor, va trebui să luați în considerare toate aceste aspecte. Pentru aceasta, trebuie să colectați și să analizați metricile simultan din clienți și nodurile rețelei, să căutați corelații între ele, să evaluați timpul de livrare a serviciilor pentru clienți, să considerați toate resursele de bază: cpu/memory/network/storage, și să înțelegeți cum sunt utilizate și cum se influențează reciproc. Toate acestea fac ca compararea vitezelor diferitelor blockchain-uri sub forma „cât TPS” să fie o sarcină extrem de nerecompensantă, deoarece există o cantitate uriașă de configurații și stării diferite. În sisteme mari centralizate, clustere de sute de servere, aceste probleme sunt, de asemenea, complexe și necesită colectarea unui număr mare de metrici diferite, dar în blockchain-uri, din cauza rețelelor p2p, mașinilor virtuale, contractelor inteligente, economiei interne, numărul de grade de libertate este mult mai mare, ceea ce face ca testarea chiar și pe câteva servere să fie neconcludentă și să arate doar valori extrem de aproximative, aproape fără legătură cu realitatea.
Prin urmare, atunci când dezvoltăm în nucleul blockchain-ului, pentru a evalua performanța și a răspunde la întrebarea „s-a îmbunătățit comparativ cu ultima oară”, folosim un software destul de complex, care orchestrează lansarea blockchain-ului cu zeci de noduri și lansarea automată a benchmark-ului și colectarea metricilor; fără aceste informații, este extrem de greu să debug-ăm protocoalele care funcționează cu mulți participanți.
Așadar, atunci când primiți întrebarea „cât TPS are blockchain-ul vostru?”, oferiți interlocutorului o ceașcă de ceai și întrebați-l dacă este pregătit să examineze zeci de grafice, precum și să asculte toate cele trei mari probleme de performanță ale blockchain-urilor și propunerile voastre pentru soluționarea acestora...
Sursa: habr.com
