Benchmark ECOM 2015 GRP5
Evaluation système
Introduction
JMeter est l’outil que nous utilisons pour l’évaluation de performance de notre site. Nous avons créé 1 scénario de test qui nous pensons reprennent les actions les plus récurrentes dans le cadre d’un site commercial.
Aussi nous avons réalisé des tests de burst sur plusieurs URI de l’API.
Lors de la création de ces tests nous avons été contraint d’implémenter une authentification dynamique. Ce qui signifie un échange de requête successif permettant l’authentification d’un client ou d’un artisan à la manière OAuth1.
Afin d’éliminer les contraintes liées à la connectivé et à la limite de bande passante du réseau de la machine executant JMeter, nous avons choisi d’exécuter nos scénarios avec BlazeMeter. Une application web permettant de lancer de manière distribuée un ensemble d’utilisateurs. En effet après lecture d’un article sur le sujet il semblerait qu’une seule JVM ne soit pas scalable. Tout au moins de manière empirique il s’avère qu’au-dela de 1000 utilisateurs (thread) en parallèle, la multiplication de puissance CPU et d’allocation mémoire n’évolue plus de manière proportionnelle au rendement d’une JVM.
Description du système :
Notre site web est construit selon le principe de Single page application. Ce qui signifie la présence d’au moins deux entités : une API côté backend et une application cliente en AngularJS/Html côté utilisateur. L’ensemble de notre packaging Java (Ear = {War, Ejb}) est hébergé sous une même VM accueillant un serveur Glassfish 4.1 mais aussi une base de données Derby.
La VM est hébergée derrière une Box orange, avec les propriétés suivantes :
- Bande passante :
- Montante : 300 Mégabytes
- Descendante : 700 Mégabytes
Capacité alloué a la machine virtuelle :
- RAM allouée : 4 Go
- HDD alloué : 20 Go
- CPU alloué : 1 Process 4 coeurs (Intel i5 2.5 Ghz)
Objectif :
Le but de nos tests est d’identifier le nombre d’utilisateurs maximum que notre application peut supporter. Une fois trouvé, il s’agira d’identifier le goulot d’étranglement de notre application. D’ors et déjà nous identifions plusieurs points pouvons limiter notre capacité de charge :
- La VM se trouvant derrière une Box Orange, il est possible que le débit montant soit limitant, dans une certaine mesure.
- La Box Orange n’est pas conçu pour supporter une charge importante de requêtes entrante, ce qui constitue un cas limitant.
Par ailleurs, Le choix de d'Apache Derby comme BDD influe certainement sur les performances. Le serveur Glassfish n'a pas été "optimisé", par ailleurs. Nous utilisons pas de système de cache également. De manière générale, une machine seule se retrouve assez vite saturée.
- L’utilisation de d’un système de retour en arrière (Rollback) constitue de faite un goulot d’étranglement en raison de l’atomicité des actions affectées du flag @TransactionAttribute.
Scénarios :
Plan 1 :
Notre premier plan de test consiste à effectuer une suite d’actions de basiques communes à un utilisateur normale. Ce dernier effectuerait ses actions dans l’ordre suivant :
- Visiter la page du site
- Visiter 2 formules
- Ajouter les 2 formules au panier
- Authentification et passage de la commande.
Propriétés de simulations :
- Durée de la simulation : 15 min
- Nombre de thread total (user) : 205
- Montée en charge : 300 secondes. Soit ~3 nouveaux utilisateurs toutes les 2 secondes jsuqu’à atteindre les 205 utilisateurs.
- Nombre d’itération : infini
- Nombre de requête par seconde (RPS) : 1
Remarquons que nous avons délibérarément choisi un RPS de 1, afin d’évaluer la tenue en en charge du site en terme de nombre de clients connectés.
Identification du point de rupture :
Sur ce diagramme temporel nous observons en fonction du temps :
- l’évolution du nombre d’utilisateurs (en bleu), lors de la montée progressive en charge.
- l’évolution du # de requetes total par seconde (en vert)
- l’évolution du # d’erreurs par seconde (en rouge)
Précisons que la résolution du graphe est de 1 point / 15 secondes. Ce qui signifie que le nombre de Hit/s est une moyenne sur 15 secondes.
Ce qu’il faut principalement noter sur ce graphe est le décollement de la courbe du taux de Hit/s d’erreurs, à partir de 34 utilisateurs en parallèles effectuant chacun à leur tour 1 requête toute les secondes. Il s’agit du début de rupture de notre système, puisqu’il se trouve au pied du premier pic (cf figure ci-dessous) d’erreurs culminant à une moyenne de 0.14 Hit/s (moyenne sur 15 échantillons), soit 14% des requeêtes générées se sont soldées par une erreur HTTP.
Sur la figure ci-dessus, remarquons la présence de la courbe du temps de réponse moyen, qui dans son évolution globale s’oppose de manière symétrique au pics des erreurs. Rien de plus normal car le serveur ne supportant plus le nombre de clients connectés simultanément, renvoie des erreurs Http avec un temps de réponse court.
En on peut noter la chute drastique de la courbe verte (= moyenne de Hit/s ayant reçu une réponse Http, même s’il s’agit d’erreurs 500 ou autre) dès l’atteinte du seuil des 205 utilisateurs. Cela signifie que ne reçoit aucune réponse Http et que la connexion au site échoue.
Cette étude nous a premis de determiné la charge maximal (sans erreurs) en terme de nombre d’utilisateurs connectés au même moment.
Plan 2 :
Nous allons maintenant nous intéresser à une variante du plan de test. Puisque seule la fréquence de requêtes est réévaluée à 50 Rps (initialement à 1).
Nous avons donc conservés les mêmes valeurs de montée en charge, du # d’utilisateurs et de durée d’expérience. Ce qui est possible d’extraire de ce graphe, est l’allure globale
Ce qui est notable est l’affolement du système quasi instantané. En zoomant légèrement sur le début de la simulation (cf figure ci-dessous) on peut attester que dès le début de la simulation environs 3 requêtes sur 25 se sont soldées en échec.
Identification du goulot d’étranglement :
A l’observation du temps de réponse des differentes requêtes émisent en fonction de l’évolution du nombre d’utilisateur, l’action GET /formules est nettement en tête.
En effet il s’agit d’une requête retournant l’ensemble des produits contenus en base de données. Elle est exécutée à chaque visite de la page principale du site. Ce premier goulot d’étranglement est dû à l’absence de pagination limitant le résultat d’une requête Http et donc du temps de réponse moyen. Voici donc un premier point d’amélioration à apporter à notre prochaine version.
A l’observation des erreurs retournée par l’API, une attire particulièrement notre attention : javax.ejb.EJBTransactionRolledbackException Les Rollbacks sont principalement utilisés dans les opérations nécessitant de mettre à jour des informations de manière simultanée. Dans notre cas, elle concerne l’ajout/suppression d’une formule, mais aussi d’un artisan ou d’un client ou d’une commande. Par exemple la suppression d’un compte artisan nécessite la suppression de l’ensemble de ses formules et produits du catalogue. Cette opération doit être atomique sinon l’intégrité des données ne serait plus garantie
Ainsi voici le second goulot d’étranglement rencontré qui porte sur l’accès non concurrentiel aux ressources add/delete d’un artisan/client/formule/commande.
Cette erreur de Rollback signifie un retour vers l’état précédent l’éxécution d’une transaction. Elle s’est manifestée au travers de teste de burst sur les endpoint simultané de création/suppression de formule et d’artisan.
Limitations du système
- L’option https n’a pas été explorée, même si nous avons conscience qu’elle constitue un critère important dans l’intéraction avec l’API. En effet, les tokens ne devraient jamais être utilisés en clair mais crypté par une session https.
Cette tâche fait partie des améliorations prévues.
- Une transaction bancaire dans notre système est traduite par une simple écriture d’entrée dans une table Orders. En effet dans notre système nous supposons qu’une transaction bancaire n’échoue pas. Ce qui implique indirectement que :
les coordonnées bancaires du client sont correctes la connectivité à l’API bancaire est fiable.
- Nous n’avons pas implémenté d’entité JMS (Java Message Service. En effet on suppose que le système ne tombe pas en panne au cours d’une validation de commande. Car si tel est le cas, nous n'aurions pas moyen de récupérer l’état avant crash.
Ainsi certaines fonctionnalités telle que les news letters, ou la validation d’un compte client après redirection vers un lien envoyé par mail, sont gérés à l’aide d’un timer. Enfin l’atomicité de certaines opération tel que la commande est géré par un système de rolling back par un flag de type @TransactionAttribute .