Любимым вопросом о любой распределенной системе от нетехнического специалиста является “Сколько tps в вашем блокчейне?”. Однако, названное в ответ число обычно имеет мало общего с тем, что хотел бы услышать вопрошающий. На деле, он хотел спросить “подойдет ли ваш блокчейн под мои бизнес требования”, и эти требования — это не одно число, а множество условий — здесь и отказоустойчивость сети, и требования к финальности, размеры, характер транзакций и множество других параметров. Так что ответ на вопрос “сколько tps” вряд ли будет простым, и почти никогда не будет полным. Распределенная система с десятками и сотнями узлов, выполняющих довольно сложные вычисления, может находиться в огромном количестве различных состояний, связанных с состоянием сети, содержимым блокчейна, техническими сбоями, экономическими проблемами, атаками на сеть и множеством других причин. Этапы, на которых возможны проблемы с производительностью отличаются от традиционных сервисов, а сервер блокчейн-сети — это сетевой сервис, сочетающий в себе функционал базы данных, web-сервера и torrent-клиента, что делает его крайне сложным в плане профиля нагрузки на все подсистемы: процессор, память, сеть, storage Так получилось, что децентрализованные сети и блокчейны являются довольно специфическим и непривычным софтом для разработчиков централизованного ПО. Поэтому я хотел бы осветить важные аспекты производительности и устойчивости децентрализованных сетей, подходы к их измерению и нахождению bottlenecks. Мы рассмотрим различные проблемы производительности, ограничивающие скорость предоставления сервиса пользователям блокчейнов и отметим особенности, характерные для этого вида софта. Для того, чтобы честно говорить о качестве любого более-менее сложного сервиса, нужно учесть не только средние значения, но и максимальные/минимальные, медианы, персентили. Теоретически, можно говорить о 1000 tps в каком-нибудь блокчейне, но если 900 транзакций выполнились с огромной скоростью, а 100 — "зависли" на несколько секунд, то среднее время, собранное по всем транзакциям — это не совсем честная метрика для клиента, который за несколько секунд не смог завершить сделку. Временные "ямы", вызванные пропущенными раундами консенсуса или разделением сети могут сильно испортить сервис, который на тестовых стендах показывал прекрасную производительность. Чтобы идентифицировать такие bottleneck-и необходимо хорошо понимать этапы, на которых реальный блокчейн может испытывать затруднения при обслуживании пользователей. Давайте опишем цикл доставки и процессинга транзакции, а также получения нового состояния блокчейна, из которого клиент может убедиться, что его транзакция была обработана и учтена. Теперь давайте подробней разберем эти этапы и опишем потенциальные проблемы с производительностью на каждом этапе. В отличие от централизованных систем, мы также рассмотрим исполнение кода на клиентах сети. Довольно часто при измерении 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 нужно также собирать метрики с клиентских машин, а не только с блокчейн-нод. В блокчейнах для передачи между участниками транзакций и блоков используется 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. Этот этап, в плане определения bottleneck проще, чем сетевой p2p слой, т.к. исполнение транзакций и алгоритм консенсуса строго детерминированы, и измерять что-либо здесь проще.
Главное — не спутать случайную деградацию производительности этого этапа с проблемами сети — ноды медленней отдают блоки и информацию об основной цепочке и для внешнего клиента это может выглядеть как медленная сеть, хотя проблема кроется совсем в другом месте. Для оптимизации производительности на этом этапе полезно собирать и мониторить метрики с самих нод, и включать в них те, которые касаются обновления state-datаbase: число блоков, обрабатываемых на ноде, их размер, число транзакций, количество переключений между форками цепочек, число невалидных блоков, время работы виртуальной машины, время фиксации данных и т.д. Это позволит не спутать сетевые проблемы с ошибками в алгоритмах процессинга цепочек. Процессящая транзакции виртуальная машина может быть полезным источником информации, способной оптимизировать работу блокчейна. Количествo аллокаций памяти, количество read/write инструкций, и другие метрики, касающиеся эффективности исполнения кода контрактов могут дать много полезной информации разработчикам. В то же время, смарт-контракты — это программы, а значит в теории они могут потреблять любые из ресурсов: cpu/memory/network/storage, так что процессинг транзакций — довольно неопределенный этап, который вдобавок сильно меняется при переходе между версиями и при изменении кода контрактов. Поэтому метрики, касающиеся процессинга транзакций также нужны для эффективной оптимизации производительности блокчейна. Это завершающий этап получения сервиса клиентом блокчейна, по сравнению с другими этапами здесь нет больших накладных расходов, но все равно стоит учитывать возможность получения клиентом объемного ответа от ноды (например смарт-контракт, отдающий массив данных). В любом случае, именно этот момент является самым важным для того, кто задал вопрос "а сколько tps в вашем блокчейне ?", т.к. в этот момент и фиксируется время получения сервиса.  В это месте обязательно присутствует отправка полного времени, которое пришлось затратить клиенту на ожидание ответа от блокчейна, именно это время пользователь будет ожидать подтверждения в своем приложении, и именно его оптимизация является основной задачей разработчиков. В результате, можно описать типы операций, выполняющихся в блокчейнах и разделить их на несколько категорий: Вообще технические требования к нодам современных блокчейнов крайне серьезные — это быстрые CPU для криптографии, большой объем оперативной памяти для того, чтобы хранить и быстро обращаться к state database, сетевое взаимодействие, использующее большое число одновременно открытых соединений, объемный storage. Такие высокие требования и обилие различных типов операций неизбежно приводят к тому, что ресурсов у нод может не хватать, и, тогда любой из рассмотренных выше этапов может стать очередным bottleneck-ом для общей производительности сети. Разрабатывая и оценивая производительность блокчейнов, вам придется учитывать все эти моменты. Для этого нужно собирать и анализировать метрики одновременно с клиентов и нод сети, искать корреляции между ними, оценивать время предоставления сервиса клиентам, учитывать все основные ресурсы: cpu/memory/network/storage, понимать как они используются и влияют друг на друга. Все это делает сравнение скоростей различных блокчейнов в виде "сколько TPS" крайне неблагодарным занятием, так как существует огромное количество различных конфигураций и состояний. В больших централизованных системах, кластерах из сотен серверов, эти проблемы также сложны и также требуют сбора большого числа различных метрик, но в блокчейнах, из за p2p сетей, виртуальных машин, процессящих контракты, внутренней экономики, число степеней свободы гораздо больше, что делает тест даже на нескольких серверах непоказательным и показывающим лишь крайне примерные значения, почти не имеющие связи с реальностью. Поэтому, при разработке в ядре блокчейна, для оценки производительности и ответа на вопрос "улучшилось ли по сравнению с прошлым разом" мы используем довольно сложный софт, оркестрирующий запуск блокчейна с десятками узлов и автоматическим запуском бенчмарка и сбором метрик, без этой информации крайне сложно отлаживать протоколы, работающие со множеством участников. Так что, получив вопрос "сколько TPS в вашем блокчейне?", предложите собеседнику чаю и уточните, готов ли он ознакомиться с десятком графиков а также выслушать все три короба проблем производительности блокчейнов и вашими предложения по их решению... Software Researcher