Verteilte Registrierung für Radsätze: Eine Erfahrung mit Hyperledger Fabric

Hallo, ich arbeite im Team des DRD KP-Projekts (verteiltes Datenregister zur Überwachung des Lebenszyklus von Radsätzen). Hier möchte ich die Erfahrungen unseres Teams bei der Entwicklung einer Unternehmens-Blockchain für dieses Projekt unter den Einschränkungen der Technologie teilen. Im Wesentlichen werde ich über Hyperledger Fabric sprechen, aber der hier beschriebene Ansatz kann auf jede autorisierte Blockchain übertragen werden. Das ultimative Ziel unserer Forschung ist es, Blockchain-Lösungen für Unternehmen so vorzubereiten, dass das Endprodukt angenehm zu verwenden und nicht zu schwierig zu warten ist.

Es wird hier keine Entdeckungen, unerwarteten Lösungen und keine einzigartigen Entwicklungen geben (weil ich sie nicht habe). Ich möchte nur meine bescheidene Erfahrung teilen, zeigen, dass „es möglich war“ und vielleicht in den Kommentaren über die Erfahrungen anderer lesen, gute und weniger gute Entscheidungen zu treffen.

Problem: Blockchains sind noch nicht skalierbar

Heutzutage zielen die Bemühungen vieler Entwickler darauf ab, die Blockchain zu einer wirklich praktischen Technologie zu machen und nicht zu einer tickenden Zeitbombe in einer schönen Hülle. Staatliche Kanäle, optimistisches Rollup, Plasma und Sharding könnten alltäglich werden. Irgendwann mal. Oder vielleicht verschiebt TON den Start erneut um sechs Monate und die nächste Plasma Group wird nicht mehr existieren. Wir können an eine andere Roadmap glauben und nachts brillante Weißbücher lesen, aber hier und jetzt müssen wir etwas mit dem tun, was wir haben. Mach die Scheiße fertig.

Die Aufgabe unseres Teams im aktuellen Projekt sieht im Großen und Ganzen so aus: Es gibt viele Probanden, die mehrere Tausend Menschen erreichen, die keine vertrauensvollen Beziehungen aufbauen wollen; Es ist notwendig, auf DLT eine Lösung aufzubauen, die auf normalen PCs ohne besondere Leistungsanforderungen funktioniert und ein Benutzererlebnis bietet, das nicht schlechter ist als jedes zentralisierte Buchhaltungssystem. Die Technologie hinter der Lösung soll die Möglichkeit einer böswilligen Datenmanipulation minimieren – deshalb gibt es hier die Blockchain.

Slogans aus Whitepapers und Medien versprechen uns, dass die nächste Entwicklung Millionen von Transaktionen pro Sekunde ermöglichen wird. Was ist es wirklich?

Mainnet Ethereum läuft derzeit mit ~30 tps. Allein aus diesem Grund ist es schwierig, sie als Blockchain wahrzunehmen, die in irgendeiner Weise für Unternehmensbedürfnisse geeignet ist. Unter den zugelassenen Lösungen sind Benchmarks bekannt, die 2000 tps anzeigen (Quorum) oder 3000 tps (Hyperledger Stoff, es gibt etwas weniger in der Veröffentlichung, aber bedenken Sie, dass der Benchmark auf der alten Konsens-Engine durchgeführt wurde). War ein Versuch, Fabric radikal zu überarbeiten, das nicht die schlechtesten Ergebnisse lieferte, 20000 tps, aber bisher sind dies nur akademische Studien, die auf ihre stabile Umsetzung warten. Es ist unwahrscheinlich, dass ein Unternehmen, das es sich leisten kann, eine Abteilung für Blockchain-Entwickler zu unterhalten, sich mit solchen Indikatoren abfinden wird. Das Problem liegt jedoch nicht nur im Durchsatz, sondern auch in der Latenz.

Latency

Die Verzögerung vom Moment der Initiierung einer Transaktion bis zu ihrer endgültigen Genehmigung durch das System hängt nicht nur von der Geschwindigkeit ab, mit der die Nachricht alle Phasen der Validierung und Bestellung durchläuft, sondern auch von den Blockbildungsparametern. Selbst wenn unsere Blockchain es uns ermöglicht, 1000000 tps zu begehen, die Bildung eines 10-MB-Blocks jedoch 488 Minuten dauert, wird es für uns dann einfacher?

Schauen wir uns den Lebenszyklus einer Transaktion in Hyperledger Fabric genauer an, um zu verstehen, was Zeit braucht und wie sie mit den Blockbildungsparametern zusammenhängt.

Verteilte Registrierung für Radsätze: Eine Erfahrung mit Hyperledger Fabric
von hier genommen: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) Der Client bildet eine Transaktion, sendet sie an unterstützende Peers, diese simulieren die Transaktion (wenden die durch den Chaincode vorgenommenen Änderungen auf den aktuellen Status an, übermitteln sie jedoch nicht an das Hauptbuch) und erhalten RWSet – Schlüsselnamen, Versionen usw Werte aus der Sammlung in CouchDB, (2) Endorser senden das signierte RWSet zurück an den Client, (3) der Client prüft entweder die Signaturen aller erforderlichen Peers (Endorser) und sendet dann die Transaktion an den Bestelldienst , oder sendet es ohne Verifizierung (die Verifizierung findet noch später statt), der Bestelldienst bildet einen Block und (4) sendet es an alle Peers zurück, nicht nur an Endorser; Die Peers überprüfen, ob die Versionen der Schlüssel im Lesesatz mit den Versionen in der Datenbank und den Signaturen aller Endorser übereinstimmen, und schreiben schließlich den Block fest.

Aber das ist noch nicht alles. Hinter den Worten „Orderer bildet einen Block“ verbirgt sich nicht nur die Reihenfolge der Transaktionen, sondern auch drei aufeinanderfolgende Netzwerkanfragen vom Anführer an die Follower und zurück: Der Anführer fügt eine Nachricht zum Protokoll hinzu, sendet sie an die Follower, letztere fügen hinzu ihr Protokoll, sendet eine Bestätigung der erfolgreichen Replikation an den Anführer, der Anführer schreibt die Nachricht fest, sendet eine Festschreibungsbestätigung an die Follower, die Follower verpflichten sich. Je kleiner Größe und Zeit der Blockbildung sind, desto häufiger muss der anordnende Dienst einen Konsens herstellen. Hyperledger Fabric verfügt über zwei Blockbildungsparameter: BatchTimeout – Blockbildungszeit und BatchSize – Blockgröße (die Anzahl der Transaktionen und die Größe des Blocks selbst in Bytes). Sobald einer der Parameter das Limit erreicht, wird ein neuer Block ausgegeben. Je mehr Orderer-Knoten vorhanden sind, desto länger dauert dies. Daher müssen Sie BatchTimeout und BatchSize erhöhen. Da RWSets jedoch versioniert sind, ist die Wahrscheinlichkeit von MVCC-Konflikten umso höher, je größer wir den Block machen. Darüber hinaus verschlechtert sich die Benutzererfahrung mit zunehmendem BatchTimeout katastrophal. Das folgende Schema zur Lösung dieser Probleme erscheint mir vernünftig und offensichtlich.

So vermeiden Sie das Warten auf die Block-Finalisierung und verlieren den Überblick über den Transaktionsstatus

Je länger die Entstehungszeit und die Blockgröße, desto höher ist der Durchsatz der Blockchain. Das eine folgt nicht direkt aus dem anderen, aber es sollte beachtet werden, dass für die Konsensbildung bei RAFT drei Netzwerkanfragen vom Anführer an die Anhänger und zurück erforderlich sind. Je mehr Auftragsknoten vorhanden sind, desto länger dauert es. Je kleiner die Größe und Zeit der Blockbildung, desto mehr solcher Wechselwirkungen. Wie können die Bildungszeit und die Blockgröße erhöht werden, ohne die Systemreaktionszeit für den Endbenutzer zu verlängern?

Zunächst müssen Sie MVCC-Konflikte irgendwie lösen, die durch eine große Blockgröße verursacht werden, die verschiedene RWSets mit derselben Version umfassen kann. Offensichtlich auf der Client-Seite (in Bezug auf das Blockchain-Netzwerk kann dies durchaus ein Backend sein, und das meine ich auch so) MVCC-Konflikthandler, der entweder ein separater Dienst oder ein regulärer Dekorator über einen transaktionsinitiierenden Aufruf mit Wiederholungslogik sein kann.

Ein Wiederholungsversuch kann mit einer exponentiellen Strategie implementiert werden, aber dann nimmt auch die Latenz exponentiell ab. Daher sollten Sie entweder einen zufälligen Wiederholungsversuch innerhalb bestimmter kleiner Grenzen oder einen konstanten Wiederholungsversuch verwenden. Mit Blick auf mögliche Kollisionen in der ersten Variante.

Der nächste Schritt besteht darin, die Interaktion des Clients mit dem System asynchron zu gestalten, sodass er nicht 15, 30 oder 10000000 Sekunden wartet, die wir als BatchTimeout festlegen. Gleichzeitig muss jedoch die Fähigkeit erhalten bleiben, sicherzustellen, dass die durch die Transaktion initiierten Änderungen in der Blockchain aufgezeichnet bzw. nicht aufgezeichnet werden.
Zur Speicherung des Status von Transaktionen kann eine Datenbank genutzt werden. Die einfachste Option ist CouchDB aufgrund seiner Benutzerfreundlichkeit: Die Datenbank verfügt über eine sofort einsatzbereite Benutzeroberfläche und eine REST-API, und Sie können problemlos Replikation und Sharding dafür einrichten. Sie können einfach eine separate Sammlung in derselben CouchDB-Instanz erstellen, die Fabric zum Speichern seines Weltzustands verwendet. Wir müssen solche Dokumente aufbewahren.

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

Dieses Dokument wird in die Datenbank geschrieben, bevor die Transaktion an Peers gesendet wird, die Entitäts-ID wird an den Benutzer zurückgegeben (die gleiche ID wird als Schlüssel verwendet), wenn es sich um einen Erstellungsvorgang handelt, und dann werden die Felder Status, TxID und Fehler angezeigt aktualisiert, sobald relevante Informationen von Kollegen eingehen.

Verteilte Registrierung für Radsätze: Eine Erfahrung mit Hyperledger Fabric

Bei diesem Schema wartet der Benutzer nicht darauf, dass sich der Block schließlich bildet, sondern beobachtet 10 Sekunden lang das sich drehende Rad auf dem Bildschirm, sondern erhält sofort eine Antwort vom System und arbeitet weiter.

Wir haben uns für BoltDB zum Speichern von Transaktionsstatus entschieden, weil wir Speicher sparen müssen und keine Zeit mit der Netzwerkinteraktion mit einem eigenständigen Datenbankserver verschwenden möchten, insbesondere wenn diese Interaktion über das Nur-Text-Protokoll erfolgt. Unabhängig davon, ob Sie CouchDB zur Implementierung des oben beschriebenen Schemas oder nur zur Speicherung des Weltzustands verwenden, ist es in jedem Fall sinnvoll, die Art und Weise der Datenspeicherung in CouchDB zu optimieren. Standardmäßig beträgt die Größe der B-Tree-Knoten in CouchDB 1279 Byte, was viel weniger ist als die Sektorgröße auf der Festplatte, was bedeutet, dass sowohl das Lesen als auch das Neuausgleichen des Baums mehr Zugriffe auf die physische Festplatte erfordern. Die optimale Größe entspricht dem Standard Erweitertes Format und beträgt 4 Kilobyte. Zur Optimierung müssen wir den Parameter festlegen btree_chunk_size gleich 4096 in der CouchDB-Konfigurationsdatei. Bei BoltDB handelt es sich um einen manuellen Eingriff nicht erforderlich.

Gegendruck: Pufferstrategie

Aber es kann viele Nachrichten geben. Mehr, als das System bewältigen kann, indem es Ressourcen mit einem Dutzend anderer Dienste außer den im Diagramm gezeigten teilt – und all dies sollte selbst auf Maschinen, auf denen die Ausführung von Intellij Idea äußerst mühsam wäre, einwandfrei funktionieren.

Das Problem des unterschiedlichen Durchsatzes kommunizierender Systeme, Erzeuger und Verbraucher, wird auf unterschiedliche Weise gelöst. Mal sehen, was wir tun können.

Abwurf: Wir können behaupten, höchstens X Transaktionen in T Sekunden verarbeiten zu können. Alle Anfragen, die diesen Grenzwert überschreiten, werden verworfen. Es ist ziemlich einfach, aber dann können Sie UX vergessen.

Regelung: Der Verbraucher muss über eine Schnittstelle verfügen, über die er je nach Auslastung die TPS des Produzenten steuern kann. Nicht schlecht, aber es verpflichtet die Entwickler des Ladeclients, diese Schnittstelle zu implementieren. Für uns ist dies inakzeptabel, da die Blockchain in Zukunft in eine Vielzahl von seit langem bestehenden Systemen integriert sein wird.

Pufferung: Anstatt zu versuchen, dem Eingabedatenstrom Widerstand zu leisten, können wir diesen Strom puffern und mit der erforderlichen Geschwindigkeit verarbeiten. Dies ist natürlich die beste Lösung, wenn wir ein gutes Benutzererlebnis bieten möchten. Wir haben den Puffer mithilfe einer Warteschlange in RabbitMQ implementiert.

Verteilte Registrierung für Radsätze: Eine Erfahrung mit Hyperledger Fabric

Dem Schema wurden zwei neue Aktionen hinzugefügt: (1) Nachdem eine API-Anfrage empfangen wurde, wird eine Nachricht mit den zum Aufruf der Transaktion erforderlichen Parametern in die Warteschlange gestellt und der Client erhält eine Nachricht, dass die Transaktion vom System akzeptiert wurde, ( 2) das Backend liest die Daten mit einer in der Konfiguration angegebenen Geschwindigkeit aus der Warteschlange; initiiert eine Transaktion und aktualisiert die Daten im Statusspeicher.
Jetzt können Sie die Build-Zeit und die Blockkapazität beliebig verlängern und so Verzögerungen vor dem Benutzer verbergen.

Andere Werkzeuge

Über Chaincode wurde hier nichts gesagt, da es in der Regel nichts zu optimieren gibt. Der Chaincode sollte so einfach und sicher wie möglich sein – das ist alles, was von ihm verlangt wird. Das Framework hilft uns sehr dabei, Chaincode einfach und sicher zu schreiben. CSKit von S7 Techlab und statischem Analysator wiederbeleben^CC.

Darüber hinaus entwickelt unser Team eine Reihe von Dienstprogrammen, um die Arbeit mit Fabric einfach und angenehm zu gestalten: Blockchain-Explorer, Dienstprogramm für automatische Neukonfiguration des Netzwerks (Organisationen hinzufügen/entfernen, RAFT-Knoten), Dienstprogramm für Zertifikatssperrung und Identitätsentfernung. Wenn Sie einen Beitrag leisten möchten, sind wir herzlich willkommen.

Abschluss

Dieser Ansatz macht es einfach, Hyperledger Fabric durch Quorum und andere private Ethereum-Netzwerke (PoA oder sogar PoW) zu ersetzen, den realen Durchsatz deutlich zu reduzieren, aber gleichzeitig die normale UX aufrechtzuerhalten (sowohl für Benutzer im Browser als auch von der Seite integrierter Systeme). ). Beim Ersetzen von Fabric durch Ethereum im Schema muss nur die Logik des Wiederholungsdienstes/Dekorators von der Behandlung von MVCC-Konflikten auf ein atomares Nonce-Inkrement und erneutes Senden geändert werden. Durch Pufferung und Statusspeicherung konnte die Antwortzeit von der Blockbildungszeit entkoppelt werden. Jetzt können Sie Tausende von Bestellknoten hinzufügen und müssen keine Angst haben, dass zu oft Blöcke gebildet werden und den Bestelldienst belasten.

Im Allgemeinen ist das alles, was ich mitteilen wollte. Ich freue mich, wenn es jemandem bei seiner Arbeit hilft.

Source: habr.com

Kommentar hinzufügen