Registre distribuït per a jocs de rodes: una experiència amb Hyperledger Fabric

Hola, treballo a l'equip del projecte DRD KP (registre de dades distribuïdes per al seguiment del cicle de vida dels jocs de rodes). Aquí vull compartir l'experiència del nostre equip en el desenvolupament d'una cadena de blocs empresarial per a aquest projecte sota les limitacions de la tecnologia. Parlaré principalment del teixit Hyperledger, però l'enfocament descrit aquí es pot extrapolar a qualsevol cadena de blocs autoritzada. L'objectiu final de la nostra investigació és preparar solucions de cadena de blocs empresarials perquè el producte final sigui agradable d'utilitzar i no massa difícil de mantenir.

No hi haurà descobriments, solucions inesperades i no es destacarà cap desenvolupament únic aquí (perquè no en tinc cap). Només vull compartir la meva modesta experiència, mostrar que "va ser possible" i, potser, llegir sobre les experiències d'altres persones de prendre decisions bones i no tan bones als comentaris.

Problema: les cadenes de blocs encara no s'escalen

Avui en dia, els esforços de molts desenvolupadors tenen com a objectiu fer de blockchain una tecnologia realment convenient, i no una bomba de rellotgeria en un bonic embolcall. Els canals estatals, la recuperació optimista, el plasma i la fragmentació probablement esdevindran habituals. Algun dia. O potser TON tornarà a posposar el llançament durant sis mesos i el proper Plasma Group deixarà d'existir. Podem creure en el següent full de ruta i llegir brillants llibres blancs a la nit, però aquí i ara hem de fer alguna cosa amb el que tenim. Fes la merda.

La tasca que s'ha plantejat al nostre equip en el projecte actual es veu en general així: hi ha molts subjectes, que arriben a diversos milers, que no volen construir relacions de confiança; Cal crear una solució a DLT que funcioni en ordinadors normals sense requisits especials de rendiment i que ofereixi una experiència d'usuari no pitjor que qualsevol sistema de comptabilitat centralitzat. La tecnologia que hi ha darrere de la solució ha de minimitzar la possibilitat de manipulació maliciosa de dades; per això, blockchain és aquí.

Els eslògans dels llibres blancs i dels mitjans de comunicació ens prometen que el proper desenvolupament ens permetrà fer milions de transaccions per segon. Què és realment?

Mainnet Ethereum funciona actualment a ~ 30 tps. Només per això, és difícil percebre-la com una cadena de blocs de cap manera adequada per a les necessitats corporatives. Entre les solucions autoritzades hi ha punts de referència que mostren 2000 tps (Quòrum) o 3000 tps (Teixit hiperàlgic, hi ha una mica menys a la publicació, però cal tenir en compte que el benchmark es va fer a l'antic motor de consens). Era un intent de processament radical del teixit, que no va donar els pitjors resultats, 20000 tps, però fins ara es tracta només de recerca acadèmica, a l'espera de la seva implantació estable. És poc probable que una corporació que es pugui permetre el luxe de mantenir un departament de desenvolupadors de blockchain suporti aquests indicadors. Però el problema no és només el rendiment, també hi ha la latència.

Latència

El retard des del moment en què s'inicia una transacció fins a la seva aprovació definitiva pel sistema depèn no només de la velocitat amb què el missatge passa per totes les etapes de validació i comanda, sinó també dels paràmetres de formació del bloc. Fins i tot si la nostra cadena de blocs ens permet comprometre's a una velocitat de 1000000 tps, però requereix 10 minuts per generar un bloc de 488 MB, ens serà més fàcil?

Fem una ullada més de prop al cicle de vida de la transacció a Hyperledger Fabric per entendre on es gasta el temps i com es relaciona amb els paràmetres de generació de blocs.

Registre distribuït per a jocs de rodes: una experiència amb Hyperledger Fabric
pres d'aquí: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) El client crea una transacció, l'envia als companys d'aprovació, aquests últims simulen la transacció (aplica els canvis fets pel codi de cadena a l'estat actual, però no es compromet amb el llibre major) i rep RWSet: noms de clau, versions i valors ​​tret de la col·lecció de CouchDB, (2) els endorsants envien un RWSet signat al client, (3) el client comprova la presència de signatures de tots els parells necessaris (endorsants) i després envia la transacció al servei de comandes. , o l'envia sense verificació (la comprovació encara es farà més tard), el servei de comandes forma un bloc i (4) l'envia a tots els companys, no només als endorsants; Els companys comproven que les versions de clau del conjunt de lectura coincideixen amb les versions de la base de dades, que tots els endorsants tenen signatures i, finalment, comprometen el bloqueig.

Però això no és tot. Les paraules "l'ordre forma un bloc" amaguen no només l'ordenació de les transaccions, sinó també 3 peticions de xarxa seqüencials del líder als seguidors i viceversa: el líder afegeix un missatge al registre, l'envia als seguidors, aquest últim l'afegeix. al seu registre, envia la confirmació de la rèplica correcta al líder, el líder envia el missatge, envia confirmació de confirmació als seguidors, els seguidors es comprometen. Com més petit sigui la mida i el temps de formació del bloc, més sovint el servei de comandes haurà d'establir consens. Hyperledger Fabric té dos paràmetres per a la formació de blocs: BatchTimeout - temps de formació del bloc i BatchSize - mida del bloc (el nombre de transaccions i la mida del bloc en bytes). Tan bon punt un dels paràmetres arriba al límit, s'allibera un nou bloc. Com més nodes d'ordre, més temps trigarà. Per tant, cal augmentar BatchTimeout i BatchSize. Però com que els RWSets estan versionats, com més gran sigui el bloc que fem, més gran serà la probabilitat de conflictes MVCC. A més, a mesura que augmenta BatchTimeout, la UX es degrada catastròficament. El següent esquema per resoldre aquests problemes em sembla raonable i obvi.

Com evitar esperar a la finalització del bloc i no perdre la capacitat de fer un seguiment de l'estat de la transacció

Com més llarg sigui el temps de formació i la mida del bloc, més gran serà el rendiment de la cadena de blocs. Una no segueix directament de l'altra, però cal recordar que establir consens a RAFT requereix tres peticions de xarxa del líder als seguidors i viceversa. Com més nodes d'ordre, més temps trigarà. Com més petit sigui la mida i el temps de formació del bloc, més interaccions hi ha. Com augmentar el temps de generació i la mida del bloc sense augmentar el temps de resposta del sistema per a l'usuari final?

En primer lloc, hem de resoldre d'alguna manera els conflictes MVCC causats per una mida de bloc gran, que pot incloure diferents RWSets amb la mateixa versió. Òbviament, pel costat del client (en relació a la xarxa blockchain, aquest podria ser el backend, i ho dic en serio), necessiteu Gestor de conflictes MVCC, que pot ser un servei independent o un decorador normal per sobre de la trucada que inicia la transacció amb la lògica de reintent.

Reintentar es pot implementar amb una estratègia exponencial, però aleshores la latència es degradarà de manera exponencial. Per tant, hauríeu d'utilitzar un reintent aleatori dins de certs límits petits o un de constant. Amb la mirada posada en possibles xocs en primera opció.

El següent pas és fer que la interacció del client amb el sistema sigui asíncrona perquè no s'esperi 15, 30 o 10000000 segons, que establirem com a BatchTimeout. Però al mateix temps, cal mantenir la capacitat de verificar que els canvis iniciats per la transacció estan/no estan registrats a la cadena de blocs.
Es pot utilitzar una base de dades per emmagatzemar l'estat de la transacció. L'opció més senzilla és CouchDB per la seva facilitat d'ús: la base de dades té una interfície d'usuari des de la caixa, una API REST i podeu configurar fàcilment la replicació i la fragmentació. Simplement podeu crear una col·lecció separada a la mateixa instància de CouchDB que utilitza Fabric per emmagatzemar el seu estat mundial. Hem d'emmagatzemar aquest tipus de documents.

{
 Status string // Статус транзакции: "pending", "done", "failed"
 TxID: string // ID транзакции
 Error: string // optional, сообщение об ошибке
}

Aquest document s'escriu a la base de dades abans que la transacció s'enviï als companys, l'identificador de l'entitat es retorna a l'usuari (el mateix ID s'utilitza com a clau) si es tracta d'una operació de creació i, a continuació, es mostren els camps Estat, TxID i Error. s'actualitza a mesura que es rep la informació rellevant dels companys.

Registre distribuït per a jocs de rodes: una experiència amb Hyperledger Fabric

En aquest esquema, l'usuari no espera que finalment es formi el bloc, mirant la roda que gira a la pantalla durant 10 segons, rep una resposta instantània del sistema i segueix treballant.

Hem escollit BoltDB per emmagatzemar els estats de les transaccions perquè necessitem estalviar memòria i no volem perdre temps en la interacció de la xarxa amb un servidor de bases de dades independent, especialment quan aquesta interacció es produeix mitjançant un protocol de text sense format. Per cert, ja sigui que utilitzeu CouchDB per implementar l'esquema descrit anteriorment o simplement per emmagatzemar l'estat mundial, en qualsevol cas té sentit optimitzar la manera com s'emmagatzemen les dades a CouchDB. Per defecte, a CouchDB, la mida dels nodes b-tree és de 1279 bytes, que és molt més petita que la mida del sector del disc, la qual cosa significa que tant la lectura com el reequilibri de l'arbre requeriran més accés físic al disc. La mida òptima correspon a l'estàndard Format avançat i té 4 kilobytes. Per optimitzar hem de configurar el paràmetre btree_chunk_size igual a 4096 al fitxer de configuració de CouchDB. Per a BoltDB, aquesta intervenció manual no obligatori.

Contrapressió: estratègia d'amortiment

Però hi pot haver molts missatges. Més del que el sistema pot gestionar, compartir recursos amb una dotzena de serveis més a més dels que es mostren al diagrama, i tot això hauria de funcionar perfectament fins i tot en màquines en què executar Intellij Idea seria extremadament tediós.

El problema de la diferent capacitat dels sistemes de comunicació, productor i consumidor, es resol de diferents maneres. A veure què podem fer.

Deixar caure: Podem afirmar que som capaços de processar com a màxim X transaccions en T segons. Es descarten totes les sol·licituds que superin aquest límit. Això és bastant senzill, però després us podeu oblidar de l'UX.

Controlador: el consumidor ha de tenir algun tipus d'interfície a través de la qual, en funció de la càrrega, pugui controlar el TPS del productor. No està malament, però imposa obligacions als desenvolupadors del client creant la càrrega per implementar aquesta interfície. Això és inacceptable per a nosaltres, ja que la cadena de blocs s'integrarà en el futur en un gran nombre de sistemes de llarga durada.

Buffering: en lloc d'intentar resistir el flux de dades d'entrada, podem emmagatzemar aquest flux i processar-lo a la velocitat requerida. Evidentment, aquesta és la millor solució si volem oferir una bona experiència d'usuari. Hem implementat la memòria intermèdia mitjançant una cua a RabbitMQ.

Registre distribuït per a jocs de rodes: una experiència amb Hyperledger Fabric

S'han afegit dues accions noves a l'esquema: (1) després que arribi una sol·licitud a l'API, un missatge amb els paràmetres necessaris per cridar una transacció es col·loca a la cua i el client rep un missatge que la transacció ha estat acceptada per el sistema, (2) el backend llegeix les dades a la velocitat especificada a la configuració des de la cua; inicia una transacció i actualitza les dades al magatzem d'estat.
Ara podeu augmentar el temps de formació i bloquejar la capacitat tant com vulgueu, amagant els retards a l'usuari.

Altres eines

Aquí no es va dir res sobre el codi en cadena, perquè, per regla general, no hi ha res a optimitzar. El codi en cadena ha de ser el més senzill i segur possible; això és tot el que se li requereix. El marc ens ajuda a escriure codi en cadena de manera senzilla i segura CCKit de S7 Techlab i analitzador estàtic reviure^CC.

A més, el nostre equip està desenvolupant un conjunt d'utilitats per fer que treballar amb Fabric sigui senzill i agradable: explorador de cadena de blocs, una utilitat per canvis automàtics de configuració de la xarxa (afegir/eliminar organitzacions, nodes RAFT), utilitat per a revocació de certificats i eliminació de la identitat. Si voleu col·laborar, sou benvingut.

Conclusió

Aquest enfocament us permet substituir fàcilment Hyperledger Fabric per Quorum, altres xarxes privades d'Ethereum (PoA o fins i tot PoW), reduir significativament el rendiment real, però al mateix temps mantenir una UX normal (tant per als usuaris del navegador com per als sistemes integrats). Quan substituïu Fabric per Ethereum a l'esquema, només haureu de canviar la lògica del servei/decorador de reintents de processar conflictes MVCC a increment i reenviament de nonce atòmic. La memòria intermèdia i l'emmagatzematge d'estat van permetre desacoblar el temps de resposta del temps de formació del bloc. Ara podeu afegir milers de nodes de comanda i no tenir por que els blocs es formen massa sovint i carregar el servei de comandes.

Bàsicament, això és tot el que volia compartir. M'alegraré si això ajuda algú en la seva feina.

Font: www.habr.com

Afegeix comentari