Мы стремились к наименьшей возможной задержке. Мы создавали новый стильный функционал кошелька на devnet Midnight, и код был прекрасен.
Мы разработали мастерскую архитектуру состояния — единую, всеобъемлющую карту, которая хранила все данные пользователей. Это было чисто. Это было просто. Это было элегантно.
В 3:13 AM, во время стресс-теста, эта элегантность стала обременением.
Пустота
Мы отправили два перевода. Один и тот же кошелек. Разные получатели. На традиционной цепочке это не проблема; второй просто ждет своей очереди. Но в параллельной среде исполнения Midnight у нас не было задержки.
У нас образовалась пустота.
Первая транзакция захватила блокировку на запись состояния этого кошелька. Вторая транзакция попала в мемпул, была признана действительной и затем… встала в очередь. Она ждала своей очереди на запись, но время шло.
Окно слота Midnight — это строгие 150 мс — закрылось, прежде чем вторая транзакция смогла получить блокировку и выполнить операцию.
Транзакция не завершилась с кодом ошибки. Она не вернулась к отправителю. Она просто испарилась. Никаких оповещений. Никакой записи в блокчейне. Просто призрачный пробел в нашей последовательности, где значение должно было перемещаться.
Это был момент, когда мы поняли, что на Midnight тихий убийца — это не пропускная способность. Это коллизия состояния.
Иллюзия успеха
Мы узнали жесткую правду той ночью: в параллельных цепочках выполнения, таких как Midnight, "успех" может выглядеть точно так же, как "тихая утрата."
Если две транзакции затрагивают один и тот же аккаунт в одном слоте, одна из них исчезает без следа. Вы не найдете ее в журналах. Сеть не закричит на вас. Вы только обнаружите утечку, когда проверите баланс на рассвете и увидите зияющую дыру, где должны быть средства.
Проблема заключалась не в нашей логике; это была наша архитектура. Упаковав все пользовательские состояния в одну огромную карту, мы создали единую точку конфликта. Каждая транзакция стала узким местом, борющимся за доступ к записи в одном и том же блоке данных.
Исправление: Шардинг или Смерть
Чтобы выжить на Midnight, вы должны изменить свое представление о состоянии. Вы должны предположить, что конфликт — это враг, и проектировать свои данные так, чтобы избежать его любой ценой.
1. Примите фрагментацию
Перестаньте рассматривать состояние пользователя как единую монолитную сущность. Разбейте его на части. Разделите ваше состояние пользователя на изолированные, независимые сегменты. Если у пользователя есть несколько активов или действий, они не должны все находиться под одной крышей.
2. Группировка nonce — ваш друг
Вместо того чтобы блокировать целый пользовательский профиль, изолируйте состояние по группам nonce или конкретным операционным доменам. Перевод должен блокировать только конкретный UTXO или ведро баланса, которое ему нужно — не всю историю транзакций пользователя.
3. Уменьшите путь записи
Аудируйте каждую строку вашего взаимодействия с состоянием. Если транзакция не должна абсолютно записывать данные, не позволяйте ей касаться их. Чем уже путь записи, тем меньше вероятность коллизии.
Двусторонний меч
Сила Midnight заключается в ее параллельном выполнении, но эта сила — двусторонний меч. Она вознаграждает тех, кто разрабатывает для параллелизма, и наказывает тех, кто приносит монолитные, устаревшие цепные менталитеты.
Мы пересобрали нашу модель состояния. Мы разделили монолитную карту на тонкие ключи, каждый из которых представляет собой отдельный ресурс. Теперь параллельные переводы, нацеленные на одного и того же пользователя, размещаются в разных слотах, не борясь за одну и ту же блокировку.
Аудит перед развертыванием
Аудируйте свое состояние перед развертыванием. Потому что на Midnight, если вы не уважаете время слота, ваши транзакции не просто провалятся — они исчезнут.
И тишина будет самым дорогим звуком, который вы никогда не услышите.
@MidnightNetwork $NIGHT #night
