Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Toto je pokračovanie dlhého príbehu o našej tŕnistej ceste k vytvoreniu výkonného, ​​vysoko zaťaženého systému, ktorý zabezpečuje chod burzy. Prvá časť je tu: habr.com/en/post/444300

Záhadná chyba

Po mnohých testoch bol sprevádzkovaný aktualizovaný systém obchodovania a zúčtovania a my sme narazili na chybu, o ktorej by sme mohli napísať detektívno-mystický príbeh.

Krátko po spustení na hlavnom serveri bola jedna z transakcií spracovaná s chybou. Na záložnom serveri však bolo všetko v poriadku. Ukázalo sa, že jednoduchá matematická operácia výpočtu exponentu na hlavnom serveri poskytla negatívny výsledok skutočného argumentu! Pokračovali sme vo výskume a v registri SSE2 sme našli rozdiel v jednom bite, ktorý je zodpovedný za zaokrúhľovanie pri práci s číslami s pohyblivou rádovou čiarkou.

Napísali sme jednoduchý testovací nástroj na výpočet exponentu s nastaveným zaokrúhľovacím bitom. Ukázalo sa, že vo verzii RedHat Linux, ktorú sme používali, sa vyskytla chyba v práci s matematickou funkciou, keď bol vložený nešťastný bit. Nahlásili sme to RedHatu, po chvíli sme od nich dostali patch a spustili sme ho. Chyba sa už nevyskytla, ale nebolo jasné, odkiaľ tento bit vôbec pochádza? Zodpovedala za to funkcia fesetround z jazyka C. Starostlivo sme analyzovali náš kód pri hľadaní predpokladanej chyby: skontrolovali sme všetky možné situácie; pozrel sa na všetky funkcie, ktoré používali zaokrúhľovanie; pokúsil sa reprodukovať neúspešnú reláciu; používa rôzne kompilátory s rôznymi možnosťami; Bola použitá statická a dynamická analýza.

Príčinu chyby sa nepodarilo nájsť.

Potom začali kontrolovať hardvér: vykonali záťažové testovanie procesorov; skontrolujte RAM; Dokonca sme testovali veľmi nepravdepodobný scenár viacbitovej chyby v jednej bunke. Bezvýsledne.

Nakoniec sme sa rozhodli pre teóriu zo sveta fyziky vysokých energií: nejaká vysokoenergetická častica vletela do nášho dátového centra, prerazila stenu puzdra, zasiahla procesor a spôsobila, že sa tam zapichla západka spúšte. Táto absurdná teória sa nazývala „neutríno“. Ak ste ďaleko od časticovej fyziky: neutrína takmer neinteragujú s vonkajším svetom a určite nie sú schopné ovplyvniť činnosť procesora.

Keďže nebolo možné zistiť príčinu poruchy, „provinilý“ server bol pre každý prípad odstránený z prevádzky.

Po určitom čase sme začali vylepšovať systém horúcej zálohy: zaviedli sme takzvané „teplé rezervy“ (teplé) - asynchrónne repliky. Dostali tok transakcií, ktoré mohli byť umiestnené v rôznych dátových centrách, ale warms aktívne neinteragoval s inými servermi.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Prečo sa to urobilo? Ak záložný server zlyhá, potom sa nové zálohovanie stane teplom viazaným na hlavný server. To znamená, že po zlyhaní systém nezostane s jedným hlavným serverom až do konca obchodnej relácie.

A keď bola nová verzia systému otestovaná a uvedená do prevádzky, chyba zaokrúhľovacieho bitu nastala opäť. Navyše s nárastom počtu teplých serverov sa chyba začala objavovať častejšie. Predajca zároveň nemal čo ukázať, keďže neexistovali žiadne konkrétne dôkazy.

Pri ďalšom rozbore situácie vznikla teória, že problém by mohol súvisieť s OS. Napísali sme jednoduchý program, ktorý volá funkciu v nekonečnej slučke fesetround, si zapamätá aktuálny stav a skontroluje ho cez spánok, a to sa robí v mnohých konkurenčných vláknach. Po výbere parametrov spánku a počtu vlákien sme začali dôsledne reprodukovať zlyhanie bitu po približne 5 minútach spustenia pomôcky. Podpora Red Hat ho však nedokázala reprodukovať. Testovanie našich ďalších serverov ukázalo, že k chybe sú náchylné iba servery s určitými procesormi. Zároveň prechod na nové jadro problém vyriešil. Nakoniec sme jednoducho vymenili OS a skutočná príčina chyby zostala nejasná.

A zrazu minulý rok vyšiel článok na Habré “Ako som našiel chybu v procesoroch Intel Skylake" Situácia v ňom opísaná bola veľmi podobná tej našej, ale autor potiahol vyšetrovanie ďalej a predložil teóriu, že chyba bola v mikrokóde. A keď sa aktualizujú jadrá Linuxu, výrobcovia aktualizujú aj mikrokód.

Ďalší vývoj systému

Hoci sme sa chyby zbavili, tento príbeh nás prinútil prehodnotiť architektúru systému. Neboli sme predsa chránení pred opakovaním takýchto chýb.

Nasledujúce princípy tvorili základ pre ďalšie vylepšenia rezervačného systému:

  • Nemôžete veriť nikomu. Servery nemusia fungovať správne.
  • Väčšinová výhrada.
  • Zabezpečenie konsenzu. Ako logický doplnok k väčšinovej výhrade.
  • Možné sú dvojité zlyhania.
  • Vitalita. Nová schéma horúceho pohotovostného režimu by nemala byť horšia ako predchádzajúca. Obchodovanie by malo pokračovať bez prerušenia až do posledného servera.
  • Mierne zvýšenie latencie. Akékoľvek prestoje so sebou prinášajú obrovské finančné straty.
  • Minimálna interakcia so sieťou na udržanie čo najnižšej latencie.
  • Výber nového hlavného servera za pár sekúnd.

Žiadne z riešení dostupných na trhu nám nevyhovovalo a protokol Raft bol ešte len v plienkach, preto sme si vytvorili vlastné riešenie.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

vytváranie sietí

Okrem rezervačného systému sme začali modernizovať sieťovú interakciu. I/O subsystém pozostával z mnohých procesov, ktoré mali najhorší dopad na jitter a latenciu. Pri stovkách procesov spracovávajúcich TCP spojenia sme boli nútení medzi nimi neustále prepínať a v mikrosekundovom meradle je to dosť časovo náročná operácia. Najhoršie na tom však je, že keď proces prijal paket na spracovanie, poslal ho do jedného frontu SystemV a potom čakal na udalosť z iného frontu SystemV. Keď však existuje veľký počet uzlov, príchod nového paketu TCP v jednom procese a príjem údajov vo fronte v inom predstavujú pre OS dve konkurenčné udalosti. V tomto prípade, ak nie sú k dispozícii žiadne fyzické procesory pre obe úlohy, jeden sa spracuje a druhý sa zaradí do čakacej fronty. Nie je možné predvídať dôsledky.

V takýchto situáciách možno použiť dynamické riadenie priority procesu, ale to si bude vyžadovať použitie systémových volaní náročných na zdroje. V dôsledku toho sme prešli na jedno vlákno pomocou klasického epollu, čo výrazne zvýšilo rýchlosť a skrátilo čas spracovania transakcie. Zbavili sme sa aj samostatných procesov sieťovej komunikácie a komunikácie cez SystemV, výrazne sme znížili počet systémových volaní a začali kontrolovať priority operácií. Len na I/O subsystéme bolo možné ušetriť približne 8-17 mikrosekúnd, v závislosti od scenára. Táto jednovláknová schéma sa odvtedy používa nezmenená; jedno vlákno epoll s okrajom stačí na obsluhu všetkých pripojení.

Spracovanie transakcie

Rastúce zaťaženie nášho systému si vyžiadalo upgrade takmer všetkých jeho komponentov. Stagnácia rastu rýchlostí procesorov v posledných rokoch však, žiaľ, už neumožňuje škálovať procesy bezhlavo. Preto sme sa rozhodli rozdeliť proces Engine do troch úrovní, pričom najvyťaženejšou z nich je systém kontroly rizika, ktorý vyhodnocuje dostupnosť prostriedkov na účtoch a vytvára samotné transakcie. Peniaze ale môžu byť v rôznych menách a bolo potrebné vymyslieť, na základe čoho by sa malo spracovanie žiadostí rozdeliť.

Logickým riešením je rozdeliť ho podľa meny: jeden server obchoduje v dolároch, druhý v librách a tretí v eurách. Ak sa však pri takejto schéme odošlú dve transakcie na nákup rôznych mien, vznikne problém s desynchronizáciou peňaženky. Synchronizácia je však náročná a drahá. Preto by bolo správne čriepať zvlášť po peňaženkách a zvlášť po nástrojoch. Mimochodom, väčšina západných búrz nemá za úlohu kontrolovať riziká tak akútne ako my, takže najčastejšie sa to robí offline. Potrebovali sme implementovať online overenie.

Vysvetlíme si to na príklade. Obchodník chce kúpiť 30 USD a požiadavka prejde na overenie transakcie: skontrolujeme, či má tento obchodník povolený tento obchodný režim a či má potrebné práva. Ak je všetko v poriadku, požiadavka ide do systému overovania rizika, t.j. skontrolovať dostatok finančných prostriedkov na uzavretie transakcie. Je tam poznámka, že požadovaná suma je momentálne zablokovaná. Požiadavka je následne postúpená obchodnému systému, ktorý transakciu schváli alebo neschváli. Povedzme, že transakcia je schválená - potom systém overovania rizika označí, že peniaze sú odblokované a ruble sa premenia na doláre.

Vo všeobecnosti systém kontroly rizík obsahuje zložité algoritmy a vykonáva veľké množstvo výpočtov veľmi náročných na zdroje a nekontroluje len „zostatok účtu“, ako by sa na prvý pohľad mohlo zdať.

Keď sme začali proces Engine rozdeľovať na úrovne, narazili sme na problém: kód, ktorý bol v tom čase k dispozícii, aktívne využíval rovnaké pole údajov vo fázach validácie a overovania, čo si vyžadovalo prepísanie celej kódovej základne. V dôsledku toho sme si požičali techniku ​​spracovania pokynov od moderných procesorov: každá z nich je rozdelená na malé etapy a niekoľko akcií sa vykonáva paralelne v jednom cykle.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Po malej úprave kódu sme vytvorili pipeline pre paralelné spracovanie transakcií, v ktorom bola transakcia rozdelená do 4 fáz pipeline: sieťová interakcia, validácia, realizácia a zverejnenie výsledku

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Pozrime sa na príklad. Máme dva systémy spracovania, sériový a paralelný. Prvá transakcia príde a odošle sa na overenie v oboch systémoch. Okamžite prichádza druhá transakcia: v paralelnom systéme sa okamžite spustí a v sekvenčnom systéme sa zaradí do radu, kým prvá transakcia prejde aktuálnou fázou spracovania. To znamená, že hlavnou výhodou spracovania potrubia je, že spracovávame front transakcií rýchlejšie.

Takto sme prišli so systémom ASTS+.

Pravda, ani s dopravníkmi nie je všetko také hladké. Povedzme, že máme transakciu, ktorá ovplyvňuje dátové polia v susednej transakcii; toto je typická situácia pre výmenu. Takáto transakcia nemôže byť vykonaná v potrubí, pretože môže ovplyvniť ostatných. Táto situácia sa nazýva dátové riziko a takéto transakcie sa jednoducho spracovávajú oddelene: keď sa minú „rýchle“ transakcie vo fronte, potrubie sa zastaví, systém spracuje „pomalú“ transakciu a potom znova spustí potrubie. Našťastie podiel takýchto transakcií na celkovom toku je veľmi malý, takže potrubie sa zastaví tak zriedka, že to neovplyvní celkový výkon.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Potom sme začali riešiť problém synchronizácie troch vlákien vykonávania. Výsledkom bol systém založený na kruhovom pufri s bunkami s pevnou veľkosťou. V tomto systéme všetko podlieha rýchlosti spracovania, dáta sa nekopírujú.

  • Všetky prichádzajúce sieťové pakety vstupujú do fázy prideľovania.
  • Umiestnime ich do poľa a označíme ich ako dostupné pre fázu #1.
  • Prišla druhá transakcia, opäť je dostupná pre etapu č.1.
  • Prvé vlákno spracovania vidí dostupné transakcie, spracuje ich a presunie ich do ďalšej fázy druhého vlákna spracovania.
  • Potom spracuje prvú transakciu a označí príslušnú bunku príznakom deleted — teraz je k dispozícii na nové použitie.

Týmto spôsobom sa spracuje celý rad.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Spracovanie každej fázy trvá jednotky alebo desiatky mikrosekúnd. A ak použijeme štandardné schémy synchronizácie OS, stratíme viac času na samotnú synchronizáciu. Preto sme začali používať spinlock. Toto je však veľmi zlá forma v systéme v reálnom čase a RedHat to striktne neodporúča, takže aplikujeme spinlock na 100 ms a potom prepneme do semaforového režimu, aby sme eliminovali možnosť uviaznutia.

Výsledkom je, že sme dosiahli výkon okolo 8 miliónov transakcií za sekundu. A to doslova o dva mesiace neskôr článok o LMAX Disruptor sme videli popis obvodu s rovnakou funkcionalitou.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Teraz by mohlo byť niekoľko vlákien vykonávania v jednej fáze. Všetky transakcie boli spracované jeden po druhom v poradí, v akom boli prijaté. V dôsledku toho sa špičkový výkon zvýšil z 18 tisíc na 50 tisíc transakcií za sekundu.

Systém riadenia burzových rizík

Dokonalosti sa medze nekladú a čoskoro sme opäť začali s modernizáciou: v rámci ASTS+ sme začali presúvať systémy riadenia rizík a zúčtovacích operácií do autonómnych komponentov. Vyvinuli sme flexibilnú modernú architektúru a nový hierarchický model rizika a snažili sme sa použiť triedu všade, kde to bolo možné fixed_point namiesto double.

Okamžite však vyvstal problém: ako zosynchronizovať všetku obchodnú logiku, ktorá funguje už mnoho rokov, a preniesť ju do nového systému? V dôsledku toho sa musela prvá verzia prototypu nového systému opustiť. Druhá verzia, ktorá je momentálne vo výrobe, je založená na rovnakom kóde, ktorý funguje v obchodnej aj rizikovej časti. Počas vývoja bolo najťažšie urobiť zlúčenie git medzi dvoma verziami. Náš kolega Evgeniy Mazurenok robil túto operáciu každý týždeň a zakaždým veľmi dlho nadával.

Pri výbere nového systému sme okamžite museli riešiť problém interakcie. Pri výbere dátovej zbernice bolo potrebné zabezpečiť stabilný jitter a minimálnu latenciu. Sieť InfiniBand RDMA bola na to najvhodnejšia: priemerný čas spracovania je 4-krát kratší ako v sieťach 10 G Ethernet. Čo nás ale naozaj uchvátilo, bol rozdiel v percentiloch – 99 a 99,9.

Samozrejme, InfiniBand má svoje výzvy. Po prvé, iné API - ibverbs namiesto socketov. Po druhé, neexistujú takmer žiadne široko dostupné riešenia na odosielanie správ s otvoreným zdrojom. Pokúšali sme sa vyrobiť vlastný prototyp, no ukázalo sa, že je to veľmi náročné, a tak sme zvolili komerčné riešenie – Confinity Low Latency Messaging (predtým IBM MQ LLM).

Potom vyvstala úloha správne rozdeliť rizikový systém. Ak jednoducho odstránite Risk Engine a nevytvoríte medziľahlý uzol, transakcie z dvoch zdrojov môžu byť zmiešané.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Takzvané riešenia s ultra nízkou latenciou majú režim preobjednávania: transakcie z dvoch zdrojov je možné po prijatí usporiadať v požadovanom poradí, čo sa realizuje pomocou samostatného kanála na výmenu informácií o objednávke. Tento režim však zatiaľ nepoužívame: komplikuje celý proces a v mnohých riešeniach nie je vôbec podporovaný. Okrem toho by každej transakcii museli byť priradené zodpovedajúce časové pečiatky a v našej schéme je tento mechanizmus veľmi ťažké správne implementovať. Použili sme preto klasickú schému s brokerom správ, teda s dispečerom, ktorý distribuuje správy medzi Risk Engine.

Druhý problém súvisel s prístupom klienta: ak existuje niekoľko brán rizika, klient sa musí pripojiť ku každej z nich, čo si bude vyžadovať zmeny na klientskej vrstve. V tejto fáze sme sa tomu chceli vyhnúť, takže súčasný návrh brány rizík spracováva celý dátový tok. To výrazne obmedzuje maximálnu priepustnosť, ale výrazne zjednodušuje integráciu systému.

zdvojenie

Náš systém by nemal mať jediný bod zlyhania, to znamená, že všetky komponenty musia byť duplikované, vrátane sprostredkovateľa správ. Tento problém sme vyriešili pomocou systému CLLM: obsahuje RCMS klaster, v ktorom môžu dvaja dispečeri pracovať v režime master-slave a keď jeden zlyhá, systém sa automaticky prepne na druhý.

Práca so záložným dátovým centrom

InfiniBand je optimalizovaný na prevádzku ako lokálna sieť, teda na pripojenie zariadení na montáž do racku, pričom sieť InfiniBand nie je možné umiestniť medzi dve geograficky distribuované dátové centrá. Preto sme implementovali bridge/dispečer, ktorý sa pripája k úložisku správ cez bežné ethernetové siete a prenáša všetky transakcie do druhej IB siete. Keď potrebujeme migrovať z dátového centra, môžeme si vybrať, s ktorým dátovým centrom budeme teraz pracovať.

Výsledky

Všetko vyššie uvedené sa neuskutočnilo naraz, vývoj novej architektúry si vyžiadal niekoľko iterácií. Prototyp sme vytvorili za mesiac, no dostať ho do funkčného stavu trvalo viac ako dva roky. Snažili sme sa dosiahnuť najlepší kompromis medzi zvýšením času spracovania transakcií a zvýšením spoľahlivosti systému.

Keďže systém bol intenzívne aktualizovaný, implementovali sme obnovu dát z dvoch nezávislých zdrojov. Ak úložisko správ z nejakého dôvodu nefunguje správne, môžete získať protokol transakcií z druhého zdroja – z Risk Engine. Tento princíp sa dodržiava v celom systéme.

Okrem iného sa nám podarilo zachovať klientske API tak, aby makléri ani nikto iný nevyžadoval výrazné prepracovanie novej architektúry. Museli sme zmeniť niektoré rozhrania, ale nebolo potrebné robiť zásadné zmeny v prevádzkovom modeli.

Súčasnú verziu našej platformy sme nazvali Rebus – ako skratku pre dve najvýraznejšie inovácie v architektúre, Risk Engine a BUS.

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Pôvodne sme chceli vyčleniť len zúčtovaciu časť, no výsledkom bol obrovský distribuovaný systém. Klienti teraz môžu interagovať buď s Trade Gateway, Clearing Gateway, alebo s oboma.

Čo sme nakoniec dosiahli:

Vývoj architektúry obchodného a zúčtovacieho systému Moskovskej burzy. Časť 2

Znížená úroveň latencie. Pri malom objeme transakcií systém funguje rovnako ako predchádzajúca verzia, no zároveň znesie oveľa vyššiu záťaž.

Špičkový výkon vzrástol z 50 tisíc na 180 tisíc transakcií za sekundu. Ďalšiemu nárastu bráni jediný prúd párovania objednávok.

Existujú dva spôsoby ďalšieho zlepšenia: paralelizácia párovania a zmena spôsobu, akým funguje s bránou. Teraz všetky brány fungujú podľa schémy replikácie, ktorá pri takomto zaťažení prestáva normálne fungovať.

Na záver môžem dať pár rád tým, ktorí dokončujú podnikové systémy:

  • Buďte vždy pripravení na najhoršie. Problémy sa vždy objavia nečakane.
  • Rýchlo prerobiť architektúru je zvyčajne nemožné. Najmä ak potrebujete dosiahnuť maximálnu spoľahlivosť naprieč viacerými ukazovateľmi. Čím viac uzlov, tým viac zdrojov potrebných na podporu.
  • Všetky vlastné a proprietárne riešenia budú vyžadovať dodatočné zdroje na výskum, podporu a údržbu.
  • Neodkladajte vyriešenie problémov so spoľahlivosťou systému a obnovou po poruchách; vezmite ich do úvahy v počiatočnej fáze návrhu.

Zdroj: hab.com

Pridať komentár