Distributed Ledger for Wheelset: Erfarenhet av Hyperledger Fabric

Hej, jag arbetar i teamet för DRD KP-projektet (distribuerat dataregister för övervakning av livscykeln för hjuluppsättningar). Här vill jag dela med mig av vårt teams erfarenhet av att utveckla en företagsblockkedja för detta projekt under teknologins begränsningar. Jag kommer mest att prata om Hyperledger Fabric, men tillvägagångssättet som beskrivs här kan extrapoleras till vilken blockchain som helst. Det slutliga målet med vår forskning är att förbereda blockchain-lösningar för företag så att slutprodukten är trevlig att använda och inte alltför svår att underhålla.

Det kommer inga upptäckter, oväntade lösningar och inga unika utvecklingar kommer att lyftas fram här (eftersom jag inte har några). Jag vill bara dela med mig av min blygsamma erfarenhet, visa att "det var möjligt" och kanske läsa om andras erfarenheter av att fatta bra och mindre bra beslut i kommentarerna.

Problem: Blockkedjor skalas inte ännu

Idag är insatserna från många utvecklare inriktade på att göra blockchain till en verkligt bekväm teknik, och inte en tidsinställd bomb i en vacker omslag. Statliga kanaler, optimistisk rollup, plasma och sharding kommer förmodligen att bli vardag. Någon dag. Eller kanske TON återigen kommer att skjuta upp lanseringen i sex månader, och nästa Plasma Group kommer att upphöra att existera. Vi kan tro på nästa färdplan och läsa lysande vitböcker på natten, men här och nu måste vi göra något med det vi har. Gör skit gjort.

Uppgiften för vårt team i det aktuella projektet ser generellt ut så här: det finns många ämnen, som når flera tusen, som inte vill bygga relationer på förtroende; Det är nödvändigt att bygga en lösning på DLT som fungerar på vanliga datorer utan speciella prestandakrav och ger en användarupplevelse som inte är sämre än alla centraliserade redovisningssystem. Tekniken bakom lösningen måste minimera möjligheten för skadlig manipulation av data – det är därför blockchain är här.

Slogans från whitepapers och media lovar oss att nästa utveckling kommer att tillåta oss att göra miljontals transaktioner per sekund. Vad är det egentligen?

Mainnet Ethereum körs för närvarande på ~30 tps. Bara på grund av detta är det svårt att uppfatta det som blockchain på något sätt lämpligt för företagens behov. Bland tillåtna lösningar finns riktmärken som visar 2000 tps (Quorum) eller 3000 tps (Hyperledger Fabric, det finns lite mindre i publikationen, men du måste ta hänsyn till att riktmärket utfördes på den gamla konsensusmotorn). Var ett försök till radikal tygbearbetning, som inte gav de sämsta resultaten, 20000 XNUMX tps, men än så länge är detta bara akademisk forskning som väntar på dess stabila implementering. Det är osannolikt att ett företag som har råd att upprätthålla en avdelning med blockchain-utvecklare kommer att stå ut med sådana indikatorer. Men problemet är inte bara genomströmning, det finns också latens.

Latens

Fördröjningen från det att en transaktion initieras till dess slutgiltiga godkännande av systemet beror inte bara på den hastighet med vilken meddelandet passerar genom alla stadier av validering och beställning, utan också på blockbildningsparametrarna. Även om vår blockchain tillåter oss att begå med en hastighet av 1000000 10 488 tps, men kräver XNUMX minuter för att generera ett XNUMX MB block, blir det lättare för oss?

Låt oss ta en närmare titt på transaktionslivscykeln i Hyperledger Fabric för att förstå var tid spenderas och hur det relaterar till blockgenereringsparametrar.

Distributed Ledger for Wheelset: Erfarenhet av Hyperledger Fabric
hämtad härifrån: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) Klienten skapar en transaktion, skickar den till stödjande peers, de senare simulerar transaktionen (tillämpa ändringar gjorda med kedjekod till det aktuella tillståndet, men förbinder sig inte till reskontran) och tar emot RWSet - nyckelnamn, versioner och värden ​​hämtad från samlingen i CouchDB, ( 2) endossörer skickar tillbaka ett signerat RWSet till klienten, (3) klienten kontrollerar antingen om det finns signaturer från alla nödvändiga peers (endorsers) och skickar sedan transaktionen till beställningstjänsten , eller skickar den utan verifiering (kontrollen kommer fortfarande att ske senare), bildar beställningstjänsten ett block och ( 4) skickar tillbaka till alla peers, inte bara endorsers; Peers kontrollerar att nyckelversionerna i lässetet stämmer överens med versionerna i databasen, att alla endorsers har signaturer och slutligen commit blocket.

Men det är inte allt. Orden "beställaren bildar ett block" döljer inte bara beställningen av transaktioner, utan också 3 sekventiella nätverksbegäranden från ledaren till följare och tillbaka: ledaren lägger till ett meddelande i loggen, skickar det till följare, den senare lägger till det till deras logg, skickar bekräftelse på lyckad replikering till ledaren, ledaren begår meddelandet, skickar bekräftelse till följare, följare begår. Ju mindre storlek och tid för blockbildning, desto oftare måste beställningstjänsten etablera konsensus. Hyperledger Fabric har två parametrar för blockbildning: BatchTimeout - blockbildningstid och BatchSize - blockstorlek (antal transaktioner och storleken på själva blocket i byte). Så snart en av parametrarna når gränsen släpps ett nytt block. Ju fler ordernoder, desto längre tid tar detta. Därför måste du öka BatchTimeout och BatchSize. Men eftersom RWSets är versionerade, desto större block vi gör, desto högre är sannolikheten för MVCC-konflikter. Dessutom, när BatchTimeout ökar, försämras UX katastrofalt. Följande schema för att lösa dessa problem verkar rimligt och självklart för mig.

Hur man undviker att vänta på slutförande av block och inte förlorar möjligheten att spåra transaktionsstatus

Ju längre bildningstid och blockstorlek, desto högre genomströmning av blockkedjan. Det ena följer inte direkt av det andra, men man bör komma ihåg att upprättande av konsensus i RAFT kräver tre nätverksbegäranden från ledaren till anhängarna och tillbaka. Ju fler ordernoder, desto längre tid tar detta. Ju mindre storlek och tid för blockbildning, desto fler sådana interaktioner finns det. Hur ökar man genereringstiden och blockstorleken utan att öka systemets svarstid för slutanvändaren?

Först måste vi på något sätt lösa MVCC-konflikter orsakade av en stor blockstorlek, som kan inkludera olika RWSets med samma version. Uppenbarligen, på klientsidan (i förhållande till blockchain-nätverket, kan detta mycket väl vara backend, och jag menar det) du behöver MVCC konflikthanterare, som kan vara antingen en separat tjänst eller en vanlig dekoratör ovanför samtalet som initierar transaktionen med logik för att försöka igen.

Försök igen kan implementeras med en exponentiell strategi, men då kommer latensen att försämras lika exponentiellt. Så du bör använda antingen ett randomiserat försök inom vissa små gränser, eller ett konstant. Med ett öga på eventuella kollisioner i det första alternativet.

Nästa steg är att göra klientens interaktion med systemet asynkron så att den inte väntar i 15, 30 eller 10000000 sekunder, vilket vi kommer att ställa in som BatchTimeout. Men samtidigt är det nödvändigt att behålla möjligheten att verifiera att de förändringar som initieras av transaktionen är/inte registreras i blockkedjan.
En databas kan användas för att lagra transaktionsstatus. Det enklaste alternativet är CouchDB på grund av dess enkla användning: databasen har ett användargränssnitt ur lådan, ett REST API, och du kan enkelt ställa in replikering och sönderdelning för den. Du kan helt enkelt skapa en separat samling i samma CouchDB-instans som använder Fabric för att lagra sin världstillstånd. Vi måste lagra den här typen av dokument.

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

Detta dokument skrivs till databasen innan transaktionen skickas till peers, enhets-ID:t returneras till användaren (samma ID används som nyckel) om detta är en skapande operation, och sedan är fälten Status, TxID och Error uppdateras när relevant information erhålls från kollegor.

Distributed Ledger for Wheelset: Erfarenhet av Hyperledger Fabric

I detta schema väntar användaren inte på att blocket äntligen ska bildas, tittar på det snurrande hjulet på skärmen i 10 sekunder, han får ett omedelbart svar från systemet och fortsätter att arbeta.

Vi valde BoltDB för att lagra transaktionsstatus eftersom vi behöver spara minne och inte vill slösa tid på nätverksinteraktion med en separat databasserver, speciellt när denna interaktion sker med ett vanlig textprotokoll. Förresten, oavsett om du använder CouchDB för att implementera schemat som beskrivs ovan eller helt enkelt för att lagra världstillstånd, är det i alla fall vettigt att optimera hur data lagras i CouchDB. Som standard, i CouchDB, är storleken på b-tree noder 1279 byte, vilket är mycket mindre än sektorstorleken på disken, vilket innebär att både läsning och ombalansering av trädet kommer att kräva mer fysisk åtkomst till disken. Den optimala storleken motsvarar standarden Avancerat format och är 4 kilobyte. För att optimera måste vi ställa in parametern btree_chunk_size lika med 4096 i CouchDB-konfigurationsfilen. För BoltDB sådana manuella ingrepp inte nödvändigt.

Mottryck: buffertstrategi

Men det kan vara många meddelanden. Mer än systemet kan hantera, delar resurser med ett dussin andra tjänster förutom de som visas i diagrammet - och allt detta borde fungera felfritt även på maskiner där det skulle vara extremt tråkigt att köra Intellij Idea.

Problemet med olika kapacitet hos kommunicerande system, producent och konsument, löses på olika sätt. Låt oss se vad vi kan göra.

Drop: Vi kan hävda att vi kan behandla som mest X transaktioner på T sekunder. Alla förfrågningar som överskrider denna gräns ignoreras. Detta är ganska enkelt, men då kan du glömma UX.

Styra: konsumenten måste ha något slags gränssnitt genom vilket han, beroende på belastningen, kan styra tillverkarens TPS. Inte dåligt, men det ålägger skyldigheter för utvecklarna av klienten som skapar belastningen för att implementera detta gränssnitt. Detta är oacceptabelt för oss, eftersom blockchain i framtiden kommer att integreras i ett stort antal sedan länge existerande system.

buffring: Istället för att försöka motstå indataströmmen kan vi buffra denna ström och bearbeta den med den hastighet som krävs. Självklart är detta den bästa lösningen om vi vill ge en bra användarupplevelse. Vi implementerade bufferten med en kö i RabbitMQ.

Distributed Ledger for Wheelset: Erfarenhet av Hyperledger Fabric

Två nya åtgärder har lagts till i schemat: (1) efter att en förfrågan till API anländer placeras ett meddelande med de parametrar som krävs för att anropa en transaktion i kön och klienten får ett meddelande om att transaktionen har accepterats av systemet, (2) backend läser data med den hastighet som anges i konfigurationen från kön; initierar en transaktion och uppdaterar data i statusarkivet.
Nu kan du öka formationstiden och blockera kapaciteten så mycket du vill, och dölja förseningar från användaren.

Andra verktyg

Inget sades här om kedjekod, eftersom det som regel inte finns något att optimera i den. Chaincode ska vara så enkel och säker som möjligt – det är allt som krävs av det. Ramverket hjälper oss att skriva kedjekod enkelt och säkert CCKit från S7 Techlab och statisk analysator återuppliva^CC.

Dessutom utvecklar vårt team en uppsättning verktyg för att göra det enkelt och roligt att arbeta med Fabric: blockchain explorer, ett verktyg för automatiska nätverkskonfigurationsändringar (lägga till/ta bort organisationer, RAFT-noder), verktyg för återkallelse av certifikat och borttagande av identitet. Vill du bidra är du välkommen.

Slutsats

Detta tillvägagångssätt gör att du enkelt kan ersätta Hyperledger Fabric med Quorum, andra privata Ethereum-nätverk (PoA eller till och med PoW), minska den faktiska genomströmningen avsevärt, men samtidigt behålla normal UX (både för användare i webbläsaren och för integrerade system). När du ersätter Fabric med Ethereum i schemat behöver du bara ändra logiken för försökstjänsten/dekoratören från att bearbeta MVCC-konflikter till atomic nonce-inkrement och återsändning. Buffring och statuslagring gjorde det möjligt att frikoppla svarstiden från blockbildningstiden. Nu kan du lägga till tusentals beställningsnoder och inte vara rädd för att block bildas för ofta och laddar beställningstjänsten.

I grund och botten var det allt jag ville dela med mig av. Jag blir glad om detta hjälper någon i deras arbete.

Källa: will.com

Lägg en kommentar