- La plateforme de paiements d’Uber a migré vers une architecture centrée sur LedgerStore, alors que les données de registre accumulées depuis 2017 dépassaient 1 billion d’enregistrements et plusieurs PB, dans une structure mixte DynamoDB·TerraBlob·LedgerStore
- En raison du coût de DynamoDB, l’architecture ne conservait que les 12 semaines les plus récentes de données ; LedgerStore, un stockage de registre en append-only, a été retenu comme solution de long terme
- La validation a combiné une validation shadow répliquant le trafic de production et une validation hors ligne comparant des dumps complets, afin de vérifier séparément les risques liés aux données actuelles et aux données froides
- Le backfill pouvant générer une charge supérieure au trafic normal, il a été mené progressivement avec de petits lots, de l’idempotence, un contrôle de débit, un arrêt d’urgence et l’isolation des enregistrements problématiques
- Le basculement a été déployé prudemment sur plusieurs semaines, puis finalisé sans interruption, depuis le fallback initial jusqu’à la suppression finale des tables après un mois de conservation des données DynamoDB
Cible de la migration et structure de stockage existante
- La plateforme de paiements d’Uber, Gulfstream, utilisait DynamoDB comme stockage lors de son lancement en 2017
- À l’échelle d’Uber, le coût de DynamoDB est devenu important, ce qui a conduit à une structure de stockage en trois branches
- Les données chaudes des 12 dernières semaines étaient stockées dans DynamoDB
- Les données froides plus anciennes étaient stockées dans TerraBlob, le blob store interne d’Uber
- Des données étaient déjà écrites dans LedgerStore, qui est devenu la cible finale de la migration
- Le périmètre de la migration couvrait les données de registre de l’ensemble des activités d’Uber depuis 2017
- Enregistrements immuables : 1,2 PB une fois compressés
- Index secondaires : 0,5 PB non compressés
- Les enregistrements du registre, une fois écrits, ne peuvent en pratique plus être modifiés ; si une correction est nécessaire, les données des index secondaires peuvent être mises à jour
Pourquoi avoir choisi LedgerStore
- LedgerStore est une base de données de type registre append-only
- Sa conception, adaptée aux données de paiement, correspondait aux besoins de Gulfstream
- Immutabilité vérifiable permettant de confirmer, via des signatures cryptographiques, qu’un enregistrement n’a pas été modifié
- Stockage hiérarchisé séparant données chaudes et froides selon les besoins de traitement des requêtes et les coûts de stockage
- Meilleures caractéristiques de latence pour les index secondaires à cohérence éventuelle
- Réduire trois stockages à un seul simplifie le code d’accès au stockage de Gulfstream ainsi que la conception de génération des index
- LedgerStore fonctionne on-premise dans les datacenters d’Uber, ce qui permet une latence réseau plus faible
- La migration vers LedgerStore apporte aussi une réduction significative des coûts récurrents
Stabilité du trafic actuel vérifiée par validation shadow
- Cinq critères ont été définis pour juger si le backfill était correct
- Exhaustivité : tous les enregistrements ont été backfillés
- Exactitude : tous les enregistrements sont corrects
- Charge : LedgerStore peut absorber la charge actuelle
- Latence : la latence P99 de LedgerStore reste dans la plage acceptable
- Latence des index : le retard de création des index secondaires en arrière-plan reste acceptable
- La validation shadow compare les réponses fondées sur le stockage existant avec celles obtenues lorsque LedgerStore sert de source de données
- L’objectif était d’atteindre au moins 99,99 % d’exhaustivité et d’exactitude du backfill selon les critères de validation shadow, avec un plafond fixé à 99,9999 %
- Ce plafond était nécessaire, car enquêter jusqu’au bout sur tous les cas suspects à grande échelle peut bloquer le projet
- Une migration de données historiques peut inclure de mauvaises écritures effectuées au début du développement ou des corruptions de données liées au changement d’échelle
- Même si S3 garantit une durabilité de 11 nines, à l’échelle de 1 billion d’enregistrements on peut s’attendre à 10 corruptions
- Dans des index à cohérence éventuelle, des enregistrements qui apparaîtront quelques secondes plus tard peuvent être vus comme manquants lors de la validation shadow, créant des faux positifs
- Vérifier de manière fiable 6 nines exige de comparer 100 millions d’enregistrements, ce qui demande plus d’une journée de collecte à 1 000 comparaisons par seconde
- Pour 7 nines, il faudrait attendre 12 jours dans les mêmes conditions
- En répliquant le trafic de production vers LedgerStore, il a été possible de vérifier simultanément la charge, la latence, la latence des index et la fiabilité du code d’accès
- Les problèmes de latence et de retard d’index observés pendant la migration ont conduit à plusieurs corrections
- Optimisation des clés de partition pour améliorer la répartition des données d’index
- Correction d’un problème d’index qui provoquait des scans d’enregistrements au lieu de consultations ponctuelles
- La validation shadow en live est utile pour les données actuellement consultées, mais offre difficilement de fortes garanties sur l’ensemble des données historiques rarement accédées
Validation hors ligne et backfill incrémental
- La validation hors ligne compare l’intégralité des données de LedgerStore avec des dumps de données DynamoDB
- Comme le trafic live accède surtout aux données récentes, les problèmes cachés dans les données froides sont difficiles à détecter avec la seule validation shadow
- Les enregistrements présentant des problèmes de données doivent être ignorés pour permettre la poursuite du backfill, tout en tenant compte de la possibilité de bugs dans le job de backfill lui-même
- La plus grande opération de validation portait sur 70 TB de données compressées, soit environ 300 TB non compressés, avec 760 milliards d’enregistrements comparés dans un seul job
- À cette échelle, le job Apache Spark nécessitait du data shuffle, et a utilisé conjointement Distributed Shuffle as a Service for Spark, Dynamic Resource Allocation et Speculative Execution
- Les enregistrements manquants identifiés par la validation hors ligne ont servi d’entrée à un backfill incrémental
- La répétition des cycles de validation et de backfill a permis de vérifier que tous les enregistrements avaient bien été écrits
Problèmes opérationnels rencontrés pendant le backfill
- Le backfill doit démarrer à petite échelle puis monter progressivement jusqu’aux limites du système
- Forcer brutalement au-delà de ces limites revient à infliger un DDoS à son propre système
- Il faut identifier et résoudre les goulets d’étranglement avant de réaugmenter l’échelle
- Une surveillance étroite est nécessaire après chaque montée en charge
- Backfiller plusieurs années de données en quelques mois génère une charge bien supérieure au trafic normal
- Si la production traite 1 000 requêtes par seconde, backfiller 100 milliards d’enregistrements à 10 000 requêtes par seconde nécessite 120 jours
- Si le job de backfill risque de provoquer une panne en cours de route, il doit être arrêté immédiatement
- Le backfill ne doit pas être un job monolithique lancé d’un trait jusqu’au bout, mais être découpé en lots incrémentaux
- Chaque lot doit être assez petit pour se terminer en quelques minutes
- Il doit être idempotent, car le job peut s’interrompre en cours de lot
- À la fin de chaque lot, des statistiques comme le nombre d’enregistrements lus et backfillés sont écrites dans un fichier puis agrégées pour suivre la progression
- Un backfill sûr exige un contrôle de débit ajustable
- En Java/Scala, on peut utiliser le
RateLimiterde Guava - S’il est possible d’accélérer lorsque le trafic de production est faible, le système ajuste le RPS en surveillant l’état de la plateforme
- Uber a ajusté le RPS selon une logique additive increase/multiplicative decrease, tout en conservant un plafond de sécurité
- En Java/Scala, on peut utiliser le
- En cas de panne ou de suspicion de surcharge, il faut pouvoir arrêter rapidement le backfill
- Pendant un incident, le backfill doit être stoppé à titre préventif et pour réduire le bruit
- Même après l’incident, la restauration du système peut générer une charge supplémentaire
- Une fonction d’arrêt d’urgence aide aussi à déboguer les problèmes de montée en charge
Fichiers volumineux, tolérance aux erreurs et logging
- Il est approprié de maintenir la taille des fichiers de dump autour de 1 GB, avec une flexibilité d’environ un facteur 10 dans les deux sens
- Des fichiers trop volumineux peuvent se heurter aux limites MultiPart de plusieurs outils
- Des fichiers trop petits entraînent un nombre excessif de fichiers, au point que leur simple listing prend beaucoup de temps
- Lors de l’exécution de commandes shell, on peut atteindre la limite ARGMAX
- Pendant la transformation des données de backfill, des problèmes de qualité ou des enregistrements corrompus apparaissent inévitablement
- Les enregistrements problématiques étant dispersés de manière aléatoire, il est impossible d’arrêter le job à chaque fois
- Mais ils ne doivent pas non plus être ignorés, car cela peut révéler un bug du code
- Les enregistrements problématiques sont dumpés séparément et leurs statistiques surveillées
- Si le taux d’échec devient élevé, le backfill est interrompu manuellement, les problèmes corrigés, puis repris
- L’écriture d’enregistrements peut échouer à cause d’un RPC timeout
- Des retries sont possibles, mais à un certain moment il faut abandonner et continuer, quelle qu’en soit la raison, pour faire progresser l’ensemble du job
- Même si l’on souhaite produire beaucoup de logs pour le débogage et le suivi de progression, cela peut mettre une forte pression sur l’infrastructure de logging
- Même si les logs peuvent être écrits, leur volume à conserver peut devenir excessif
- Un rate limiting est appliqué aux parties qui génèrent beaucoup de logs
- Si les erreurs restent rares, il est possible de journaliser toutes les erreurs
Déploiement progressif et suppression du fallback
- En plus de la validation et de l’analyse des statistiques de backfill, le basculement vers LedgerStore a réduit le risque grâce à un déploiement prudent
- Le rollout s’est étalé sur plusieurs semaines, avec l’approbation des ingénieurs d’astreinte des principaux services appelants
- Au départ, un fallback était utilisé : si les données n’étaient pas trouvées dans LedgerStore, elles étaient récupérées depuis DynamoDB
- Chaque enregistrement signalé comme manquant dans les logs de fallback était revérifié pour confirmer s’il manquait réellement dans LedgerStore
- Même après la suppression du fallback, les données DynamoDB ont été conservées pendant un mois
- Ensuite, les écritures vers DynamoDB ont été arrêtées, une sauvegarde finale a été créée, puis les tables ont été supprimées
- L’ensemble de la migration s’est déroulé sur deux ans et s’est achevé sans downtime ni incident, pendant ou après la migration
1 commentaires
Commentaires sur Hacker News
Je me demande s’il serait possible de faire tenir 1,7 pétaoctet de données (1 billion d’enregistrements indexés) sur un seul serveur bare metal très costaud, à moins de quelques milliers de dollars par mois, et de le servir avec SQLite
Par exemple avec une approche comme celle-ci : https://use.expensify.com/blog/scaling-sqlite-to-4m-qps-on-a...
SQLite prend en charge des bases de données jusqu’à 281 téraoctets en supposant qu’on puisse trouver un disque et un système de fichiers supportant des fichiers de 281 téraoctets. Malgré cela, si vous voyez venir des contenus de plusieurs téraoctets, il vaut mieux envisager une base de données client/serveur centralisée plutôt que SQLite
Il paraît déjà difficile de faire tenir ce volume de stockage dans un seul serveur, et alors à quelques milliers de dollars par mois, encore moins. Et SQLite n’est pas adapté à cet usage
Une entreprise disposant de suffisamment de capital et de capacité organisationnelle peut bâtir son propre cloud à bien moindre coût, mais une grande partie du calcul consiste à externaliser les risques
[0] https://www.sqlite.org/releaselog/3_33_0.html
[1] https://www.sqlite.org/limits.html (#12)
LedgerStore ne semble pas être open source [1], et pour trouver des informations à son sujet il faut suivre des billets de blog Uber qui se renvoient mutuellement
Celui de 2021 qui semble contenir le plus d’informations sur LedgerStore est probablement celui-ci :
https://www.uber.com/en-US/blog/dynamodb-to-docstore-migrati...
[1]:https://github.com/uber
Globalement, Uber semble avoir une forte culture du build it yourself. Ils ont tendance à conclure que les solutions open source existantes ne suffisent pas et à construire eux-mêmes. C’est différent par exemple de l’approche de Facebook, qui a amélioré MySQL avec MyRocks/RocksDB tout en gardant cela en open source
En lisant l’article, il apparaît assez vite qu’Uber utilisait mal DynamoDB
Certains parcours utilisateurs critiques semblaient nécessiter une forte cohérence, et les transactions historiques demandaient un entrepôt de données massif
Il est étrange qu’ils n’aient pas d’abord fait évoluer leur architecture DynamoDB à deux tables vers quelque chose comme DynamoDB + Redshift. C’est un schéma assez courant
Vers 2015, il y a eu une période où des entreprises tech cool comme Netflix, Spotify, SoundCloud ou Uber construisaient beaucoup d’outils d’infrastructure et de bases de données
Aujourd’hui, les ingénieurs parlent souvent en termes AWS/cloud
C’est rafraîchissant de voir qu’il existe encore des organisations qui fabriquent ce genre d’outils elles-mêmes
Je ne connais pas l’économie de ce projet en particulier, mais DynamoDB est vraiment cher
À une époque, je pensais que c’était parce que tout le monde utilisait mal DynamoDB, en faisant des scans et des requêtes au lieu de simples lectures ponctuelles sur des tables pré-calculées
Mais même en l’utilisant comme une table de hachage distribuée, on paie toujours une grosse prime
Une RCU lit jusqu’à 4 Ko, donc lire 100 Mo demande 100 000 RCU, soit 30 000 $ par an ou 2 500 $ par mois. Sauf erreur dans mon calcul, je ne vois rien de comparable à ce prix
Je me demande s’ils ont étudié https://tigerbeetle.com
Et Uber fait probablement partie des rares grandes entreprises à avoir un contrat de support avec la Zig Foundation
Félicitations à ceux qui ont travaillé là-dessus. Cela dit, le seul coût d’exploitation de cette équipe doit déjà être assez élevé, probablement pas très loin des 6 millions de dollars d’économies, sans même parler de la charge de maintenance supplémentaire
En plus, il ne semble pas très probable qu’un système de paiement soit un pari de long terme, donc je trouve intéressant de voir pourquoi des équipes se lancent dans ce type de projet. Est-ce une forme de coût irrécupérable liée à l’existence d’une équipe d’ingénierie déjà en place ?
Entre les deux, il y a largement de la place pour une estimation des coûts plus réaliste
Si les données et les index représentent 1,7 Po, alors au tarif public du stockage DynamoDB on est à environ 5,1 millions de dollars par an
Cela semble tout à fait réaliste pour disposer d’un système dédié bien adapté à une partie essentielle de l’activité
Ce n’était pas un chantier aussi énorme que certains semblent le dire. Et comme les enregistrements étaient immuables, une grande partie du problème devenait nettement plus simple
Je me demande si c’est simplement un autre cas exceptionnel où, passé une certaine échelle, construire soi-même devient plus avantageux. L’ampleur qu’Uber doit gérer est assez stupéfiante
Le texte original ne permet pas vraiment de savoir quel est le coût total de possession du service refactorisé. Ils doivent maintenant gérer leur propre base de données ainsi que le stockage derrière, non ? Ai-je raté quelque chose ?
Cette entreprise ne vendait pas un produit pour utilisateurs finaux, mais un système de fichiers distribué
À mon avis, la plupart des entreprises n’ont pas l’expertise nécessaire pour construire des systèmes comme des bases de données, et même si cela semble économiquement souhaitable sur le papier, elles ont en réalité peur de se lancer
C’est un très bon exemple de à quel point un stockage de données cloud propriétaire peut coûter cher, et du fait qu’il est malgré tout réellement possible d’en migrer vers autre chose
En supposant que les acteurs agissent rationnellement, j’ai l’impression qu’un moment de vérité approche pour les fournisseurs de services cloud
J’ai fait une mission dans une petite entreprise qui utilisait GCP Bigtable pour générer des rapports à partir des données issues d’une base MySQL de 375 Mo, et la facture dépassait 11 000 $ par mois
Ils avaient embauché une data scientist tout juste sortie d’école pour produire les rapports, et elle faisait des choses absurdement inefficaces sur un tout petit jeu de données. On m’a demandé de corriger ça dès le lendemain pour une misère, et j’ai refusé
C’est juste un système mal conçu. On aurait eu exactement le même problème avec une base on-prem massivement surprovisionnée pour faire la même chose