Ingénierie chez Qumulo : création d'un magasin de valeurs-clés distribué

Chez Qumulo, nous construisons un système de fichiers évolutif. Comme vous vous en doutez, nous avons beaucoup de «magasins de valeurs-clés» dans notre système: par exemple, il y a des B-Trees distribués pour les composants du système de fichiers tels que les répertoires et les extensions. Il existe également divers magasins de valeurs-clés de configuration. Ces structures sont construites sur notre architecture de magasin protégé distribué. Récemment, mon équipe a créé un nouveau magasin clé-valeur distribué pour définir la composition du système de protection utilisé par notre système de fichiers en cluster (QSFS).

Magasins protégés

En gros, QSFS est construit sur une collection de magasins protégés (PStores). Les PStores fournissent les propriétés dont nous avons besoin pour notre système de fichiers : transactionnalité, tolérance aux pannes (obtenue via codage d'effacement ou mise en miroir), récupération après panne, adressage indépendant du disque, lectures et écritures efficaces, etc. Les PStores sont composés de blocs de données sur SSD et de disques tournants sur plusieurs nœuds. Comment nous obtenons les propriétés ACID avec les PStores est un sujet pour un autre article.

Magasins protégés

Etat du système de protection

Le mappage entre PStores et leurs composants constitutifs est essentiel à la fiabilité et aux performances de notre système. Nous appelons cela la carte PStore. Cette carte est normalement statique. cependant, il change pendant la reprotection après une défaillance du disque ou du nœud, après le remplacement du disque et lorsque nous ajoutons des nœuds au système. De plus, l'état du système de protection inclut un numéro de transaction validée globalement qui est utilisé pour contrôler les transactions. Ce nombre est normalement incrémenté quelques fois par seconde. La carte Pstore, le numéro de génération et quelques autres éléments d'information constituent l'état du système de protection que nous devons stocker de manière fiable de manière distribuée, durable et cohérente.

Paxos V1

Le système existant de stockage de l’état du système de protection que nous avons décidé de remplacer était une collection de magasins multi-Paxos. Chaque nœud du cluster remplissait les fonctions d’accepteur Paxos et d’apprenant Paxos. Un proposant Paxos a vécu sur les nœuds 1 ou 2 (leaders du quorum [1]). Ce système avait un certain nombre de problèmes que nous espérions résoudre avec notre nouveau magasin de clés:

  • Les données sur les accepteurs et les apprenants de Paxos étaient stockées sur des disques système moins fiables que les disques SSD utilisés pour les blocs PStore. Nous avons constaté que même les charges relativement modestes générées par le magasin Paxos entraînaient des défaillances prématurées de ces appareils.
  • La carte PStore était stockée sous la forme d'une valeur Paxos unique. Cette valeur peut être très grande en fonction de la taille du cluster. Bien que nous mettions à jour chaque PStore indépendamment lors de la reprotection (comme pour les reconstructions de lecteur), ces mises à jour ont nécessité le verrouillage et l'écriture de la totalité de la carte. Cela a entraîné des goulots d'étranglement au niveau des performances et une usure accélérée de nos lecteurs système.
  • Parce que Paxos V1 était un système écrit au début de l'histoire de Qumulo, il n'utilisait pas nos nouvelles méthodes et frameworks de test. En conséquence, le code de test pour Paxos V1 était difficile à maintenir et à étendre.

Un nouveau magasin Key-Value est conçu

Pour résoudre ces problèmes, nous avons conçu un nouveau système dans lequel Paxos joue un rôle réduit. Une seule instance multi-Paxos stocke l'ensemble de disques SSD contenant des magasins de valeurs-clés contenant l'état du système de protection. Les données relatives à l’accepteur et à l’apprenant Paxos sont stockées dans le superbloc résidant sur chaque disque SSD. Chaque disque SSD fournit de l'espace pour l'ensemble complet de clés système de protection que nous devons stocker dans un ensemble de blocs appelés un volume KV. À chaque quorum, nous lisons et écrivons à Paxos une seule fois. L'identité des volumes NKV définit le magasin DKV (Distributed Key-Value) mis en miroir. Ecrire au DKV est simple. écrire à tous les N miroirs. La lecture peut être faite à partir de n’importe quel miroir. Lors du démarrage du quorum, nous devons synchroniser l'un des volumes KV du dernier quorum avec les différents volumes KV N-1 du nouveau quorum. Une simple transaction est suffisante pour ce système. Une erreur lors de l'écriture peut entraîner un désaccord entre les magasins individuels. Nous sélectionnons la nouvelle valeur ou l'ancienne valeur comme source de synchronisation, éliminant ainsi toute incohérence.

Magasin de valeurs-clés

Notre système de quorum garantit que les opérations de cluster s'exécutent avec une majorité de nœuds connectés. Les erreurs de connexion amènent le cluster à réformer le quorum où chaque nœud participe à une séquence d'étapes coordonnée par un responsable. Les opérations en ligne se produisent généralement dans des quorums à long terme.

Paxos V2

Notre nouveau magasin de valeurs-clés distribuées s'appuierait toujours sur le protocole Paxos pour stocker l'ensemble des ID de lecteurs composant le magasin DKV actuel. L'implémentation de Paxos V2 était simple car nos besoins sont modestes; Notre système de quorum nous garantit une garantie unique et nous avons des exigences de performances modestes, car nous n'avons besoin de lire et d'écrire qu'une petite quantité de données Paxos une fois par quorum.

Volumes KV

Ensuite, nous avions besoin d'un moyen d'identifier les blocs sur un disque SSD contenant le magasin de valeurs-clés. Nous savons que les disques SSD peuvent fournir l’espace nécessaire au stockage du DKV, mais nous aurons besoin de prendre en charge le provisionnement dans les grappes existantes ultérieurement. À ce stade du projet, nous avons créé les blocs au besoin (tests) ou nous sommes basés sur la disponibilité des blocs sur les clusters nouvellement créés.

Volumes KV

Les blocs SSD nécessaires étaient gérés par un volume KV sur chaque SSD. Le volume a fourni plusieurs fonctionnalités importantes:

  • Marquage d'allocation Au démarrage, les volumes KV ont rejoint d'autres structures sur SSD que nous parcourons pour déterminer ce qui est alloué et ce qui est gratuit. Les blocs de volume sont liés à des blocs d'arbre pour permettre des lectures parallèles rapides.
  • Mappage de la clé de volume à l'adresse du bloc.
  • Lire et écrire des blocs de valeur par clé de volume.
  • Synchronisation efficace des volumes d'un SSD à un autre.

Étant donné une liste de blocs disponibles (à nouveau, garantis à ce stade), une classe de générateur produirait le couplage sur disque nécessaire et les objets de volume résultants.

Magasins KV

Le magasin KV fournit un mappage des clés aux valeurs situées en haut du volume KV sur un seul SSD. Les blocs de notre système ont une taille 4KiB; Nous avons déterminé que les valeurs d'état du système de protection n'auraient jamais besoin d'être aussi grandes que 1KiB, nous stockons donc les valeurs 4 par bloc de volume. L'espace clé est linéaire et non épars. La modification des valeurs implique read-modify-write, nous avons donc construit un simple cache de volume pour améliorer les performances.

Magasins KV

Les magasins KV doivent être sécurisés pour les threads, de sorte que cette couche possède également un verrouillage local des nœuds. Comme les clés sont aliasées par les blocs de valeur de volume, nous avons haché l'ID de bloc de volume dans un ensemble fixe de mutex. Les API de lecture et d'écriture comportent des variantes de clé unique et de masse.

Stockage KV distribué et synchronisation

Nous avons créé une simple classe dkv_store composée de N instances de kv_store (pour nos exigences de tolérance aux pannes, N est actuellement 3). Les écritures sont transmises en parallèle à chaque membre kv_store via RPC, and reads are forwarded to any member store. This is the first part of the system that is exposed to users (e.g. to the protection system state components). We expose a keyspace that is a direct mapping of the components’ kv_store keys. Later, when we introduced sharding, this mapping changed.

Building a dkv_store instance is the responsibility of a synchronization function. When we start a new quorum, synchronization performs these steps:

  1. Query online nodes for the list of online KV volumes.
  2. Read the KV volume ID set from the previous quorum from Paxos.
  3. Pick an online volume from the previous quorum set to be the source.
  4. Replicate from the source to N-1 destination volumes unused in the previous quorum.
  5. Store the new set of volume IDs in Paxos.

Testing of this component was crucial; we built exhaustive tests to cover a matrix of cluster size, down nodes, down disks, and partial progress errors. Since synchronization delays quorum start and hence filesystem availability, we spent some time optimizing and parallelizing this process to reduce its runtime down to a few seconds.

Upgrade

Up until this point, we had been dealing with new clusters only. To deploy our new system, we needed to handle existing clusters at customer sites. We wrote code to upgrade the data from the old Paxos store to the DKV. There were two phases to this: volume provisioning and data translation.

On an active cluster, SSD blocks are in-use for a variety of purposes (e.g. logging blocks, block trees, and write-back cache, for example). We can request blocks but only while the system is fully online. An agent makes blocks available by migrating blocks in a PStore from the SSD to its backing spinning disk. The first part of the upgrade starts an online background process on all nodes to provision blocks from each SSD. Recall that once a volume is built on an SSD, the blocks are linked and allocated, preventing further use for other needs. As each volume is built, the background process informs an upgrade master residing on the leader node. Once all SSDs have volumes, the master initiates a quorum restart.

In the next quorum, the second half of upgrade takes over. Noticing that all volumes are present but no dkv_store is defined in the new Paxos, a translation process picks an arbitrary set of volumes defining a dkv_store. The process then reads the old data from Paxos V1 and writes it into the DKV. The process then persists the DKV volume ID set in Paxos V2. As we finish the quorum start, a protection system state shim switches from using the old storage to the new storage. Future quorum starts go through the normal synchronization process outlined above.

Share this post