Registre distribué pour les essieux : une expérience avec Hyperledger Fabric

Bonjour, je travaille dans l'équipe du projet DRD KP (registre de données distribuées pour le suivi du cycle de vie des essieux montés). Ici, je veux partager l'expérience de notre équipe dans le développement d'une blockchain d'entreprise pour ce projet sous les contraintes imposées par la technologie. Pour l'essentiel, je parlerai d'Hyperledger Fabric, mais l'approche décrite ici peut être extrapolée à n'importe quelle blockchain autorisée. Le but ultime de nos recherches est de préparer des solutions de blockchain d'entreprise de manière à ce que le produit final soit agréable à utiliser et pas trop difficile à entretenir.

Il n'y aura pas de découvertes, de solutions inattendues et aucun développement unique ne sera couvert ici (parce que je ne les ai pas). Je veux juste partager mon humble expérience, montrer que "c'était possible" et, peut-être, lire l'expérience de quelqu'un d'autre en prenant de bonnes et de moins bonnes décisions dans les commentaires.

Problème : les blockchains ne sont pas encore scalables

Aujourd'hui, les efforts de nombreux développeurs visent à faire de la blockchain une technologie vraiment pratique, et non une bombe à retardement dans un bel emballage. Les canaux d'état, le cumul optimiste, le plasma et le sharding peuvent devenir monnaie courante. Un jour. Ou peut-être que TON reportera à nouveau le lancement de six mois et que le prochain groupe Plasma cessera d'exister. Nous pouvons croire en une autre feuille de route et lire de brillants livres blancs la nuit, mais ici et maintenant, nous devons faire quelque chose avec ce que nous avons. Faites de la merde.

La tâche assignée à notre équipe dans le projet actuel ressemble à ceci en termes généraux : il y a beaucoup de sujets, atteignant plusieurs milliers, qui ne veulent pas construire des relations de confiance ; il est nécessaire de construire sur DLT une solution qui fonctionnera sur des PC ordinaires sans exigences de performances particulières et offrira une expérience utilisateur pas pire que n'importe quel système de comptabilité centralisé. La technologie derrière la solution devrait minimiser la possibilité de manipulation malveillante des données - c'est pourquoi la blockchain est là.

Les slogans des livres blancs et des médias nous promettent que le prochain développement permettra des millions de transactions par seconde. Qu'est-ce que c'est vraiment ?

Mainnet Ethereum fonctionne actuellement à environ 30 tps. Pour cette seule raison, il est difficile de la percevoir comme une blockchain adaptée de quelque manière que ce soit aux besoins des entreprises. Parmi les solutions autorisées, des benchmarks montrant 2000 tps sont connus (Quorum) ou 3000 tps (Hyperledger Fabric, il y en a un peu moins dans la publication, mais gardez à l'esprit que le benchmark a été réalisé sur l'ancien moteur de consensus). Était une tentative de retravailler radicalement Fabric, qui n'a pas donné les pires résultats, 20000 tps, mais jusqu'à présent, ce ne sont que des études académiques en attente de leur mise en œuvre stable. Il est peu probable qu'une entreprise qui peut se permettre de maintenir un département de développeurs de chaînes de blocs accepte de tels indicateurs. Mais le problème n'est pas seulement dans le débit, il y a aussi la latence.

Latence

Le délai entre le moment où une transaction est initiée et son approbation finale par le système dépend non seulement de la vitesse du message passant par toutes les étapes de validation et de commande, mais également des paramètres de formation du bloc. Même si notre blockchain nous permet de nous engager à 1000000 10 488 tps, mais qu'il faut XNUMX minutes pour former un bloc de XNUMX Mo, cela deviendra-t-il plus facile pour nous ?

Examinons de plus près le cycle de vie d'une transaction dans Hyperledger Fabric pour comprendre ce qui prend du temps et son lien avec les paramètres de formation des blocs.

Registre distribué pour les essieux : une expérience avec Hyperledger Fabric
pris d'ici: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) Le client forme une transaction, l'envoie à des pairs qui l'approuvent, ces derniers simulent la transaction (appliquez les modifications apportées par le code blockchain à l'état actuel, mais ne vous engagez pas dans le grand livre) et recevez RWSet - noms de clé, versions et valeurs extraites de la collection dans CouchDB, (2) les endosseurs renvoient un RWSet signé au client, (3) le client vérifie les signatures de tous les pairs nécessaires (endosseurs), puis envoie la transaction au donneur d'ordre service, ou l'envoie sans vérification (la vérification aura encore lieu plus tard), le service de commande forme un bloc et ( 4) le renvoie à tous les pairs, pas seulement aux endosseurs ; les pairs vérifient que les versions des clés dans l'ensemble de lecture correspondent aux versions de la base de données, aux signatures de tous les endosseurs, et enfin valident le bloc.

Mais ce n'est pas tout. Derrière les mots « l'ordonnateur forme un bloc » se cache non seulement l'ordonnancement des transactions, mais aussi 3 requêtes réseau consécutives du leader vers les suiveurs et inversement : le leader ajoute un message au journal, envoie aux suiveurs, ces derniers ajoutent à leur journal, envoyer la confirmation de la réplication réussie au leader, le leader valide le message, envoie la confirmation de validation aux suiveurs, les suiveurs s'engagent. Plus la taille et la durée du bloc sont petites, plus le service de commande devra souvent établir un consensus. Hyperledger Fabric a deux paramètres de formation de bloc : BatchTimeout - temps de formation de bloc et BatchSize - taille de bloc (le nombre de transactions et la taille du bloc lui-même en octets). Dès que l'un des paramètres atteint la limite, un nouveau bloc est émis. Plus il y a de nœuds de commande, plus cela prendra de temps. Par conséquent, vous devez augmenter BatchTimeout et BatchSize. Mais comme les RWSets sont versionnés, plus nous agrandissons le bloc, plus la probabilité de conflits MVCC est élevée. De plus, avec une augmentation de BatchTimeout, l'UX se dégrade de manière catastrophique. Il me semble raisonnable et évident le schéma suivant pour résoudre ces problèmes.

Comment éviter d'attendre la finalisation du bloc et ne pas perdre de vue l'état de la transaction

Plus le temps de formation et la taille des blocs sont longs, plus le débit de la blockchain est élevé. L'un ne découle pas directement de l'autre, mais il convient de rappeler que l'établissement d'un consensus dans RAFT nécessite trois demandes de réseau du leader aux suiveurs et inversement. Plus il y a de nœuds de commande, plus cela prendra de temps. Plus la taille et le temps de formation du bloc sont petits, plus ces interactions sont nombreuses. Comment augmenter le temps de formation et la taille des blocs sans augmenter le temps de réponse du système pour l'utilisateur final ?

Tout d'abord, vous devez résoudre d'une manière ou d'une autre les conflits MVCC causés par une grande taille de bloc, qui peut inclure différents RWSets avec la même version. Évidemment, côté client (par rapport au réseau blockchain, cela peut bien être un backend, et je le pense) Gestionnaire de conflits MVCC, qui peut être soit un service distinct, soit un décorateur standard sur un appel de lancement de transaction avec une logique de nouvelle tentative.

Une nouvelle tentative peut être mise en œuvre avec une stratégie exponentielle, mais la latence se dégradera également de manière exponentielle. Vous devez donc utiliser soit une nouvelle tentative aléatoire dans certaines petites limites, soit une tentative constante. Avec un œil sur les collisions possibles dans la première variante.

L'étape suivante consiste à rendre l'interaction du client avec le système asynchrone afin qu'il n'attende pas 15, 30 ou 10000000 XNUMX XNUMX secondes, ce que nous définirons comme BatchTimeout. Mais en même temps, il faut conserver la capacité de s'assurer que les changements initiés par la transaction sont enregistrés/non enregistrés dans la blockchain.
Une base de données peut être utilisée pour stocker le statut des transactions. L'option la plus simple est CouchDB en raison de sa facilité d'utilisation : la base de données a une interface utilisateur prête à l'emploi, une API REST, et vous pouvez facilement configurer la réplication et le partage pour celle-ci. Vous pouvez simplement créer une collection distincte dans la même instance CouchDB que Fabric utilise pour stocker son état mondial. Nous devons stocker des documents de ce type.

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

Ce document est écrit dans la base de données avant que la transaction ne soit envoyée aux pairs, l'identifiant de l'entité est renvoyé à l'utilisateur (le même identifiant est utilisé comme clé) s'il s'agit d'une opération de création, puis les champs Status, TxID et Error sont mis à jour au fur et à mesure que des informations pertinentes sont reçues des pairs.

Registre distribué pour les essieux : une expérience avec Hyperledger Fabric

Dans ce schéma, l'utilisateur n'attend pas que le bloc se forme enfin, regarde la roue qui tourne à l'écran pendant 10 secondes, il reçoit une réponse instantanée du système et continue à travailler.

Nous avons choisi BoltDB pour stocker les statuts des transactions car nous avons besoin d'économiser de la mémoire et ne voulons pas perdre de temps en interaction réseau avec un serveur de base de données autonome, en particulier lorsque cette interaction a lieu à l'aide du protocole de texte brut. Soit dit en passant, que vous utilisiez CouchDB pour implémenter le schéma décrit ci-dessus ou simplement pour stocker l'état du monde, dans tous les cas, il est logique d'optimiser la manière dont les données sont stockées dans CouchDB. Par défaut, dans CouchDB, la taille des nœuds b-tree est de 1279 octets, ce qui est beaucoup plus petit que la taille du secteur sur le disque, ce qui signifie que la lecture et le rééquilibrage de l'arborescence nécessiteront davantage d'accès physiques au disque. La taille optimale répond à la norme Format avancé et est de 4 kilo-octets. Pour l'optimisation, nous devons définir le paramètre btree_chunk_size égal à 4096 dans le fichier de configuration de CouchDB. Pour BoltDB une telle intervention manuelle ne nécessite pas.

Contre-pression : stratégie tampon

Mais il peut y avoir beaucoup de messages. Plus que ce que le système peut gérer, partager des ressources avec une douzaine d'autres services en plus de ceux indiqués dans le diagramme - et tout cela devrait fonctionner parfaitement même sur des machines sur lesquelles exécuter Intellij Idea serait extrêmement fastidieux.

Le problème du débit différent des systèmes communicants, producteur et consommateur, est résolu de différentes manières. Voyons ce que nous pourrions faire.

Goutte: on peut prétendre pouvoir traiter au plus X transactions en T secondes. Toutes les requêtes qui dépassent cette limite sont abandonnées. C'est assez simple, mais vous pouvez alors oublier UX.

Contrôle: le consommateur doit avoir une interface à travers laquelle, en fonction de la charge, il peut contrôler les tps du producteur. Pas mal, mais cela impose aux développeurs du client de charge l'obligation d'implémenter cette interface. Pour nous, c'est inacceptable, car la blockchain sera à l'avenir intégrée dans un grand nombre de systèmes existants de longue date.

Buffering: au lieu de s'arranger pour résister au flux de données d'entrée, nous pouvons tamponner ce flux et le traiter à la vitesse requise. Évidemment, c'est la meilleure solution si nous voulons offrir une bonne expérience utilisateur. Nous avons implémenté le tampon en utilisant une file d'attente dans RabbitMQ.

Registre distribué pour les essieux : une expérience avec Hyperledger Fabric

Deux nouvelles actions ont été ajoutées au schéma : (1) après la réception d'une requête API, un message est mis en file d'attente avec les paramètres nécessaires pour appeler la transaction, et le client reçoit un message indiquant que la transaction a été acceptée par le système, ( 2) le backend lit les données à une vitesse spécifiée dans la configuration à partir de la file d'attente ; lance une transaction et met à jour les données dans le magasin d'état.
Vous pouvez désormais augmenter le temps de construction et la capacité de blocage autant que vous le souhaitez, en masquant les retards à l'utilisateur.

Autres outils

Rien n'a été dit ici sur le code blockchain, car il n'y a généralement rien à optimiser. Le code blockchain doit être aussi simple et sécurisé que possible - c'est tout ce qui lui est demandé. Le framework nous aide beaucoup pour écrire du code blockchain simplement et en toute sécurité. CSKitComment de S7 Techlab et analyseur statique revivre ^ CC.

De plus, notre équipe développe un ensemble d'utilitaires pour rendre le travail avec Fabric simple et agréable : explorateur de chaînes de blocs, utilité pour reconfiguration automatique du réseau (ajout/suppression d'organisations, nœuds RAFT), utilitaire pour révocation de certificat et suppression d'identité. Si vous souhaitez contribuer, bienvenue.

Conclusion

Cette approche permet de remplacer facilement Hyperledger Fabric par Quorum, d'autres réseaux Ethereum privés (PoA ou même PoW), de réduire considérablement le débit réel, mais en même temps de maintenir une UX normale (à la fois pour les utilisateurs dans le navigateur et du côté des systèmes intégrés ). Lors du remplacement de Fabric par Ethereum dans le schéma, seule la logique du service/décorateur de nouvelle tentative devra être modifiée pour passer de la gestion des conflits MVCC à un incrément atomique nonce et à un renvoi. La mise en mémoire tampon et le stockage d'état ont permis de découpler le temps de réponse du temps de formation du bloc. Vous pouvez maintenant ajouter des milliers de nœuds de commande et ne pas avoir peur que des blocs se forment trop souvent et chargent le service de commande.

En général, c'est tout ce que je voulais partager. Je serai heureux si cela aide quelqu'un dans son travail.

Source: habr.com

Ajouter un commentaire