Le plan d'urbanisme avant la première rue
Une canalisation d'eau potable se pose une fois. Elle passe sous l'avenue, on referme, on bitume, et personne ne la revoit pendant quarante ans. Le diamètre choisi ce jour-là décide de ce que la ville pourra devenir.
L'urbaniste qui fixe ce diamètre ne connaît pas le nombre exact d'habitants en 2050. Il pose quand même un chiffre, parce que ne rien poser revient à laisser le hasard décider à sa place. Il l'obtient en dix minutes : tant de logements, tant de personnes par logement, tant de litres par personne et par jour, un facteur de pointe pour le matin.
Le system design commence par le même geste. Avant de choisir une base de données, un cache ou un nombre de machines, on chiffre. Combien de requêtes par seconde, combien d'octets par jour, combien de mémoire, combien de serveurs.
L'estimation à la louche ne cherche pas le bon chiffre. Elle cherche le bon ordre de grandeur, celui qui dit si on est dans un problème à mille requêtes par seconde ou à un million. Ce ne sont pas les mêmes systèmes.
Ce billet déroule les quatre calculs, en entier, sur un exemple unique qu'on suit du début à la fin.
Vue d'ensemble : trois règles et quatre chiffres
L'exercice porte un nom en anglais, back-of-the-envelope estimation, l'estimation qui tient au dos d'une enveloppe. Le format fait partie de la méthode.
Règle 1 : viser l'ordre de grandeur. Se tromper d'un facteur deux est sans conséquence, la marge de sécurité l'absorbe. Se tromper d'un facteur cent change le système de nature. Toute la valeur de l'exercice est là.
Règle 2 : tenir dans dix minutes. Au tableau, de tête, sans tableur. Un modèle de capacité qui prend trois jours sera faux lui aussi, mais il aura coûté trois jours et il inspirera une confiance qu'il ne mérite pas.
Règle 3 : écrire les hypothèses, une par ligne. Arrondir à 10, à 100, à 500, aux puissances de dix. Le résultat ne se discute pas, il se recalcule ; ce qui se discute, ce sont les hypothèses. Une estimation dont les hypothèses restent dans la tête de son auteur n'est pas révisable.
Quatre chiffres sortent de l'exercice, dans cet ordre, et chacun alimente le suivant.
inscrits, part d'actifs, actions"] T["Trafic
requêtes par seconde"] S["Stockage
volume sur 5 ans"] M["Mémoire
données chaudes"] N["Serveurs
nombre de machines"] D{"Décisions"} B["Type de base
et partitionnement"] C["Cache et diffusion
de contenu"] R["Redondance
et répartition"] H --> T T --> S T --> M T --> N S --> D M --> D N --> D D --> B D --> C D --> R classDef entree fill:#b5651d,stroke:#8a4d16,color:#f5f2ec; classDef calcul fill:#d89253,stroke:#8a4d16,color:#2c3338; classDef sortie fill:#ede9e1,stroke:#8a4d16,color:#2c3338; class H entree; class T,S,M,N calcul; class D,B,C,R sortie;
Aucun de ces quatre chiffres n'est une décision. Ce sont les chiffres qui rendent les décisions calculables, au lieu de les laisser à l'appréciation de chacun.
L'exemple qui sert de fil : un service de publication de messages courts, texte plus image optionnelle. Rien d'original, et c'est voulu, parce que la méthode compte plus que le cas.
L'aide-mémoire des ordres de grandeur
L'urbaniste manipule des mètres cubes par seconde sans jamais hésiter sur les unités. C'est ce qui lui permet de calculer en marchant.
Le même réflexe se construit avec deux tables. La première convertit un compte quotidien en débit et en volume. On y pose une convention : un kilo-octet vaut mille octets, pas 1 024. L'écart de 2,4 % ne survivra pas au premier arrondi.
| Zéros | Nom | Si c'est un compte par jour | Si chaque unité pèse 1 octet |
|---|---|---|---|
| 3 | mille | 0,01 par seconde | 1 Ko |
| 6 | million | 10 par seconde | 1 Mo |
| 9 | milliard | 10 000 par seconde | 1 Go |
| 12 | mille milliards | 10 000 000 par seconde | 1 To |
| 15 | million de milliards | sans objet à cette échelle | 1 Po |
La colonne du milieu applique déjà l'astuce centrale de l'exercice. Une journée compte 86 400 secondes, on divise par 100 000. Le résultat est inférieur d'environ 14 % à la vérité, ce qui est négligeable ici, et que le facteur de pointe appliqué ensuite compense de toute façon.
La seconde table donne le poids des données usuelles. Elle évite une erreur fréquente : estimer un volume sans jamais avoir posé la taille d'une ligne.
| Donnée | Taille retenue |
|---|---|
| Caractère ASCII, ou latin en UTF-8 | 1 octet |
| Caractère du plan de base en UTF-16 | 2 octets |
| Entier 32 bits | 4 octets |
| Entier 64 bits, flottant double, horodatage | 8 octets |
| UUID stocké en binaire | 16 octets |
| UUID stocké en texte | 36 octets |
| Message court avec ses métadonnées | environ 500 octets |
| Photo compressée prise au téléphone | environ 300 Ko |
Ces deux tables ne s'apprennent pas par cœur, elles se réutilisent jusqu'à devenir automatiques. Le jour où la conversion « un milliard par jour égale dix mille par seconde » sort sans réfléchir, l'estimation devient un geste de dix minutes.
Estimer le trafic
Le débit d'eau d'un quartier se calcule en trois multiplications : le nombre de logements, la consommation par logement, un facteur pour l'heure de pointe. Le trafic d'un service se calcule exactement pareil.
On part des hypothèses, écrites d'abord, discutées ensuite.
500 000 000 utilisateurs inscrits
x 10 % d'actifs par jour = 50 000 000 actifs quotidiens
x 2 publications par actif = 100 000 000 écritures par jour
x 50 consultations par actif = 2 500 000 000 lectures par jour
Les 10 % d'actifs quotidiens et les deux publications par jour sont des choix, pas des mesures. Un service de messagerie professionnelle aurait 60 % d'actifs et vingt actions par jour ; un service de déclaration fiscale resterait sous le pour cent onze mois sur douze, avec un pic au moment de la campagne. C'est la ligne qu'on discutera en réunion, et c'est pour ça qu'elle est écrite.
Passage au débit, avec la division par 100 000.
100 000 000 écritures par jour / 100 000 = 1 000 écritures par seconde
2 500 000 000 lectures par jour / 100 000 = 25 000 lectures par seconde
Le trafic n'est pas plat. Une ville tire le plus d'eau entre sept et neuf heures du matin, un service grand public prend son pic le soir. Un facteur de pointe de 2 est un choix prudent et courant pour un usage réparti sur la journée, un facteur 5 se justifie pour un service événementiel.
1 000 x 2 = 2 000 écritures par seconde au pic
25 000 x 2 = 50 000 lectures par seconde au pic
Dernier chiffre à extraire, et le plus structurant de tous : le ratio entre lectures et écritures.
2 500 000 000 / 100 000 000 = 25 lectures pour 1 écriture
Un ratio de 25 pour 1 dit à lui seul que l'argent doit aller sur le chemin de lecture. Cache, réplicas de lecture, contenu servi en périphérie. Optimiser l'écriture d'abord reviendrait à élargir la rue par laquelle personne ne passe.
Estimer le stockage
Le réservoir d'une ville se dimensionne sur des années de consommation, pas sur une journée. Il faut donc savoir ce que pèse une unité, puis multiplier par le temps.
On pose la taille d'un enregistrement, poste par poste.
identifiant du message 8 octets
identifiant de l'auteur 8 octets
horodatage 8 octets
compteurs et drapeaux 8 octets
texte, 280 caractères 280 octets
---------------------------------------
total 312 octets -> retenu : 500 octets
L'arrondi de 312 à 500 couvre ce que le calcul poste par poste oublie : l'en-tête de ligne, les index, le remplissage, et un texte moyen parfois plus long que prévu. Arrondir vers le haut sur la taille unitaire coûte moins cher que de découvrir un facteur deux en production.
100 000 000 messages/jour x 500 octets = 50 000 000 000 octets = 50 Go par jour
50 Go x 400 jours (année arrondie) = 20 000 Go = 20 To par an
20 To x 5 ans = 100 To
100 To x 3 (trois copies) = 300 To
Cent téraoctets de texte sur cinq ans, trois cents avec la réplication. Un chiffre parfaitement gérable. Le vrai sujet arrive avec les images.
10 % de 100 000 000 messages = 10 000 000 images par jour
10 000 000 x 300 Ko = 3 000 000 000 Ko = 3 To par jour
3 To x 400 jours x 5 ans = 6 000 To = 6 Po sur cinq ans
Trois téraoctets par jour contre cinquante gigaoctets, soit un facteur soixante pour une fonctionnalité qui ne concerne qu'un message sur dix. La décision tombe toute seule : les médias ne vivent pas dans la base, ils vivent dans un stockage objet, et la base ne garde que leur adresse sur quelques dizaines d'octets.
Une estimation de stockage qui oublie les fichiers binaires se trompe d'un ou deux ordres de grandeur, et toujours dans le même sens.
Estimer la mémoire
Le château d'eau ne contient pas la réserve annuelle de la ville. Il contient quelques heures de consommation, sous pression, tout près des robinets. Sa raison d'être tient au délai : de l'eau disponible à la seconde où quelqu'un ouvre un robinet.
Un cache suit la même logique. On n'y met pas les cent téraoctets, on y met ce qui sera relu dans l'heure. Reste à chiffrer ce « ce qui ».
Hypothèse retenue : pour chaque utilisateur actif du jour, on garde en mémoire les vingt derniers messages de son fil.
20 messages x 500 octets = 10 Ko par utilisateur actif
10 Ko x 50 000 000 actifs = 500 000 000 Ko = 500 Go de données chaudes
500 Go / 50 Go utiles par machine = 10 machines
x 2 (une réplique par partition) = 20 machines
Les 50 Go utiles par machine supposent des serveurs de 64 Go de mémoire dont on ne remplit jamais la totalité, faute de quoi le premier pic déclenche des évictions en cascade.
Change une hypothèse et regarde ce que ça fait. Si seuls 20 % des actifs reviennent assez souvent pour mériter une entrée de cache, la donnée chaude tombe à 100 Go et l'addition passe de vingt machines à quatre. Le même exercice, la même méthode, un cinquième du budget.
Vingt machines ou quatre : le calcul est identique dans les deux cas. Ce qui change, c'est une ligne d'hypothèse que quelqu'un a osé écrire et que l'équipe a pu contester.
Le chiffre à retenir de cette section tient en une phrase : le cache ne tient pas sur une seule machine. Cela impose un cache distribué et, avec lui, une question d'invalidation traitée dans le domaine Trafic.
Estimer le nombre de serveurs
Une ligne de bus se dimensionne avec deux nombres : le temps qu'un bus met à boucler son trajet, et le nombre de passagers qu'il transporte. Le reste est une division.
Un serveur applicatif fonctionne pareil, à condition de choisir son modèle. On prend ici le plus courant : un fil d'exécution occupé du début à la fin d'une requête.
latence moyenne visée par requête = 50 ms
1 fil : 1 000 ms / 50 ms = 20 requêtes par seconde
200 fils par serveur : 200 x 20 = 4 000 requêtes par seconde en théorie
marge de 50 % (pics, GC, déploiements) = 2 000 requêtes par seconde retenues
50 000 requêtes/s au pic / 2 000 = 25 serveurs
Vingt-cinq machines pour absorber le pic de lecture, à condition de tenir sur un seul site. Si le service tourne sur deux sites et que chacun doit encaisser seul la disparition de l'autre, il en faut vingt-cinq de chaque côté, donc cinquante.
Deux précautions sur ce calcul, parce qu'il est le plus fragile des quatre.
La première tient au modèle de concurrence. Un fil bloqué sur une entrée-sortie ne consomme pas de processeur ; sur une pile asynchrone, la contrainte se déplace vers le processeur et la mémoire, et le nombre de fils cesse d'être le facteur limitant. Le calcul donne alors une borne haute, pas une capacité.
La seconde tient à la latence. Les 50 ms sont une cible, pas une observation. Si la requête moyenne monte à 200 ms, un fil ne sert plus que 5 requêtes par seconde et la flotte est multipliée par quatre. La latence n'est pas un critère de confort, c'est un facteur de coût direct.
Le nombre de serveurs est le seul des quatre chiffres qu'une mesure en préproduction remplace immédiatement. Tant qu'il n'y a rien à mesurer, une division vaut mieux qu'un pressentiment.
Ce que ces chiffres décident
Le plan d'urbanisme ne construit aucune maison. Il fixe les emprises, les diamètres et les hauteurs, et tout ce qui se bâtit ensuite s'y conforme ou se heurte à lui.
Les quatre estimations jouent ce rôle. Voici ce qu'elles ont produit, et ce que chaque ligne ferme ou ouvre.
| Chiffre obtenu | Ce qu'il décide |
|---|---|
| 2 000 écritures par seconde au pic | N'oblige pas à partitionner d'emblée. On garde une architecture simple et on surveille. |
| 25 lectures pour 1 écriture | L'effort porte sur le chemin de lecture : cache, réplicas, contenu en périphérie. |
| 500 Go de données chaudes | Le cache ne tient pas en mémoire d'une machine, il sera distribué et il faudra gérer son invalidation. |
| 100 To de texte sur cinq ans | Une instance unique ne suffira pas sur la durée. Le partitionnement devient une échéance, pas une hypothèse. |
| 6 Po de médias | Stockage objet et diffusion en périphérie. Aucun binaire dans la base. |
| 50 serveurs au pic, sur deux sites | Répartiteur de charge, services sans état, bascule à préparer. |
Le tableau se lit aussi à l'envers. Une conception qui prétend partitionner sa base alors que l'estimation annonce mille écritures par seconde vient de s'infliger une complexité que rien ne réclame. Le domaine Mise à l'échelle traite ce piège en détail, et il est plus courant que le sous-dimensionnement.
Ces chiffres alimentent enfin les exigences non fonctionnelles du dossier d'architecture, cette phase décrite dans Le processus d'architecture : six étapes, des exigences au support. Une exigence de charge écrite sans estimation reste un souhait.
Une estimation à la louche se refait entièrement en dix minutes le jour où une hypothèse change. C'est sa vraie valeur : elle est jetable, donc on ose la corriger.
Scénario B : le lancement de Pokémon GO, juillet 2016
Le 6 juillet 2016, Niantic ouvre Pokémon GO en Australie et en Nouvelle-Zélande, puis très vite aux États-Unis. Le jeu tourne sur Google Cloud, avec un déploiement conteneurisé sur le service Kubernetes managé de la plateforme.
Google Cloud a publié un retour d'expérience sur ce lancement, « Bringing Pokémon GO to life on Google Cloud ». Le texte raconte que Niantic était arrivée avec deux chiffres : une cible de trafic, et un scénario catastrophe fixé à cinq fois cette cible, censé couvrir toutes les surprises.
Le trafic réel a atteint environ dix fois ce scénario catastrophe, soit près de cinquante fois la cible initiale. L'infrastructure a dû être renforcée dans l'urgence, avec les équipes de Google et de Niantic sur le pont, et les ouvertures dans les autres pays ont été échelonnées pendant que la plateforme suivait.
Ce récit passe facilement pour un échec d'estimation. Il démontre plutôt l'utilité de l'exercice.
Une équipe qui n'aurait pas chiffré du tout n'aurait eu aucun scénario catastrophe à multiplier, aucun repère pour dire « nous sommes à dix fois le pire cas », et surtout aucune raison d'avoir choisi, avant le lancement, une architecture dont la capacité se multiplie en ajoutant des machines. Les deux chiffres préparés à l'avance ont donné à la réaction un point de départ chiffré, dès la première heure.
L'estimation sert à mesurer de combien on peut se tromper avant que le système soit à réécrire. Un facteur cinquante sur une architecture multipliable se paie en nuits blanches ; sur une architecture verrouillée, il se paie en réécriture complète.
Deux enseignements transposables. Un scénario haut chiffré à cinq fois la cible reste un scénario, pas une borne physique. Et la question à poser en revue de conception n'est pas « ce chiffre est-il juste », mais « que fait-on s'il est faux d'un facteur dix ».
Points clés
- L'estimation à la louche vise l'ordre de grandeur, en dix minutes, avec des hypothèses arrondies et écrites une par ligne. Un facteur deux d'erreur est sans importance, un facteur cent change le système.
- Deux automatismes suffisent pour aller vite : un compte quotidien se divise par 100 000 pour obtenir un débit par seconde, et une taille de ligne se pose poste par poste avant toute multiplication.
- Quatre chiffres sortent de l'exercice dans l'ordre : trafic, stockage, mémoire, serveurs, chacun nourrissant le suivant, et le ratio lecture/écriture décide de l'endroit où porter l'effort.
- Les binaires écrasent le texte : dans l'exemple, 100 To de messages sur cinq ans contre 6 Po d'images, ce qui sort les médias de la base sans autre discussion.
- Le lancement de Pokémon GO en juillet 2016 a dépassé d'environ dix fois le pire cas préparé par Niantic. Ce qui a sauvé le service n'est pas la justesse de l'estimation, c'est le fait d'en avoir eu une et d'avoir choisi une architecture multipliable.
Dans la série
Palier 1 : Dimensionner la ville. Domaine : Fondamentaux.
- Suivant : De zéro à un million d'utilisateurs, étape par étape
- Vue d'ensemble : System design : par où commencer
Pour aller plus loin
- De zéro à un million d'utilisateurs, étape par étape, pour la trajectoire complète que ces chiffres déclenchent, étape après étape.
- Concevoir un raccourcisseur d'URL, où la méthode de ce billet est reprise et déroulée sur un système entier.
- Le processus d'architecture : six étapes, des exigences au support, pour la place de ces chiffres dans les exigences non fonctionnelles.
- Les *-ilities : traduire les exigences en capacités techniques, pour la chaîne qui relie une exigence de charge à une décision technique.
- Latency Numbers Every Programmer Should Know, la table de référence des latences matérielles, utile dès qu'une estimation touche au temps de réponse.
- Bringing Pokémon GO to life on Google Cloud, le billet d'ingénierie de Luke Stone (30 septembre 2016), pour le récit du lancement par ceux qui l'ont encaissé.
- Designing Data-Intensive Applications, de Martin Kleppmann, pour le traitement long des sujets que ces estimations ouvrent.