Tutoriel pour tester la charge d'une base de données avec Apache JMeter

Ce chapitre de livre s'intéresse à tester la charge d'une base de données avec l'outil JMeter.

Pour réagir à ce tutoriel, un espace de dialogue vous est proposé sur le forum Commentez Donner une note à l'article (5).

Article lu   fois.

Les trois auteurs

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Un peu de théorie

L'architecture d'un moteur de base est devenue très complexe comme le montre le schéma ci-dessous (notons que ce n'est qu'une description simplifiée de l'architecture d'Oracle).

La complexité est telle que l'expertise sur une version d'Oracle est remise en cause dès la sortie de la version suivante.

Description simplifiée de l'architecture d'Oracle

Chaque partie de cette architecture peut être paramétrée afin de s'adapter au mieux à l'utilisation de la base de données.

Si on ajoute que les données stockées en base sont l'un des éléments les plus importants d'une application et celui qui pose le plus de problèmes de performance, on comprend l'importance de faire des tests de charge sur le serveur de bases de données.

Pour ceux qui ne sont pas encore convaincus par l'importance de faire du tuning de l'architecture du moteur de base de données, ci-dessous un exemple de temps de réponse d'une requête SQL sur un Oracle sans tuning (la SGA, un des caches de la base de données Oracle, n'est pas bien configurée) par rapport à un Oracle optimisé (on a augmenté la taille de la SGA pour qu'elle soit cohérente par rapport à la taille des données stockées en base).

Importance du SGA

Comme on peut le voir, cela vaut le coup de passer un peu de temps à optimiser Oracle à l'aide d'un test de charge.

Regardons maintenant comment JMeter permet de tester un serveur de base de données.

II. Mise en place avec JMeter

JMeter étant un programme Java, l'accès à une base de données se fait à l'aide du protocole JDBC.

La première chose à faire est donc de mettre le pilote JDBC dans le ClassPath de JMeter si ce n'est pas déjà fait (en pratique, on déposera le fichier pilote .jar dans JMETER_HOME/lib/).

Une fois cela fait, il faut configurer la connexion à la base avec l'élément Configuration de connexion JDBC.

Cela va nous permettre de configurer la chaîne de connexion à notre base de données (URL, port, identifiant de connexion, mot de passe, etc.).

Configuration de connexion JDBC

Cet élément est composé de quatre parties nommées Nom de liaison du pool, Configuration du pool de connexions, Validation des connexions par le poolet Configuration de connexion à la base de données.

Leurs noms étant parlants, nous ne nous attarderons donc pas plus.

Cependant, il est important de faire attention aux parties Configuration du pool de connexions et Validation des connexions par le pool afin de ne pas surcharger JMeter (en particulier le nombre maximum de connexions) et la base de données (niveau d'isolation de la Transaction, auto commit et requête de validation).

Bien sûr, il est possible d'avoir plusieurs éléments Configuration de connexion JDBC qui pointent sur plusieurs bases de données.

Maintenant, on peut passer aux requêtes SQL elles-mêmes à l'aide de l'élément Requête JDBC.

Requête JDBC

Dans un premier temps, on choisit sur quelle base de données les requêtes vont être lancées à l'aide du champ Nom de liaison (nom défini dans l'élément Configuration de connexion JDBC).

Comme on peut le voir dans la liste Type de requête, tous les types de requêtes peuvent être réalisés (UPDATE, SELECT, DELETE, INSERT, appel de procédure stockée, etc.).

À l'aide de ces deux éléments, on peut tester tout type de base de données (la seule exigence est la présence d'un pilote JDBC pour la base de données cible) avec toutes les requêtes SQL imaginables.

III. Méthodologie

Avant de passer à des exemples concrets, il est important d'avoir une méthodologie afin de réaliser des tests pertinents.

Quelques conseils qu'il est préférable d'intégrer à votre processus :

Chose importante lors d'un test d'une base de données, s'assurer qu'elle est isoproduction, à défaut s'assurer que la différence est acceptable et que le test reste utilisable.

Par isoproduction il faut comprendre deux choses :

  • Le paramétrage du moteur de base de données doit être identique à celui de production (s'il existe).
  • Le volume des données en base doit être lui aussi le plus proche de la réalité (le plus simple est d'avoir une sauvegarde de ce qu'il y a en production ou si on part de zéro, d'avoir une idée de la future volumétrie).

Pour les sceptiques ou curieux, on peut voir sur le graphique suivant, le temps de réponse de la même requête SQL exécutée sur des volumes de données différents.

Importance du volume de données

Maintenant que nous avons une base de données isoproduction, penchons-nous sur le plan de test.

Dans notre plan de test, il faut prendre en compte la durée des tests et la diversité des requêtes d'entrée.

De nos jours, il y a forcément des caches dans l'architecture que l'on va tester (que cela soit au niveau du moteur de base de données ou au niveau d'autres composants de l'architecture technique).

Les caches sont conçus pour éviter que les mêmes traitements lourds (requêtes SQL, etc.) avec les mêmes paramètres (valeurs des paramètres, etc.) soient exécutés à chaque fois. Pour cela un cache stocke le résultat du traitement lourd.

Cela implique que le cache sera inutile si la durée de test est trop courte, car il n'aura pas le temps de se remplir pour être utile. Et le cache sera trop utilisé si la diversité des requêtes envoyées (et donc leurs types et leurs paramètres) est trop réduite. Inversement le cache sera inutile si le jeu de données est trop diversifié.

IV. Mise en pratique avec JMeter

Passons à la pratique.

IV-A. Exemple 1 : test de charge d'une base de données

Démarrons par un exemple simple (que nous complexifierons au fur et à mesure) qui consiste à tester une base de données sous MySQL à l'aide d'une requête SQL.

Occupons-nous dans un premier temps des requêtes SQL de type SELECT.

Commençons par configurer notre connexion à MySQL à l'aide de l'élément Configuration de connexion JDBC.

Pour MySQL, l'URL de la base de données doit être de la forme jdbc:mysql://host:port/dbnom et la classe de pilote JDBC égale à com.mysql.jdbc.Driver.

Dans notre cas, la base MySQL est installée en local et par défaut sur la même machine que JMeter (à éviter absolument lors de vrais tests), son URL sera jdbc:mysql://localhost:3306/test.

On règle à zéro le nombre maximum de connexions afin que chaque thread ait sa propre connexion.

Configuration de connexion JDBC

Maintenant, ajoutons une Requête JDBC afin d'exécuter notre fameux SELECT.

Mettre le nom de la connexion qui a été défini avant.

Choisir Select Statement comme type de requête SQL

Remplir le champ Requête avec notre select (ici select nom,prenom from Clients where nom like 'D%').

Requête JDBC

Ajouter une assertion afin de contrôler la bonne exécution de notre requête.

Dans notre cas, lorsqu'il y a une erreur d'exécution, la réponse contiendra l'expression exceptions.

Assertion

Dans l'état actuel, le script ne couvrira qu'une partie infime d'un bon plan de test, puisqu'avec une seule requête SQL, on testera surtout les caches.

Heureusement, nous disposons d'une liste des principales requêtes SQL de type SELECT exécutées.

Un moyen simple d'intégrer ces nouvelles requêtes à notre script est d'utiliser un élément Source de données CSV.

Pour cela on va mettre toutes les requêtes dans un fichier CSV.

Extrait du fichier CSV :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
    req_sql 
    select nom,prenom from Clients where nom like 'D%' 
    select nom from Clients 
    select nom,prenom from Clients where sex = 'MALE' 
    select nom,prenom from Clients where nom like 'T%' 
    select nom,prenom from Clients where sex = 'FEMALE' 
    select nom,prenom,mail,code_postal from Clients where code_postal = '58418'

On va renseigner le nom de notre fichier CSV dans le champ Nom de fichier de l'élément Source de données CSV.

Ne pas oublier de changer la valeur du délimiteur de virgule en, par exemple, point-virgule afin qu'il n'y ait pas de problème avec les virgules qui composent nos requêtes.

Ici, on n'a pas besoin de définir le nom de la variable où sera stockée la requête afin d'être utilisée par la suite, car elle existe déjà dans notre fichier CSV.

Source de données CSV

Il ne nous reste plus qu'à remplacer la requête dans le champ Requête de l'élément Requête JDBC par notre variable ${req_sql}.

Utilisation de notre variable *${req_sql} dans Requête JDBC

Afin de vérifier que cela fonctionne, on ajoute un élément Arbre de résultats à notre plan de test (ne pas oublier de le désactiver pour la suite lorsqu'on fera un test de charge).

Arbre de résultats

Voilà qui est beaucoup mieux, mais les requêtes restent statiques et, au bout d'un moment (plus ou moins long en fonction du nombre de requêtes SQL dans le fichier CSV), elles se retrouveront toutes dans le cache.

Afin d'éviter ce problème et de rendre notre test plus réaliste, on va rendre dynamiques nos requêtes SQL.

Pour commencer, on va regrouper nos requêtes par famille ayant la même forme syntaxique.

Dans notre cas nous avons deux familles :

other

 
Sélectionnez
1.
2.
3.
    select XXXXXXX from XXXXX where XXXXX 

    select XXXXXXX from XXXXX

On aura donc besoin de deux Requêtes JDBC.

Maintenant, pour chaque groupe de requêtes, on va noter ce qui peut être variabilisé.

Par exemple :

sql

 
Sélectionnez
1.
    select nom, prenom from Clients where nom like 'D%'

deviendra :

other

 
Sélectionnez
1.
    select {liste_selection} from {table} where {clause where}

Toutes ces variables seront dans un fichier CSV.

Extrait du fichier CSV :

other

 
Sélectionnez
1.
2.
3.
4.
5.
6.
    liste_selection_grp1;table_grp1;clause_where_grp1 
    nom,prenom;Clients;nom like 'D%' 
    nom,prenom;Clients;sex = 'MALE' 
    nom,prenom;Clients;nom like 'T%' 
    nom,prenom;Clients;sex = 'FEMALE' 
    nom,prenom,mail,code_postal;Clients;code_postal = '58418'

On modifie notre élément Source de données CSV afin qu'il pointe sur notre nouveau fichier CSV.

Enfin on modifie notre champ requête qui devient :

other

 
Sélectionnez
1.
    select ${liste_selection_grp1} from ${table_grp1} where ${clause_where_grp1}
Notre requête SQL

On fait la même chose pour le deuxième groupe de requêtes SQL.

Extrait du fichier CSV :

other

 
Sélectionnez
1.
2.
    liste_selection_grp2;table_grp2 
    nom,prenom;Clients

Ajoutons l'élément Contrôleur d'Ordre aléatoire afin d'ajouter encore un peu plus de réalisme en simulant des utilisateurs avec des comportements différents.

Contrôleur d'Ordre aléatoire

Avec encore un peu d'effort, on peut encore rendre plus réalistes les clauses WHERE de nos requêtes SQL.

On remarque que la clause WHERE peut être séparée en plusieurs parties.

other

 
Sélectionnez
1.
    {clause where} = {clause where gauche} {clause where condition} {clause where droite}

Avec la même méthodologie que précédemment, on peut affiner notre fichier CSV et notre Requête JDBC pour ainsi multiplier les requêtes possibles avec un jeu de données réduit (il suffira d'utiliser un script qui génère notre fichier CSV en combinant les valeurs possibles).

Par exemple.

Extrait du fichier CSV :

other

 
Sélectionnez
1.
2.
3.
    liste_selection_grp1;table_grp1;clause_where_grp1 
    nom,prenom;Clients;nom like 'D%' 
    nom,prenom,mail,code_postal;Clients;code_postal = '58418'

Devient :

Extrait du fichier CSV :

other

 
Sélectionnez
1.
2.
3.
4.
5.
    liste_selection_grp1;table_grp1;clause_where_gauche_grp1;clause_where_condition_grp1;clause_where_droite_grp1 
    nom,prenom;Clients;nom;like;'D%' 
    nom,prenom,mail,code_postal;Clients;code_postal;=;'58418' 
    nom,prenom;Clients;code_postal;=;'5841' 
    nom,prenom,mail,code_postal;Clients;nom;like;'D%'
Requête JDBC utilisant notre fichier CSV

Dans la majorité des cas, il y a des utilisateurs avec des droits de modification (appelons-les administrateurs) qu'il faudra simuler. Rien de plus simple avec JMeter.

Afin de séparer les deux types d'utilisateurs, on va créer un autre Groupe d'unités. On pourra ainsi paramétrer de manière fine chaque groupe.

Par exemple si on sait qu'il y a 24 % d'utilisateurs qui ont des droits de modification, il sera facile de trouver la valeur du Nombre d'unités pour chaque groupe.

Afin d'éviter de faire des UPDATE qui ne mettent rien à jour, les conditions de nos requêtes UPDATE seront les résultats de requêtes SQL exécutées juste avant par nos UPDATE.

Imaginons que ces utilisateurs puissent modifier le numéro de téléphone des clients.

Dans un premier temps nous devons récupérer l'identifiant de la personne dont le numéro de téléphone va être modifié.

Utilisons un élément Requête JDBC afin d'exécuter cette requête SQL. On pourra prendre select id_client from Clients where nom like 'F%' comme requête SQL (je vous laisse appliquer ce que l'on vient d'apprendre pour rendre plus dynamique cette requête SQL).

Ne pas oublier de récupérer les résultats de la requête SQL.

Requête JDBC

Comme on peut le voir, la requête récupère plusieurs identifiants.

Résultat de notre requête SQL

On a le nombre de réponses dans la variable identifiant_client_#

On va choisir un identifiant au hasard dans la liste retournée. Pour cela on va utiliser la fonction __Random de JMeter.

La formule ${_Random(1,${identifiant_client#},identifiant_client_final)} mise dans un échantillon BeanShell nous permettra de réaliser ce que l'on veut en mettant l'identifiant dans la variable identifiant_client_final.

échantillon BeanShell

Maintenant que l'on a notre identifiant de client, il nous faut un nouveau numéro de téléphone.

Utilisons l'élément Variable aléatoire.

Variable aléatoire

Il ne nous reste plus qu'à utiliser la variable identifiant_client_final et notre nouveau numéro de téléphone dans notre update à l'aide d'un autre élément Requête JDBC.

Notre UPDATE sera :

other

 
Sélectionnez
1.
    update Clients set telephone_fixe = ${num_tel} where id_client = ${identifiant_client_final}
Requête JDBC

Les administrateurs font aussi des requêtes SQL de type SELECT, et l'on connaît la proportion des SELECT et des UPDATE.

Afin d'implémenter cette proportion, on va utiliser l'élément Contrôleur Débit de JMeter.

Par exemple ici, on définit que les requêtes UPDATE représentent 30 % des requêtes totales.

Contrôleur Débit

On peut vérifier à l'aide d'un Rapport agrégé que cela est bien respecté (j'ai regroupé les requêtes à l'aide d'un Contrôleur Transaction afin de faciliter la lecture des résultats).

Rapport agrégé

Une bonne pratique dans le développement logiciel est la philosophie DRY (Don't Repeat Yourself). Malheureusement, si on regarde notre dernière modification du script, on peut voir qu'il y a des duplications dans la partie du script qui exécute les requêtes SELECT.

Duplications dans la partie du script

Pour éviter cette duplication, on va utiliser l'élément Contrôleur Inclusion qui nous permet d'inclure un script dans un autre script.

La première chose à faire est de sauvegarder la partie dupliquée dans un fichier au format JMeter.

Sauvegarder la partie dupliquée

Maintenant, il suffit de remplacer dans le script les parties dupliquées par des Contrôleur Inclusion.

[ALT-PASTOUCHE]

Les faire pointer sur notre script sauvegardé précédemment.

Contrôleur Inclusion

On aurait pu s'arrêter là tout en ayant répondu aux besoins d'un test de charge de notre serveur de base de données, mais comme je pense que l'industrialisation des tests est quelque chose d'important, nous allons faire quelques modifications. Ceci aura pour but de faciliter l'intégration de notre script dans une usine logicielle.

Le tout à l'aide de la fonction ${__P(xxx,yyy)} avec xxx le nom de la variable et yyy sa valeur par défaut.

Par exemple pour le groupe d'unités « Administrateurs ».

Groupe d'unités Administrateurs

Ici, on définit que par défaut il y a une unité qui exécute une itération et que la durée de montée en charge est d'une seconde.

Lors de l'exécution de JMeter en ligne de commande, si l'on veut changer les valeurs de ces paramètres, il suffira d'ajouter -J{nom de la variable}={valeur de la variable}.

Par exemple.

other

 
Sélectionnez
1.
    jmeter -n -l resultats.csv -t scenario.jmx -JnbUnites=10 -JrampUp=20 -JnbIterations=100

Notre plan de test enfin complet.

Notre plan de test

IV-B. Exemple 2 : Preuve de faisabilité

Dans le tuning SQL, les index tiennent une bonne place, mais comme souvent il y a un coût. Pour démontrer ce coût, on va réaliser un POC (Proof Of Concept = Preuve de faisabilité) à l'aide de JMeter.

Cette fois-ci, notre test sera réalisé sur Oracle 11g Express Edition (ne pas oublier d'ajouter les drivers JDBC d'Oracle dans le ClassPath de JMeter).

Notre plan de test final ressemblera à celui-ci.

Notre plan de test final

Commençons par définir la connexion à Oracle à l'aide de l'élément Configuration de connexion JDBC.

Connexion à Oracle à l'aide de l'élément Configuration de connexion JDBC

Dorénavant, on peut se connecter à notre base de données, mais malheureusement elle est vide.

Résolvons ce problème grâce à l'élément Groupe d'unités de début qui va nous permettre d'exécuter des commandes au début du test.

Ajoutons-lui un élément Appel de processus système afin de remplir cette base de données (par le chargement d'une sauvegarde, par la création de données à l'aide d'outils ou de commandes SQL, etc.).

Afin d'être sûr que les statistiques de notre base de données sont à jour, nous allons demander à Oracle de le faire par la procédure SQL dbms_stats.gather_table_stats('SYSTEM','Clients',cascade=>TRUE).

Comme pour l'exemple précédent, on va utiliser l'élément Requête JDBC.

L'appel d'une procédure SQL pour Oracle avec cet élément se fait de la manière suivante.

Le type de requête SQL doit être à Callable Statement.

Le champ Requête doit être :

other

 
Sélectionnez
1.
2.
3.
    begin 
    {call proçedure SQL} 
    end;

Dans notre cas, on aura :

Requête JDBC

Ne pas oublier de tester la réponse avec un élément Assertion Réponse.

En cas d'erreur, Oracle retourne un code d'erreur commençant par ORA-

Assertion Réponse

Notre base de données est prête.

On veut que notre test tourne tant qu'il y a des index à créer.

Pour cela nous allons utiliser un élément Groupe d'unités dont le nombre d'itérations sera égal à l'infini et le nombre d'unités à 1.

Groupe d'unités

Les requêtes de création d'index seront dans un fichier CSV.

Et pour arrêter notre test à la fin du fichier CSV (et donc à la dernière création d'index), il suffira de le préciser dans l'élément Source de données CSV.

Source de données CSV

Maintenant, passons à l'exécution de nos requêtes SELECT. À l'aide de l'élément Contrôleur Boucle nous allons réaliser dix requêtes afin d'avoir des temps de réponse plus précis.

Contrôleur Boucle

Il suffit d'ajouter notre requête SELECT.

Ajout de notre requête SELECT

Faisons de même pour les UPDATE. Mais cette fois-ci nous allons utiliser des Prepared Update Statement comme type de requête.

Notre UPDATE

Il est temps de créer notre premier index automatiquement.

Encore une fois, nous utiliserons l'élément Requête JDBC.

La requête SQL de création des index sera directement récupérée à l'aide de la variable ${Create_Index_SQL} du fichier CSV défini précédemment.

Création des index

Ne pas oublier de mettre à jour les statistiques après la création de l'index.

Pour l'instant il est impossible d'analyser de manière fine le résultat du script, car il nous manque deux informations dans le fichier de résultat de JMeter.

La première information est le nombre d'index qui seront récupérés à l'aide d'un élément Echantillon BeanShell associé à la fonction __counter.

Calcul du nombre d'index créés

La deuxième information est la requête SQL de création de l'index et elle est déjà dans la variable ${Create_Index_SQL}.

Voilà qui est beaucoup mieux, mais si on récupère un fichier de résultat de l'exécution de notre test, ces deux informations n'y sont pas.

Pour les ajouter, il faut utiliser la propriété sample_variables du fichier properties de JMeter de la manière suivante :

other

 
Sélectionnez
1.
2.
3.
4.
    # Optional list of JMeter variable names whose values are to be saved in the result data files. 
    # Use commas to separate the names. For example: 

    sample_variables=iteration_number,Create_Index_SQL

Ceci conclut notre script de test.

IV-C. Exemple 3 : ETL

Dans ce dernier exemple, nous allons utiliser JMeter comme un ETL (Extract Transform Load) pour nous permettre de transférer des données d'une base de données à une autre en y appliquant des transformations.

En particulier on va anonymiser les colonnes nom et telephone_mobile d'une table Clients.

Dans un premier temps, définissons nos connexions aux deux bases de données à l'aide de l'élément Configuration de connexion JDBC.

Configuration de connexion JDBC*

Afin de générer un nouveau numéro de téléphone, utilisons l'élément Variable aléatoire :

Variable aléatoire

Pour le nouveau nom, nous utiliserons un élément Echantillon BeanShell avec la fonction :

other

 
Sélectionnez
1.
    ${__RandomString(20,ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz,nouveau_nom)}

Un nouveau nom de vingt caractères sera stocké dans la variable nouveau_nom :

Création d'un nom de manière aléatoire

Commençons par récupérer les valeurs dans la base de données source (dans notre cas on ne récupérera que certaines colonnes d'une table) à l'aide d'un élément Requête JDBC :

Récupération des valeurs dans la base de données source

Plusieurs lignes seront récupérées :

other

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
    id_client_1=1 
    id_client_2=2 
    id_client_3=3 
    id_client_4=4 

    ... 

    mail_1=florenceroberts@hotmail.com 
    mail_2=jennifer_lane@yahoo.com 
    mail_3=wramos@ldjle.org 

    ...

Maintenant, il faut parcourir chaque ligne récupérée avec l'élément Contrôleur Pour chaque (ForEach).

Contrôleur Pour chaque (ForEach)

Comme on peut le voir, on ne peut boucler que sur une variable (ici on a choisi id_client), on perd donc le lien avec les autres valeurs de la même ligne (prenom, mail, etc.).

Par exemple, id_client_1 est associé avec prenom_1, mail_1, sex_1 et salutation_1.

Heureusement, on peut facilement recréer le lien entre les variables de la même ligne avec l'élément Compteur et la fonction ${_V(xxx${yyyyy})}

Le compteur va nous permettre de générer un entier incrémenté de 1 à chaque itération de la boucle ForEach (on aura 1, puis 2, puis 3…).

Notre compteur

Puis, la fonction ${_V(xxx${yyy})} concaténera la chaîne de caractères xxx_ avec la valeur de la variable yyy.

Par exemple, ${_V(prenom${compteur})} retournera prenom_1 si compteur est égal à un.

Fonction qu'on utilisera dans notre requête SQL d'insertion dans la base de données cible.

Notre requête SQL d'insertion

Finalement, notre plan de test ressemblera à celui-ci.

Notre plan de test

V. Conclusion

Comme on a pu le voir, réaliser un test de charge d'une base de données à l'aide de JMeter est possible sans grande difficulté. Les possibilités de JMeter permettent en plus de rendre un test de charge réaliste, de réaliser à peu près tout ce qui vous passe par la tête.

VI. Remerciements

Cet article est une œuvre de Antonio Gomes Rodrigues, Bruno Demion et Philippe Mouawad, auteurs du livre Maîtriser JMeter : du test de charge à Devops.

Nous tenons à remercier FRANOUCH pour sa relecture attentive de cet article et Mickael Baron pour la mise au gabarit.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Antonio Gomes Rodrigues. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.