Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Toto je pokračování dlouhého příběhu o naší trnité cestě k vytvoření výkonného, ​​vysoce zatěžovaného systému, který zajišťuje chod Burzy. První část je zde: habr.com/en/post/444300

Záhadná chyba

Po četných testech byl zprovozněn aktualizovaný obchodní a clearingový systém a narazili jsme na chybu, o které bychom mohli napsat detektivně-mystický příběh.

Krátce po spuštění na hlavním serveru byla jedna z transakcí zpracována s chybou. Na záložním serveru však bylo vše v pořádku. Ukázalo se, že jednoduchá matematická operace výpočtu exponentu na hlavním serveru dala negativní výsledek skutečného argumentu! Pokračovali jsme ve výzkumu a v registru SSE2 jsme našli rozdíl v jednom bitu, který je zodpovědný za zaokrouhlování při práci s čísly s pohyblivou řádovou čárkou.

Napsali jsme jednoduchý testovací nástroj pro výpočet exponentu s nastaveným zaokrouhlovacím bitem. Ukázalo se, že ve verzi RedHat Linuxu, kterou jsme používali, byla chyba v práci s matematickou funkcí, když byl vložen nešťastný bit. Nahlásili jsme to RedHatu, po chvíli jsme od nich dostali patch a spustili ho. Chyba se již nevyskytovala, ale nebylo jasné, odkud se tento bit vůbec vzal? Zodpovídala za to funkce fesetround z jazyka C. Pečlivě jsme analyzovali náš kód při hledání předpokládané chyby: zkontrolovali jsme všechny možné situace; podíval se na všechny funkce, které používaly zaokrouhlování; pokusil se reprodukovat neúspěšnou relaci; používal různé kompilátory s různými možnostmi; Byla použita statická a dynamická analýza.

Příčina chyby nebyla nalezena.

Poté začali s kontrolou hardwaru: provedli zátěžové testování procesorů; zkontrolovat RAM; Provedli jsme dokonce testy pro velmi nepravděpodobný scénář vícebitové chyby v jedné buňce. Bezvýsledně.

Nakonec jsme se rozhodli pro teorii ze světa fyziky vysokých energií: nějaká vysokoenergetická částice vletěla do našeho datového centra, prorazila stěnu pouzdra, narazila do procesoru a způsobila, že se tam zastrčila západka spouště. Tato absurdní teorie se nazývala „neutrino“. Pokud jste daleko od částicové fyziky: neutrina téměř neinteragují s vnějším světem a rozhodně nejsou schopna ovlivnit činnost procesoru.

Protože nebylo možné zjistit příčinu poruchy, byl „provinilý“ server pro každý případ odstraněn z provozu.

Po nějaké době jsme začali vylepšovat systém horké zálohy: zavedli jsme takzvané „teplé rezervy“ (teplé) – asynchronní repliky. Obdrželi proud transakcí, které mohly být umístěny v různých datových centrech, ale s jinými servery aktivně neinteragovaly.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Proč se to udělalo? Pokud záložní server selže, pak se nové zálohování stane teplem spojeným s hlavním serverem. To znamená, že po selhání systém nezůstane u jednoho hlavního serveru až do konce obchodní seance.

A když byla nová verze systému otestována a uvedena do provozu, chyba zaokrouhlovacího bitu opět nastala. Navíc s nárůstem počtu teplých serverů se chyba začala objevovat častěji. Prodejce přitom neměl co ukázat, protože neexistoval žádný konkrétní důkaz.

Při dalším rozboru situace vznikla teorie, že by problém mohl souviset s OS. Napsali jsme jednoduchý program, který volá funkci v nekonečné smyčce fesetround, si pamatuje aktuální stav a kontroluje jej pomocí spánku, a to se děje v mnoha konkurenčních vláknech. Po výběru parametrů pro režim spánku a počtu vláken jsme začali důsledně reprodukovat selhání bitu po přibližně 5 minutách spuštění nástroje. Podpora Red Hat jej však nedokázala reprodukovat. Testování ostatních našich serverů ukázalo, že k chybě jsou náchylné pouze servery s určitými procesory. Zároveň přechod na nové jádro problém vyřešil. Nakonec jsme prostě vyměnili OS a skutečná příčina chyby zůstala nejasná.

A najednou loni vyšel článek na Habré “Jak jsem našel chybu v procesorech Intel Skylake" Situace v něm popsaná byla velmi podobná té naší, ale autor šel vyšetřování dále a předložil teorii, že chyba byla v mikrokódu. A když se aktualizují jádra Linuxu, výrobci aktualizují také mikrokód.

Další vývoj systému

Přestože jsme se chyby zbavili, tento příběh nás donutil přehodnotit architekturu systému. Nebyli jsme přece chráněni před opakováním takových chyb.

Následující principy vytvořily základ pro další vylepšení rezervačního systému:

  • Nemůžeš nikomu věřit. Servery nemusí fungovat správně.
  • Většinová výhrada.
  • Zajištění konsenzu. Jako logický doplněk k většinové výhradě.
  • Jsou možné dvojité poruchy.
  • Vitalita. Nové schéma horkého pohotovostního režimu by nemělo být horší než předchozí. Obchodování by mělo probíhat bez přerušení až do posledního serveru.
  • Mírné zvýšení latence. Jakýkoli výpadek s sebou nese obrovské finanční ztráty.
  • Minimální interakce se sítí pro udržení co nejnižší latence.
  • Výběr nového hlavního serveru během několika sekund.

Žádné z řešení dostupných na trhu nám nevyhovovalo a protokol Raft byl teprve v plenkách, takže jsme vytvořili vlastní řešení.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

vytváření sítí

Kromě rezervačního systému jsme začali modernizovat síťovou interakci. I/O subsystém se skládal z mnoha procesů, které měly nejhorší dopad na jitter a latenci. Se stovkami procesů, které obsluhují TCP spojení, jsme byli nuceni mezi nimi neustále přepínat a v mikrosekundovém měřítku jde o časově poměrně náročnou operaci. Ale nejhorší na tom je, že když proces přijal paket ke zpracování, odeslal jej do jedné fronty SystemV a pak čekal na událost z jiné fronty SystemV. Pokud však existuje velký počet uzlů, příchod nového paketu TCP v jednom procesu a příjem dat ve frontě v jiném představují pro OS dvě konkurenční události. V tomto případě, pokud pro obě úlohy nejsou k dispozici žádné fyzické procesory, bude jeden zpracován a druhý bude zařazen do fronty čekajících. Předvídat následky je nemožné.

V takových situacích lze použít dynamické řízení priority procesu, ale to bude vyžadovat použití systémových volání náročných na zdroje. V důsledku toho jsme přešli na jedno vlákno pomocí klasického epollu, což výrazně zvýšilo rychlost a zkrátilo dobu zpracování transakce. Zbavili jsme se také samostatných procesů síťové komunikace a komunikace přes SystemV, výrazně jsme snížili počet systémových volání a začali řídit priority operací. Na samotném I/O subsystému bylo možné ušetřit asi 8-17 mikrosekund, v závislosti na scénáři. Toto jednovláknové schéma se od té doby používá beze změny, k obsluze všech připojení stačí jedno vlákno epoll s okrajem.

Zpracování transakcí

Rostoucí zatížení našeho systému si vyžádalo upgrade téměř všech jeho součástí. Ale bohužel stagnace růstu taktů procesorů v posledních letech již neumožňovala škálovat procesy bezhlavě. Proto jsme se rozhodli proces Engine rozdělit do tří úrovní, přičemž nejvytíženější z nich je systém kontroly rizik, který vyhodnocuje dostupnost prostředků na účtech a vytváří samotné transakce. Peníze ale mohou být v různých měnách a bylo potřeba vymyslet, na jakém základě by se zpracování žádostí mělo rozdělit.

Logickým řešením je rozdělení podle měny: jeden server obchoduje v dolarech, druhý v librách a třetí v eurech. Ale pokud jsou s takovým schématem odeslány dvě transakce k nákupu různých měn, pak nastane problém desynchronizace peněženky. Synchronizace je ale obtížná a drahá. Správné by tedy bylo stříhat zvlášť po peněženkách a zvlášť po přístrojích. Mimochodem, většina západních burz nemá za úkol kontrolovat rizika tak intenzivně jako my, takže se to nejčastěji provádí offline. Potřebovali jsme implementovat online ověřování.

Vysvětlíme si to na příkladu. Obchodník chce koupit 30 USD a požadavek přejde na ověření transakce: zkontrolujeme, zda má tento obchodník povolen tento obchodní režim a zda má potřebná práva. Pokud je vše v pořádku, požadavek jde do systému ověřování rizik, tzn. zkontrolovat dostatek finančních prostředků k uzavření transakce. Je tam poznámka, že požadovaná částka je aktuálně blokována. Požadavek je poté předán obchodnímu systému, který transakci schválí nebo neschválí. Řekněme, že transakce je schválena – pak systém ověřování rizik označí, že peníze jsou odblokovány, a rubly se změní na dolary.

Obecně platí, že systém kontroly rizik obsahuje složité algoritmy a provádí velké množství výpočtů velmi náročných na zdroje a nekontroluje pouze „zůstatek účtu“, jak by se na první pohled mohlo zdát.

Když jsme začali proces Engine rozdělovat na úrovně, narazili jsme na problém: kód, který byl v té době k dispozici, aktivně využíval stejné pole dat ve fázích validace a ověřování, což vyžadovalo přepsání celé kódové základny. V důsledku toho jsme si vypůjčili techniku ​​pro zpracování instrukcí od moderních procesorů: každý z nich je rozdělen do malých fází a několik akcí se provádí paralelně v jednom cyklu.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Po malé úpravě kódu jsme vytvořili pipeline pro paralelní zpracování transakcí, ve kterém byla transakce rozdělena do 4 fází pipeline: síťová interakce, validace, provedení a zveřejnění výsledku

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Podívejme se na příklad. Máme dva systémy zpracování, sériový a paralelní. První transakce přijde a je odeslána k ověření v obou systémech. Okamžitě přichází druhá transakce: v paralelním systému je okamžitě uvedena do provozu a v sekvenčním systému je zařazena do fronty a čeká, až první transakce projde aktuální fází zpracování. To znamená, že hlavní výhodou zpracování potrubí je, že zpracujeme frontu transakcí rychleji.

Takto jsme přišli se systémem ASTS+.

Pravda, ani u dopravníků není vše tak hladké. Řekněme, že máme transakci, která ovlivňuje datová pole v sousední transakci; toto je typická situace pro směnu. Takovou transakci nelze provést v potrubí, protože může ovlivnit ostatní. Této situaci se říká datové riziko a takové transakce se jednoduše zpracovávají odděleně: když „rychlé“ transakce ve frontě dojdou, potrubí se zastaví, systém zpracuje „pomalou“ transakci a poté znovu spustí potrubí. Naštěstí je podíl takových transakcí na celkovém toku velmi malý, takže potrubí se zastaví tak zřídka, že to neovlivní celkový výkon.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Poté jsme začali řešit problém synchronizace tří vláken provádění. Výsledkem byl systém založený na kruhovém pufru s buňkami s pevnou velikostí. V tomto systému je vše podřízeno rychlosti zpracování, data se nekopírují.

  • Všechny příchozí síťové pakety vstupují do fáze přidělování.
  • Umístíme je do pole a označíme je jako dostupné pro fázi #1.
  • Přišla druhá transakce, opět je k dispozici pro etapu č. 1.
  • První vlákno zpracování vidí dostupné transakce, zpracuje je a přesune je do další fáze druhého vlákna zpracování.
  • Poté zpracuje první transakci a označí odpovídající buňku deleted — nyní je k dispozici pro nové použití.

Tímto způsobem je zpracována celá fronta.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Zpracování každé fáze trvá jednotky nebo desítky mikrosekund. A pokud použijeme standardní schémata synchronizace OS, pak ztratíme více času na samotné synchronizaci. Proto jsme začali používat spinlock. V systému reálného času je to však velmi špatná forma a RedHat to striktně nedoporučuje, takže aplikujeme spinlock na 100 ms a poté přepneme do semaforového režimu, abychom eliminovali možnost uváznutí.

Díky tomu jsme dosáhli výkonu zhruba 8 milionů transakcí za sekundu. A to doslova o dva měsíce později článek o LMAX Disruptor jsme viděli popis obvodu se stejnou funkčností.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Nyní by mohlo být několik vláken provádění v jedné fázi. Všechny transakce byly zpracovány jedna po druhé v pořadí, v jakém byly přijaty. Špičkový výkon se tak zvýšil z 18 tisíc na 50 tisíc transakcí za sekundu.

Systém řízení burzovních rizik

Dokonalosti se meze nekladou a brzy jsme opět zahájili modernizaci: v rámci ASTS+ jsme začali přesouvat systémy řízení rizik a vypořádání operací do autonomních komponent. Vyvinuli jsme flexibilní moderní architekturu a nový hierarchický model rizik a snažili jsme se třídu používat všude, kde to bylo možné fixed_point místo double.

Okamžitě však vyvstal problém: jak synchronizovat veškerou řadu let fungující obchodní logiku a přenést ji do nového systému? V důsledku toho musela být první verze prototypu nového systému opuštěna. Druhá verze, která je v současné době ve výrobě, je založena na stejném kódu, který funguje jak v obchodní, tak v rizikové části. Během vývoje bylo nejtěžší git merge mezi dvěma verzemi. Náš kolega Evgeniy Mazurenok tuto operaci prováděl každý týden a pokaždé velmi dlouho nadával.

Při výběru nového systému jsme okamžitě museli řešit problém interakce. Při výběru datové sběrnice bylo nutné zajistit stabilní jitter a minimální latenci. K tomu se nejlépe hodila síť InfiniBand RDMA: průměrná doba zpracování je 4krát kratší než v 10G ethernetových sítích. Co nás ale opravdu uchvátilo, byl rozdíl v percentilech – 99 a 99,9.

InfiniBand má samozřejmě své výzvy. Za prvé, jiné API - ibverbs místo socketů. Za druhé, neexistují téměř žádná široce dostupná řešení pro zasílání zpráv s otevřeným zdrojovým kódem. Zkoušeli jsme vyrobit vlastní prototyp, ale ukázalo se to jako velmi obtížné, a tak jsme zvolili komerční řešení – Confinity Low Latency Messaging (dříve IBM MQ LLM).

Pak vyvstal úkol správně rozdělit systém rizik. Pokud jednoduše odeberete Risk Engine a nevytvoříte mezilehlý uzel, lze transakce ze dvou zdrojů smíchat.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Takzvaná řešení s ultranízkou latencí mají režim přeskupování: transakce ze dvou zdrojů lze po přijetí seřadit v požadovaném pořadí, což je realizováno pomocí samostatného kanálu pro výměnu informací o objednávce. Tento režim ale zatím nepoužíváme: celý proces komplikuje a v řadě řešení není podporován vůbec. Navíc by každé transakci musela být přiřazena odpovídající časová razítka a v našem schématu je velmi obtížné tento mechanismus správně implementovat. Použili jsme proto klasické schéma s brokerem zpráv, tedy s dispečerem, který distribuuje zprávy mezi Risk Engine.

Druhý problém se týkal klientského přístupu: pokud existuje několik rizikových bran, klient se musí připojit ke každé z nich, což bude vyžadovat změny na klientské vrstvě. V této fázi jsme se tomu chtěli vyhnout, takže současný návrh brány rizik zpracovává celý datový tok. To značně omezuje maximální propustnost, ale výrazně zjednodušuje integraci systému.

Zdvojení

Náš systém by neměl mít jediný bod selhání, to znamená, že všechny komponenty musí být duplikovány, včetně zprostředkovatele zpráv. Tento problém jsme vyřešili pomocí systému CLLM: obsahuje RCMS cluster, ve kterém mohou dva dispečeři pracovat v režimu master-slave a při výpadku jednoho se systém automaticky přepne na druhý.

Práce se záložním datovým centrem

InfiniBand je optimalizován pro provoz jako lokální síť, tedy pro připojení zařízení pro montáž do racku, a síť InfiniBand nelze položit mezi dvě geograficky distribuovaná datová centra. Proto jsme implementovali bridge/dispečer, který se k úložišti zpráv připojuje přes běžné ethernetové sítě a všechny transakce předává do druhé IB sítě. Když potřebujeme migrovat z datového centra, můžeme si vybrat, se kterým datovým centrem budeme nyní pracovat.

Výsledky

Vše výše uvedené nebylo provedeno najednou, trvalo několik iterací vývoje nové architektury. Prototyp jsme vytvořili za měsíc, ale jeho uvedení do funkčního stavu trvalo více než dva roky. Snažili jsme se dosáhnout nejlepšího kompromisu mezi prodloužením doby zpracování transakcí a zvýšením spolehlivosti systému.

Vzhledem k rozsáhlé aktualizaci systému jsme implementovali obnovu dat ze dvou nezávislých zdrojů. Pokud úložiště zpráv z nějakého důvodu nefunguje správně, můžete protokol transakcí převzít z druhého zdroje – z Risk Engine. Tento princip je dodržován v celém systému.

Mimo jiné se nám podařilo zachovat klientské API tak, aby brokeři ani nikdo jiný nevyžadoval výrazné přepracování nové architektury. Museli jsme změnit některá rozhraní, ale nebylo potřeba dělat výrazné změny v provozním modelu.

Aktuální verzi naší platformy jsme nazvali Rebus – jako zkratku pro dvě nejnápadnější novinky v architektuře, Risk Engine a BUS.

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Původně jsme chtěli alokovat pouze clearingovou část, ale výsledkem byl obrovský distribuovaný systém. Klienti nyní mohou komunikovat buď s Trade Gateway, Clearing Gateway, nebo s oběma.

Čeho jsme nakonec dosáhli:

Vývoj architektury obchodního a clearingového systému Moskevské burzy. Část 2

Snížila úroveň latence. Při malém objemu transakcí systém funguje stejně jako předchozí verze, ale zároveň snese mnohem vyšší zátěž.

Špičkový výkon vzrostl z 50 tisíc na 180 tisíc transakcí za sekundu. Další nárůst je brzděn jediným proudem párování objednávek.

Existují dva způsoby dalšího zlepšení: paralelizace párování a změna způsobu, jakým funguje s bránou. Nyní všechny brány fungují podle schématu replikace, které při takovém zatížení přestane normálně fungovat.

Na závěr mohu dát pár rad těm, kteří dokončují podnikové systémy:

  • Buďte vždy připraveni na nejhorší. Problémy vždy nastanou nečekaně.
  • Rychle předělat architekturu je obvykle nemožné. Zejména pokud potřebujete dosáhnout maximální spolehlivosti napříč více indikátory. Čím více uzlů, tím více zdrojů potřebných pro podporu.
  • Všechna vlastní a proprietární řešení budou vyžadovat dodatečné zdroje pro výzkum, podporu a údržbu.
  • Neodkládejte řešení problémů spolehlivosti systému a obnovy po selhání; vezměte je v úvahu již v počáteční fázi návrhu.

Zdroj: www.habr.com

Přidat komentář