Размеркаваны рэестр для колавых пар: досвед з Hyperledger Fabric

Прывітанне, я працую ў камандзе праекту РРД КП (размеркаваны рэестр дадзеных для кантролю жыццёвага цыклу колавых пар). Тут я хачу падзяліцца вопытам нашай каманды ў распрацоўцы карпаратыўнага блокчейна для дадзенага праекта ва ўмовах абмежаванняў, якія накладаюцца тэхналогіяй. Па большай частцы я буду казаць аб Hyperledger Fabric, але апісаны тут падыход можа быць экстрапаляваць на любы permissioned блокчейн. Канчатковая мэта нашых пошукаў - рыхтаваць карпаратыўныя блокчейн-рашэнні так, каб выніковым прадуктам было прыемна карыстацца і не занадта цяжка падтрымліваць.

Тут не будзе ніякіх адкрыццяў, нечаканых рашэнняў і тут не будуць асвятляцца ніякія ўнікальныя распрацоўкі (таму што іх у мяне няма). Я проста хачу падзяліцца сваім сціплым досведам, паказаць, што «так можна было» і, магчыма, прачытаць пра чужы досвед прыняцця добрых і не вельмі рашэнняў у каментарах.

Праблема: блокчейны пакуль што не маштабуюцца

Сёння намаганні многіх распрацоўшчыкаў накіраваны на тое, каб зрабіць блокчейн сапраўды зручнай тэхналогіяй, а не бомбай запаволенага дзеяння ў прыгожай абгортцы. Каналы станаў, optimistic rollup, plasma і шардынг, магчыма, стануць паўсядзённасцю. Калі-небудзь. А магчыма, TON зноў адкладзе запуск на паўгода, а чарговая Plasma Group спыніць сваё існаванне. Мы можам верыць у чарговы roadmap і чытаць на ноч бліскучыя white papers, але тут і зараз трэба нешта рабіць з тым, што мы маем. Get shit done.

Задача, пастаўленая перад нашай камандай у бягучым праекце, выглядае ў агульным выглядзе так: ёсць мноства суб'ектаў, якое дасягае некалькіх тысяч, не жадаючае будаваць адносіны на даверы; неабходна пабудаваць на DLT такое рашэнне, якое будзе працаваць на звычайных ПК без спецыяльных патрабаванняў да прадукцыйнасці і забяспечваць карыстацкі досвед не горш любых цэнтралізаваных сістэм ўліку. Тэхналогія, якая ляжыць у аснове рашэння, павінна звесці да мінімуму магчымасць зламысных маніпуляцый з дадзенымі - менавіта таму тут блокчейн.

Лозунгі з whitepapers і СМІ абяцаюць нам, што чарговая распрацоўка дазволіць здзяйсняць мільёны транзакцый у секунду. Што ж на самой справе?

Mainnet Ethereum зараз працуе са хуткасцю ~30 tps. Ужо толькі з-за гэтага яго складана ўспрымаць як колькі-небудзь прыдатны для карпаратыўных патрэб блокчэйн. Сярод permissioned-рашэнняў вядомыя бенчмаркі, якія паказваюць 2000 tps (кворум) або 3000 tps (Тканіна Hyperledger, у публікацыі крыху менш, але трэба ўлічваць, што бенчмарк праводзіўся на старым consensus engine). Была спроба радыкальнай перапрацоўкі Fabric, Якая дала не самыя дрэнныя вынікі, 20000 tps, але пакуль гэта толькі акадэмічныя пошукі, якія чакаюць сваёй стабільнай імплементацыі. Ці наўрад карпарацыя, якая можа сабе дазволіць утрымоўваць аддзел блокчейн-распрацоўнікаў, будзе мірыцца з такімі паказчыкамі. Але праблема не толькі ў throughput, есць яшчэ latency.

латэнтнасьць

Затрымка ад моманту ініцыяцыі транзакцыі да яе канчатковага зацвярджэння сістэмай залежыць не толькі ад хуткасці праходжання паведамлення праз усе этапы валідацый і парадкавання, але і ад параметраў фармавання блока. Нават калі наш блокчейн дазваляе нам камячыць са хуткасцю 1000000 tps, але патрабуе пры гэтым 10 хвілін на фармаванне блока памерам 488 Мб, ці стане нам лягчэй?

Давайце паглядзім бліжэй на жыццёвы цыкл транзакцыі ў Hyperledger Fabric, каб зразумець, на што сыходзіць час і як гэта суадносіцца з параметрамі фармавання блока.

Размеркаваны рэестр для колавых пар: досвед з Hyperledger Fabric
узята адсюль: hyperledger-fabric.readthedocs.io/en/release-1.4/arch-deep-dive.html#swimlane

(1) Кліент фармуе транзакцыю, адпраўляе на endorsing peers, апошнія сімулююць транзакцыю (ужываюць змены, якія ўносяцца чейнкодом, на бягучы стан, але не коммятят у лэджэр) і атрымліваюць RWSet - імёны ключоў, версіі і значэнні, узятыя з калекцыі ў Couch 2) endorsers адпраўляюць назад кліенту падпісаны RWSet, (3) кліент альбо правярае наяўнасць подпісаў усіх неабходных баляў (endorsers), а потым адпраўляе транзакцыю на ordering service, альбо адпраўляе без праверкі (праверка ўсё роўна адбудзецца пазней), ordering service фармуе блок і ( 4) адпраўляе назад на ўсе балі, не толькі endorsers; балі правяраюць адпаведнасць версій ключоў у read set версіям у базе дадзеных, наяўнасць подпісаў усіх endorsers і нарэшце камяць блок.

Але гэта яшчэ не ўсё. Па словах «ордэрэр фармуе блок» хаваецца не толькі парадкаванне транзакцый, але і 3 паслядоўных сеткавых запыту ад лідэра да фолавераў і назад: лідэр дадае паведамленне ў лог, адпраўляе фолаверам, апошнія дадаюць у свой лог, адпраўляюць пацвярджэнне паспяховай рэплікацыі лідэру, лідэр комит , адпраўляе пацверджанне комміта фолаверам, фолаверы коміцяць. Чым меншы памер і час фармавання блока, тым часцей прыйдзецца ordering service усталёўваць кансэнсус.. У Hyperledger Fabric ёсць два параметры фарміравання блока: BatchTimeout - час фарміравання блока і BatchSize - памер блока (колькасць транзакцый і памер самога блока ў байтах). Як толькі адзін з параметраў дасягае ліміту, выпускаецца новы блок. Чым больш нод ордэрараў, тым даўжэй гэта будзе адбывацца. Такім чынам, трэба павялічваць BatchTimeout і BatchSize. Але паколькі RWSet'ы версіянуюцца, то чым больш мы зробім блок, тым вышэйшая верагоднасць MVCC-канфліктаў. Да таго ж, з павелічэннем BatchTimeout катастрафічна дэградуе UX. Мне падаецца разумнай і відавочнай наступная схема для вырашэння гэтых праблем.

Як пазбегнуць чаканні фіналізацыі блока і не страціць магчымасці адсочвання статусу транзакцыі

Чым больш час фармавання і памер блока, тым вышэй прапускная здольнасць блокчейна. Адно з іншага прама не варта, аднак варта ўспомніць, што ўсталяванне кансенсусу ў RAFT патрабуе трох сеткавых запытаў ад лідэра да фолавераў і зваротна. Чым больш нод ордэраў, тым даўжэй гэта будзе адбывацца. Чым меншы памер і час фарміравання блока, тым больш такіх інтэракцый. Як павялічваць час фарміравання і памер блока, не павялічваючы час чакання адказу сістэмы для канчатковага карыстальніка?

Па-першае, трэба неяк вырашаць MVCC-канфлікты, выкліканыя вялікім памерам блока, які можа ўключаць розныя RWSet'ы з адной і той жа версіяй. Відавочна, на баку кліента (у адносінах да блокчэйн-сеткі, гэта цалкам можа быць бэкенд, і я маю на ўвазе менавіта яго) патрэбен хэндлер MVCC-канфліктаў, які можа быць як асобным сэрвісам, так і звычайным дэкаратарам над які ініцыюе транзакцыю выклікам з логікай retry.

Retry можна рэалізаваць з экспанентнай стратэгіяй, але тады latency будзе дэградаваць гэтак жа экспанентна. Так што варта выкарыстоўваць або рандомізірованное ў пэўных невялікіх межах retry, або пастаянны. З аглядкай на магчымыя калізіі ў першым варыянце.

Наступны этап - зрабіць узаемадзеянне кліента з сістэмай асінхронным, каб ён не чакаў 15, 30 або 10000000 секунд, якія мы ўсталюем у якасці BatchTimeout. Але пры гэтым трэба захаваць магчымасць пераканацца ў тым, што змены, ініцыяваныя транзакцыяй, запісаныя/не запісаныя ў блокчэйн.
Для захоўвання статусу транзакцый можна выкарыстоўваць базу даных. Самы просты варыянт - CouchDB з-за выгоды выкарыстання: у базы ёсць UI са скрынкі, REST API, для яе лёгка можна наладзіць рэплікацыю і шаліраванне. Можна стварыць проста асобную калекцыю ў тым жа інстансе CouchDB, які выкарыстоўвае Fabric для захоўвання свайго world state. Нам трэба захоўваць дакументы такога віду.

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

Гэты дакумент запісваецца ў базу да перадачы транзакцыі на балі, карыстачу вяртаецца ID сутнасці (гэты ж ID выкарыстоўваецца ў якасці ключа), калі гэта аперацыя стварэння чаго-небудзь, а затым палі Status, TxID і Error абнаўляюцца па меры паступлення рэлевантнай інфармацыі ад баляў.

Размеркаваны рэестр для колавых пар: досвед з Hyperledger Fabric

У гэтай схеме карыстач не чакае, калі ж нарэшце сфармуецца блок, назіраючы крутоўнае колца на экране 10 секунд, ён атрымлівае маментальны водгук сістэмы і працягвае працаваць.

Мы абралі BoltDB для захоўвання статутаў транзакцый, таму што нам трэба эканоміць памяць і не жадаецца марнаваць час на сеткавае ўзаемадзеянне з асобна стаячым серверам базы дадзеных, тым больш калі гэтае ўзаемадзеянне адбываецца па plain text пратаколу. Дарэчы, карыстаецеся вы CouchDB для рэалізацыі апісанай вышэй схемы ці проста для захоўвання world state, у любым выпадку мае сэнс аптымізаваць спосаб захоўвання дадзеных у CouchDB. Па змаўчанні ў CouchDB памер b-tree вузлоў роўны 1279 байт, што моцна менш памеру сектара на дыску, а значыць як чытанне, так і перабалансіроўка дрэва будзе патрабаваць больш фізічных зваротаў да дыска. Аптымальны памер адпавядае стандарту Пашыраны фармат і складае 4 кілабайты. Для аптымізацыі нам трэба ўстанавіць параметр btree_chunk_size роўным 4096 у файле канфігурацыі CouchDB. Для BoltDB такое ручное ўмяшанне не патрабуецца.

Backpressure: buffer strategy

Але паведамленняў можа быць вельмi шмат. Больш, чым сістэма здольная апрацаваць, падзяляючы рэсурсы з дзясяткам іншых сэрвісаў акрамя адлюстраваных на схеме – і ўсё гэта павінна працаваць безадмоўна нават на машынах, на якіх запуск Intellij Idea будзе справай вельмі стомным.

Праблема рознай прапускной здольнасці сазлучаных сістэм, прад'юсера і кансьюмера, вырашаецца рознымі спосабамі. Паглядзім, што мы маглі б зрабіць.

Падзенне: мы можам заявіць, што здольныя апрацоўваць не больш за X транзакцый за T секунд. Усе запыты, якія перавышаюць гэты ліміт, скідаюцца. Гэта даволі проста, але пра UX тады можна забыцца.

Кіраванне: у кансьюмера павінен быць нейкі інтэрфейс, праз які ён у залежнасці ад нагрузкі зможа кантраляваць tps прад'юсера. Нядрэнна, але гэта накладвае абавязацельствы на распрацоўшчыкаў кліента, які стварае нагрузку, рэалізоўваць гэты інтэрфейс. Для нас гэта непрымальна, бо блокчэйн у даляглядзе будзе інтэграваны ў вялікую колькасць даўно існых сістэм.

Буферызацыя: замест таго, каб хітравацца ў супраціве ўваходным струмені дадзеных, мы можам буферызаваць гэты струмень і апрацоўваць яго з неабходнай хуткасцю. Відавочна, гэта лепшае рашэнне, калі мы хочам забяспечыць добры карыстацкі досвед. Буфер мы рэалізоўвалі з дапамогай чаргі ў RabbitMQ.

Размеркаваны рэестр для колавых пар: досвед з Hyperledger Fabric

У схему дадалося два новыя дзеянні: (1) пасля паступлення запыту на API у чаргу кладзецца паведамленне з параметрамі, неабходнымі для выкліку транзакцыі, і кліент атрымлівае паведамленне аб тым, што транзакцыя прынятая сістэмай, (2) бэкенд з задаецца ў канфігу хуткасцю чытае дадзеныя з чаргі; ініцыюе транзакцыю і абнаўляе дадзеныя ў сховішча статутаў.
Зараз можна павялічваць час фармавання і ёмістасць блока настолькі, наколькі захочацца, хаваючы затрымкі ад карыстача.

Іншыя інструменты

Тут не было нічога сказанае пра чейнкод, таму што ў ім, як правіла, няма чаго аптымізаваць. Чэйнкад павінен быць максімальна простым і бяспечным - гэта ўсё, што ад яго патрабуецца. Пісаць чейнкод проста і бяспечна нам моцна дапамагае фрэймворк ССKit ад S7 Techlab і статычны аналізатар revive^CC.

Акрамя таго, наша каманда распрацоўвае набор утыліт для таго, каб зрабіць працу з Fabric простай і прыемнай: блокчэйн эксплорар, утыліта для аўтаматычнай змены канфігурацыі сеткі (даданне/выдаленне арганізацый, нод RAFT), утыліта для водгуку сертыфікатаў і выдаленні identity. Калі хочаце ўнесці свой уклад - welcome.

Заключэнне

Гэты падыход дазваляе лёгка замяніць Hyperledger Fabric на Quorum, іншыя прыватныя Ethereum сеткі (PoA ці нават PoW), істотна знізіць рэальную прапускную здольнасць, але пры гэтым захаваць нармальны UX (як для карыстачоў у браўзэры, так і са боку інтэгравальных сістэм). Пры замене Fabric на Ethereum у схеме трэба будзе змяніць толькі логіку retry-сэрвісу/дэкаратара c апрацоўкі MVCC-канфліктаў на атамарны інкрымент nonce і паўторную адпраўку. Буферызацыя і сховішча статутаў дазволілі адвязаць час водгуку ад часу фармавання блока. Цяпер можна дадаваць тысячы нод ордэраў і не баяцца, што блокі фармуюцца занадта часта і нагружаюць ordering service.

Увогуле, гэта ўсё, чым я хацеў падзяліцца. Буду рады, калі гэта камусьці дапаможа ў рабоце.

Крыніца: habr.com

Дадаць каментар