TCP, un protocole obsolète pour le datacenter ?

TCP datacenter

TCP, fondamentalement incompatible avec les applications modernes ? L’informaticien vétéran John Ousterhout en fait la démonstration.

TCP, un non-sens dans les datacenters ? « Il est temps de [le] remplacer », affirme John K. Ousterhout.

L’informaticien américain – auquel on doit notamment le langage de script Tcl – reconnaît les avancées effectuées en vue d’améliorer le protocole. Par exemple avec les technologies DCTCP et HPCC pour le contrôle de congestion. Il estime toutefois que les défauts sont trop nombreux et trop intrinsèquement liés pour pouvoir espérer les corriger tous.

TCP : un protocole qui ne connaît pas les messages

De quels défauts parle-t-on ? Entre autres, de l’orientation des flux. Alors que les applications « modernes » implémentent les RPC par des échanges de messages, TCP travaille avec des flux d’octets.
La transmission des messages sous cette forme implique des compromis. TCP, en particulier, ne gère pas nativement la délimitation des messages (indiquant leur début et leur fin). En conséquence, lorsque les applications lisent un flux, on n’est pas certain qu’elles reçoivent un message complet. Elles doivent donc marquer elles-mêmes le début et la fin des messages quand elles les sérialisent, à l’aide d’un préfixe. Et être également capable de traiter cette information en entrée, afin de reconstituer les messages. Un tel modèle ajoute de la charge de travail, en particulier pour maintenir l’état des messages partiellement reçus.

Le modèle du flux perturbe significativement la répartition de charge, ajoute John Ousterhout. Il prend l’exemple d’une application utilisant plusieurs tâches pour servir les requêtes en provenance d’un ensemble de flux. Dans l’idéal, toutes les tâches attendraient qu’un message arrive sur un flux, et les messages seraient distribués entre tâches. Mais avec TCP, on n’est pas certain d’obtenir, à chaque opération de lecture, un message entier. Plusieurs parties d’un même message peuvent ainsi être reçues par différentes tâches.

Coordonner les tâches pour reconstituer le message étant trop gourmand en ressources, les applications TCP ont recours à d’autres mécanismes d’équilibrage de charge, moins « qualitatifs », où chaque flux est rattaché à une seule tâche. Une approche, exploitée avec memcached, consiste à effectuer une division statistique des flux entre les tâches. Le risque : qu’une tâche reçoive trop de requêtes entrantes. Autre approche, utilisée par RAMCloud : dédier une tâche à la lecture de tous les messages entrants, puis les dispatcher. Le problème ? Cette tâche « maîtresse » devient un goulet d’étranglement. Et, chaque requête traversant deux tâches, la latence augmente.

Des connexions à entretenir…

Deuxième « défaut » de TCP : l’orientation des connexions. Le protocole exige l’ouverture et le maintien d’une connexion pour chaque nœud avec lequel une application communique. Une contrainte dans l’univers du datacenter, où les pairs peuvent se compter par centaines, voire milliers. Résultat : du temps perdu… et de l’espace. Le noyau Linux, par exemple, réserve environ 2000 octets – hors tampons – à l’état de chaque socket TCP.

Pour limiter la surcharge, on peut exploiter la mécanique du proxy, en dédiant ce rôle à un certain nombre de tâches. Chaque serveur peut alors partager une connexion entre toutes les tâches d’une application. Mais cela alourdit la charge. Facebook, par exemple, a préféré substituer UDP à TCP lorsque son manque de fiabilité peut être toléré.

TCP, en outre, requiert un aller-retour de plus entre les hôtes par rapport à l’option échange de messages. Ce qui n’est pas rien dans les environnements serverless, où les applications ont une durée de vie courte.

… et une bande passante à partager

Troisième « défaut » : la gestion du partage de la bande passante. Lorsqu’un lien est surchargé sur un hôte, TCP tente de la répartir entre les connexions actives. Ce type de planification a malheureusement un impact sur les performances : tous les messages tendent à arriver en retard. Des approches, comme SRPT (Shortest Remaining Processing Time), apportent une forme de solution en réservant la bande passante à une tâche. Mais il est délicat de l’implémenter avec TCP, encore une fois à défaut d’informations sur le début et la fin des messages.

Quatrième « défaut » : le contrôle de la congestion. Avec TCP, c’est l’expéditeur qui l’effectue : il peut ralentir le taux de transmission des paquets.
Pour détecter la congestion, TCP s’appuie sur l’occupation du tampon. Dans le pire des cas, les files d’attente des switchs débordent et des paquets se perdent, entraînant des timeouts. Plus communément, les switchs génèrent des notifications ECN quand la file d’attente dépasse un certain seuil.

Outre la dépendance au signal « occupation du tampon », TCP n’est pas capable d’exploiter les files d’attente prioritaires des switchs modernes. Tous les paquets ont droit au même traitement. Dans ce contexte, l’acheminement des longs messages peut perturber celui des courts.
On se trouve en quelque sorte face à un dilemme. Pour maintenir une faible latence sur les messages courts, il faut conserver les files d’attente à un niveau proche de zéro… au risque de sous-utiliser les tampons. Au contraire, solliciter les tampons évite de réduire la bande passante pour les messages longs, mais augmente le délai d’acheminement des courts.

TCP, un sens de l’ordre qui gêne

Cinquième « défaut » : l’acheminement des paquets, qui doit impérativement se faire dans l’ordre.

Dans le datacenter, la pratique du packet spraying est la plus efficace pour la répartition de charge : chaque paquet est routé indépendamment. Mais on ne peut l’employer avec TCP, car elle est susceptible de changer l’ordre d’arrivée. Il faut se rabattre sur le routage à flux constant : tous les paquets d’une même connexion suivent le même chemin. À la clé, un risque de surcharge au cœur du réseau, même lorsque la charge globale est faible. Deux gros flux convergeant vers un même lien intermédiaire peuvent suffire à perturber la transmission.

Le problème se pose aussi sur la répartition de charge par logiciel. Linux, par exemple, équilibre le trafic en distribuant le traitement des paquets entrants entre de multiples cœurs. Chaque paquet est traité sur deux cœurs avant d’atteindre l’application (qui peut résider sur un troisième cœur). Pour assurer une livraison dans l’ordre, tous les paquets doivent suivre le même chemin… avec les risques d’engorgement que cela implique quand deux connexions actives vont vers le même cœur.

Homa, une alternative qui a tout pour elle ?

Plutôt que d’apporter des modifications graduelles ou de passer au « modèle message » (ce qui induirait de lourds changements d’API), John Ousterhout avance le protocole Homa. Il implémente les appels de procédures distants (RPC) et permet à plusieurs tâches de lire des messages sur un même socket.

Homa ne nécessite pas de maintenir des connexions : une application peut utiliser un seul socket pour gérer des RPC concurrents avec de multiples pairs. L’état est maintenu au niveau du socket, à hauteur d’environ 300 octets pour chaque RPC actif (espace libéré ensuite). C’est au destinataire que revient le contrôle de la congestion. Ce qui fait sens, assure John Ousterhout, le premier goulet d’étranglement étant le lien descendant. Dans les grandes lignes, l’expéditeur peut envoyer quelques paquets… et ne transmettre les autres qu’une fois que le destinataire l’y a autorisé.

La plupart des applications à grande échelle reposant sur des frameworks RPC de type gRPC ou Apache Thrift, c’est une voie plus aisée que les API pour intégrer Homa.

Photo d’illustration générée par IA