Consultez nos archives

Les bons outils ne suffisent pas.

Il ne suffit pas d’avoir les bons outils, il faut les utiliser de la bonne manière .

Au cours des derniers mois , nous avons remarqué que nos processus d’envoi (pour l’envoi de courriels à nos clients) étaient beaucoup plus lents que leur limite théorique et que, dans certains cas, ils performaient encore pire que le système qu’ils ont remplacé. C’était un peu inquiétant, comme nous les avions construits spécifiquement pour surpasser l’ancien système par un facteur d’au moins 5. C’était assez décevant de voir que nous étions à peine en mesure d’envoyer autant. Nous avions eu des pics de grande capacité qui nous ont demontré que notre concept était solide, il y avait juste un goulot d’étranglement quelque part que nous n’avions pas encore été en mesure de traquer.

Nous avions choisi redis comme mémoire de stockage de clés-valeur pour les quelques pièces de notre processus que nous avions besoin qui performent le plus et, dans l’ensemble, nous étions très heureux de celà. Éventuellement par contre, après quelques jours dévoués à l’espionnage de bogue, nous avons réalisé que c’était redis qui était notre goulot d’étranglement.

Nous avons été très surpris par ça, car c’était le dernier endroit où nous aurions pensé regarder si nous n’avions pas déjà été à la recherche de bogues. Test après test ont démontré que c’était certainement redis qui dévorait tout notre temps de traitement. Malgré que ça été difficile à croire, nous avons commencé à regarder chacune de nos méthodes utilisant redis pour voir où le problème pouvait se trouver .

Après un bon nombre de pistes de profilage, et quelques états d’impression bien placés, nous avons suivi le goulot d’étranglement qui nous a conduit vers une seule fonction.

public function isBlacklisted($emailaddress)

Il s’agit d’une fonction qui prend une adresse e-mail en tant que paramètre et vérifie si le nom de domaine de cette adresse correspond à l’un de nos domaines sur la liste noire. Si c’est le cas, la fonction nous revient et aucun courriel n’est envoyé à cette adresse.

Maintenant, nous stockons notre liste noire de domaine dans redis comme un ensemble. En général, ce n’est pas cher pour chercher quelque chose dans un ensemble, car c’est juste un ensemble de valeurs uniques. Ça devrait être une opération constante de temps O(1), indépendamment de la taille de l’ensemble. Notre liste noire de domaines n’est pas non plus énorme, c’est seulement environ 33 000 entrées. Rechercher une seule entrée dans l’ensemble devrait être très rapide, mais ce n’est pas ce que nous voyons. Chaque appel à cette fonction prenait de 500 ms à 1 seconde.

Nous avons commencé à regarder de plus près ce que cette fonction faisait et nous sommes tombés sur ceci :

$blacklist = $this->_redisConn->getAllSetMembers('sender_bad_domains');
return in_array($domainName, $blacklist);

Attendez, est-ce que c’est en train de récupérer TOUTE la liste de redis? Et ensuite la vérification de membre en PHP ?

On dirait que oui, c’est exactement ce qui se passait. À chaque fois que cette fonction était appelée (ce qui était *très* souvent), nous demandions à redis de nous donner l’ensemble des domaines de la liste noire, toutes les 33 000 entrées, puis de vérifier pour voir si le domaine du courriel était dans cette liste et de revenir.. tout ça pour refaire exactement la même chose la prochaine fois que nous appelions cette fonction. Il n’y avait même pas une couche de persistance pour s’accrocher à cette grande liste entre les appels de fonction, nous demandions simplement à redis de le faire encore, et encore, et encore.

Ce que nous aurions dû faire à la place est ceci :

return($this->_redisConn->IsMember('sender_bad_domains', $domain));

Laissant ainsi Redis faire ce qu’il est fait pour faire, ce qui est de vérifier pour voir si le domaine nous préoccupant est un membre des sender_bad_domains fixés. Par contre, avant de faire ça, nous avons décidé de voir si c’était vraiment ça le problème en ajoutant un peu de persistance à la liste des domaines que nous récupérons de redis. Au lieu de le demander à chaque fois, nous le demandons juste une fois, et le stockons dans une variable membre de notre classe. Ce n’est pas la solution idéale, mais comme petit test rapide, il nous demontrerait si c’était vraiment notre problème. Nous avons fait un changement rapide qui consistait à sauvegarder la liste dans une variable, puis d’exécuter notre in_array contre cette liste. Maintenant, au lieu de demander la liste noire complète à redis environ 2 000 fois au sein d’une seule exécution de notre script, nous lui demandons juste une fois. Les résultats?

Nous avons vu le trafic réseau sortant du serveur de redis chuter de 200 Mbps à seulement 6 Mbps et puis chuter encore jusqu’à 300Kbps et y rester.

C’est beaucoup plus impressionnant si l’on considère le graphe de la circulation :

{F100}
bugfix

Ce changement nous a permis de se remonter là où notre limite théorique aurait dû être, et a amélioré notre vitesse d’envoi de façon spectaculaire. Au lieu de prendre jusqu’à 15 minutes pour traiter 2 000 courriels, il faut maintenant seulement 30 secondes pour le faire.

Je suis sûr que le lecteur avisé est en train de s’écrier « ATTENDEZ! VOUS NE LE FAITES TOUJOURS PAS CORRECTEMENT! » et bien sûr, vous avez raison. Nous demandons encore à redis de nous donner l’ensemble de la liste noire, au lieu de simplement demander de vérifier si le domaine nous préoccupant est un membre de l’ensemble, donc oui.. il y a encore place à l’amélioration.

La morale de cette histoire est que peu importe si vous utilisez la toute dernière technologie de webscale à la mode; si vous l’utilisez mal , elle ne vous aidera pas.

– Gabriel

(traduit par Fannie B.)

Comments

comments