Сколько TPS в вашем блокчейне?

    Любимым вопросом о любой распределенной системе от нетехнического специалиста является “Сколько tps в вашем блокчейне?”. Однако, названное в ответ число обычно имеет мало общего с тем, что хотел бы услышать вопрошающий. На деле, он хотел спросить “подойдет ли ваш блокчейн под мои бизнес требования”, и эти требования — это не одно число, а множество условий — здесь и отказоустойчивость сети, и требования к финальности, размеры, характер транзакций и множество других параметров. Так что ответ на вопрос “сколько tps” вряд ли будет простым, и почти никогда не будет полным. Распределенная система с десятками и сотнями узлов, выполняющих довольно сложные вычисления, может находиться в огромном количестве различных состояний, связанных с состоянием сети, содержимым блокчейна, техническими сбоями, экономическими проблемами, атаками на сеть и множеством других причин. Этапы, на которых возможны проблемы с производительностью отличаются от традиционных сервисов, а сервер блокчейн-сети — это сетевой сервис, сочетающий в себе функционал базы данных, web-сервера и torrent-клиента, что делает его крайне сложным в плане профиля нагрузки на все подсистемы: процессор, память, сеть, storage


    Так получилось, что децентрализованные сети и блокчейны являются довольно специфическим и непривычным софтом для разработчиков централизованного ПО. Поэтому я хотел бы осветить важные аспекты производительности и устойчивости децентрализованных сетей, подходы к их измерению и нахождению bottlenecks. Мы рассмотрим различные проблемы производительности, ограничивающие скорость предоставления сервиса пользователям блокчейнов и отметим особенности, характерные для этого вида софта.


    Этапы запроса сервиса клиентом блокчейна


    Для того, чтобы честно говорить о качестве любого более-менее сложного сервиса, нужно учесть не только средние значения, но и максимальные/минимальные, медианы, персентили. Теоретически, можно говорить о 1000 tps в каком-нибудь блокчейне, но если 900 транзакций выполнились с огромной скоростью, а 100 — "зависли" на несколько секунд, то среднее время, собранное по всем транзакциям — это не совсем честная метрика для клиента, который за несколько секунд не смог завершить сделку. Временные "ямы", вызванные пропущенными раундами консенсуса или разделением сети могут сильно испортить сервис, который на тестовых стендах показывал прекрасную производительность.


    Чтобы идентифицировать такие bottleneck-и необходимо хорошо понимать этапы, на которых реальный блокчейн может испытывать затруднения при обслуживании пользователей. Давайте опишем цикл доставки и процессинга транзакции, а также получения нового состояния блокчейна, из которого клиент может убедиться, что его транзакция была обработана и учтена.


    1. транзакция формируется на клиенте
    2. транзакция подписывается на клиенте
    3. клиент выбирает одну из нод и отправляет в нее свою транзакцию
    4. клиент подписывается на обновления state database ноды, ожидая появления результатов исполнения своей транзакции
    5. нода распространяет транзакцию по p2p сети
    6. несколько, или один BP (block producer) процессят накопленные транзакции, обновляя state database
    7. BP формирует новый блок, обработав нужное количество транзакций
    8. BP распространяет новый блок по p2p сети
    9. новый блок доставляется до ноды, к которой обращается клиент
    10. нода обновляет state database
    11. нода видит обновление, касающееся клиента, и отправляет ему уведомление о транзакции

    Теперь давайте подробней разберем эти этапы и опишем потенциальные проблемы с производительностью на каждом этапе. В отличие от централизованных систем, мы также рассмотрим исполнение кода на клиентах сети. Довольно часто при измерении tps время процессинга транзакций собирают с нод, а не с клиента — это не совсем честно. Клиенту плевать, насколько быстро нода запроцессила его транзакцию, самое важное для него — момент, когда достоверная информация об этой транзакции, включенной в блокчейн, станет доступной ему. Именно эта метрика и является по сути временем исполнения транзакции. Это означает, что разные клиенты, даже отправляя одну и ту же транзакцию, могут получить совершенно разные времена, которые зависят от канала, загруженности и близости ноды, и т.п. Так что совершенно необходимо измерять это время на клиентах, поскольку именно этот параметр нужно оптимизировать.


    Подготовка транзакции на стороне клиента


    Начнем с первых двух пунктов: транзакция формируется и подписывается клиентом. Как ни странно, это тоже может быть bottleneck-ом производительности блокчейна с точки зрения клиента. Это непривычно для централизованных сервисов, которые все вычисления и операции с данными забирают себе, а клиент просто готовит короткий запрос, способный запросить большой объем данных или вычислений, получая готовый результат. В блокчейнах клиентский код становится все более и более мощным, а блокчейн ядро — все более и более легковесным, а массивные вычислительные задачи принято отдавать клиентскому софту. В блокчейнах существуют клиенты, которые могут готовить одну транзакцию довольно долго (я говорю о различных merkle proof-ах, succinct proof-ах, threshold подписях и других сложных операциях на стороне клиента). Хорошим примером легкой on-chain верификации и тяжелой подготовки трназакции на клиенте является доказательство принадлежности списку на основе Merkle-tree, вот статья.


    Также не стоит забывать, что клиентский код не просто шлет транзакции в блокчейн, а сначала запрашивает состояние блокчейна — а эта деятельность может влиять на загруженность сети и блокчейн нод. Так что, проводя измерения, разумным будет эмулировать как можно более полным образом поведение клиентского кода. Даже если в вашем блокчейне обычные легкие клиенты, которые ставят обычную цифровую подпись на простейшую транзакцию по переводу какого нибудь asset-а, с каждым годом массивных вычислений на клиенте все равно становится больше, криптоалгоритмы крепчают, и эта часть процессинга может превратиться в весомый bottleneck в будущем. Поэтому будьте осторожны, и не пропустите ситуацию, когда в транзакции, длящейся 3.5s, 2.5s уходит на подготовку и подписание транзакции, и 1.0s- на отправку в сеть и ожидание ответа. Для оценки рисков появления этого bottleneck нужно собирать метрики с клиентских машин, а не только с блокчейн-нод.


    Отправка транзакции и мониторинг ее статуса


    Следующим этапом является отправка транзакции в выбранную блокчейн-ноду и получение статуса принятия ее в пул транзакций. Этот этап похож на обычное обращение к базе данных, нода должна записать транзакцию в пул и начать распространять информацию о ней через p2p сеть. Подход к оценке производительности здесь похож на оценку работы традиционных микросервисов Web API, причем сами транзакции в блокчейнах могут обновляться, активно менять статус. Вообще, обновление информации о транзакции в некоторых блокчейнах может произойти несколько раз, например при переключениями между форками цепочки или когда BP сообщают о намерении включить транзакцию в блок. Ограничения на объем этого пула и количество транзакций в нем могут оказывать влияние на производительность блокчейна. Если пул транзакций забит до максимально возможного размера, или не помещается в оперативной памяти — производительность сети может резко упасть. Блокчейны не имеют централизованных средств защиты от потока мусорных сообщений, и, если блокчейн поддерживает транзакциии большого объема и низкие комиссии, это может привести к переполнению пула транзакций — это еще один потенциальный bottleneck производительности.


    В блокчейнах, клиент оправляет транзакцию в любую понравившуюся ему ноду блокчейна, хеш транзакции обычно известен клиенту еще до отправки, так что все что ему требуется — добиться соединения и после передачи ожидать когда блокчейн изменит свое состояние, включив его транзакцию. Заметим, что измеряя "tps" можно получить совершенно разные результаты для различных способов подключения к ноде блокчейна. Это может быть обычный HTTP RPC или WebSocket, позволяющий реализовать паттерн "subscribe". Во втором случае клиент получит уведомление раньше, а нода потратит меньше ресурсов (в основном памяти и трафика) на ответы о состоянии транзакции. Так что при измерении "tps" необходимо учитывать способ подключения клиентов к нодам. Поэтому, для оценки рисков появления этого bottleneck-а, benchmark блокчейна должен уметь эмулировать клиентов и с WebSocket и с HTTP RPC запросами, в долях, соответствующих реальным сетям, а также менять характер транзакций и их размер.


    Для оценки рисков появления этого bottleneck нужно также собирать метрики с клиентских машин, а не только с блокчейн-нод.


    Передача транзакций и блоков по p2p сети


    В блокчейнах для передачи между участниками транзакций и блоков используется peer-to-peer (p2p) networking. Транзакции распространяются по сети, начиная с одной из нод, пока не достигают peer-ов-block producer-ов, которые упаковывают транзакции в блоки и с помощью того же p2p распространяют новые блоки по всем нодам сети. Основа большинства современных p2p сетей — различные модификации протокола Kademlia. Вот хороший краткий обзор этого протокола, а вот — статья с различными измерениями в сети BitTorrent, по которой можно понять — что этот вид сетей сложнее, и менее предсказуем, чем жестко сконфигурированная сеть централизованного сервиса. Также, вот статья про измерение различных интересных метрик для нод Ethereum.


    Если вкратце, каждый peer в таких сетях поддерживает свой собственный динамический список других peer-ов, у которых запрашивает блоки информации, которые адресуются по содержимому. При получении запроса peer либо отдает нужную информацию, либо передает запрос следующему псевдослучайному peer-у из списка, а получив ответ, передает его запрашивающему и на некоторое время кеширует его, отдавая этот блок информации раньше в следующий раз. Таким образом популярная информация оказывается в большом числе кешей у большого числа peer-ов, а непопулярная постепенно вытесняется. Peer-ы ведут учет кто кому сколько передал информации, и сеть старается стимулировать активных раздающих, повышая их рейтинг и обеспечивая им более высокий уровень сервиса, автоматически вытесняя неактивных участников из списков peer-ов.


    Итак, транзакцию теперь надо распространить по сети, чтобы ее увидели block-producer-ы и включили в блок. Нода активно "раздает" новую транзакцию всем желающим и слушает сеть, ожидая блок, в индексе которого будет появится нужная транзакция, чтобы уведомить ожидающего клиента. Время, пока сеть перебрасывает друг другу информацию о новых транзакциях и блоках в p2p сетях зависит от очень большого количества факторов: количества честных, работающих рядом (с сетевой точки зрения) нод, "прогретости" кешей этих нод, размера блоков, транзакций, характера изменений, географии сети, числа нод и еще множества факторов. Комплексные измерения метрик быстродействия в таких сетях — сложное дело, необходимо одновременно оценивать время обработки запросов и на клиентах, и на peer-ах (блокчейн нодах). Проблемы в каком либо из p2p механизмов, неверное вытеснение и кеширование данных, неэффективное управление списками активных peer-ов, и множество других факторов могут стать причиной задержек, влияющих на эффективность всей сети в целом, и этот bottleneck — наиболее сложный для анализа, тестирования и интерпретации результатов.


    Процессинг цепочки блоков и обновление state database


    Самой важной частью работы блокчейна является алгоритм консенсуса, его применение к новым, полученным из сети блокам и процессинг транзакций с записью результатов в state database. Добавление нового блока в цепочку и следующий за этим выбор основной цепочки должен работать максимально быстро. Однако, в реальной жизни "должен" не значит "работает", и можно, например, представить себе ситуацию когда две длинные конкурирующие цепочки постоянно переключаются между собой, меняя метаданные тысяч транзакций в пуле на каждом переключении, и производя постоянные откаты состояния state database. Этот этап, в плане определения bottleneck проще, чем сетевой p2p слой, т.к. исполнение транзакций и алгоритм консенсуса строго детерминированы, и измерять что-либо здесь проще.
    Главное — не спутать случайную деградацию производительности этого этапа с проблемами сети — ноды медленней отдают блоки и информацию об основной цепочке и для внешнего клиента это может выглядеть как медленная сеть, хотя проблема кроется совсем в другом месте.


    Для оптимизации производительности на этом этапе полезно собирать и мониторить метрики с самих нод, и включать в них те, которые касаются обновления state-datаbase: число блоков, обрабатываемых на ноде, их размер, число транзакций, количество переключений между форками цепочек, число невалидных блоков, время работы виртуальной машины, время фиксации данных и т.д. Это позволит не спутать сетевые проблемы с ошибками в алгоритмах процессинга цепочек.


    Процессящая транзакции виртуальная машина может быть полезным источником информации, способной оптимизировать работу блокчейна. Количествo аллокаций памяти, количество read/write инструкций, и другие метрики, касающиеся эффективности исполнения кода контрактов могут дать много полезной информации разработчикам. В то же время, смарт-контракты — это программы, а значит в теории они могут потреблять любые из ресурсов: cpu/memory/network/storage, так что процессинг транзакций — довольно неопределенный этап, который вдобавок сильно меняется при переходе между версиями и при изменении кода контрактов. Поэтому метрики, касающиеся процессинга транзакций также нужны для эффективной оптимизации производительности блокчейна.


    Получение клиентом уведомления о включении транзакции в блокчейн


    Это завершающий этап получения сервиса клиентом блокчейна, по сравнению с другими этапами здесь нет больших накладных расходов, но все равно стоит учитывать возможность получения клиентом объемного ответа от ноды (например смарт-контракт, отдающий массив данных). В любом случае, именно этот момент является самым важным для того, кто задал вопрос "а сколько tps в вашем блокчейне ?", т.к. в этот момент и фиксируется время получения сервиса.


    В это месте обязательно присутствует отправка полного времени, которое пришлось затратить клиенту на ожидание ответа от блокчейна, именно это время пользователь будет ожидать подтверждения в своем приложении, и именно его оптимизация является основной задачей разработчиков.


    Заключение


    В результате, можно описать типы операций, выполняющихся в блокчейнах и разделить их на несколько категорий:


    1. криптографические преобразования, построение доказательств
    2. peer-to-peer networking, репликация транзакций и блоков
    3. процессинг транзакций, исполнение смарт-контрактов
    4. применение изменений в блокчейне к state database, обновление данных о транзакциях и блоках
    5. read-only запросы к state database, API блокчейн ноды, subscription сервисы

    Вообще технические требования к нодам современных блокчейнов крайне серьезные — это быстрые CPU для криптографии, большой объем оперативной памяти для того, чтобы хранить и быстро обращаться к state database, сетевое взаимодействие, использующее большое число одновременно открытых соединений, объемный storage. Такие высокие требования и обилие различных типов операций неизбежно приводят к тому, что ресурсов у нод может не хватать, и, тогда любой из рассмотренных выше этапов может стать очередным bottleneck-ом для общей производительности сети.


    Разрабатывая и оценивая производительность блокчейнов, вам придется учитывать все эти моменты. Для этого нужно собирать и анализировать метрики одновременно с клиентов и нод сети, искать корреляции между ними, оценивать время предоставления сервиса клиентам, учитывать все основные ресурсы: cpu/memory/network/storage, понимать как они используются и влияют друг на друга. Все это делает сравнение скоростей различных блокчейнов в виде "сколько TPS" крайне неблагодарным занятием, так как существует огромное количество различных конфигураций и состояний. В больших централизованных системах, кластерах из сотен серверов, эти проблемы также сложны и также требуют сбора большого числа различных метрик, но в блокчейнах, из за p2p сетей, виртуальных машин, процессящих контракты, внутренней экономики, число степеней свободы гораздо больше, что делает тест даже на нескольких серверах непоказательным и показывающим лишь крайне примерные значения, почти не имеющие связи с реальностью.


    Поэтому, при разработке в ядре блокчейна, для оценки производительности и ответа на вопрос "улучшилось ли по сравнению с прошлым разом" мы используем довольно сложный софт, оркестрирующий запуск блокчейна с десятками узлов и автоматическим запуском бенчмарка и сбором метрик, без этой информации крайне сложно отлаживать протоколы, работающие со множеством участников.


    Так что, получив вопрос "сколько TPS в вашем блокчейне?", предложите собеседнику чаю и уточните, готов ли он ознакомиться с десятком графиков а также выслушать все три короба проблем производительности блокчейнов и вашими предложения по их решению...

    Поделиться публикацией

    Комментарии 7

      0
      Сторона клиента не должна учитываться т к параметр tps относится лишь к магистральной части любой сети ( телекоммуникации, интернет, p2p,,, ). Когда запрос (сигнал) попадает в магистральную сеть (ядро ) здесь этот параметр важен.
        0
        я считаю это не совсем честно по отношению к клиенту сети. Почему «последняя миля» не учитывается? Если взять например блокчейн, где клиенты выполняют массивные вычисления, или при получении некоторых данных должны их расшифровать, накатить на свои базы, или пообщаться с другими клиентами — то это может быть значимый кусок производительности всей сети. Вы можете получить ситуацию «алло шеф, у меня транзакции по полминуты висят», а в ответ: «ничего не знаем, у нас по графику 1000 tps в магистральной сети».
        Есть еще проекты где клиенты соединяются между собой, а еще есть L2 решения — когда одна цепочка коммитит изменения и диспуты в вышестоящую L1 цепочку — в этих случаях с производителностью все совсем сложно. Так что клиентскую часть в децентрализованных сетях имеет смысл учитывать. В традиционных системах клиент обычно ничего серьезного не делает, шлет мелкие запросы да качает данные, поэтому их и не измеряют.
          +1
          Последняя миля, представьте дайлап или нерасторопный пользователь, это не имеет отношение к мощности магистральной сети. В любой системе есть узкое место на которое не может повлиять ни один пользователь, это всегда магистраль. Не важно сколько труб втекает в магистральный трубопровод, если магистраль прокачивает 1000 tps, а локальные могут 10000 tps подтянуть к магистрали, они просто будут ждать.
            0
            В теоретическом плане — да, в теории интернет может предоставить неограниченное количество tps. Но что если я строю блокчейн, в котором клиент выполняет множество сложных вычислений, и при этом измеряю кучу метрик быстродействия? Для клиента важно, что его компьютер не успевает вычислять содержимое транзакций и отправлять их вовремя. Как мне не перепутать ситуацию, когда я уперся в блокчейн часть, с той, когда клиент тупо не успевает формировать запросы? Кроме того у многих проектов есть промежуточные сервера, например в LIghtning или Plasma. С точки зрения обычных сетей — наверное метрики клиентов не важны. Но в сетях, где клиенты выполняют большую часть процессинга надо измерять и на клиентах, также, уже виднеются на горизонте сети, где общая производительность будет почти складываться из производительностей клиентов, и там роль этих метрик еще возрастет.
              0
              Включаете систему на полную нагрузку, находите самое узкое место и высчитываете мощность системы.
                0
                а полная нагрузка зависит сильно от характера действий пользвателей. В случае сетей все просто — задача доставить данные дальше. В блокчейнах надо сначала запроцессить, а потом, если данные ок — передать дальше. И вот это «запроцессить» может быть как простейшей транзакцией по переводу криптовалюты, так и довольно тяжеловесным вызовом смарт-контракта, который занимает по полсекунды на исполнение, а может чатсью атаки на консенсус, которая вызовет активную «переписку» между валидаторами блокчейна. Поэтому внутри аналога «магистральной» в p2p сети все куда менее предсказуемо и сильнее зависит от характера клиентской нагрузки. Ну и «включаете систему на полную нагрузку» здесь трудно определить — какой профиль нагрузки считается «на полную»?
            0

            С таким же успехом можно сказать, что это проблема создания stateless client

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое