Blog ·
La réservation devient l’affectation
Déplacer une machine entre deux racks comporte un instant dangereux : celui où la destination n’est ni réservée ni occupée. Voici comment nous l’avons retiré.
Par Provibr
Déplacer un serveur d’une unité de rack à une autre, c’est physiquement un travail rapide, avec un tournevis et une deuxième paire de mains. C’est l’administration autour qui abrite la panne intéressante, et elle tient en un instant : le moment où la destination n’est plus réservée et pas encore occupée.
Si l’écriture de quelqu’un d’autre tombe dans cet instant, deux machines se retrouvent enregistrées au même emplacement. Dans une base de données, cela se lit comme une violation de contrainte dont on peut discuter. Sur le plancher, cela se lit comme un technicien debout devant un rack, une machine dans les mains et nulle part où la poser.
La fenêtre, et comment ne pas en avoir
La séquence évidente compte trois étapes. Libérer l’ancienne affectation, libérer la réservation de déplacement sur la destination, écrire la nouvelle affectation. À l’intérieur d’une seule transaction, cette fenêtre est invisible pour les autres sessions, et c’est exactement pour cela qu’il est facile de la laisser en place : elle passera tous les tests que vous écrirez. C’est tout de même une fenêtre, parce que l’instruction qui suit opère sur un emplacement que l’instruction précédente vient de libérer, et un seul mauvais ordre lors d’un remaniement ultérieur suffit.
Nous faisons plus simple. La réservation de déplacement est promue plutôt que remplacée. Les lignes qui enregistrent quelles unités sont occupées restent exactement où elles sont ; seul l’en-tête change de nature, de réservation de déplacement en affectation, et son échéance passe à nulle dans la même mise à jour. L’ancienne affectation se ferme dans la même transaction. Il n’existe aucun moment où la destination est libre, il n’y a donc pas de seconde occasion de collision, et l’ensemble est une mise à jour plutôt qu’une insertion qui doit réussir.
L’identifiant de la ligne reste inchangé au cours de la promotion, et c’est la façon de vérifier que c’est bien ce qui s’est passé plutôt que quelque chose qui y ressemble vu de l’extérieur.
Mettre l’échéance à nulle n’est pas de la coquetterie. Un contrôle en base refuse une affectation qui en porte une, car une affectation qui cesse discrètement de compter un mardi alors que la machine est toujours boulonnée, c’est un rack dont le logiciel croit qu’il contient une unité libre. Une réservation qui expire est une fonctionnalité ; une affectation qui expire est un mensonge avec une minuterie dessus.
L’arbitre reste un seul index
Rien de ce qui précède n’est ce qui empêche une double réservation. Cela reste un index unique partiel sur le rack, la face et l’emplacement, restreint aux lignes qui n’ont pas été libérées, et une réservation de déplacement se range dessous exactement comme une affectation.
Deux techniciens qui choisissent la même unité à la même seconde la voient tous deux libre dans la fonction de planification qui tourne avant l’écriture, et exactement l’un des deux passe la base de données. L’écriture de l’autre échoue sur l’index, et l’unique chemin qui écrit l’occupation intercepte cette violation et la transforme en un refus lisible nommant les emplacements qui entrent en conflit. La phrase obtenue est choisie d’après le nom de la contrainte qui s’est déclenchée, car « cette unité est prise » et « cette machine se tient déjà ailleurs » envoient une personne vers deux racks différents. Une insertion directe qui contourne ce chemin reçoit à la place la violation brute. Mesuré des deux manières, parce que raisonner sur les niveaux d’isolation n’est pas une preuve.
La fonction de planification est un message, pas une défense. L’index est la défense. Tenir ces deux rôles séparés est ce qui permet au message d’être utile sans que personne soit tenté de lui faire confiance.
Neuf états, et deux sorties qui cessent d’exister
Un déplacement a neuf états : brouillon, planifié, approuvé, en cours, en attente de vérification, à réconcilier, terminé, annulé et échoué. Savoir quelles transitions sont légales est un enregistrement portant sur les neuf plutôt qu’une instruction de branchement. Un branchement avec un cas par défaut laisse un état nouvellement ajouté retomber sur autorisé, et la direction dangereuse ici est précisément celle-là : un état que quelqu’un ajoute parce qu’il se passe quelque chose d’inhabituel serait le premier état autorisé à tout faire.
La ligne intéressante est celle qui quitte in_progress, car deux sorties que possède tout état antérieur en sont absentes : il n’y a pas de chemin de retour vers approuvé ni de chemin vers annulé. Quelqu’un a la machine dans les mains à ce moment-là, et les seules sorties honnêtes sont « signalé fait », « nous ne savons pas » et « cela a échoué ». Annuler est une décision prise à l’avance ; une fois qu’un travail physique a été signalé, un retour arrière dans la base de données est une affirmation sur le monde physique que personne n’a vérifiée.
Les trois états terminaux, terminé, annulé et échoué, portent des listes vides plutôt qu’aucune entrée du tout. C’est la différence entre « rien ne continue à partir d’ici » et « personne n’y a réfléchi », et seul le premier est une décision.
Une seconde table dit, pour chaque état, si la destination doit rester retenue. L’entrée qui mérite d’être défendue est needs_reconcile, qui la garde. Personne ne sait où se trouve la machine à ce moment-là, donc libérer la destination pourrait poser une seconde machine sur une unité peut-être déjà occupée.
Se déplacer n’est pas être en route
Les actifs portent un état de processus, et ce travail a ajouté moving à côté du in_transit existant. Ils sonnent comme des synonymes et n’en sont pas. moving veut dire que la machine est déplacée à l’intérieur de ce datacenter et que son espace de destination est réservé. in_transit veut dire qu’elle est sur la route entre deux sites et qu’elle n’est donc nulle part. Les deux bloquent la livraison à un client, mais ils répondent à des questions différentes quand quelqu’un demande où se trouve une machine, et les fusionner rendrait fausse la réponse à la seconde question. La séparation est imposée et pas seulement décrite : un contrôle en base exige que le site d’origine et le site de destination d’un déplacement soient le même, si bien qu’un déplacement est par construction quelque chose qui se passe à l’intérieur d’un seul bâtiment, et que la route entre deux sites est un autre processus, avec un autre mot pour le désigner.
L’état qu’avait la machine avant le déplacement est conservé dans le contrôle préalable figé, il n’est pas supposé. Du matériel qui vient d’arriver se trouve en staging, et tout remettre en normal ensuite ferait discrètement sortir de la réception toute machine déplacée.
Deux issues à l’incertitude, et aucune n’est un retour arrière
Quand l’issue physique est incertaine, le déplacement passe en needs_reconcile et quelqu’un doit aller voir. Il y a deux réponses, et toutes deux sont enregistrées comme des observations.
| Observation | Ce qui arrive au déplacement | Ce qui arrive à l’espace |
|---|---|---|
| Vue à la destination | Terminé | La réservation devient l’affectation, comme ci-dessus |
| Vue à l’origine | Échoué, avec un motif disant qu’il n’a jamais été exécuté | La réservation est libérée ; l’affectation d’origine reste intacte |
La seconde est délibérément échouée plutôt qu’annulée. Quelqu’un a fait un travail physique et la machine n’a pas bougé ; c’est une issue, pas un choix. Et ce n’est pas un retour arrière : la base de données n’est pas ramenée dans un état qui prétend qu’il ne s’est rien passé. L’historique des emplacements est strictement additif, les corrections sont de nouvelles écritures compensatoires, et la différence apparaît un mois plus tard quand quelqu’un demande pourquoi une machine est restée immobile pendant une journée. Un retour arrière ne laisse rien derrière lui. Une écriture compensatoire montre qu’il y a eu une incertitude et comment elle a été tranchée.
Ces lignes d’historique portent les identifiants de site et de rack sans clés étrangères, exprès. Un rack mis hors service l’année prochaine ne change pas ce qui s’y trouvait en mars dernier.
Deux petites décisions qui compteront plus tard
Le déplacement pointe vers son opération d’arrière-plan par une référence en SET NULL quand l’opération est supprimée, plutôt qu’en CASCADE. Les opérations sont élaguées après quatre-vingt-dix jours. L’administration du matériel ne l’est pas, et une cascade voudrait dire que la politique de rétention des barres de progression supprime silencieusement la trace de l’endroit où une machine est allée.
Et un index unique partiel autorise un seul déplacement ouvert par actif. Sans lui, deux personnes peuvent planifier deux destinations différentes pour la même machine, et toutes deux ont l’air correctes sur leur propre écran.
Ce que nous retiendrions
- Promouvez, ne recréez pas. Si une réservation et une affectation ont la même forme, changez la nature plutôt que de libérer puis d’écrire, et vérifiez que l’identifiant a survécu.
- Gardez l’arbitre dans la base de données et l’explication dans le code. L’index décide qui gagne ; la fonction de planification dit quels emplacements entrent en conflit.
- Écrivez la table des transitions sur tous les états. Une branche par défaut transforme un nouvel état en état autorisé.
- Après un travail physique signalé, ne revenez jamais en arrière. Ajoutez un état qui veut dire « inconnu » et donnez-lui deux sorties documentées, toutes deux des observations.
- Rendez l’historique strictement additif et laissez les corrections être de nouvelles lignes, car le fait que quelque chose ait été un jour incertain est en soi une information.
- Vérifiez à quoi votre politique de rétention est attachée. Une cascade d’une table à vie courte vers une table à vie longue est un bogue de perte de données avec un calendrier.
Pour être clair sur ce dont il s’agit : le travail sur la logistique de datacenter est derrière un indicateur de déploiement désactivé par défaut, avec un réglage par organisation par-dessus, si bien que rien de tout cela n’est achetable aujourd’hui. Quatre choses ne sont pas prouvées. L’événement de progression en direct pour un déplacement a été exercé sans son bus de messages. L’annulation d’un déplacement depuis l’écran des opérations n’a pas été pilotée dans un navigateur. Une réservation qui a réellement expiré n’a jamais été observée, et il n’y a délibérément aucun balayage qui en efface une, car une réservation qui se range toute seule pendant que quelqu’un se tient debout devant le rack est exactement la contradiction que l’index existe pour empêcher. Et le contrôle de site décrit plus haut ne s’est en réalité jamais déclenché, parce qu’aucun second site n’a été semé pour le déclencher.
Issu du travail sur les datacenters chez Provibr. Pourquoi un index unique partiel est la seule vraie limite à l’occupation est dans la seule vraie limite à l’occupation d’un rack, et pourquoi les cinq questions de capacité au-dessus d’un rack ne peuvent pas s’additionner est dans l’espace en rack n’est pas de la capacité.