Gedistribueerde Ledger voor wielsets: ervaring met Hyperledger Fabric

Hallo, ik werk in het team van het DRD KP-project (gedistribueerde dataregistratie voor het monitoren van de levenscyclus van wielstellen). Hier wil ik de ervaring van ons team delen met het ontwikkelen van een enterprise blockchain voor dit project onder de beperkingen van de technologie. Ik zal het vooral hebben over de Hyperledger Fabric, maar de hier beschreven aanpak kan worden geëxtrapoleerd naar elke goedgekeurde blockchain. Het uiteindelijke doel van ons onderzoek is om enterprise blockchain-oplossingen zo voor te bereiden dat het eindproduct prettig in gebruik is en niet te moeilijk te onderhouden.

Er zullen geen ontdekkingen zijn, onverwachte oplossingen, en er zullen geen unieke ontwikkelingen worden belicht (omdat ik die niet heb). Ik wil gewoon mijn bescheiden ervaring delen, laten zien dat “het mogelijk was” en misschien in de reacties lezen over de ervaringen van anderen met het nemen van goede en minder goede beslissingen.

Probleem: Blockchains zijn nog niet schaalbaar

Tegenwoordig zijn de inspanningen van veel ontwikkelaars erop gericht om van blockchain een echt handige technologie te maken, en niet een tijdbom in een mooie verpakking. Staatszenders, optimistische roll-up, plasma en sharding zullen waarschijnlijk gemeengoed worden. Op een dag. Of misschien stelt TON de lancering opnieuw een half jaar uit en houdt de volgende Plasmagroep op te bestaan. We kunnen in de volgende routekaart geloven en 's nachts briljante whitepapers lezen, maar hier en nu moeten we iets doen met wat we hebben. Krijg dingen gedaan.

De taak die voor ons team in het huidige project is gesteld, ziet er in het algemeen als volgt uit: er zijn veel proefpersonen, die enkele duizenden bereiken, die geen relaties op basis van vertrouwen willen opbouwen; Het is noodzakelijk om een ​​oplossing op DLT te bouwen die op gewone pc's werkt zonder speciale prestatie-eisen en een gebruikerservaring biedt die niet slechter is dan welke gecentraliseerde boekhoudsystemen dan ook. De technologie achter de oplossing moet de mogelijkheid van kwaadwillige manipulatie van gegevens minimaliseren - daarom is blockchain er.

Slogans uit whitepapers en de media beloven ons dat de volgende ontwikkeling ons in staat zal stellen miljoenen transacties per seconde te doen. Wat is het werkelijk?

Mainnet Ethereum draait momenteel op ~30 tps. Alleen al hierdoor is het moeilijk om het als blockchain te beschouwen op een manier die geschikt is voor bedrijfsbehoeften. Onder de toegestane oplossingen bevinden zich benchmarks die 2000 tps tonen (quorum) of 3000 tps (Hyperledger Fabric, er staat iets minder in de publicatie, maar je moet er rekening mee houden dat de benchmark is uitgevoerd op de oude consensusengine). Was een poging tot radicale Fabric-verwerking, wat niet de slechtste resultaten opleverde, 20000 tps, maar tot nu toe is dit slechts academisch onderzoek, wachtend op een stabiele implementatie. Het is onwaarschijnlijk dat een bedrijf dat het zich kan veroorloven een afdeling blockchain-ontwikkelaars in stand te houden, dergelijke indicatoren zal tolereren. Maar het probleem is niet alleen de doorvoer, er is ook latentie.

Wachttijd

De vertraging vanaf het moment dat een transactie wordt geïnitieerd tot de uiteindelijke goedkeuring door het systeem hangt niet alleen af ​​van de snelheid waarmee het bericht alle stadia van validatie en bestelling doorloopt, maar ook van de blokvormingsparameters. Zelfs als onze blockchain ons in staat stelt om met een snelheid van 1000000 tps te committen, maar 10 minuten nodig heeft om een ​​blok van 488 MB te genereren, zal het dan gemakkelijker voor ons worden?

Laten we de transactielevenscyclus in Hyperledger Fabric eens nader bekijken om te begrijpen waar tijd wordt besteed en hoe deze zich verhoudt tot parameters voor het genereren van blokken.

Gedistribueerde Ledger voor wielsets: ervaring met Hyperledger Fabric
vanaf hier genomen: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) De client creëert een transactie, stuurt deze naar peers die de transactie onderschrijven, deze simuleren de transactie (pas wijzigingen aangebracht door chaincode toe op de huidige status, maar verbinden zich niet tot het grootboek) en ontvangen RWSet - sleutelnamen, versies en waarden ​genomen uit de collectie in CouchDB, (2) endorsers sturen een ondertekende RWSet terug naar de klant, (3) de client controleert of de handtekeningen van alle benodigde peers (endorsers) aanwezig zijn, en stuurt vervolgens de transactie naar de bestellende service , of verzendt het zonder verificatie (de controle vindt later nog plaats), de bestelservice vormt een blok en (4) stuurt het terug naar alle peers, niet alleen naar endorsers; peers controleren of de sleutelversies in de leesset overeenkomen met de versies in de database, of alle endorsers handtekeningen hebben, en committen uiteindelijk het blok.

Maar dat is niet alles. De woorden “besteller vormt een blok” verbergen niet alleen de volgorde van transacties, maar ook 3 opeenvolgende netwerkverzoeken van de leider naar de volgers en terug: de leider voegt een bericht toe aan het logboek, stuurt het naar de volgers, de laatste voegt het toe naar hun log, stuurt een bevestiging van succesvolle replicatie naar de leider, de leider commit het bericht, stuurt een commit-bevestiging naar volgers, volgers commit. Hoe kleiner de omvang en tijd van blokvorming, hoe vaker de besteldienst consensus zal moeten bereiken. Hyperledger Fabric heeft twee parameters voor blokvorming: BatchTimeout - blokvormingstijd en BatchSize - blokgrootte (het aantal transacties en de grootte van het blok zelf in bytes). Zodra een van de parameters de limiet bereikt, wordt een nieuw blok vrijgegeven. Hoe meer orderknooppunten, hoe langer dit zal duren. Daarom moet u BatchTimeout en BatchSize verhogen. Maar aangezien RWSets een versieversie hebben, geldt: hoe groter het blok dat we maken, hoe groter de kans op MVCC-conflicten. Bovendien gaat de UX catastrofaal achteruit naarmate BatchTimeout toeneemt. Het volgende schema voor het oplossen van deze problemen lijkt mij redelijk en voor de hand liggend.

Hoe u kunt voorkomen dat u moet wachten op blokafronding en hoe u de mogelijkheid om de transactiestatus te volgen niet verliest

Hoe langer de formatietijd en blokgrootte, hoe hoger de doorvoer van de blockchain. Het een volgt niet direct uit het ander, maar we moeten niet vergeten dat het tot stand brengen van consensus in RAFT drie netwerkverzoeken vereist van de leider naar de volgers en terug. Hoe meer orderknooppunten, hoe langer dit zal duren. Hoe kleiner de omvang en tijd van blokvorming, hoe meer van dergelijke interacties er zijn. Hoe kan ik de generatietijd en blokgrootte vergroten zonder de systeemresponstijd voor de eindgebruiker te vergroten?

Ten eerste moeten we op de een of andere manier MVCC-conflicten oplossen die worden veroorzaakt door een grote blokgrootte, die verschillende RWSets met dezelfde versie kunnen bevatten. Het is duidelijk dat je aan de clientkant (met betrekking tot het blockchain-netwerk zou dit wel eens de backend kunnen zijn, en ik meen het) nodig heb MVCC-conflictbehandelaar, wat een afzonderlijke service kan zijn of een reguliere decorateur boven de oproep die de transactie initieert met logica voor opnieuw proberen.

Opnieuw proberen kan worden geïmplementeerd met een exponentiële strategie, maar de latentie zal dan net zo exponentieel afnemen. U moet dus een willekeurige nieuwe poging binnen bepaalde kleine limieten gebruiken, of een constante poging. Met het oog op mogelijke botsingen bij de eerste optie.

De volgende stap is om de interactie van de klant met het systeem asynchroon te maken, zodat deze niet 15, 30 of 10000000 seconden wacht, wat we zullen instellen als BatchTimeout. Maar tegelijkertijd is het noodzakelijk om de mogelijkheid te behouden om te verifiëren dat de door de transactie geïnitieerde wijzigingen wel/niet in de blockchain worden vastgelegd.
Een database kan worden gebruikt om de transactiestatus op te slaan. De eenvoudigste optie is CouchDB vanwege het gebruiksgemak: de database heeft een kant-en-klare gebruikersinterface, een REST API, en u kunt er eenvoudig replicatie en sharding voor instellen. U kunt eenvoudigweg een afzonderlijke verzameling maken in dezelfde CouchDB-instantie die Fabric gebruikt om de wereldstatus op te slaan. Dit soort documenten moeten we opslaan.

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

Dit document wordt naar de database geschreven voordat de transactie naar peers wordt verzonden, de entiteits-ID wordt teruggestuurd naar de gebruiker (dezelfde ID wordt gebruikt als sleutel) als dit een aanmaakbewerking is, en vervolgens worden de velden Status, TxID en Error weergegeven. bijgewerkt zodra relevante informatie wordt ontvangen van collega's.

Gedistribueerde Ledger voor wielsets: ervaring met Hyperledger Fabric

In dit schema wacht de gebruiker niet tot het blok eindelijk is gevormd, maar kijkt hij 10 seconden naar het draaiende wiel op het scherm, ontvangt hij onmiddellijk een reactie van het systeem en blijft hij werken.

We hebben voor BoltDB gekozen om transactiestatussen op te slaan, omdat we geheugen moeten besparen en geen tijd willen verspillen aan netwerkinteractie met een aparte databaseserver, vooral wanneer deze interactie plaatsvindt met behulp van een platte-tekstprotocol. Trouwens, of je CouchDB nu gebruikt om het hierboven beschreven schema te implementeren of gewoon om de wereldstatus op te slaan, het is in ieder geval zinvol om de manier te optimaliseren waarop gegevens in CouchDB worden opgeslagen. Standaard is de grootte van b-tree-knooppunten in CouchDB 1279 bytes, wat veel kleiner is dan de sectorgrootte op de schijf, wat betekent dat zowel het lezen als het opnieuw in evenwicht brengen van de boom meer fysieke toegang tot de schijf vereist. De optimale maat komt overeen met de standaard Geavanceerd formaat en is 4 kilobyte. Om te optimaliseren moeten we de parameter instellen btree_chunk_size gelijk aan 4096 in het CouchDB-configuratiebestand. Voor BoltDB een dergelijke handmatige tussenkomst Het is niet noodzakelijk.

Tegendruk: bufferstrategie

Maar er kunnen veel berichten zijn. Meer dan het systeem aankan, is het delen van bronnen met een tiental andere services naast de services die in het diagram worden weergegeven - en dit alles zou feilloos moeten werken, zelfs op machines waarop het draaien van Intellij Idea uiterst vervelend zou zijn.

Het probleem van de verschillende capaciteiten van communicerende systemen, producent en consument, wordt op verschillende manieren opgelost. Laten we eens kijken wat we kunnen doen.

dropping: We kunnen beweren dat we maximaal X transacties in T seconden kunnen verwerken. Alle verzoeken die deze limiet overschrijden, worden genegeerd. Dit is vrij eenvoudig, maar dan kun je UX vergeten.

Controlling: de consument moet over een soort interface beschikken waarmee hij, afhankelijk van de belasting, de TPS van de producent kan controleren. Niet slecht, maar het legt verplichtingen op aan de ontwikkelaars van de client die de belasting creëert om deze interface te implementeren. Voor ons is dit onaanvaardbaar, omdat blockchain in de toekomst geïntegreerd zal worden in een groot aantal reeds lang bestaande systemen.

Bufferen: In plaats van te proberen weerstand te bieden aan de invoergegevensstroom, kunnen we deze stroom bufferen en met de vereiste snelheid verwerken. Uiteraard is dit de beste oplossing als we een goede gebruikerservaring willen bieden. We hebben de buffer geïmplementeerd met behulp van een wachtrij in RabbitMQ.

Gedistribueerde Ledger voor wielsets: ervaring met Hyperledger Fabric

Er zijn twee nieuwe acties aan het schema toegevoegd: (1) nadat een verzoek aan de API binnenkomt, wordt een bericht met de parameters die nodig zijn om een ​​transactie aan te roepen in de wachtrij geplaatst en ontvangt de cliënt een bericht dat de transactie is geaccepteerd door het systeem, (2) de backend leest de gegevens met de snelheid die is opgegeven in de configuratie uit de wachtrij; initieert een transactie en werkt gegevens bij in het statusarchief.
Nu kunt u de formatietijd verhogen en de capaciteit zoveel blokkeren als u wilt, waardoor vertragingen voor de gebruiker worden verborgen.

Andere hulpmiddelen

Er werd hier niets gezegd over chaincode, omdat er in de regel niets in valt te optimaliseren. Chaincode moet zo eenvoudig en veilig mogelijk zijn; dat is alles wat ervan wordt verlangd. Het raamwerk helpt ons om eenvoudig en veilig ketencode te schrijven CCKit van S7 Techlab en statische analysator herleven^CC.

Daarnaast ontwikkelt ons team een ​​reeks hulpprogramma's om het werken met Fabric eenvoudig en plezierig te maken: blockchain-verkenner, een hulpprogramma voor automatische wijzigingen in de netwerkconfiguratie (organisaties toevoegen/verwijderen, RAFT-knooppunten), hulpprogramma voor intrekking van certificaten en verwijdering van identiteit. Als u een bijdrage wilt leveren, bent u van harte welkom.

Conclusie

Met deze aanpak kunt u Hyperledger Fabric eenvoudig vervangen door Quorum, andere particuliere Ethereum-netwerken (PoA of zelfs PoW), de daadwerkelijke doorvoer aanzienlijk verminderen, maar tegelijkertijd de normale UX behouden (zowel voor gebruikers in de browser als voor geïntegreerde systemen). Wanneer u Fabric in het schema vervangt door Ethereum, hoeft u alleen de logica van de service/decorator voor opnieuw proberen te wijzigen, van het verwerken van MVCC-conflicten naar atomaire nonce-verhoging en opnieuw verzenden. Buffering en statusopslag maakten het mogelijk om de responstijd te ontkoppelen van de blokvormingstijd. Nu kunt u duizenden bestelknooppunten toevoegen en hoeft u niet bang te zijn dat er te vaak blokken worden gevormd en de bestelservice worden geladen.

Eigenlijk is dat alles wat ik wilde delen. Ik zal blij zijn als dit iemand helpt bij zijn werk.

Bron: www.habr.com

Voeg een reactie