Goniliśmy za najniższym możliwym opóźnieniem. Budowaliśmy nową, elegancką funkcję portfela na devnecie Midnight, a kod był piękny.
Opracowaliśmy mistrzowską architekturę stanu — pojedynczą, kompleksową mapę, która zawierała wszystkie dane użytkowników. Była czysta. Była prosta. Była elegancka.
O 3:13 nad ranem, podczas testu obciążeniowego, ta elegancja stała się wadą.
Pustka
Zrealizowaliśmy dwa przelewy. Ten sam portfel. Różni odbiorcy. Na tradycyjnej sieci, to nie jest problem; drugi po prostu czeka na swoją kolej. Ale w równoległym środowisku wykonawczym Midnight, nie doświadczyliśmy opóźnienia.
Dostaliśmy pustkę.
Pierwsza transakcja zdobyła zamek zapisu na stanie tego portfela. Druga transakcja trafiła do mempoola, została uznana za ważną, a potem... została wstrzymana. Czekała na swoją kolej do zapisu, ale czas uciekał.
Okno slotu Midnight — ciasne 150ms — zamknęło się, zanim druga transakcja mogła zdobyć zamek i wykonać.
Transakcja nie zakończyła się błędem. Nie cofnęła się do nadawcy. Po prostu wyparowała. Brak alertu. Brak wpisu w eksploratorze bloków. Po prostu upiorna luka w naszej sekwencji, gdzie wartość miała się przesunąć.
To był moment, w którym zdaliśmy sobie sprawę, że na Midnight cichy zabójca nie jest przepustowością. To kolizja stanu.
Iluzja Sukcesu
Nauczyliśmy się trudnej prawdy tamtej nocy: W równoległych łańcuchach wykonywania, takich jak Midnight, "sukces" może wyglądać dokładnie jak "cicha strata."
Jeśli dwie transakcje dotykają tego samego konta w obrębie jednego slotu, jedna z nich znika bez śladu. Nie znajdziesz jej w logach. Sieć nie będzie krzyczeć. Odkrywasz krwotok tylko wtedy, gdy sprawdzasz bilans rano i znajdujesz ogromną dziurę tam, gdzie powinny być środki.
Problem nie leżał w naszej logice; leżał w naszej architekturze. Pakując cały stan użytkownika w jedną wielką mapę, stworzyliśmy jeden punkt sporu. Każda transakcja stała się wąskim gardłem, walcząc o dostęp do zapisu w tym samym fragmencie danych.
Rozwiązanie: Dziel lub Umieraj
Aby przetrwać na Midnight, musisz zmienić sposób myślenia o stanie. Musisz założyć, że spór jest wrogiem i zaprojektować swoje dane, aby unikać go za wszelką cenę.
1. Przyjmij Fragmentację
Przestań myśleć o stanie użytkownika jako o jednej monolitycznej jednostce. Rozdziel to. Podziel swój stan użytkownika na izolowane, niezależne segmenty. Jeśli użytkownik ma wiele aktywów lub działań, nie powinny one wszystkie mieszkać pod jednym dachem.
2. Grupowanie Nonce jest Twoim Przyjacielem
Zamiast blokować cały profil użytkownika, izoluj stan według grup nonce lub konkretnych obszarów operacyjnych. Transfer powinien blokować tylko konkretny UTXO lub pojemnik bilansu, którego potrzebuje — nie całą historię transakcji użytkownika.
3. Zawęż Ścieżkę Zapisu
Audytuj każdą linię swojej interakcji ze stanem. Jeśli transakcja nie musi absolutnie zapisywać do fragmentu danych, nie pozwól jej go dotknąć. Im węższa ścieżka zapisu, tym mniejsze szanse na kolizję.
Podwójnie Ostrze Miecza
Moc Midnight leży w jego równoległym wykonaniu, ale ta moc jest podwójnie ostrym mieczem. Nagradza tych, którzy projektują pod kątem współbieżności i karze tych, którzy wprowadzają monolityczne, dziedziczne mentalności łańcuchowe.
Przebudowaliśmy nasz model stanu. Podzieliliśmy monolityczną mapę na drobnoziarniste klucze, z których każdy reprezentuje odrębny zasób. Teraz współbieżne transfery skierowane do tego samego użytkownika przesuwają się do różnych slotów, nie walcząc o ten sam zamek.
Audytuj Przed Wdrożeniem
Audytuj swój stan przed wdrożeniem. Bo na Midnight, jeśli nie szanujesz czasu slotu, twoje transakcje nie tylko nie powiodą się — one znikną.
A cisza będzie najdroższym dźwiękiem, którego nigdy nie usłyszysz.
@MidnightNetwork $NIGHT #night
