~/textes/cannot-average-percentiles
On ne peut pas faire la moyenne des percentiles
Trouver la vraie capacité d'un serveur IMAP veut dire générer de la charge à partir de plusieurs hôtes en même temps. Le difficile, c'est pas de produire la charge. C'est de combiner leurs latences en un seul P99 honnête sans se mentir en douce.
Combien d'opérations par seconde un serveur IMAP peut-il vraiment soutenir avant de ne plus suivre? Ça a l'air simple. Ça l'est pas, et la difficulté est pas dans la production de la charge.
Un seul client peut pas en produire assez. Une machine seule sature son propre CPU et son réseau bien avant de stresser un vrai serveur de courriel, alors on génère la charge à partir de plusieurs hôtes en même temps. Le imaptest de Dovecot est la manière honnête de piloter chacun: une session IMAP complète, login, list, select, fetch, store, append, logout, pas juste une boucle APPEND serrée. Roule-le sur une flotte et t'as de la charge en masse. Là il faut combiner ce que chaque hôte a mesuré, et c'est là que ça devient laid statistiquement.
Trouver le coude
Le chiffre qui vaut la peine d'être rapporté, c'est pas le débit de pointe. C'est le taux auquel le serveur arrête de suivre. Monte le taux offert par paliers, 1k/s, 2k, 4k, et regarde l'écart se creuser entre ce que t'as demandé et ce qui a vraiment été livré. Le coude, c'est le premier taux où le débit livré tombe sous une fraction fixe de la cible, disons 90 pour cent. En dessous, le serveur suit la demande; au dessus, demander plus t'achète des erreurs et des queues plus longues, pas plus de travail fait. Le seuil exact est arbitraire, mais choisis-en un et tiens-le, comme ça "où ça lâche" devient un chiffre que tu peux tracer et comparer d'une run à l'autre au lieu d'une impression.
Le piège: les percentiles ne se moyennent pas
Chaque hôte mesure ses propres latences. La manière tentante d'avoir un P99 à l'échelle du cluster, c'est de prendre le P99 de chaque hôte et d'en faire la moyenne. Ce chiffre est faux, et faux d'une manière qui a l'air assez plausible pour être expédiée.
Un percentile est une propriété d'une distribution, pas quelque chose dont tu peux prendre la moyenne. Le P99 des latences combinées de cinquante machines est en général pas la moyenne de leurs cinquante P99. Disons qu'un hôte a tiré les comptes qui hachent vers un backend lent: son P99 est à 400 ms pendant que tous les autres sont à 90. Fais la moyenne des P99 par hôte et cet hôte est une voix sur cinquante, écrasée jusqu'à rien. Mais ses requêtes lentes sont de vraies requêtes qu'un vrai utilisateur aurait faites. Elles ont leur place dans la queue. Faire la moyenne des percentiles efface exactement la queue que tu essayais de mesurer.
La moyenne des percentiles n'est le percentile de rien
P99(A ∪ B) n'est pas (P99(A) + P99(B)) / 2. Tu peux pas reconstruire un percentile de l'union à partir des percentiles par groupe, et certainement pas à partir des moyennes par groupe. Un percentile de cluster honnête a besoin de la vraie distribution combinée, ou d'un échantillon fidèle de celle-ci. Y a pas de raccourci qui garde la queue, et la queue, c'est tout le rapport.
La solution: échantillonner, puis mettre en commun
T'as besoin de la vraie distribution sur tous les hôtes, mais expédier chaque latence de chaque machine roulant à fond pendant une minute, c'est beaucoup de données à déplacer. Alors fais en sorte que chaque hôte garde ses échantillons bruts et retourne un sous-échantillon aléatoire uniforme, quelques milliers de points par phase. Concatène ces sous-échantillons en une seule réserve et calcule les percentiles à partir de la réserve.
L'échantillonnage uniforme, c'est ce qui rend la chose honnête. Parce que chaque hôte échantillonne ses propres latences de manière uniforme, la réserve est elle-même un échantillon uniforme de la vraie distribution à l'échelle du cluster, queue comprise. Les requêtes de 400 ms de l'hôte lent apparaissent dans la réserve en proportion de combien il y en avait vraiment. Un P99 sur la réserve est une vraie estimation du vrai P99 du cluster, pas une moyenne de résumés qui ont déjà jeté la distribution.
Garde les phases séparées, aussi:
- Dial: le temps pour compléter le handshake TLS, par connexion.
- Login: le temps du LOGIN IMAP. Souvent bimodal: une cache d'auth chaude répond en microsecondes, un raté de cache paie le plein coût de bcrypt. Mélangées à tout le reste, les deux populations se moyennent en un milieu sans signification. Tout seul, le précipice est évident.
- Append et fetch: le vrai travail sur la boîte aux lettres.
Un seul chiffre de "latence" cache tout ça. Le précipice de bcrypt ne ressort que quand le login est mesuré tout seul.
Faire ressembler la charge à de vrais utilisateurs
L'agrégation honnête est gaspillée sur une charge caricaturale. Deux choses comptent le plus. Mets le même compte sur plusieurs hôtes en même temps: les vrais utilisateurs frappent une boîte aux lettres depuis un téléphone, un portable et le webmail tous ensemble, et cette concurrence, c'est ce qui stresse le verrouillage par utilisateur du serveur. Et ouvre plus d'une session par compte, parce qu'une seule connexion en requête-réponse synchrone cache le vrai plafond de concurrence du serveur.
Deux modes de défaillance valent la peine d'être éliminés par conception avant qu'ils polluent les chiffres. Si chaque hôte compose au même instant, tu mesures ta propre cohue à t=0, pas la capacité en régime établi, alors étale les démarrages sur une fenêtre. Et quand une connexion plante et se reconnecte, ajoute de la gigue au backoff; sinon un hoquet du serveur resynchronise tous les clients sur le même rythme et t'as construit un pic auto-infligé.
Compter les erreurs par type, pas juste le total
Quand des requêtes échouent, groupe-les par message normalisé au lieu de rapporter un simple compte. "Cent erreurs" pourrait être une limite de capacité ou un bogue serveur. "Surtout des timeouts de login" et "surtout des erreurs serveur sur l'append" pointent vers des problèmes complètement différents, et tu peux pas dire lequel à partir du total seul.
Un test de charge n'est honnête que jusqu'à sa pire étape d'agrégation. Tu peux générer une charge impeccable et quand même publier une fiction si tu fais la moyenne des percentiles à la fin. Garde la distribution, échantillonne-la uniformément, mets les échantillons en commun, et laisse la queue parler d'elle-même.