Une démarche naïve de réalisation de tests de performance est d'effectuer des améliorations successives sur un système donné, donc d'avoir un processus pseudo-itératif. Donc, pourquoi ne pas se baser sur les processus développés dans les méthogologie Agiles, voir même d'utiliser les cycles d'améliorations continues issue du Lean.
En effet, on peut très facilement se rapprocher de la roue de Deming, appelée plus communément la démarche PDCA : Plan, Do, Check, Act.
Le but des tests de charge est de trouver les points faibles d'une architecture dans le but de l'améliorer : performance, stabilité sous charge, coût, etc. On rapproche donc les étapes du PDCA avec un processus de tests de charge :
Avant de se lancer dans ce cercle vertueux d'amélioration, il est important de déterminer le but de ces tests. Est-il de :
En fonction des réponses à ces questions, on peut déterminer quel type de tests effectuer.
Tests de performances
Objectif : Tests sur un ou plusieurs scénarios sous une charge modérée du système complet et/ou d’un sous-système nécessitant un point d’attention
Exemple : La souscription est testée pour 1 utilisateur et, pour chaque étape du use case, on mesure le temps passé dans les différents composants de l’application
Test de vieillissement
Objectif : Déterminer la capacité de l’application à fonctionner sur une période étendue
Exemple : On simule l’utilisation de l’application pendant 48h, avec une charge constante et égale à la charge moyenne
Test de charge
Objectif : Mesurer la tenue en charge de l’application sur la population cible
Exemple : On simule l’utilisation de l’application par 200 utilisateurs (avec des scénarios différents) en parallèle pendant 2h
Test de rupture / Stress test
Objectif : Déterminer les limites de l’application
Exemple : On augmente le nombre d’utilisateurs en parallèle sur l’application jusqu’à ce que le taux d’erreurs / les temps de réponse ne soient plus acceptables
Une fois choisis le ou les types de tests à effectuer, il faut déterminer les profils de charge des tests. C'est-à-dire les scénarios à tester, ainsi que leur typologie.
Le choix des scénarios doit être fait en prenant en compte :
La dernière étape est de décider quelles métriques mesurer et celles que l'on veut améliorer. Il faut garder à l'esprit que trop de métriques tue les métriques. En effet, si on regarde trop de données, on risque de perdre de vue le but de notre campagne de tests. De plus, il faut séparer les métriques "business" qui sont celles à améliorer, et les métriques techniques qui vont nous permettre d'investiguer a posteriori les points de contentions de notre système. Les métriques les plus classiques sont :
L'intégration des métriques est un point critique de la démarche d'amélioration des performances. Nos tests vont générer une très grosse quantités de données, qu'il va falloir agréger.
Sans faire dans un cours de statistiques, on peut déjà calculer facilement le minimum, le maximum et la moyenne d'une métrique. Il est aussi intéressant de calculer l'écart type, qui va nous donner comment nos valeurs sont réparties autour de la moyenne. Plus cette valeur est petite plus les données sont uniformes (proche de la moyenne). Mais si vous n'avez qu'un calcul à faire, c'est le percentile ou centile. Le percentile X est la Xème mesure sur 100 triées par ordre croissant, c'est donc la valeur où X% des mesures lui sont inférieures. De manière générale on calcule le percentile 90, qui est plus représentatif du ressenti utilisateur qu'une simple moyenne ou que l'écart-type. Cette valeur de 90 est déduite de la distribution de Gauss (pour plus de détails). Pour un service, dont dépend d'autres services ou applications, un bon percentile 99 (ou plus) sera nécessaire pour limiter l'impact de ce service sur les systèmes qui l'utilisent.
Une fois toutes ses données intégrées, il faut préparer notre campagne de tests. Tout d'abord il faut choisir les outils pour :
Le but de notre article n'est pas de présenter ni de comparer ses outils, mais il faut garder en tête quelques points important en les choisissant. Il est important de pouvoir mettre en relation les données envoyées par l'injecteur et les sondes, ce qui va permettre de relever rapidement les relations entre les données remontées et les actions faites par l'injecteur. De même, il faut pouvoir générer un rapport, à l'usage des non-experts, à partir des métriques.
Ainsi, avant de commencer toute campagne de test il est important d'automatiser au maximum toute la chaîne des tests. Le but ultime étant que toute la chaîne test + agrégation + génération du rapport se fasse automatiquement.
Il faut néanmoins raccourcir au maximum la phase entre la fin des tests et la génération du rapport, ce qui permet de faire une modification rapidement pour relancer des tests le plus vite possible.
La première étape d'une itération de test de performance est de savoir où l'on veut aller. Nous verrons à la dernière phase (celle du A) que l'on ne doit changer qu'un seul paramètre dans notre système entre chaque test. Ce faisant, on doit être capable de globalement prévoir comment nos métriques vont évoluer. De plus, cette étape doit nous garantir que 2 tests consécutifs sans modification doivent avoir des résultats équivalents (à quelques pour cents près)
Si cette itération est la première, elle va nous servir de référence pour toutes les itérations suivantes, c'est le point de départ de notre système. Il est même préférable de faire quelques itérations de "rodages", donc sans amélioration du système à tester, mais uniquement pour effectuer des améliorations sur le processus de tests.
Dans un cas idéal, on peut réaliser plusieurs itérations de constructions de notre démarche de tests, avec la méthode des petits pas. On commence par un scénario simpliste qui permet de mettre en place les outils. Puis on fait grossir ce scénario jusqu'au résultat souhaité. Ensuite on met en place les sondes puis toute la démarche d'automatisation de l'agrégation et présentation des métriques. Ce faisant, on est assuré d'avoir une process fluide et surtout adapté à nos besoins. On peut rapprocher ce processus incrémental de construction de la démarche du TDD.
La phase d'éxecution des tests de performance est l'étape que l'on pense naturellement à automatiser. Il est en effet peu envisageable de réaliser un test de charge manuellement.
Même s'il est possible de réaliser un test d'optimisation unitaire manuellement, l'automatisation permettra de :
Afin de fiabiliser le résultat des tests et de s'assurer qu'ils sont exécutés dans les mêmes conditions à chaque fois, la phase d'initialisation de l'environnement testé gagnera à être automatisée. En effet, ces étapes d'initialisation sont crutiales afin de garantir que les tests soient répétables (ie. avec les même résultats) et que l'on puisse en tirer les bonnes conclusions. Réalisées manuellement, elles peuvent devenir rapidement fastidieuses (initialiser la base de données, vider les caches, purger les files de messages, ...) et sujettes à erreurs et/ou oublis faussant ainsi les résultats.
De plus, l'environnement de tests doit être entièrement dédié pour garantir la stabilité des tests.
Une fois les tests exécutés, il faut recueillir les métriques à la fois côté injecteur mais aussi sur le système testé. Cette phase peut-être manuelle ou automatique. Tout dépend de la complexité du système testé : si vous n'avez qu'un seul serveur d'application, une base de données et l'injecteur, les métriques pourront être exploitées en allant voir chacun des outils de collecte des métriques; si l'architecture du système se complexifie avec des reverses proxy, un cluster de serveurs d'application, un broker de messages, l'injecteur doit être distribué, ..., l'aggrégation automatique des métriques de chacun des sous-systèmes devient nécessaire pour ne pas perdre son temps à collecter ces resources.
Certains outils de tests de performance vous propose de le faire pour vous en installant leur propre sonde de monitoring sur le système testé. Une autre possibilité pourrait être de requêter (avec un aggrégateur de logs tel que Splunk) les logs du système testé qui contiennent généralement de précieuses informations.
Si les mesures de chaque tir doivent être tracées (utile pour suivre l'impact sur les performances des optimisations et du développement), il est judicieux de tracer :
Cette tracabilité permettra d'identifier les différences entre chaque tir et de comprendre l'origine des résultats constatés. En effet, un tir de performance doit avoir des résultats prédictifs :
Lorsque l'on a recueilli les métriques, que l'on a validé que celles-ci sont stables d'un tir à l'autre et que les modifications apportées au système (optimisation ou ajout de fonctionnalités) ont permis d'améliorer le système, il faut identifier le point de contention suivant.
Pour cela, la loi d'Amdahl nous dit qu'il vaut mieux investir sur une petite optimisation d'un sous-système gourmant au global plutôt qu'une grosse optimisation d'un sous-système peu consommateur. Ça parait évident dit comme ça mais ça ne fait pas de mal de se reposer la question lorsque l'on se lance dans une optimisation. Il faut aussi bien sûr tenir compte du coût d'implémentation de l'optimisation. S'il faut revoir complètement l'architecture pour une amélioration somme toute pas peu importante, il vaut peut-être mieux chercher une autre amélioration.
Un dernier point à prendre en compte lorsque l'on veut identifier un point de contention est de ne pas oublier l'outil d'injection lui-même qui peut devenir lui-même le point de contention. Par exemple, s'il n'arrive plus à simuler le nombre d'utilisateurs virtuels voulus, alors que le système testé peut répondre sans problème, il se peut que l'injecteur ne puisse plus récupérer les réponses du système à la cadence souhaitée. Dans le cas où il s'agit de requêtes HTTP, le serveur HTTP affichera des temps de réponse mauvais alors que c'est l'injecteur qui ne suit pas.
Afin d'obtenir une vision plus global du système testé pour identifier les points de contention, observer les impacts des optimisations ou pour communiquer ses résultats au métier, il est nécessaire de présenter de façon conscise et compréhensible les mesures recueillies. Sans ce type de communication, l'effort de test de performance risque de ne pas être reconnu à sa juste valeur.
En effet, un tableau plein de chiffres peut être difficile à analyser et très peu compréhensible par un non-expert. Les outils de test de performance éditeur, accompagnés de sondes, sont souvent complétés par des outils de reporting offrant une représentation graphique des mesures de performance. Sans ces derniers, Excel vous rendra de grands services malgrès une exploitation plus manuelle.
Une fois que l'on a identifié le sous-système à optimiser, il faut creuser plus profond afin de comprendre l'origine du point de contention et pouvoir ainsi imaginer une solution pour lever la limite. Cependant, il s'avère que de temps à autre la cause profonde du problème de performance n'est pas si évidente qu'il n'y parait. C'est pour cela qu'il faut se mettre en position de tester rapidement nos hypothèses avant d'effectuer des changements radicaux qui pourraient s'avérer au final inutiles. Pour cela, il est possible de monter un micro-benchmark d'un sous-système ou d'exécuter le test de performance complet avec une modification quick & dirty sur une branche séparée.
Que ce soit, pour une optimisation en bonne et due forme ou une optimisation quick & dirty, chaque tir de performance ne doit tester qu'une modification à la fois afin de valider le gain (ou la dégradation) apporté et tirer les bonnes conclusions ("c'est bien ce sous-système le problème et ma solution quick & dirty valide que je suis sur la bonne voie"). ex: rajout d'un seul index, modification d'une requête, mise à jour d'une brique, etc.
De même chaque modification ayant pour objectif d'améliorer les performances ou ayant un potentiel impact sur les performances (tuning DB, VM, ...) doit être testé pour au moins confirmer que ces modifications n'ont pas un impact négatif (non-assumé) sur les temps de réponse du système.
Enfin, toutes les optimisations ne valent pas le coût d'être entreprises (Constant Tweak Syndrome). En effet, il y aura toujours des optimisations possibles, mais si celles-ci n'ont pas d'impact réel sur le système ou si elles coûtent plus à implémenter que la valeur qu'elles pourraient apporter, il est nécessaire de savoir se poser la question sur leur utilité.
La mise en place d'une campagne de tests de performance est généralement coûteuse. Afin de la rendre la plus efficace possible, il convient d'adopter une démarche stricte pour ne pas se perdre dans les méandres de l'optimisation sans fin et/ou inutile.
Une démarche d'amélioration continue de type PDCA apporte un cadre aidant à se concentrer sur l'objectif même de ces tests : l'amélioration des performances, de la stabilité ou encore du coût du système testé. D'autre part, l'aspect itératif de cette démarche permet d'introduire les tests de performance au plus tôt dans les cycles de développement et ainsi éviter tous les écueils rencontrés en déroulant les tests à la fin des développements. De plus, il est important de travailler en étroite collaboration avec les équipes de développement, voir que ça soit l'équipe de développement elle-même qui réalise les tests. En effet, cela permet d'avoir connaissance au fil de l'eau des futurs fonctionnalités qui pourraient impacter les scénarios, de pouvoir mettre en place des bonnes pratiques de performances au sein de l'équipe et d'éviter que les efforts d'optimisations réalisés soient détruits par une action involontaire d'un développeur mal (in)formé.
Enfin, l'automatisation de l'ensemble de la démarche (initialisation de l'environnement, exécution des tests, collecte des mesures et reporting) permet d'exécuter des tests de non régression de performance dans son intégration continue.
Le but ultime est d'intégrer les tests de performances dans sa démarche de déploiement continue, afin de valider la conformité de toute nouvelle version du système vis a vis de ses SLA.