Une page qui répond en 200 ms contre un Redis local prend quatre secondes dès que le client se trouve derrière un VPN, alors que les compteurs du serveur restent inactifs. Le journal lent ne montre rien au-dessus d’une milliseconde, le graphe CPU est plat et l’instance sert d’autres clients sans se plaindre. Ce qui a changé, c’est la distance, et ce qui multiplie la distance, c’est le nombre de fois où votre code s’arrête et attend. Parcourez le chemin de la requête depuis le client vers l’extérieur et la solution est presque toujours moins d’attentes séquentielles plutôt qu’un serveur plus rapide.

Partez du client, pas des métriques du serveur

Observez le processus client entre la première et la dernière commande. Si la trace montre une réponse, une pause, puis la commande suivante, vous payez un aller-retour complet par commande et aucun réglage du serveur n’y changera rien. Le journal lent est le mauvais instrument ici, car il enregistre le temps d’exécution et exclut délibérément les E/S de communication. Un journal lent propre à côté d’un point de terminaison lent est la signature d’un problème d’allers-retours.

Mesurez le chemin lui-même avant de toucher au code. redis-cli -h redis.example.internal --latency envoie des PING répétés et affiche le temps moyen de requête et de réponse, qui est votre coût par échange. Multipliez-le par le nombre de commandes séquentielles dans une action utilisateur et vous obtenez un plancher pour le point de terminaison.

Comptez les allers-retours dont une action utilisateur a réellement besoin

Prenez un seul point de terminaison et notez chaque appel Redis dans l’ordre, y compris ceux qui se cachent dans un ORM, un middleware de session ou une recherche de feature flag. La plupart des points de terminaison lents finissent par faire vingt ou trente appels là où trois suffiraient, et les appels cachés sont généralement majoritaires.

Une chaîne de dépendances ne peut pas être repliée : si la seconde commande a besoin de la clé renvoyée par la première, l’attente est réelle et les seules options sont un autre modèle de données ou une opération côté serveur qui exprime toute l’étape d’un coup. Les lectures indépendantes sont un autre cas, car rien ne vous oblige à attendre la première réponse avant d’envoyer la seconde. Marquez chaque appel comme dépendant ou indépendant avant de modifier quoi que ce soit, et la forme de la solution devient évidente.

Le nombre qui compte, ce sont les attentes séquentielles, pas le total des commandes. Trente commandes dans un lot coûtent toujours une seule attente.

Ne regroupez que les commandes qui ne dépendent pas les unes des autres

Un pipeline met en file des commandes indépendantes et collecte les réponses dans l’ordre, transformant N attentes en une seule. La plupart des clients l’exposent comme un bloc, un callback ou un gestionnaire de contexte plutôt qu’un mode de connexion séparé, vérifiez donc l’API fournie par votre version installée. Le gain croît avec le temps d’aller-retour, et c’est précisément pourquoi il compte sur un VPN et se remarque à peine sur localhost.

import redis

r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
    pipe.get(key)
print(pipe.execute())

Passez transaction=False quand vous voulez seulement regrouper. Par défaut, le client enveloppe le lot dans MULTI et EXEC, ce qui le rend atomique et change ce que les autres clients peuvent observer en vol. L’atomicité est une décision de correction, pas un bouton de performance, et un pipeline n’est pas une transaction simplement parce que les deux réduisent les allers-retours.

Lisez chaque réponse plutôt que seulement l’envoi du lot. Une commande mise en file peut renvoyer une erreur alors que le lot dans son ensemble se termine, et un client qui jette la liste des réponses transforme une écriture échouée en écriture silencieuse. Comparez la longueur de la liste des réponses au nombre de commandes que vous avez mises en file.

Gardez la connexion vivante entre les actions

Chaque nouvelle connexion paie la résolution de noms, une poignée de main TCP, une poignée de main TLS et un AUTH, et sur un VPN chacun de ces éléments est un aller-retour que vous ne pouvez pas regrouper. Créez le client une fois par processus et réutilisez-le, ou utilisez le pool fourni par votre bibliothèque avec une taille bornée. Se reconnecter à chaque requête est la deuxième cause la plus fréquente de ce symptôme après les attentes par commande.

Gardez le nom d’hôte privé dans la configuration plutôt qu’une adresse littérale. La validation du certificat et l’identité du service s’appliquent toujours sur un réseau privé, et épingler une IP casse à l’instant où le fournisseur renumérote un sous-réseau. Si la résolution est la partie lente, corrigez le chemin du résolveur plutôt que la chaîne de connexion.

Définissez un délai d’expiration de socket explicite pour qu’un tunnel bloqué échoue vite au lieu de suspendre un worker. Confirmez ensuite que le pool est réellement réutilisé en journalisant la création de connexions dans le client.

Dimensionnez le lot avant qu’il ne devienne un problème de mémoire

Redis conserve les réponses en file pendant toute la durée d’un lot, et le client conserve ensuite le résultat analysé. Un pipeline de dix mille grandes valeurs échange des attentes réseau contre des pics de mémoire et de longues pauses aux deux extrémités.

Choisissez la taille du lot en fonction du volume de réponse plutôt que du nombre de commandes. Quelques centaines de petites commandes dans un lot sont généralement confortables, alors qu’une centaine de charges de plusieurs kilo-octets ne le sont pas. Répartissez par octets autant que par nombre, et gardez chaque lot dans le budget de latence que le point de terminaison possède réellement.

Mesurez de bout en bout après le changement : redis-cli -h redis.example.internal --latency-history montre si le chemin lui-même est stable, et votre propre chronomètre de requêtes montre si le point de terminaison s’est amélioré. Si le point de terminaison reste lent alors que l’historique de latence est plat, le coût restant est le travail côté serveur ou votre propre sérialisation, et l’étape suivante est un profil du gestionnaire plutôt qu’un autre changement de regroupement.

Articles similaires