
2017 haben wir den Wettbewerb für die Entwicklung des Transaktionskerns des Investmentgeschäfts der Alfa Bank gewonnen und unsere Arbeit aufgenommen (auf der HighLoad ++ 2018 präsentierte Vladimir Drynkin, Leiter des Transaktionskerns des Investmentgeschäfts der Alfa Bank, einen Bericht über den Kern des Investmentgeschäfts). Dieses System sollte Transaktionsdaten aus verschiedenen Quellen in verschiedenen Formaten zusammenfassen, die Daten in eine einheitliche Form bringen, speichern und Zugriff darauf gewähren.
Während des Entwicklungsprozesses entwickelte sich das System weiter und wurde funktionsfähig. Irgendwann stellten wir fest, dass wir viel mehr als nur Anwendungssoftware kristallisierten, die zur Lösung eines genau definierten Aufgabenspektrums entwickelt wurde: Wir haben ein
System zum Erstellen verteilter Anwendungen mit dauerhafte Speicherung . Unsere Erfahrung bildete die Grundlage für ein neues Produkt -
Tarantool Data Grid (TDG).
Ich möchte über die TDG-Architektur und die Lösungen sprechen, die wir während des Entwicklungsprozesses entwickelt haben, Ihnen die grundlegenden Funktionen vorstellen und zeigen, wie unser Produkt die Grundlage für die Erstellung vollständiger Lösungen werden kann.
Architektonisch haben wir das System in separate
Rollen unterteilt , von denen jede für die Lösung eines bestimmten Aufgabenbereichs verantwortlich ist. Eine laufende Instanz einer Anwendung implementiert einen oder mehrere Rollentypen. Ein Cluster kann mehrere Rollen desselben Typs haben:

Anschluss
Connector ist für die Kommunikation mit der Außenwelt verantwortlich. Seine Aufgabe besteht darin, die Anforderung zu akzeptieren, sie zu analysieren und bei Erfolg die Daten zur Verarbeitung an den Eingabeprozessor zu senden. Wir unterstützen die Formate HTTP, SOAP, Kafka, FIX. Die Architektur ermöglicht es Ihnen, einfach Unterstützung für neue Formate hinzuzufügen. Die IBM MQ-Unterstützung wird in Kürze verfügbar sein. Wenn die Analyse der Anforderung fehlgeschlagen ist, gibt der Connector einen Fehler zurück. Andernfalls antwortet er, dass die Anforderung erfolgreich verarbeitet wurde, auch wenn bei der weiteren Verarbeitung ein Fehler aufgetreten ist. Dies geschieht absichtlich, um mit Systemen zu arbeiten, die nicht wissen, wie Anforderungen wiederholt werden sollen - oder umgekehrt, zu aggressiv. Um keine Daten zu verlieren, wird eine Reparaturwarteschlange verwendet: Das Objekt tritt zuerst ein und erst nach erfolgreicher Verarbeitung wird es gelöscht. Der Administrator kann Benachrichtigungen über Objekte erhalten, die in der Reparaturwarteschlange verbleiben. Versuchen Sie es erneut, nachdem Sie einen Softwarefehler oder einen Hardwarefehler behoben haben.
Eingabeprozessor
Der Eingabeprozessor klassifiziert die empfangenen Daten nach Merkmalen und ruft geeignete Handler auf. Handler sind Lua-Code, der in der Sandbox ausgeführt wird, sodass sie die Funktionsweise des Systems nicht beeinträchtigen können. In diesem Stadium können die Daten auf die gewünschte Form reduziert werden und bei Bedarf eine beliebige Anzahl von Aufgaben ausführen, die die erforderliche Logik implementieren können. Beispielsweise führen wir in dem auf dem Tarantool-Datenraster basierenden MDM-Produkt (Master Data Management) beim Hinzufügen eines neuen Benutzers eine separate Aufgabe aus, um die Verarbeitung der Anforderung nicht zu verlangsamen. Die Sandbox unterstützt Anforderungen zum Lesen, Ändern und Hinzufügen von Daten. Sie ermöglicht es Ihnen, einige Funktionen für alle Rollen auszuführen, z. B. das Speichern und Aggregieren des Ergebnisses (Zuordnen / Reduzieren).
Handler können in Dateien beschrieben werden:
sum.lua local x, y = unpack(...) return x + y
Und dann in der Konfiguration deklariert:
functions: sum: { __file: sum.lua }
Warum Lua? Lua ist eine sehr einfache Sprache. Basierend auf unserer Erfahrung beginnen die Leute einige Stunden nach ihrem Treffen, Code zu schreiben, der ihr Problem löst. Und das sind nicht nur professionelle Entwickler, sondern zum Beispiel Analysten. Außerdem ist Lua dank des JIT-Compilers sehr schnell.
Lagerung
Der Speicher speichert persistente Daten. Vor dem Speichern werden die Daten auf Übereinstimmung mit dem Datenschema überprüft. Zur Beschreibung des Schemas verwenden wir das erweiterte
Apache Avro- Format. Ein Beispiel:
{ "name": "User", "type": "record", "logicalType": "Aggregate", "fields": [ { "name": "id", "type": "string"}, {"name": "first_name", "type": "string"}, {"name": "last_name", "type": "string"} ], "indexes": ["id"] }
Basierend auf dieser Beschreibung werden automatisch DDL (Data Definition Language) für Tarantula DBMS und
GraphQL für den Datenzugriff generiert.
Die asynchrone Datenreplikation wird unterstützt (das Hinzufügen von synchronen Daten ist geplant).
Ausgabeprozessor
Manchmal ist es notwendig, externe Verbraucher über das Eintreffen neuer Daten zu informieren, da dies die Rolle des Ausgabeprozessors spielt. Nach dem Speichern der Daten können sie an den entsprechenden Handler übertragen werden (um sie beispielsweise in das vom Verbraucher gewünschte Formular zu bringen) - und dann zum Senden an den Connector übertragen werden. Die Reparaturwarteschlange wird auch hier verwendet: Wenn niemand das Objekt akzeptiert hat, kann der Administrator es später erneut versuchen.
Skalieren
Die Rollen des Connectors, des Eingabeprozessors und des Ausgabeprozessors sind zustandslos. Dadurch können wir das System horizontal skalieren und einfach neue Instanzen der Anwendung mit der enthaltenen Rolle des gewünschten Typs hinzufügen. Für die horizontale Speicherskalierung wird ein
Ansatz zur Clusterorganisation mithilfe virtueller Buckets verwendet. Nach dem Hinzufügen eines neuen Servers wird ein Teil des Buckets von alten Servern im Hintergrund auf den neuen Server verschoben. Dies geschieht für Benutzer transparent und wirkt sich nicht auf den Betrieb des gesamten Systems aus.
Dateneigenschaften
Objekte können sehr groß sein und andere Objekte enthalten. Wir stellen die Atomizität des Hinzufügens und Aktualisierens von Daten sicher und speichern das Objekt mit allen Abhängigkeiten von einem virtuellen Bucket. Dadurch wird das "Verschmieren" des Objekts auf mehreren physischen Servern vermieden.
Die Versionierung wird unterstützt: Jedes Update des Objekts erstellt eine neue Version, und wir können immer eine Zeitscheibe erstellen und sehen, wie die Welt damals aussah. Für Daten, die keinen langen Verlauf benötigen, können wir die Anzahl der Versionen begrenzen oder sogar nur eine - die letzte - speichern, dh die Versionierung für einen bestimmten Typ tatsächlich deaktivieren. Sie können den Verlauf auch zeitlich begrenzen: Löschen Sie beispielsweise alle Objekte eines bestimmten Typs, die älter als 1 Jahr sind. Die Archivierung wird ebenfalls unterstützt: Wir können Objekte entladen, die älter als die angegebene Zeit sind, wodurch Speicherplatz im Cluster frei wird.
Die Aufgaben
Unter den interessanten Funktionen ist die Möglichkeit hervorzuheben, Aufgaben nach einem Zeitplan, auf Anforderung des Benutzers oder programmgesteuert über die Sandbox auszuführen:

Hier sehen wir eine andere Rolle - Läufer. Diese Rolle hat keinen Status, und bei Bedarf können dem Cluster zusätzliche Anwendungsinstanzen mit dieser Rolle hinzugefügt werden. Die Verantwortung des Läufers besteht darin, die Aufgaben zu erledigen. Wie bereits erwähnt, ist das Erstellen neuer Aufgaben aus der Sandbox möglich. Sie werden im Speicher in die Warteschlange gestellt und dann auf dem Läufer ausgeführt. Diese Art von Aufgabe heißt Job. Wir haben auch eine Art von Aufgabe namens Aufgabe - dies sind benutzerdefinierte Aufgaben, deren Ausführung (unter Verwendung der Cron-Syntax) oder bei Bedarf geplant ist. Um solche Aufgaben auszuführen und zu verfolgen, verfügen wir über einen praktischen Task-Manager. Damit diese Funktionalität verfügbar ist, müssen Sie die Scheduler-Rolle aktivieren. Diese Rolle hat einen Zustand, daher skaliert sie nicht, was jedoch nicht erforderlich ist. Wie alle anderen Rollen kann sie jedoch eine Replik haben, die zu funktionieren beginnt, wenn der Master sich plötzlich weigert.
Logger
Eine andere Rolle heißt Logger. Es sammelt Protokolle aller Clustermitglieder und bietet eine Schnittstelle zum Hochladen und Anzeigen über eine Weboberfläche.
Dienstleistungen
Es ist erwähnenswert, dass das System das Erstellen von Diensten vereinfacht. In der Konfigurationsdatei können Sie angeben, welche Anforderungen an den vom Benutzer geschriebenen Handler gesendet werden sollen, der in der Sandbox ausgeführt wird. In diesem Handler können Sie beispielsweise eine Art analytische Abfrage ausführen und das Ergebnis zurückgeben.
Der Dienst wird in der Konfigurationsdatei beschrieben:
services: sum: doc: "adds two numbers" function: sum return_type: int args: x: int y: int
Die GraphQL-API wird automatisch generiert und der Dienst kann aufgerufen werden:
query { sum(x: 1, y: 2) }
Dies ruft den Summenhandler auf, der das Ergebnis zurückgibt:
3
Abfrageprofilerstellung und Metriken
Um das System- und Abfrageprofil zu verstehen, haben wir die Unterstützung für das OpenTracing-Protokoll implementiert. Das System kann bei Bedarf Informationen an Tools senden, die dieses Protokoll unterstützen, z. B. Zipkin, damit Sie verstehen, wie die Anforderung ausgeführt wurde:

Natürlich bietet das System interne Metriken, die mit Prometheus erfasst und mit Grafana visualisiert werden können.
Bereitstellen
Tarantool Data Grid kann über RPM-Pakete oder -Archive bereitgestellt werden. Verwenden Sie dazu das Dienstprogramm aus der Lieferung oder Ansible. Außerdem wird Kubernetes (
Tarantool Kubernetes Operator ) unterstützt.
Eine Anwendung, die Geschäftslogik (Konfiguration, Handler) implementiert, wird als Archiv über die Benutzeroberfläche oder mithilfe eines Skripts über die von uns bereitgestellte API in den Tarantool Data Grid-Cluster geladen.
Anwendungsbeispiele
Welche Anwendungen kann ich mit dem Tarantool Data Grid erstellen? Tatsächlich hängen die meisten Geschäftsaufgaben in irgendeiner Weise mit der Verarbeitung, dem Speichern und dem Zugriff auf den Datenstrom zusammen. Wenn Sie also große Datenströme haben, die sicher gespeichert werden müssen und Zugriff darauf haben, kann unser Produkt Ihnen viel Zeit bei der Entwicklung sparen und sich auf Ihre Geschäftslogik konzentrieren.
Zum Beispiel möchten wir Informationen über den Immobilienmarkt sammeln, um anschließend beispielsweise Informationen über die besten Angebote zu erhalten. In diesem Fall unterscheiden wir folgende Aufgaben:
- Roboter, die Informationen aus offenen Quellen sammeln - dies sind unsere Datenquellen. Sie können dieses Problem mit vorgefertigten Lösungen oder durch Schreiben von Code in einer beliebigen Sprache lösen.
- Als Nächstes akzeptiert und speichert das Tarantool-Datenraster die Daten. Wenn das Format der Daten aus verschiedenen Quellen unterschiedlich ist, können Sie Code in der Lua-Sprache schreiben, was zur Konvertierung in ein einziges Format führt. In der Vorverarbeitungsphase können Sie beispielsweise auch wiederkehrende Angebote filtern oder zusätzlich Informationen zu auf dem Markt tätigen Agenten in der Datenbank aktualisieren.
- Jetzt haben Sie bereits eine skalierbare Lösung im Cluster, die mit Daten gefüllt und Datenproben erstellt werden können. Anschließend können Sie neue Funktionen implementieren, z. B. einen Dienst schreiben, der die Daten abfragt und das rentabelste Angebot an einem Tag ausgibt. Dies erfordert einige Zeilen in der Konfigurationsdatei und ein wenig Lua-Code.
Was weiter?
Unsere Priorität ist es, den Entwicklungskomfort mit dem
Tarantool Data Grid zu erhöhen. Dies ist beispielsweise eine IDE mit Unterstützung für die Profilerstellung und das Debuggen von Sandbox-Handlern.
Wir achten auch sehr auf Sicherheitsfragen. Derzeit bestehen wir die FSTEC Russia-Zertifizierung, um das hohe Sicherheitsniveau zu bestätigen und die Anforderungen für die Zertifizierung von Softwareprodukten zu erfüllen, die in Informationssystemen für personenbezogene Daten und staatlichen Informationssystemen verwendet werden.