Comment avons-nous fait le cœur de l'activité d'investissement d'Alfa-Bank basée sur Tarantool


Un cliché du film «Notre univers secret: la vie cachée de la cellule». L'

investissement est l'un des domaines les plus difficiles du monde bancaire, car il n'y a pas seulement des prêts, des prêts et des dépôts, mais aussi des titres, des devises, des biens, des dérivés et toutes sortes de difficultés sous forme de produits structurels.

Récemment, nous avons constaté une augmentation de la littératie financière de la population. De plus en plus de personnes sont impliquées dans le trading sur les marchés des valeurs mobilières. Les comptes d'investissement individuels sont apparus il n'y a pas si longtemps. Ils vous permettent de négocier sur les marchés des valeurs mobilières et en même temps, de recevoir des déductions fiscales ou de ne pas payer d'impôts. Et tous les clients qui viennent chez nous veulent gérer leur portefeuille et voir des rapports en temps réel. De plus, le plus souvent, ce portefeuille est multi-produits, c'est-à-dire que les gens sont des clients de divers secteurs d'activité.

De plus, les exigences des régulateurs, russes et étrangers, augmentent.

Pour répondre aux besoins actuels et jeter les bases de futures mises à niveau, nous avons développé le cœur de l'activité d'investissement basé sur Tarantool.

Quelques statistiques. L’activité d’investissement d’Alfa-Bank fournit des services de courtage aux particuliers et aux personnes morales offrant la possibilité de négocier sur divers marchés de valeurs mobilières, des services de garde pour le stockage de titres, des services de gestion de fiducie pour les particuliers à capital privé et important, des services d’émission de titres pour les autres entreprises. L’activité d’investissement d’Alfa-Bank représente plus de 3 000 devis par seconde, qui sont téléchargés à partir de diverses plateformes de trading. Au cours de la journée de travail, plus de 300 000 transactions sont conclues sur les marchés pour le compte de la banque ou de ses clients. Sur les plateformes externes et internes, jusqu'à 5 000 ordres sont exécutés par seconde. En même temps, tous les clients, internes et externes, souhaitent voir leurs positions en temps réel.

Contexte


Quelque part depuis le début des années 2000, nos domaines d'investissement se sont développés de manière indépendante: le trading de change, les services de courtage, le trading de devises, le trading de gré à gré de titres et divers dérivés. En conséquence, nous sommes tombés dans le piège des puits fonctionnels. Ce que c'est? Chaque secteur d'activité a ses propres systèmes qui dupliquent les fonctions des autres. Chaque système a son propre modèle de données, bien qu'ils fonctionnent sur les mêmes concepts: transactions, instruments, contreparties, devis et plus encore. Et puisque chaque système s'est développé indépendamment, un zoo diversifié de technologies a vu le jour.

De plus, la base de code des systèmes est déjà dépassée, car certains produits sont originaires du milieu des années 90. Et dans certains domaines, cela a ralenti le processus de développement, il y avait des problèmes de productivité.

Nouvelles exigences de solution


Les entreprises ont réalisé que le développement technologique est vital pour le développement futur. On nous a confié les tâches:

  1. Collectez toutes les données d'entreprise dans un seul stockage rapide et dans un modèle de données unique.
  2. Nous ne devons pas perdre ou modifier ces informations.
  3. Il est nécessaire de mettre à jour les données, car à tout moment le régulateur peut demander des statistiques pour les années précédentes.
  4. Nous ne devons pas simplement apporter de nouveaux SGBD à la mode, mais créer une plate-forme pour résoudre les problèmes commerciaux.

De plus, nos architectes fixent leurs conditions:

  1. La nouvelle solution devrait être de classe entreprise, c'est-à-dire qu'elle devrait déjà être testée dans certaines grandes entreprises.
  2. Le mode de fonctionnement de la solution doit être critique pour la mission. Cela signifie que nous devons être présents en même temps dans plusieurs centres de données et expérimenter calmement la déconnexion d'un centre de données.
  3. . , , - . , .
  4. , .

Nous avons suivi la voie standard: formulé des exigences et contacté le service des achats. De là, nous avons obtenu une liste d'entreprises qui, en général, sont prêtes pour nous à le faire. Ils ont parlé de la tâche à tout le monde et six d'entre eux ont reçu une évaluation des solutions.

À la banque, nous ne croyons pas la parole de qui que ce soit, nous aimons tout tester par nous-mêmes. Par conséquent, une condition préalable à notre appel d'offres était de réussir des tests de résistance. Nous avons formulé des tâches de test pour la charge, et déjà trois entreprises sur six ont accepté à leurs propres frais de mettre en œuvre un prototype de la solution basée sur des technologies en mémoire pour la tester.

Je ne dirai pas comment nous avons tout testé et combien de temps cela a pris, je résumerai seulement: les meilleures performances dans les tests de charge ont été démontrées par le prototype de la solution basée sur Tarantool de l'équipe de développement du groupe Mail.ru Nous avons signé un contrat et commencé le développement. Quatre personnes venaient du groupe Mail.ru et d'Alfa-Bank, il y avait trois développeurs, trois analystes système, un architecte de solution, un propriétaire de produit et un Scrum master.

Ensuite, je vais parler de la façon dont notre système a grandi, comment il a évolué, ce que nous avons fait et pourquoi.

Développement


Tout d'abord, nous nous sommes demandé comment obtenir des données de nos systèmes actuels. Nous avons décidé que HTTP nous convenait tout à fait, car tous les systèmes actuels communiquent entre eux, envoyant XML ou JSON via HTTP.

Nous utilisons le serveur HTTP Tarantool intégré, car nous n'avons pas besoin de mettre fin aux sessions SSL, et ses performances nous suffisent.

Comme je l'ai déjà dit, nous avons tous les systèmes vivant dans différents modèles de données, et à l'entrée, nous devons apporter l'objet au modèle que nous décrirons à la maison. Un langage était nécessaire pour transformer les données. Nous avons choisi l'impératif Lua. Nous exécutons tout le code pour la conversion des données dans le bac à sable - c'est un endroit sûr, au-delà duquel le code en cours ne va pas au-delà. Pour ce faire, il suffit de faire une chaîne de chargement du code nécessaire, en créant un environnement avec des fonctions qui ne peuvent rien bloquer ou supprimer quelque chose.


Après la conversion, la conformité des données avec le modèle que nous créons doit être vérifiée. Nous avons longuement discuté de ce que devrait être un modèle, du langage à utiliser pour le décrire. Nous nous sommes arrêtés chez Apache Avro, car le langage est simple et il a le support de Tarantool. De nouvelles versions du modèle et du code utilisateur peuvent être mises en service plusieurs fois par jour, même sous charge, même sans, à tout moment de la journée, et s'adapter très rapidement aux changements.


Après vérification, les données doivent être enregistrées. Nous le faisons avec vshard (nous avons des répliques géo-espacées de fragments).


De plus, les spécificités sont telles que pour la plupart des systèmes qui nous envoient des données, peu importe que nous les ayons reçues ou non. Par conséquent, dès le début, nous avons mis en œuvre la ligne de réparation. Ce que c'est? Si, pour une raison quelconque, l'objet n'a pas réussi la transformation ou la vérification des données, nous confirmons toujours la réception, mais en même temps, nous enregistrons l'objet dans la file d'attente de réparation. Il est cohérent, situé dans le référentiel principal avec les données d'entreprise. Nous avons immédiatement écrit une interface d'administration pour cela, diverses mesures et alertes. En conséquence, nous ne perdons pas de données. Même si quelque chose a changé dans la source, si le modèle de données a changé, nous le trouverons immédiatement et nous pourrons nous adapter.


Vous devez maintenant apprendre à récupérer des données stockées. Nous avons soigneusement analysé nos systèmes et constaté que sur la pile classique de Java et Oracle, il existe toujours une sorte d'ORM qui convertit les données d'une vue relationnelle en une vue objet. Alors pourquoi ne pas donner immédiatement des objets aux systèmes sous forme de graphe? Par conséquent, nous avons volontiers pris GraphQL, qui répondait à tous nos besoins. Il vous permet de recevoir des données sous forme de graphiques, de ne retirer que ce dont vous avez besoin en ce moment. Vous pouvez même versionner l'API avec suffisamment de flexibilité.


Presque immédiatement, nous avons réalisé que les données extraites n'étaient pas suffisantes pour nous. Nous avons créé des fonctions qui peuvent être attachées aux objets du modèle - en fait, des champs calculés. Autrement dit, nous attachons une certaine fonction au champ, qui, par exemple, considère le prix moyen d'un devis. Et le consommateur externe qui demande les données ne sait même pas que ce champ est calculé.


Implémentation d'un système d'authentification.


Ensuite, ils ont remarqué que plusieurs rôles se sont cristallisés dans notre solution. Un rôle est une sorte d'agrégateur de fonctions. En règle générale, les rôles ont différents profils d'utilisation de l'équipement:

  • T-Connect: gère les connexions entrantes, limitées par le processeur, consomme peu de mémoire, ne stocke pas l'état.
  • IB-Core: transforme les données qu'il reçoit via le protocole Tarantool, c'est-à-dire qu'il fonctionne avec des tablettes. Ne stocke pas non plus l'état et peut être mis à l'échelle.
  • Stockage: enregistre uniquement les données, n'utilise aucune logique. Les interfaces les plus simples sont implémentées dans ce rôle. Evolutif grâce à vshard.


C'est-à-dire qu'avec l'aide de rôles, nous avons détaché les uns des autres différentes parties du cluster qui peuvent être mises à l'échelle indépendamment les unes des autres.

Nous avons donc créé un enregistrement asynchrone d'un flux de données transactionnelles et une file d'attente de réparation avec une interface administrateur. L'enregistrement est asynchrone d'un point de vue commercial: si nous avons la garantie de nous enregistrer des données, peu importe où, nous le confirmerons. Si ce n'est pas confirmé, alors quelque chose s'est mal passé, les données doivent être envoyées. Il s'agit d'un enregistrement asynchrone.

Essai


Dès le début du projet, il a été décidé d'essayer d'inculquer un développement piloté par les tests. Nous écrivons des tests unitaires en Lua en utilisant le framework tarantool / tap, des tests d'intégration en Python en utilisant le framework pytest. Dans le même temps, les développeurs et les analystes participent à la rédaction des tests d'intégration.

Comment appliquer le développement piloté par les tests?

Si nous voulons une nouvelle fonctionnalité, nous essayons d'abord d'écrire un test pour cela. Après avoir découvert le bogue, nous devons d'abord écrire dans le test, puis le corriger. Au début, c'est difficile de travailler comme ça, il y a un malentendu de la part des employés, même du sabotage: "Corrigeons-le rapidement, faisons quelque chose de nouveau, puis couvrons-le avec des tests." Seul ce «plus tard» ne se produit presque jamais.

Par conséquent, vous devez d'abord vous forcer à écrire des tests, demander aux autres de le faire. Croyez-moi, le développement piloté par les tests est bénéfique même à court terme. Vous sentirez qu'il est devenu plus facile pour vous de vivre. Selon nos sentiments, 99% du code est actuellement couvert par des tests. Cela semble beaucoup, mais nous n'avons aucun problème: les tests sont exécutés à chaque commit.

Cependant, nous aimons avant tout les tests de résistance, nous les considérons comme les plus importants et les effectuons régulièrement.

Je vais vous raconter une courte histoire sur la façon dont nous avons effectué la première étape du test de charge d'une des premières versions. Nous avons placé le système sur l'ordinateur portable du développeur, allumé la charge et reçu 4 000 transactions par seconde. Bon résultat pour un ordinateur portable. Nous avons installé un support de charge virtuel de quatre serveurs, plus faible qu'en production. Déployé au minimum. Nous le démarrons et nous obtenons un résultat pire que sur un ordinateur portable dans un seul thread. Contenu choquant.

Nous étions très tristes. Nous regardons la charge du serveur, et ils se révèlent inactifs.


Nous appelons les développeurs, et ils nous expliquent, des gens qui sont venus du monde Java, que Tarantool est monofil. Il peut être utilisé efficacement par un seul cœur de processeur sous charge. Ensuite, nous avons déployé le nombre maximal possible d'instances Tarantool sur chaque serveur, activé la charge et reçu déjà 14,5 mille transactions par seconde.


Je vais l'expliquer à nouveau. En raison de la division en rôles qui utilisent les ressources différemment, nos rôles qui étaient responsables du traitement des connexions et de la transformation des données n'ont chargé que le processeur, et c'était strictement proportionnel à la charge.



De plus, la mémoire n'était utilisée que pour le traitement des connexions entrantes et des objets temporaires.


Au contraire, sur les serveurs de stockage, la charge du processeur a augmenté, mais beaucoup plus lentement que sur les serveurs qui gèrent les connexions.


Et la consommation de mémoire a augmenté en proportion directe de la quantité de données chargée.


Prestations de service


Pour développer notre nouveau produit spécifiquement en tant que plate-forme d'application, nous avons créé un composant pour y déployer des services et des bibliothèques.

Les services ne sont pas seulement de petits morceaux de code qui fonctionnent dans certains domaines. Il peut s'agir de structures assez grandes et complexes qui font partie du cluster, vérifient les données de référence, déforment la logique métier et donnent des réponses. Nous exportons également le schéma de service vers GraphQL, et le consommateur reçoit un point d'accès aux données universel, avec introspection dans tout le modèle. C'est très confortable.

Étant donné que les services contiennent beaucoup plus de fonctions, nous avons décidé qu'il devrait y avoir des bibliothèques dans lesquelles nous supprimerons le code fréquemment utilisé. Nous les avons ajoutés à un environnement sûr, après avoir vérifié que cela ne nous brise rien. Et maintenant, nous pouvons définir des fonctions pour des environnements supplémentaires sous forme de bibliothèques.

Nous voulions que nous ayons une plate-forme non seulement pour le stockage, mais aussi pour l'informatique. Et puisque nous avions déjà un tas de répliques et de fragments, nous avons implémenté un semblant d'informatique distribuée et l'avons appelé map réduire, car il s'est avéré être comme la carte originale réduire.

Anciens systèmes


Tous nos anciens systèmes ne peuvent pas nous appeler via HTTP et utiliser GraphQL, bien qu'ils prennent en charge ce protocole. Par conséquent, nous avons créé un mécanisme pour répliquer les données sur ces systèmes.


Si quelque chose change pour nous, des déclencheurs particuliers fonctionnent dans le rôle de stockage et le message avec les modifications tombe dans la file d'attente de traitement. Il est envoyé à un système externe à l'aide d'un rôle de réplicateur distinct. Ce rôle ne stocke pas l'état.

De nouvelles améliorations


Comme vous vous en souvenez, d'un point de vue commercial, nous avons fait un enregistrement asynchrone. Mais ensuite, ils ont réalisé que ce ne serait pas suffisant, car il existe une classe de systèmes qui doivent recevoir immédiatement une réponse sur l'état de l'opération. Par conséquent, nous avons étendu notre GraphQL et ajouté des mutations. Ils s'inscrivent organiquement dans le paradigme existant de l'utilisation des données. Nous avons un seul point de lecture et d'écriture pour une autre classe de systèmes.


Nous avons également réalisé que les services seuls ne seraient pas suffisants pour nous, car il y a des rapports assez lourds qui doivent être élaborés une fois par jour, une semaine, un mois. Cela peut prendre du temps et les rapports peuvent même bloquer la boucle d'événement Tarantool. Par conséquent, nous avons créé des rôles distincts: planificateur et coureur. Les coureurs ne stockent pas l'état. Ils lancent des tâches difficiles que nous ne pouvons pas compter à la volée. Et le rôle de planificateur surveille le calendrier de lancement de ces tâches, qui est décrit dans la configuration. Les tâches elles-mêmes sont stockées au même endroit que les données d'entreprise. Lorsque le bon moment arrive, le planificateur prend la tâche, la donne à un coureur, il la considère et enregistre le résultat.


Toutes les tâches ne doivent pas être exécutées dans les délais. Certains rapports doivent être lus sur demande. Dès que cette exigence arrive, une tâche est formée dans le bac à sable et envoyée au runner pour exécution. Après un certain temps, l'utilisateur reçoit une réponse asynchrone indiquant que tout a été calculé, le rapport est prêt.


Initialement, nous avons adhéré au paradigme de la sauvegarde de toutes les données, de la gestion des versions et de leur suppression. Mais dans la vie, de temps en temps, vous devez toujours supprimer quelque chose, principalement des informations brutes ou intermédiaires. Basé sur expirationd, nous avons créé un mécanisme pour nettoyer le stockage des données obsolètes.


Nous comprenons également que tôt ou tard, une situation se produira lorsqu'il n'y aura pas assez d'espace pour stocker des données en mémoire, mais néanmoins les données doivent être stockées. À ces fins, nous allons bientôt faire du stockage sur disque.


Conclusion


Nous avons commencé par charger les données dans un modèle unique, nous avons passé trois mois sur son développement. Nous avions six systèmes de fournisseurs de données. L'ensemble du code de transformation en un seul modèle représente environ 30 000 lignes en Lua. Et la plupart du travail reste à venir. Parfois, il y a un manque de motivation pour les équipes voisines, ce qui complique beaucoup le travail des circonstances. Si jamais vous rencontrez un problème similaire, alors le temps que vous pensez normal pour sa mise en œuvre, multipliez par trois, voire quatre.

N'oubliez pas non plus que les problèmes existants dans les processus métier ne peuvent pas être résolus à l'aide d'un nouveau SGBD, même s'il est très productif. Ce que je veux dire? Au début de notre projet, nous avons créé une impression parmi les clients que nous allons maintenant apporter une nouvelle base de données rapide et en direct! Les processus iront plus vite, tout ira bien. En fait, la technologie ne résout pas les problèmes qui existent dans les processus métier, car les processus métier sont des personnes. Et vous devez travailler avec les gens, pas avec la technologie.

Le développement par des tests dans les étapes initiales peut être douloureux et prendre du temps. Mais son effet positif sera perceptible même à court terme, lorsque vous n'aurez rien à faire pour effectuer des tests de régression.

Il est extrêmement important d'effectuer des tests de charge à toutes les étapes du développement. Plus tôt vous remarquerez une sorte de faille dans l'architecture, plus il sera facile de la corriger, cela vous fera gagner beaucoup de temps à l'avenir.

Il n'y a rien de mal avec Lua. Tout le monde peut apprendre à écrire dessus: un développeur Java, un développeur JavaScript, un développeur Python, un front-end ou un back-end. Nous avons même des analystes qui y écrivent.

Lorsque nous parlons du fait que nous n’avons pas SQL, cela terrifie les gens. "Comment obtenir des données sans SQL?" Est-ce possible? " Bien sûr. Sur un système de classes OLTP, SQL n'est pas nécessaire. Il existe une alternative sous la forme d'un langage qui vous renvoie immédiatement une vue orientée document. Par exemple, GraphQL. Et il existe une alternative sous la forme de calcul distribué.

Si vous comprenez que vous devrez évoluer, concevez immédiatement votre solution sur Tarantool afin qu'elle puisse fonctionner en parallèle sur des dizaines d'instances Tarantool. Sinon, ce sera difficile et douloureux, car Tarantool ne peut utiliser efficacement qu'un seul cœur de processeur.

All Articles