Wie haben wir den Kern des auf Tarantool basierenden Investmentgeschäfts der Alfa-Bank abgewickelt?


Eine Aufnahme aus dem Film „Unser geheimes Universum: Das verborgene Leben der Zelle“ Das

Investmentgeschäft ist einer der schwierigsten Bereiche in der Bankenwelt, da es nicht nur Kredite, Kredite und Einlagen gibt, sondern auch Wertpapiere, Währungen, Waren, Derivate und alle möglichen Schwierigkeiten in Form von Strukturprodukten.

In letzter Zeit hat die Finanzkompetenz der Bevölkerung zugenommen. Immer mehr Menschen sind am Handel an den Wertpapiermärkten beteiligt. Einzelne Anlagekonten sind vor nicht allzu langer Zeit erschienen. Sie ermöglichen es Ihnen, an den Wertpapiermärkten zu handeln und gleichzeitig entweder Steuerabzüge zu erhalten oder keine Steuern zu zahlen. Und alle Kunden, die zu uns kommen, möchten ihr Portfolio verwalten und Echtzeitberichte anzeigen. Darüber hinaus besteht dieses Portfolio meist aus mehreren Produkten, dh Menschen sind Kunden verschiedener Geschäftsbereiche.

Darüber hinaus steigen die Anforderungen der russischen und ausländischen Regulierungsbehörden.

Um den aktuellen Anforderungen gerecht zu werden und den Grundstein für zukünftige Upgrades zu legen, haben wir den Kern des Investmentgeschäfts auf Basis von Tarantool entwickelt.

Einige Statistiken. Das Investmentgeschäft der Alfa-Bank bietet Maklerdienstleistungen für Einzelpersonen und juristische Personen, die die Möglichkeit bieten, auf verschiedenen Wertpapiermärkten zu handeln, Verwahrungsdienste für die Lagerung von Wertpapieren, Treuhandverwaltungsdienste für Einzelpersonen mit privatem und großem Kapital sowie Wertpapieremissionsdienste für andere Unternehmen. Das Investmentgeschäft der Alfa-Bank umfasst mehr als 3.000 Quotes pro Sekunde, die von verschiedenen Handelsplattformen heruntergeladen werden. Während des Arbeitstages werden im Auftrag der Bank oder ihrer Kunden mehr als 300.000 Transaktionen auf den Märkten abgeschlossen. Auf externen und internen Plattformen werden bis zu 5.000 Aufträge pro Sekunde ausgeführt. Gleichzeitig möchten alle internen und externen Kunden ihre Positionen in Echtzeit sehen.

Hintergrund


Irgendwann seit Anfang der 2000er Jahre haben sich unsere Bereiche des Investmentgeschäfts unabhängig voneinander entwickelt: Börsenhandel, Maklerdienstleistungen, Devisenhandel, außerbörslicher Handel mit Wertpapieren und verschiedenen Derivaten. Infolgedessen sind wir in die Falle funktioneller Brunnen geraten. Was ist das? Jeder Geschäftsbereich verfügt über eigene Systeme, die die Funktionen des jeweils anderen duplizieren. Jedes System verfügt über ein eigenes Datenmodell, obwohl es nach denselben Konzepten arbeitet: Transaktionen, Instrumente, Gegenparteien, Quotes und mehr. Und da sich jedes System unabhängig entwickelte, entstand ein vielfältiger Zoo von Technologien.

Darüber hinaus ist die Codebasis von Systemen bereits veraltet, da einige Produkte Mitte der neunziger Jahre entstanden sind. In einigen Bereichen wurde der Entwicklungsprozess verlangsamt, und es gab Probleme mit der Produktivität.

Neue Lösungsanforderungen


Das Unternehmen erkannte, dass die technologische Entwicklung für die weitere Entwicklung von entscheidender Bedeutung ist. Uns wurden die Aufgaben zugewiesen:

  1. Sammeln Sie alle Geschäftsdaten in einem einzigen schnellen Speicher und in einem einzigen Datenmodell.
  2. Wir dürfen diese Informationen nicht verlieren oder ändern.
  3. Die Daten müssen versioniert werden, da die Regulierungsbehörde jederzeit Statistiken für frühere Jahre anfordern kann.
  4. Wir sollten nicht nur ein neues, modisches DBMS mitbringen, sondern eine Plattform zur Lösung von Geschäftsproblemen schaffen.

Darüber hinaus legen unsere Architekten ihre Bedingungen fest:

  1. Die neue Lösung sollte Enterprise-Klasse sein, dh sie sollte bereits in einigen großen Unternehmen getestet werden.
  2. Die Funktionsweise der Lösung muss geschäftskritisch sein. Dies bedeutet, dass wir gleichzeitig in mehreren Rechenzentren präsent sein und ruhig die Trennung eines Rechenzentrums erleben müssen.
  3. . , , - . , .
  4. , .

Wir gingen den Standardweg: formulierten Anforderungen und kontaktierten die Beschaffungsabteilung. Von dort haben wir eine Liste von Unternehmen erhalten, die im Allgemeinen dazu bereit sind. Sie erzählten allen von der Aufgabe und sechs von ihnen erhielten eine Bewertung der Lösungen.

Wir bei der Bank glauben niemandem, wir lieben es, alles selbst zu testen. Voraussetzung für unseren Ausschreibungswettbewerb war daher das Bestehen von Stresstests. Wir haben Testaufgaben für die Last formuliert, und bereits drei von sechs Unternehmen haben auf eigene Kosten vereinbart, einen Prototyp der Lösung zu implementieren, der auf In-Memory-Technologien basiert, um sie zu testen.

Ich werde nicht sagen, wie wir alles getestet haben und wie lange es gedauert hat. Ich fasse nur zusammen: Die beste Leistung bei Lasttests zeigte der Prototyp der Tarantool-basierten Lösung des Entwicklungsteams der Mail.ru Group. Wir haben einen Vertrag unterschrieben und mit der Entwicklung begonnen. Vier Mitarbeiter waren von der Mail.ru Group, und von der Alfa-Bank gab es drei Entwickler, drei Systemanalysten, einen Lösungsarchitekten, einen Product Owner und einen Scrum Master.

Als nächstes werde ich darüber sprechen, wie unser System gewachsen ist, wie es sich entwickelt hat, was wir getan haben und warum.

Entwicklung


Zunächst haben wir uns gefragt, wie wir Daten von unseren aktuellen Systemen erhalten können. Wir haben entschieden, dass HTTP für uns gut geeignet ist, da alle aktuellen Systeme miteinander kommunizieren und XML oder JSON über HTTP senden.

Wir verwenden den integrierten Tarantool-HTTP-Server, da wir keine SSL-Sitzungen beenden müssen und die Leistung für uns ausreicht.

Wie ich bereits sagte, leben alle Systeme in unterschiedlichen Datenmodellen, und bei der Eingabe müssen wir das Objekt zu dem Modell bringen, das wir zu Hause beschreiben werden. Eine Sprache wurde benötigt, um Daten zu transformieren. Wir haben den Imperativ Lua gewählt. Wir führen den gesamten Code für die Datenkonvertierung in der Sandbox aus - dies ist ein sicherer Ort, über den der laufende Code nicht hinausgeht. Führen Sie dazu einfach einen Loadstring des erforderlichen Codes durch und erstellen Sie eine Umgebung mit Funktionen, die nichts blockieren oder löschen können.


Nach der Konvertierung müssen die Daten auf Übereinstimmung mit dem von uns erstellten Modell überprüft werden. Wir haben lange darüber diskutiert, was ein Modell sein sollte, mit welcher Sprache es beschrieben werden soll. Wir haben bei Apache Avro angehalten, weil die Sprache einfach ist und von Tarantool unterstützt wird. Neue Versionen des Modells und des Benutzercodes können mehrmals täglich in Betrieb genommen werden, auch unter Last, auch ohne, zu jeder Tageszeit, und sich sehr schnell an Änderungen anpassen.


Nach der Überprüfung müssen die Daten gespeichert werden. Wir machen das mit vshard (wir haben Geodaten-Repliken von Shards).


Darüber hinaus sind die Besonderheiten so, dass es für die meisten Systeme, die uns Daten senden, keine Rolle spielt, ob wir sie erhalten haben oder nicht. Deshalb haben wir von Anfang an die Reparaturlinie implementiert. Was ist das? Wenn das Objekt aus irgendeinem Grund die Datentransformation oder -überprüfung nicht bestanden hat, bestätigen wir den Empfang weiterhin, speichern das Objekt jedoch gleichzeitig in der Reparaturwarteschlange. Es ist konsistent und befindet sich im Haupt-Repository mit Geschäftsdaten. Wir haben sofort eine Admin-Oberfläche dafür geschrieben, verschiedene Metriken und Warnungen. Dadurch verlieren wir keine Daten. Selbst wenn sich etwas in der Quelle geändert hat, wenn sich das Datenmodell geändert hat, werden wir es sofort finden und können es anpassen.


Jetzt müssen Sie lernen, wie Sie gespeicherte Daten abrufen. Wir haben unsere Systeme sorgfältig analysiert und festgestellt, dass es auf dem klassischen Stack von Java und Oracle immer eine Art ORM gibt, das Daten von einer relationalen Ansicht in eine Objektansicht konvertiert. Warum also nicht sofort Objekte in Form eines Graphen an Systeme weitergeben? Deshalb haben wir gerne GraphQL genommen, das alle unsere Anforderungen erfüllt. Sie können damit Daten in Form von Diagrammen empfangen und nur das herausholen, was Sie gerade benötigen. Sie können die API sogar mit ausreichender Flexibilität versionieren.


Fast sofort stellten wir fest, dass die extrahierten Daten für uns nicht ausreichten. Wir haben Funktionen erstellt, die an Objekte im Modell angehängt werden können - tatsächlich berechnete Felder. Das heißt, wir fügen dem Feld eine bestimmte Funktion hinzu, die beispielsweise den Durchschnittspreis eines Angebots berücksichtigt. Und der externe Verbraucher, der die Daten anfordert, weiß nicht einmal, dass dieses Feld berechnet wird.


Implementierte ein Authentifizierungssystem.


Dann bemerkten sie, dass in unserer Lösung mehrere Rollen kristallisierten. Eine Rolle ist eine Art Aggregator von Funktionen. Rollen haben in der Regel unterschiedliche Nutzungsprofile:

  • T-Connect: verarbeitet eingehende Verbindungen, die vom Prozessor begrenzt werden, verbraucht wenig Speicher und speichert den Status nicht.
  • IB-Core: transformiert die Daten, die es über das Tarantool-Protokoll empfängt, dh es arbeitet mit Tablets. Speichert auch keinen Status und kann skaliert werden.
  • Speicher: Speichert nur Daten, verwendet keine Logik. In dieser Rolle sind die einfachsten Schnittstellen implementiert. Skalierbar dank vshard.


Das heißt, wir haben mithilfe von Rollen verschiedene Teile des Clusters voneinander getrennt, die unabhängig voneinander skaliert werden können.

Daher haben wir einen asynchronen Datensatz eines Transaktionsdatenstroms und eine Reparaturwarteschlange mit einer Administratorschnittstelle erstellt. Die Aufzeichnung ist aus geschäftlicher Sicht asynchron: Wenn wir garantiert Daten für uns selbst aufzeichnen, egal wo, werden wir dies bestätigen. Wenn nicht bestätigt, ist ein Fehler aufgetreten. Die Daten müssen gesendet werden. Dies ist eine asynchrone Aufzeichnung.

Testen


Von Beginn des Projekts an wurde beschlossen, eine testgetriebene Entwicklung einzuführen. Wir schreiben Unit-Tests in Lua mit dem Tarantool / Tap-Framework, Integrationstests in Python mit dem Pytest-Framework. Gleichzeitig sind sowohl Entwickler als auch Analysten an der Erstellung von Integrationstests beteiligt.

Wie wenden wir testgetriebene Entwicklung an?

Wenn wir eine neue Funktion wünschen, versuchen wir zunächst, einen Test dafür zu schreiben. Nachdem wir den Fehler entdeckt haben, müssen wir zuerst in den Test schreiben und ihn dann beheben. Zunächst ist es schwierig, so zu arbeiten, es gibt ein Missverständnis seitens der Mitarbeiter, sogar Sabotage: "Lassen Sie es uns schnell beheben, etwas Neues tun und es dann mit Tests abdecken." Nur dieses "spätere" tritt fast nie auf.

Daher müssen Sie sich zuerst zwingen, Tests zu schreiben, und andere bitten, dies zu tun. Glauben Sie mir, eine testgetriebene Entwicklung ist auch kurzfristig von Vorteil. Sie werden das Gefühl haben, dass es für Sie einfacher geworden ist zu leben. Nach unseren Einschätzungen werden derzeit 99% des Codes durch Tests abgedeckt. Es scheint viel zu sein, aber wir haben keine Probleme: Bei jedem Commit werden Tests ausgeführt.

Vor allem aber lieben wir Stresstests, wir halten sie für die wichtigsten und führen sie regelmäßig durch.

Ich erzähle Ihnen eine kurze Geschichte darüber, wie wir die erste Phase des Lasttests einer der ersten Versionen durchgeführt haben. Wir haben das System auf den Laptop des Entwicklers gestellt, die Last eingeschaltet und 4.000 Transaktionen pro Sekunde empfangen. Gutes Ergebnis für einen Laptop. Wir haben einen virtuellen Laststand von vier Servern eingerichtet, der schwächer als in der Produktion ist. Auf ein Minimum bereitgestellt. Wir starten es und erzielen in einem Thread ein schlechteres Ergebnis als auf einem Laptop. Schockinhalt.

Wir waren sehr traurig. Wir sehen uns die Serverlast an und sie erweisen sich als untätig.


Wir rufen die Entwickler an und sie erklären uns, die aus der Java-Welt gekommen sind, dass Tarantool Single-Threaded ist. Es kann effektiv von nur einem Prozessorkern unter Last verwendet werden. Dann haben wir die maximal mögliche Anzahl von Tarantool-Instanzen auf jedem Server bereitgestellt, die Last eingeschaltet und bereits 14,5 Tausend Transaktionen pro Sekunde empfangen.


Ich werde es noch einmal erklären. Aufgrund der Aufteilung in Rollen, die Ressourcen unterschiedlich verwenden, haben unsere Rollen, die für die Verarbeitung von Verbindungen und die Datentransformation verantwortlich waren, nur den Prozessor geladen und waren streng proportional zur Last.



Darüber hinaus wurde der Speicher nur zur Verarbeitung eingehender Verbindungen und temporärer Objekte verwendet.


Im Gegenteil, auf Speicherservern stieg die Prozessorlast, jedoch viel langsamer als auf Servern, die Verbindungen verarbeiten.


Und der Speicherverbrauch stieg direkt proportional zur geladenen Datenmenge.


Dienstleistungen


Um unser neues Produkt speziell als Anwendungsplattform zu entwickeln, haben wir eine Komponente für die Bereitstellung von Diensten und Bibliotheken darauf erstellt.

Services sind nicht nur kleine Codeteile, die in einigen Bereichen ausgeführt werden. Dies können sehr große und komplexe Designs sein, die Teil des Clusters sind, die Referenzdaten überprüfen, die Geschäftslogik verdrehen und Antworten geben. Wir exportieren das Serviceschema auch nach GraphQL, und der Verbraucher erhält einen universellen Datenzugriffspunkt mit Introspektion im gesamten Modell. Es ist sehr bequem.

Da Dienste viel mehr Funktionen enthalten, haben wir beschlossen, dass es Bibliotheken geben sollte, in denen wir häufig verwendeten Code herausnehmen. Wir haben sie einer sicheren Umgebung hinzugefügt, nachdem wir überprüft haben, dass dies für uns nichts bringt. Und jetzt können wir Funktionen für zusätzliche Umgebungen in Form von Bibliotheken festlegen.

Wir wollten, dass wir nicht nur eine Plattform für die Speicherung, sondern auch für die Datenverarbeitung haben. Und da wir bereits eine Reihe von Replikaten und Shards hatten, haben wir einen Anschein von verteiltem Computing implementiert und es als Kartenreduzierung bezeichnet, da sich herausstellte, dass es sich um die ursprüngliche Kartenreduzierung handelt.

Alte Systeme


Nicht alle unsere alten Systeme können uns über HTTP anrufen und GraphQL verwenden, obwohl sie dieses Protokoll unterstützen. Aus diesem Grund haben wir einen Mechanismus zum Replizieren von Daten auf diese Systeme entwickelt.


Wenn sich für uns etwas ändert, funktionieren bestimmte Auslöser in der Speicherrolle und die Nachricht mit den Änderungen fällt in die Verarbeitungswarteschlange. Es wird über eine separate Replikatorrolle an ein externes System gesendet. Diese Rolle speichert keinen Status.

Neue Verbesserungen


Wie Sie sich erinnern, haben wir aus geschäftlicher Sicht asynchrone Aufzeichnungen gemacht. Aber dann wurde ihnen klar, dass dies nicht ausreichen würde, da es eine Klasse von Systemen gibt, die sofort eine Antwort auf den Status der Operation erhalten müssen. Deshalb haben wir unser GraphQL erweitert und Mutationen hinzugefügt. Sie passen organisch in das bestehende Paradigma der Arbeit mit Daten. Wir haben einen einzigen Punkt des Lesens und Schreibens für eine andere Klasse von Systemen.


Wir haben auch festgestellt, dass Services allein für uns nicht ausreichen würden, da es ziemlich umfangreiche Berichte gibt, die einmal am Tag, in der Woche und im Monat erstellt werden müssen. Dies kann lange dauern, und Berichte können sogar die Tarantool-Ereignisschleife blockieren. Aus diesem Grund haben wir separate Rollen erstellt: Scheduler und Runner. Läufer speichern den Status nicht. Sie starten schwierige Aufgaben, mit denen wir nicht im laufenden Betrieb rechnen können. Die Scheduler-Rolle überwacht den Zeitplan für das Starten dieser Aufgaben, der in der Konfiguration beschrieben wird. Die Aufgaben selbst werden am selben Ort wie die Geschäftsdaten gespeichert. Wenn der richtige Zeitpunkt gekommen ist, nimmt der Planer die Aufgabe an, gibt sie einem Läufer, berücksichtigt sie und speichert das Ergebnis.


Nicht alle Aufgaben müssen planmäßig ausgeführt werden. Einige Berichte müssen auf Anfrage gelesen werden. Sobald diese Anforderung eintrifft, wird eine Aufgabe in der Sandbox gebildet und zur Ausführung an den Läufer gesendet. Nach einer Weile erhält der Benutzer asynchron eine Antwort, dass alles berechnet wurde, der Bericht ist fertig.


Zunächst haben wir uns an das Paradigma gehalten, alle Daten zu speichern, zu versionieren und nicht zu löschen. Aber im Leben müssen Sie von Zeit zu Zeit immer noch etwas löschen, hauptsächlich einige Roh- oder Zwischeninformationen. Basierend auf expirationd haben wir einen Mechanismus zum Bereinigen der Speicherung veralteter Daten entwickelt.


Wir verstehen auch, dass früher oder später eine Situation eintreten wird, in der nicht genügend Speicherplatz zum Speichern von Daten im Speicher vorhanden ist, die Daten jedoch gespeichert werden müssen. Zu diesem Zweck werden wir in Kürze Festplattenspeicher erstellen.


Fazit


Wir begannen mit der Aufgabe, Daten in ein einziges Modell zu laden, und verbrachten drei Monate mit dessen Entwicklung. Wir hatten sechs Datenanbietersysteme. Der gesamte Transformationscode in ein einzelnes Modell besteht in Lua aus etwa 30.000 Zeilen. Und der größte Teil der Arbeit steht noch bevor. Manchmal mangelt es den benachbarten Teams an Motivation, was die Arbeit der Umstände erheblich erschwert. Wenn Sie jemals mit einem ähnlichen Problem konfrontiert sind, multiplizieren Sie die Zeit, die Sie für normal halten, mit drei oder sogar vier.

Denken Sie auch daran, dass vorhandene Probleme in Geschäftsprozessen nicht mit Hilfe eines neuen DBMS gelöst werden können, selbst wenn es sehr produktiv ist. Was ich meine? Zu Beginn unseres Projekts haben wir bei den Kunden den Eindruck erweckt, dass wir jetzt eine neue schnelle Datenbank bringen und leben werden! Die Prozesse werden schneller gehen, alles wird gut. Tatsächlich löst die Technologie nicht die Probleme, die in Geschäftsprozessen bestehen, da Geschäftsprozesse Menschen sind. Und Sie müssen mit Menschen arbeiten, nicht mit Technologie.

Die Entwicklung durch Tests in der Anfangsphase kann schmerzhaft und zeitaufwändig sein. Der positive Effekt wird sich jedoch auch kurzfristig bemerkbar machen, wenn Sie nichts tun müssen, um Regressionstests durchzuführen.

Es ist äußerst wichtig, Lasttests in allen Entwicklungsstadien durchzuführen. Je früher Sie einen Fehler in der Architektur bemerken, desto einfacher ist es, ihn zu beheben. Dadurch sparen Sie in Zukunft viel Zeit.

Es ist nichts falsch mit Lua. Jeder kann lernen, darauf zu schreiben: ein Java-Entwickler, ein JavaScript-Entwickler, ein Python-Entwickler, ein Front-End oder ein Back-End. Wir haben sogar Analysten, die darüber schreiben.

Wenn wir über die Tatsache sprechen, dass wir kein SQL haben, erschreckt dies die Menschen. "Wie erhält man Daten ohne SQL?" Ist das möglich? " Na sicher. Auf einem OLTP-Klassensystem wird SQL nicht benötigt. Es gibt eine Alternative in Form einer Sprache, die Ihnen sofort eine dokumentenorientierte Ansicht zurückgibt. Zum Beispiel GraphQL. Und es gibt eine Alternative in Form von verteiltem Computing.

Wenn Sie verstehen, dass Sie skalieren müssen, entwerfen Sie Ihre Lösung sofort auf Tarantool, damit sie auf Dutzenden von Tarantool-Instanzen parallel funktioniert. Wenn Sie dies nicht tun, wird es schwierig und schmerzhaft sein, da Tarantool nur einen Prozessorkern effizient verwenden kann.

All Articles