L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Aquesta és la continuació d'una llarga història sobre el nostre camí espinós per crear un sistema potent i d'alta càrrega que garanteixi el funcionament de l'intercanvi. La primera part és aquí: habr.com/en/post/444300

Error misteriós

Després de nombroses proves, es va posar en funcionament el sistema de negociació i compensació actualitzat i vam trobar un error sobre el qual podríem escriure una història detectivesca-mística.

Poc després de llançar-se al servidor principal, una de les transaccions es va processar amb un error. Tanmateix, tot anava bé al servidor de còpia de seguretat. Va resultar que una simple operació matemàtica de càlcul de l'exponent al servidor principal va donar un resultat negatiu de l'argument real! Vam continuar la nostra recerca, i al registre SSE2 vam trobar una diferència en un bit, que és responsable de l'arrodoniment quan es treballa amb nombres de coma flotant.

Hem escrit una utilitat de prova senzilla per calcular l'exponent amb el conjunt de bits d'arrodoniment. Va resultar que a la versió de RedHat Linux que vam utilitzar, hi havia un error en treballar amb la funció matemàtica quan es va inserir el bit desafortunat. Ho vam informar a RedHat, després d'un temps vam rebre un pedaç d'ells i el vam llançar. L'error ja no es va produir, però no estava clar d'on venia aquest bit? La funció n'era responsable fesetround del llenguatge C. Hem analitzat acuradament el nostre codi a la recerca del suposat error: hem comprovat totes les situacions possibles; va analitzar totes les funcions que utilitzaven l'arrodoniment; ha intentat reproduir una sessió fallida; utilitzava diferents compiladors amb diferents opcions; S'han utilitzat anàlisis estàtiques i dinàmiques.

No s'ha pogut trobar la causa de l'error.

Després van començar a comprovar el maquinari: van fer proves de càrrega dels processadors; comprovat la memòria RAM; Fins i tot vam fer proves per a l'escenari molt improbable d'un error de diversos bits en una cel·la. De res.

Al final, ens vam establir amb una teoria del món de la física d'alta energia: una partícula d'alta energia va volar al nostre centre de dades, va perforar la paret de la caixa, va colpejar el processador i va fer que el pestell del disparador s'enganxés en aquest punt. Aquesta teoria absurda es va anomenar el "neutrí". Si estàs lluny de la física de partícules: els neutrins gairebé no interactuen amb el món exterior i, certament, no poden afectar el funcionament del processador.

Com que no va ser possible trobar la causa de la fallada, el servidor "ofensiu" es va retirar de l'operació per si de cas.

Després d'un temps, vam començar a millorar el sistema de còpia de seguretat en calent: vam introduir les anomenades "reserves càlides" (cálides): rèpliques asíncrones. Van rebre un flux de transaccions que es podien localitzar en diferents centres de dades, però els calors no van interactuar activament amb altres servidors.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Per què es va fer això? Si el servidor de còpia de seguretat falla, llavors lligat al servidor principal es converteix en la nova còpia de seguretat. És a dir, després d'un error, el sistema no roman amb un servidor principal fins al final de la sessió de negociació.

I quan es va provar i posar en funcionament la nova versió del sistema, es va tornar a produir l'error de bits d'arrodoniment. A més, amb l'augment del nombre de servidors càlids, l'error va començar a aparèixer amb més freqüència. Al mateix temps, el venedor no tenia res a mostrar, ja que no hi havia proves concretes.

Durant la següent anàlisi de la situació, va sorgir una teoria que el problema podria estar relacionat amb el sistema operatiu. Hem escrit un programa senzill que crida una funció en un bucle sense fi fesetround, recorda l'estat actual i el comprova a través del son, i això es fa en molts fils competidors. Després d'haver seleccionat els paràmetres de repòs i el nombre de fils, vam començar a reproduir constantment la fallada de bits després d'uns 5 minuts d'execució de la utilitat. Tanmateix, el suport de Red Hat no va poder reproduir-lo. Les proves dels nostres altres servidors han demostrat que només aquells amb determinats processadors són susceptibles a l'error. Al mateix temps, canviar a un nou nucli va resoldre el problema. Al final, simplement vam substituir el sistema operatiu i la veritable causa de l'error no va quedar clara.

I de sobte l'any passat es va publicar un article sobre Habré "Com vaig trobar un error als processadors Intel Skylake" La situació que s'hi descriu era molt semblant a la nostra, però l'autor va portar la investigació més enllà i va plantejar una teoria que l'error es trobava en el microcodi. I quan s'actualitzen els nuclis de Linux, els fabricants també actualitzen el microcodi.

Més desenvolupament del sistema

Tot i que ens vam desfer de l'error, aquesta història ens va obligar a reconsiderar l'arquitectura del sistema. Després de tot, no estàvem protegits de la repetició d'aquests errors.

Els principis següents van constituir la base per a les properes millores del sistema de reserves:

  • No pots confiar en ningú. És possible que els servidors no funcionin correctament.
  • Reserva majoritària.
  • Garantir el consens. Com a complement lògic a la reserva majoritària.
  • Els dobles fracassos són possibles.
  • Vitalitat. El nou esquema d'espera en calent no hauria de ser pitjor que l'anterior. El comerç hauria de continuar ininterrompudament fins a l'últim servidor.
  • Lleuger augment de la latència. Qualsevol temps d'inactivitat comporta grans pèrdues financeres.
  • Interacció de xarxa mínima per mantenir la latència el més baixa possible.
  • Seleccionant un nou servidor mestre en segons.

Cap de les solucions disponibles al mercat ens encaixava, i el protocol Raft encara estava en els seus inicis, així que vam crear la nostra pròpia solució.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Treball en xarxa

A més del sistema de reserves, vam començar a modernitzar la interacció de la xarxa. El subsistema d'E/S constava de molts processos, que van tenir el pitjor impacte en la fluctuació i la latència. Amb centenars de processos que gestionen connexions TCP, ens hem vist obligats a canviar constantment entre ells i, a escala de microsegons, aquesta és una operació que requereix molt de temps. Però el pitjor és que quan un procés va rebre un paquet per processar-lo, l'enviava a una cua de SystemV i després esperava un esdeveniment d'una altra cua de SystemV. Tanmateix, quan hi ha un gran nombre de nodes, l'arribada d'un nou paquet TCP en un procés i la recepció de dades a la cua en un altre representen dos esdeveniments competitius per al sistema operatiu. En aquest cas, si no hi ha processadors físics disponibles per a ambdues tasques, se'n processarà un i el segon es col·locarà en una cua d'espera. És impossible predir les conseqüències.

En aquestes situacions, es pot utilitzar el control dinàmic de prioritat del procés, però això requerirà l'ús de trucades al sistema que consumeixen molts recursos. Com a resultat, vam canviar a un fil mitjançant epoll clàssic, això va augmentar molt la velocitat i va reduir el temps de processament de la transacció. També ens vam desfer de processos separats de comunicació de xarxa i comunicació a través de SystemV, vam reduir significativament el nombre de trucades al sistema i vam començar a controlar les prioritats de les operacions. Només al subsistema d'E/S, va ser possible estalviar entre 8 i 17 microsegons, depenent de l'escenari. Aquest esquema d'un sol fil s'ha utilitzat sense canvis des de llavors; un fil epoll amb un marge és suficient per donar servei a totes les connexions.

Processament de transaccions

La càrrega creixent del nostre sistema va requerir actualitzar gairebé tots els seus components. Però, malauradament, l'estancament en el creixement de la velocitat del rellotge del processador en els darrers anys ja no ha permès escalar els processos directament. Per tant, vam decidir dividir el procés del motor en tres nivells, sent el més concorregut d'ells el sistema de control de riscos, que avalua la disponibilitat de fons en els comptes i crea les transaccions per si mateixes. Però els diners poden ser en diferents monedes, i calia esbrinar sobre quina base s'havia de dividir el processament de les sol·licituds.

La solució lògica és dividir-lo per moneda: un servidor cotitza en dòlars, un altre en lliures i un terç en euros. Però si, amb aquest esquema, s'envien dues transaccions per comprar monedes diferents, es plantejarà el problema de la desincronització de la cartera. Però la sincronització és difícil i costosa. Per tant, seria correcte dividir per separat per carteres i per separat per instruments. Per cert, la majoria de les borses occidentals no tenen la tasca de comprovar els riscos tan acuradament com nosaltres, de manera que la majoria de vegades es fa fora de línia. Havíem d'implementar la verificació en línia.

Ho expliquem amb un exemple. Un comerciant vol comprar 30 dòlars i la sol·licitud passa a la validació de la transacció: comprovem si aquest comerciant té permís per a aquesta modalitat de negociació i si té els drets necessaris. Si tot està en ordre, la sol·licitud passa al sistema de verificació de riscos, és a dir. per comprovar la suficiència dels fons per concloure una transacció. Hi ha una nota que la quantitat necessària està bloquejada actualment. Aleshores, la sol·licitud s'envia al sistema comercial, que aprova o desaprova la transacció. Suposem que s'aprova la transacció; llavors el sistema de verificació de riscos marca que els diners estan desbloquejats i els rubles es converteixen en dòlars.

En general, el sistema de verificació de riscos conté algorismes complexos i realitza una gran quantitat de càlculs molt intensius en recursos, i no es limita a comprovar el "saldo del compte", com podria semblar a primera vista.

Quan vam començar a dividir el procés del motor en nivells, vam trobar un problema: el codi que estava disponible en aquell moment utilitzava activament la mateixa matriu de dades en les etapes de validació i verificació, cosa que requeria reescriure tota la base de codi. Com a resultat, vam agafar en préstec una tècnica per processar instruccions dels processadors moderns: cadascuna d'elles es divideix en petites etapes i es realitzen diverses accions en paral·lel en un cicle.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Després d'una petita adaptació del codi, vam crear un pipeline per al processament de transaccions paral·leles, en el qual la transacció es va dividir en 4 etapes del pipeline: interacció amb la xarxa, validació, execució i publicació del resultat.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Vegem un exemple. Disposem de dos sistemes de processament, en sèrie i en paral·lel. Arriba la primera transacció i s'envia per a la validació en ambdós sistemes. De seguida arriba la segona transacció: en un sistema paral·lel es posa immediatament a treballar, i en un sistema seqüencial es posa a la cua esperant que la primera transacció passi per l'etapa de processament actual. És a dir, el principal avantatge del processament de pipelines és que processem la cua de transaccions més ràpidament.

Així és com ens va sorgir el sistema ASTS+.

És cert que tampoc tot és tan suau amb les cintes transportadores. Suposem que tenim una transacció que afecta les matrius de dades en una transacció veïna; aquesta és una situació típica per a un intercanvi. Aquesta transacció no es pot executar en un pipeline perquè pot afectar altres. Aquesta situació s'anomena perill de dades, i aquestes transaccions simplement es processen per separat: quan s'acaben les transaccions "ràpides" de la cua, el pipeline s'atura, el sistema processa la transacció "lenta" i després torna a iniciar el pipeline. Afortunadament, la proporció d'aquestes transaccions en el flux global és molt petita, de manera que el gasoducte s'atura tan rarament que no afecta el rendiment general.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Llavors vam començar a resoldre el problema de sincronitzar tres fils d'execució. El resultat va ser un sistema basat en un buffer d'anell amb cèl·lules de mida fixa. En aquest sistema, tot està subjecte a la velocitat de processament; les dades no es copien.

  • Tots els paquets de xarxa entrants entren a l'etapa d'assignació.
  • Els col·loquem en una matriu i els marquem com a disponibles per a l'etapa #1.
  • La segona transacció ha arribat, torna a estar disponible per a l'etapa núm.
  • El primer fil de processament veu les transaccions disponibles, les processa i les trasllada a la següent etapa del segon fil de processament.
  • A continuació, processa la primera transacció i marca la cel·la corresponent deleted — Ara està disponible per a un nou ús.

Tota la cua es processa d'aquesta manera.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

El processament de cada etapa requereix unitats o desenes de microsegons. I si utilitzem esquemes de sincronització del sistema operatiu estàndard, perdrem més temps en la sincronització en si. Per això vam començar a utilitzar spinlock. No obstant això, això és molt dolent en un sistema en temps real, i RedHat no recomana fer-ho estrictament, de manera que apliquem un spinlock durant 100 ms i després canviem al mode semàfor per eliminar la possibilitat d'un bloqueig.

Com a resultat, vam aconseguir un rendiment d'uns 8 milions de transaccions per segon. I literalment dos mesos després article sobre LMAX Disruptor vam veure una descripció d'un circuit amb la mateixa funcionalitat.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Ara hi podria haver diversos fils d'execució en una etapa. Totes les transaccions es van processar una per una, en l'ordre en què es van rebre. Com a resultat, el rendiment màxim va augmentar de 18 mil a 50 mil transaccions per segon.

Sistema de gestió del risc de canvi

No hi ha límit a la perfecció, i aviat vam començar de nou la modernització: en el marc d'ASTS+, vam començar a traslladar els sistemes de gestió de riscos i operacions de liquidació a components autònoms. Vam desenvolupar una arquitectura moderna flexible i un nou model de risc jeràrquic, i vam intentar utilitzar la classe sempre que fos possible fixed_point en comptes de double.

Però de seguida va sorgir un problema: com sincronitzar tota la lògica empresarial que funciona des de fa molts anys i transferir-la al nou sistema? Com a resultat, es va haver d'abandonar la primera versió del prototip del nou sistema. La segona versió, que actualment està treballant en producció, es basa en el mateix codi, que funciona tant a la part comercial com a la de risc. Durant el desenvolupament, el més difícil de fer va ser combinar git entre dues versions. El nostre company Evgeniy Mazurenok va fer aquesta operació cada setmana i cada vegada va maleir durant molt de temps.

En seleccionar un nou sistema, de seguida vam haver de resoldre el problema de la interacció. A l'hora de triar un bus de dades, calia garantir una fluctuació estable i una latència mínima. La xarxa InfiniBand RDMA era la més adequada per a això: el temps mitjà de processament és 4 vegades menor que a les xarxes Ethernet 10 G. Però el que realment ens va captivar va ser la diferència de percentils: 99 i 99,9.

Per descomptat, InfiniBand té els seus reptes. En primer lloc, una API diferent: ibverbs en lloc de sòcols. En segon lloc, gairebé no hi ha solucions de missatgeria de codi obert àmpliament disponibles. Vam intentar fer el nostre propi prototip, però va resultar molt difícil, així que vam triar una solució comercial: Confinity Low Latency Messaging (abans IBM MQ LLM).

Aleshores va sorgir la tasca de dividir correctament el sistema de riscos. Si simplement elimineu el motor de risc i no creeu un node intermedi, les transaccions de dues fonts es poden barrejar.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Les anomenades solucions de latència ultra baixa tenen un mode de reordenació: les transaccions de dues fonts es poden organitzar en l'ordre requerit després de la seva recepció; això s'implementa mitjançant un canal separat per intercanviar informació sobre la comanda. Però encara no utilitzem aquest mode: complica tot el procés i en diverses solucions no s'admet en absolut. A més, a cada transacció s'hauria d'assignar les marques de temps corresponents, i en el nostre esquema aquest mecanisme és molt difícil d'implementar correctament. Per tant, hem utilitzat l'esquema clàssic amb un agent de missatges, és a dir, amb un despatxador que distribueix missatges entre el motor de risc.

El segon problema estava relacionat amb l'accés del client: si hi ha diverses passarel·les de risc, el client s'ha de connectar a cadascuna d'elles, i això requerirà canvis a la capa de client. Volíem allunyar-nos d'això en aquesta etapa, de manera que el disseny actual de Risk Gateway processa tot el flux de dades. Això limita molt el rendiment màxim, però simplifica molt la integració del sistema.

Duplicació

El nostre sistema no hauria de tenir un únic punt de fallada, és a dir, tots els components s'han de duplicar, inclòs l'agent de missatges. Hem resolt aquest problema mitjançant el sistema CLLM: conté un clúster RCMS en el qual dos despatxadors poden treballar en mode mestre-esclau, i quan un falla, el sistema canvia automàticament a l'altre.

Treballant amb un centre de dades de còpia de seguretat

InfiniBand està optimitzat per funcionar com a xarxa local, és a dir, per connectar equips de muntatge en bastidor, i una xarxa InfiniBand no es pot col·locar entre dos centres de dades distribuïts geogràficament. Per tant, hem implementat un pont/despatx, que es connecta a l'emmagatzematge de missatges mitjançant xarxes Ethernet normals i transmet totes les transaccions a una segona xarxa IB. Quan necessitem migrar des d'un centre de dades, podem triar amb quin centre de dades treballar ara.

Resultats de

Tot l'anterior no es va fer alhora; es van necessitar diverses iteracions per desenvolupar una nova arquitectura. Vam crear el prototip en un mes, però vam trigar més de dos anys a posar-lo en condicions. Hem intentat aconseguir el millor compromís entre augmentar el temps de processament de transaccions i augmentar la fiabilitat del sistema.

Com que el sistema es va actualitzar molt, vam implementar la recuperació de dades des de dues fonts independents. Si el magatzem de missatges no funciona correctament per algun motiu, podeu agafar el registre de transaccions d'una segona font: el motor de risc. Aquest principi s'observa a tot el sistema.

Entre altres coses, vam poder preservar l'API del client perquè ni els corredors ni ningú més requerissin una reelaboració significativa per a la nova arquitectura. Vam haver de canviar algunes interfícies, però no calia fer canvis significatius al model operatiu.

Vam anomenar la versió actual de la nostra plataforma Rebus, com a abreviatura de les dues innovacions més notables en l'arquitectura, Risk Engine i BUS.

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

Inicialment, volíem assignar només la part de neteja, però el resultat va ser un gran sistema distribuït. Els clients ara poden interactuar amb Trade Gateway, Clearing Gateway o ambdues.

El que finalment hem aconseguit:

L'evolució de l'arquitectura del sistema de negociació i compensació de la Borsa de Moscou. Part 2

S'ha reduït el nivell de latència. Amb un petit volum de transaccions, el sistema funciona igual que la versió anterior, però al mateix temps pot suportar una càrrega molt més gran.

El rendiment màxim va augmentar de 50 mil a 180 mil transaccions per segon. Un nou augment es veu obstaculitzat per l'únic flux de concordança de comandes.

Hi ha dues maneres de millorar encara més: paral·lelitzant la concordança i canviant la forma en què funciona amb Gateway. Ara totes les passarel·les funcionen segons un esquema de replicació que, sota aquesta càrrega, deixa de funcionar amb normalitat.

Finalment, puc donar alguns consells a aquells que estan ultimant sistemes empresarials:

  • Estigueu preparats per al pitjor en tot moment. Els problemes sempre sorgeixen de manera inesperada.
  • Normalment és impossible refer ràpidament l'arquitectura. Sobretot si necessiteu aconseguir la màxima fiabilitat en diversos indicadors. Com més nodes, més recursos necessaris per al suport.
  • Totes les solucions personalitzades i pròpies requeriran recursos addicionals per a la investigació, el suport i el manteniment.
  • No posposeu la resolució de problemes de fiabilitat i recuperació del sistema després de fallades; tingueu-los en compte en l'etapa de disseny inicial.

Font: www.habr.com

Afegeix comentari