Comment nous avons remplacé Elasticsearch et MongoDB par HorizonDB avec Rust et RocksDB pour améliorer les performances, par Radar

Chez Radar, la performance est une fonctionnalité. Notre plateforme traite plus d'un milliard d'appels API par jour provenant de centaines de millions d'appareils à travers le monde. Nous fournissons une infrastructure et des solutions de géolocalisation, notamment des API pour :

  • Géocodage : API de géocodage direct, de géocodage inversé et de géocodage IP avec une couverture mondiale.
  • Recherche : API de saisie semi-automatique d'adresses, de validation d'adresses et de recherche de lieux.
  • Routage : API de distance, de matrice, d'optimisation d'itinéraire, de correspondance d'itinéraire et d'itinéraires.
  • Conformité en matière de géolocalisation : détection de la juridiction actuelle, de la distance par rapport à la frontière, des zones d'exclusion réglementaires, etc.

Mais à mesure que nos produits et nos données évoluent, nos défis techniques évoluent également.

Pour soutenir cette croissance, nous avons développé HorizonDB, une base de données géospatiale écrite en Rust qui consolide plusieurs services de localisation en un seul binaire hautement performant. Avec HorizonDB, nous sommes en mesure de prendre en charge tous les cas d'utilisation ci-dessus avec une excellente empreinte opérationnelle :

  • Gérer 1 000 QPS par cœur.
  • Maintenir une latence médiane de géocodage direct de 50 ms.
  • Maintenir une latence médiane de géocodage inverse inférieure à 1 ms.
  • Évoluer de manière linéaire sur du matériel standard.

Pourquoi nous avons remplacé Mongo et Elasticsearch

Avant HorizonDB, nous répartissions le géocodage entre Elasticsearch et des microservices pour le géocodage direct, et MongoDB pour le géocodage inverse.

L'exploitation et la mise à l'échelle de cette pile étaient coûteuses : Elasticsearch diffusait fréquemment les requêtes à tous les shards et nécessitait des mises à jour par lots orchestrées par le service, tandis que MongoDB ne disposait pas d'une véritable ingestion par lots, nécessitait un surprovisionnement et ne disposait pas d'une fonctionnalité fiable de restauration en masse pour les données erronées.

L'architecture

Nos objectifs pour ce service étaient les suivants :

  1. Efficacité : le service peut fonctionner sur des machines standard, dispose d'une mise à l'échelle automatique prévisible et constitue la source unique de vérité pour toutes nos entités géographiques.
  2. Opérations : les ressources de données peuvent être créées et traitées plusieurs fois par jour, les modifications peuvent être déployées et annulées facilement, et le service doit être simple à utiliser.
  3. Expérience des développeurs : les développeurs doivent pouvoir exécuter le service localement, et les modifications doivent pouvoir être écrites et testées facilement.

Avec ces objectifs à l'esprit, nous avons créé HorizonDB à l'aide de RocksDB, S2, Tantivy, FSTs, LightGBM et FastText.

Les ressources de données sont prétraitées à l'aide d'Apache Spark, ingérées dans Rust et stockées sous forme de ressources versionnées dans AWS S3.


Nom : 1.jpg
Affichages : 226
Taille : 37,4 Ko
Figure 1 : Le serveur HorizonDB est un processus multithread unique qui interroge simultanément différentes « couches » et reclassifie les candidats de manière uniforme.

Rust

Un langage compilé conçu par Mozilla pour la programmation système. L'équipe a apprécié de nombreux aspects de Rust :

  • Compilation et sécurité de la mémoire sans ramasse-miettes : le système de types forts de Rust et la concurrence sûre et expressive sous la forme de Rayon et Tokio nous permettent d'écrire du code performant sans sacrifier la lisibilité. Rust facilite la gestion de la mémoire sans ramasse-miettes, ce qui nous permet de gérer de grands index de données en mémoire avec une latence prévisible.

  • Abstractions d'ordre supérieur : bon nombre de nos ingénieurs travaillent avec des langages de haut niveau où les opérations de liste expressives, la gestion des valeurs nulles et la correspondance de motifs sont monnaie courante. Rust dispose de ces primitives, ce qui permet à notre équipe d'avancer rapidement et d'exprimer clairement la logique, ce qui est important lorsqu'il s'agit de logique complexe telle que le classement des recherches.

  • Multithread et non multiprocessus : HorizonDB devant récupérer des centaines de Go de données à partir de SSD, il est plus efficace d'avoir un seul processus pouvant exploiter le même espace d'adressage mémoire que notre langage de couche API TypeScript déployé sur Node.js, qui consacre un nouveau processus à chaque cœur.

RocksDB

Un arbre LSM (Log-Structured Merge) en cours de traitement, qui sert de stockage principal pour nos enregistrements. Il est incroyablement rapide, avec des temps de réponse généralement de l'ordre de la microseconde (même avec un ensemble de données beaucoup plus volumineux, il est plus rapide que d'autres solutions hautes performances).

S2

S2 est la bibliothèque d'indexation spatiale de Google qui projette un arbre quadratique sur une sphère, transformant les recherches O(n) point-dans-polygone en recherches en temps constant pouvant être mises en cache. Lors de l'écriture de HorizonDB, nous avons écrit des liaisons Rust pour la bibliothèque C++ de Google que nous rendrons bientôt open source.

FST

Les FST sont une structure de données offrant une compression efficace des chaînes de caractères et des requêtes de préfixe. Cet article de blog d'Andrew Gallant décrit en détail comment cela est réalisé. Nous avons constaté que 80 % de nos requêtes étaient bien formées et nous voulions trouver un moyen efficace de mettre en cache ces « chemins heureux ». Grâce aux FST, nous avons pu mettre en cache des millions de ces chemins heureux sur plusieurs Mo de mémoire et souvent renvoyer des candidats de préfixe en quelques millisecondes.


Nom : 2.jpg
Affichages : 89
Taille : 29,2 Ko
Figure 2 : Un FST représente de manière compacte un corpus de mots en compressant les préfixes et suffixes courants. Les FST sont également capables d'encoder des valeurs numériques qui peuvent être récupérées lors du parcours, ce qui nous permet de compresser des métadonnées telles que la latitude et la longitude à des fins de classement.

Tantivy

Une bibliothèque d'index inversé en cours de traitement similaire à Lucene.

Nous avons décidé d'utiliser un index en cours de traitement plutôt qu'un service externe tel qu'Elasticsearch ou Meilisearch pour plusieurs raisons :

  • Qualité de la recherche : afin d'améliorer notre rappel pour des cas d'utilisation tels que la validation d'adresses, nous « élargissons » souvent nos mots-clés de recherche de manière dynamique. Cela se traduirait par l'envoi de plusieurs requêtes sur le réseau si nous utilisions un service externe.

  • Simplicité opérationnelle : tout se trouve dans le même processus, ce qui facilite considérablement la mise à l'échelle des serveurs de recherche. Le mappage mémoire nous permet d'utiliser efficacement du matériel standard avec des index volumineux. Nous avons trouvé cela beaucoup plus simple que la mise à l'échelle d'Elasticsearch, où il était très difficile de régler les paramètres JVM et d'essayer de saturer le CPU sans augmenter la latence.



Nom : 3.jpg
Affichages : 89
Taille : 31,4 Ko
Figure 3 : un index inversé (par opposition à un index direct classique comme dans une base de données SQL) permet de trouver de manière performante les documents pertinents en fonction des termes recherchés. Les identifiants des documents peuvent être compressés à l'aide de techniques telles que le codage delta. La requête « Central Park » peut être traitée de manière performante en interrogeant et en combinant les documents renvoyés par les termes « central » et « park ».

FastText

Afin d'améliorer la précision et la qualité de la recherche, nous avons mis en œuvre un modèle FastText formé à partir d'un mélange de notre corpus de géocodeurs et de nos journaux de requêtes. Avec FastText, nous pouvons représenter sémantiquement les mots d'une requête sous forme de vecteurs numériques, adaptés aux applications d'apprentissage automatique. FastText tolère les fautes de frappe et gère les mots hors vocabulaire grâce à l'utilisation de ngrams. Les vecteurs « proches » représentent des mots sémantiquement similaires, ce qui permet à nos algorithmes d'apprentissage automatique de comprendre la sémantique d'un mot donné dans une requête de recherche.

LightGBM

Nous avons formé plusieurs modèles LightGBM pour classer l'intention des requêtes et baliser certaines parties de nos requêtes en fonction de cette intention. Cela nous permet de « structurer » nos requêtes, améliorant ainsi les performances et la précision de la recherche. Par exemple, une requête considérée comme régionale, telle que « New York », peut ignorer la recherche d'adresse, tandis qu'une requête telle que « 841 broadway » nous permet d'ignorer la recherche de points d'intérêt et de régions.

Apache Spark

Avec Spark, nous sommes en mesure de traiter des centaines de millions de points de données en moins d'une heure, avec une évolutivité quasi linéaire. Nous devions souvent ajuster ou refactoriser les tâches pour obtenir des performances optimales lors des jointures ou des agrégations.

Comme nos données sont écrites dans S3, il devient facile d'inspecter les résultats via Amazon Athena, un déploiement hébergé d'Apache Presto qui peut lire les ressources de stockage d'objets à l'aide de SQL. DuckDB est un autre outil léger que nos ingénieurs utilisent pour inspecter ces ressources à la volée.


Résultats

HorizonDB a transformé à la fois les aspects opérationnels et développementaux de nos offres de géolocalisation. Nous avons obtenu des améliorations générales en termes de coût, de performances et d'évolutivité :

  • Notre service est désormais plus rapide, plus simple à utiliser et plus fiable.
  • Nos développeurs sont en mesure de réagir rapidement aux nouvelles fonctionnalités et aux modifications des données. Nous sommes capables d'ingérer et d'évaluer de nouvelles sources de données en moins d'une journée.
  • Nous avons fermé plusieurs clusters Mongo, un grand cluster Elasticsearch et plusieurs microservices géographiques, ce qui nous a permis d'économiser plusieurs dizaines de milliers de dollars en coûts mensuels.

Nous sommes satisfaits de nos choix de conception avec HorizonDB et sommes prêts à évoluer dans un avenir proche. Nous aborderons la manière dont nous avons conçu certaines fonctionnalités du système dans de futurs articles de blog.

Un grand merci à nos ingénieurs Bradley Schoeneweis, Jason Liu, Jacky Wang, Binh Robles, Greg Sadetsky, David Gurevich et Felix Li, qui ont travaillé d'arrache-pied pour faire de ce système une réalité.

À propos de Radar

Radar relie les mondes numérique et physique grâce à une infrastructure de localisation pour chaque produit et service. La société a été fondée en 2016 par Nick Patrick et Coby Berman, deux anciens collègues de Foursquare qui ont dix ans d'expérience dans la création et la vente de produits et services basés sur la localisation.

Source : Radar

Et vous ?

Pensez-vous que cette déclaration est crédible ou pertinente ?
Quel est votre avis sur le sujet ?

Voir aussi :

Comment nous avons économisé 98 % des coûts du cloud en écrivant notre propre base de données, par Hivekit

Redéfinir la base de données pour l'IA : pourquoi MongoDB a acquis Voyage AI, par Dev Ittycheria, PDG de MongoDB

Elasticsearch est à nouveau open source, en ajoutant l'AGPL, conforme à l'OSI, comme troisième option, après trois ans où les produits d'Elastic ne possédaient qu'une double-licence non open source