- Apple utilise Cassandra et FoundationDB pour iCloud et CloudKit
- Ces bases de données stockent des milliards de bases de données dans une architecture de multi-location extrême
Leçons intemporelles tirées du monde réel
- Meta et Apple utilisent toutes deux le traitement asynchrone pour fluidifier les fonctionnalités utilisateur
- Les deux entreprises utilisent une architecture sans état (
stateless) pour résoudre les problèmes de scalabilité
- Elles isolent logiquement les ressources pour garantir la fiabilité et la disponibilité
- Elles traitent simplement des exigences variées
- Elles construisent des couches d’abstraction pour améliorer l’expérience développeur
- Elles connaissent leurs utilisateurs et déterminent chaque couche, API et design en conséquence
Cassandra
- Cassandra est un système de gestion de base de données NoSQL orienté colonnes à grande échelle
- Il a été développé à l’origine chez Facebook pour la fonction de recherche dans la boîte de réception Facebook
- Fait intéressant, Meta a elle-même remplacé l’essentiel de son usage de Cassandra par ZippyDB ZippyDB
- iCloud utilise en partie Cassandra, et Apple exploite l’un des plus grands déploiements Cassandra au monde
- plus de 300 000 instances/nœuds
- des centaines de pétaoctets de données
- plus de 2 pétaoctets par cluster
- des millions de requêtes par seconde
- des milliers d’applications
- Cassandra continue d’être activement amélioré chez Apple
- Cependant, CloudKit + Cassandra s’est heurté à des limites de scalabilité, ce qui a conduit à l’adoption de FoundationDB
FoundationDB
- Apple utilise publiquement FoundationDB et l’a racheté en 2015
- FoundationDB est un magasin clé-valeur transactionnel distribué open source conçu pour traiter de très grands volumes de données
- adapté aussi bien aux charges de travail en lecture/écriture qu’aux charges dominées par les écritures
- Apple utilise largement la Record Layer de FoundationDB dans CloudKit
- La Record Layer de FoundationDB fournit une API Java pour le stockage de données structurées
- La Record Layer prend en charge la multi-location extrême
Pourquoi utiliser la Record Layer avec FoundationDB
- FoundationDB gère les systèmes distribués et le contrôle de concurrence.
- La Record Layer joue le rôle d’une base de données relationnelle qui rend FoundationDB plus simple à utiliser.
- CloudKit se situe à la couche la plus haute et fournit des fonctionnalités et des API aux développeurs d’applications.
- Grâce à la Record Layer, Apple prend en charge la multi-location à très grande échelle
- utilisée pour une multi-location extrême offrant à chaque utilisateur de chaque application un magasin d’enregistrements indépendant
- hébergement de milliards de bases de données indépendantes partageant quelques milliers de schémas
Comment CloudKit utilise FoundationDB et la Record Layer
- Dans CloudKit, les applications sont représentées comme des « conteneurs logiques » suivant un schéma défini
- ce schéma décrit brièvement les types d’enregistrements, champs et index nécessaires pour une récupération et des requêtes efficaces
- les applications peuvent organiser les données en « zones » dans CloudKit afin de regrouper logiquement les enregistrements et de permettre une synchronisation optionnelle avec les appareils clients
- Chaque utilisateur se voit attribuer un sous-espace unique dans FoundationDB, et un magasin d’enregistrements est créé pour chaque application avec laquelle l’utilisateur interagit
- en pratique, CloudKit gère un très grand nombre de bases de données logiques correspondant au nombre d’utilisateurs multiplié par le nombre d’applications
- chaque base de données inclut son propre ensemble d’enregistrements, d’index et de métadonnées, ce qui aboutit à des milliards de bases de données
- Lorsque CloudKit reçoit une requête d’un appareil client, il l’achemine via équilibrage de charge vers un processus de service CloudKit disponible
- le processus traite la requête en interagissant avec le magasin d’enregistrements concerné de la Record Layer
- CloudKit convertit le schéma applicatif défini en définitions de métadonnées dans la Record Layer, stockées dans un magasin de métadonnées séparé
- ces métadonnées sont enrichies de champs système propres à CloudKit qui suivent la création des enregistrements, leur heure de modification et la zone où ils sont stockés
- pour permettre un accès efficace aux enregistrements dans chaque zone, le nom de la zone est préfixé à la clé primaire
- en plus des index définis par les utilisateurs, CloudKit maintient aussi des « index système » à usage interne, comme le suivi de la taille des enregistrements par type pour gérer les quotas de stockage
L’utilisation conjointe de FoundationDB et de la Record Layer permet de résoudre quatre grands problèmes chez Apple que Cassandra seul ne pouvait pas régler
1. Résoudre le problème de la recherche plein texte personnalisée
- FoundationDB prend en charge la recherche plein texte personnalisée afin que les utilisateurs puissent accéder rapidement à leurs propres données
- En exploitant l’ordre des clés de FoundationDB, il est possible non seulement de rechercher rapidement le début d’un texte (correspondance par préfixe), mais aussi d’effectuer des recherches plus complexes sans surcoût supplémentaire, comme la recherche de proximité et la recherche d’expressions, pour trouver des mots proches les uns des autres ou dans un ordre précis
- Dans les systèmes de recherche classiques, il faut souvent exécuter des processus supplémentaires en arrière-plan pour garder l’index de recherche à jour, mais le système d’Apple traite tout en temps réel, de sorte que l’index est mis à jour immédiatement dès qu’une donnée change, sans étape additionnelle
2. Résoudre le problème des zones à forte concurrence
- CloudKit utilise FoundationDB pour gérer sans heurt un grand nombre de mises à jour simultanées
- Auparavant, avec Cassandra, CloudKit s’appuyait sur un index spécial qui suivait les changements de chaque zone afin de synchroniser les données entre plusieurs appareils
- lorsqu’un appareil devait mettre à jour des données, il consultait cet index pour voir ce qui était nouveau
- mais l’inconvénient était que des conflits pouvaient se produire lorsque plusieurs mises à jour survenaient en même temps
- Avec FoundationDB, CloudKit utilise un type spécial d’index qui suit l’ordre exact de chaque modification sans provoquer de conflit
- cela se fait en attribuant à chaque changement une « version » unique, que CloudKit consulte lors de la synchronisation pour savoir quelles mises à jour un appareil a manquées
- Lorsque CloudKit doit déplacer des données entre plusieurs clusters de stockage afin de mieux répartir la charge, la situation devient plus délicate, car les numéros de version de chaque cluster ne correspondent pas
- pour résoudre ce problème, CloudKit attribue aux données de chaque utilisateur un « nombre de déplacements » (appelé
incarnation/화신), qui augmente à chaque transfert vers un nouveau cluster
- chaque mise à jour d’enregistrement inclut le numéro d’« incarnation » courant de l’utilisateur, si bien qu’après un déplacement, CloudKit peut toujours déterminer le bon ordre des mises à jour en vérifiant à la fois l’incarnation et le numéro de version
- Lors du passage au nouveau système, CloudKit a dû aussi gérer d’anciennes données ne disposant pas de ces numéros de version
- mais le problème a été intelligemment résolu grâce à une fonctionnalité spéciale qui classe les anciennes mises à jour issues de l’ancien système avant celles du nouveau système
- cela a permis d’éviter des modifications complexes dans les applications ou le maintien d’ancien code
- pour préserver le bon ordre de l’historique, le système prend en compte l’incarnation, la version et la valeur d’un ancien compteur de mises à jour
3. Résoudre le problème des requêtes à forte latence
- FoundationDB est conçu pour une forte concurrence plutôt que pour une faible latence. Autrement dit, il est pensé pour traiter un grand nombre d’opérations simultanées plutôt que pour maximiser la vitesse de chaque opération individuelle
- Pour tirer pleinement parti de cette conception, la Record Layer exécute beaucoup d’opérations de manière « asynchrone »
- cela permet de mettre en file d’attente les tâches à terminer plus tard et d’effectuer d’autres opérations entre-temps
- cette approche aide à masquer la latence susceptible d’apparaître pendant ces opérations
- Toutefois, l’outil utilisé par FoundationDB pour communiquer avec la base de données est conçu pour le réseau avec un seul thread, et donc pour ne faire qu’une seule chose à la fois
- dans les versions précédentes, cette configuration provoquait des embouteillages, car toutes les opérations attendaient leur tour sur ce thread réseau
- comme la Record Layer utilisait cette approche à thread unique, cela créait un goulot d’étranglement
- Pour améliorer cela, Apple a réduit la charge de travail de ce thread réseau
- le système peut désormais travailler avec la base de données sur plusieurs aspects en parallèle, sans former de file d’attente, ce qui accélère les opérations complexes
- cela masque la latence, ou la lenteur perçue, car le système n’attend plus qu’une opération soit terminée avant d’en lancer une autre
4. Résoudre le problème des transactions en conflit
- Dans FoundationDB, un « conflit de transaction » se produit lorsqu’une transaction lit une clé donnée pendant qu’une autre transaction modifie cette même clé
- FoundationDB fournit des mécanismes de contrôle précis sur les ensembles de clés susceptibles de provoquer ces conflits à la lecture ou à l’écriture
- Une manière courante d’éviter les conflits inutiles consiste à effectuer, sur diverses clés, un type spécial de lecture qui ne provoque pas de conflit, appelé lecture « snapshot »
- si cette lecture découvre une clé importante, la transaction ne signale alors que cette clé précise comme source potentielle de conflit, plutôt que toute la plage
- cela garantit que la transaction n’est affectée que par des modifications réellement importantes pour le résultat
- La Record Layer utilise cette stratégie pour gérer efficacement une structure appelée skip list, qui fait partie d’un système d’indexation par classement
- toutefois, définir manuellement ces plages de conflit peut être délicat, et peut conduire à des bugs difficiles à identifier, surtout lorsque cela est mêlé à la logique principale de l’application
- c’est pourquoi, dans les systèmes construits sur FoundationDB, il est recommandé de créer des outils de plus haut niveau, comme des index personnalisés, pour gérer ces schémas
- cette approche évite de laisser à chaque application cliente la responsabilité d’assouplir les règles de conflit, ce qui pourrait entraîner des erreurs et des incohérences
1 commentaires
Avis Hacker News
Un utilisateur de Hacker News a partagé, à partir de son expérience chez Apple, une réflexion sur la différence entre bases de données et systèmes de fichiers. Il explique qu’ils remplissent fondamentalement la même fonction et qu’il s’agit d’optimisations pour résoudre des problèmes spécifiques. Par exemple, iCloud montre une manière de définir un système de fichiers à partir d’une base de données. Cet utilisateur a également raconté son expérience de l’utilisation de Cassandra pour stocker des vidéos.
Un autre utilisateur a mentionné son expérience dans une ancienne entreprise, où il avait construit un système de catalogue transactionnel avec FoundationDB et RecordLayer. Ce système était très efficace, et l’usage de gRPC et Protobuf s’était imposé naturellement. Il souligne toutefois que la barrière à l’entrée pour exploiter FoundationDB à grande échelle est élevée.
Un utilisateur estime que la synchronisation d’Apple Notes gère mieux les conflits que les applications de prise de notes basées sur Markdown. Il indique que cela l’a finalement conduit à passer sur Apple Notes.
D’anciens billets au sujet de FoundationDB sont mentionnés. Ils incluent des liens sur le magasin distribué clé-valeur de FoundationDB, la Record Layer, le rachat par Apple, ainsi que sur le fonctionnement et les caractéristiques de FoundationDB.
Un point intéressant est soulevé concernant l’architecture des logiciels desktop natifs qui évoluent progressivement vers le stockage cloud et la collaboration. Il est important de bien gérer les changements de schéma et les migrations de version, car cela se produit à grande échelle sans intervention d’un administrateur.
Un utilisateur dit souhaiter qu’iCloud puisse stocker les sauvegardes Time Machine.
Puisque FoundationDB repose sur SQLite, une question est posée sur la possibilité d’appliquer le moteur HCTree à FoundationDB. HCTree aurait le potentiel d’améliorer par 10 les performances de lecture/écriture de SQLite.
Des critiques sont formulées sur la manière dont iCloud gère les fichiers des utilisateurs. Il arrive que le fait qu’iCloud déplace automatiquement vers le cloud les fichiers, applications et photos récemment utilisés afin de libérer de l’espace devienne problématique.
Un utilisateur se remémore un système de reporting appelé Hyperion, qu’il utilisait autrefois dans une banque. Ce système créait une nouvelle base de données pour chaque rapport ; cela lui semblait étrange à l’époque, mais avec le recul, il estime que l’approche était en avance sur son temps.