OpenIDM : purge des tables audit

Contexte

Dans un environnement client OpenIDM 3.1 en production depuis plusieurs mois, l'accès à l'écran d'administration des mappings met de plus en plus longtemps à s'afficher.

English Summary

It may help : if the access time to mapping administration form in OpenIDM becomes very slow, then check the numbers of lines in auditrecon table, and delete old records.

Analyse du problème

En regardant les échanges avec le serveur (via la console du navigateur), on voit que l'URL "endpoint/mappingDetails" est celle qui prend le plus de temps.
Ce endpoint appelle un script Javascript, ./bin/defaults/script/ui/mappingDetails.js, qui lance une requête SQL permettant de récupérer la date de dernière réconciliation, et ce pour chaque mapping :

  lastRecon = openidm.query("audit/recon", {
    "_queryId": "audit-last-recon-for-mapping",
    "mapping": m.name,
    "formatted": false
  });

En regardant dans le fichier conf/repo.jdbc.json, on peut récupérer le détail de la requête :

audit-last-recon-for-mapping" : "SELECT * FROM ${_dbSchema}.auditrecon WHERE entryType = 'start' 
AND mapping = ${mapping} and reconaction <> 'reconById' ORDER BY activitydate DESC LIMIT 1"

C'est cette requête qui pose problème, notamment car la table auditrecon n'a jamais été purgée.

Si on regarde le nombre de lignes par type de mapping, on peut avoir une idée de la volumétrie :

select mapping, count(1) from auditrecon group by mapping ;
+-------------------------------------+----------+
| mapping                             | count(1) |
+-------------------------------------+----------+
| AdOrganizationalUnit_Officelocation |     3956 |
| ldapFunctionnal_Orga                |   482874 |
+-------------------------------------+----------+
2 rows in set (3.36 sec)

On peut alors tester la requête lancée par le script mappingDetails.js :

SELECT * FROM auditrecon WHERE entryType = 'start' AND mapping = 'ldapFunctionnal_Orga' and reconaction <> 'reconById' ORDER BY activitydate DESC LIMIT 1 ;

Le nombre élevé s'explique car il s'agit d'une réconciliation lancée à intervalle régulier (toutes les 15 minutes), pour récupérer des objets de type 'organisation' : départements, fonctions, sections.
De ce fait, on arrive rapidement à un nombre élevé de lignes, sachant que chaque réconciliation traite près de 500 lignes, soit 4*24*500 = 48 000 lignes par jour !

On peut tenter d'en savoir un peu plus sur la volumétrie. Pour avoir la taille des tables :

SELECT table_name, table_rows as 'Rows #', data_length /1024 as 'Data size KB', index_length/1024 as 'Index size KB' , 
  round( (data_length + index_length) / 1024 / 1024,2 ) as 'Taille MB' 
  FROM information_schema.tables WHERE table_schema = 'openidm' AND table_name like 'audit%';

+---------------+--------+--------------+---------------+-----------+
| table_name    | Rows # | Data size KB | Index size KB | Taille MB |
+---------------+--------+--------------+---------------+-----------+
| auditaccess   |   6153 |    2080.0000 |     2144.0000 |      4.13 |
| auditactivity |    843 |   22544.0000 |     1056.0000 |     23.05 |
| auditrecon    | 449959 |  541952.0000 |   592448.0000 |   1107.81 |
| auditsync     |    797 |    7696.0000 |        0.0000 |      7.52 |
+---------------+--------+--------------+---------------+-----------+
4 rows in set (0.15 sec)

Note : s'agissant de tables utilisant le moteur InnoDB, le nombre de lignes donnés par cette requête peut varier. Il vaut mieux utiliser un SELECT COUNT pour avoir le nombre réel de lignes. La volumétrie en KB, par contre, semble stable.

Epuration des tables d'audit

Il existe une tâche programmée qui permet de lancer un nettoyage régulier des tables d'audit. Sur une installation OpenIDM 3.1, le fichier de définition se trouve dans samples/schedules/schedule-autoPurgeAuditRecon.json :

{
    "enabled" : false,
    "type" : "cron",
    "schedule" : "0 0 */12 * * ?",
    "persisted" : true,
    "misfirePolicy" : "doNothing",
    "invokeService" : "script",
    "invokeContext" : {
        "script" : {
            "type" : "text/javascript",
            "file" : "audit/autoPurgeAuditRecon.js",
            "input" : {
                "mappings" : [ "%" ],
                "purgeType" : "purgeByNumOfReconsToKeep",
                "numOfRecons" : 1,
                "intervalUnit" : "minutes",
                "intervalValue" : 1
            }
        }
    }
}

Dans la configuration par défaut, on limite à un nombre fini de réconciliations. Si on veut garder un nombre de jours limité, il faut modifier la définition pour utiliser le type de purge purgeByExpired :

{
    "enabled" : true,
    "type" : "cron",
    "schedule" : "0 0 22 * * ?",
    "persisted" : true,
    "misfirePolicy" : "doNothing",
    "invokeService" : "script",
    "invokeContext" : {
        "script" : {
            "type" : "text/javascript",
            "file" : "audit/autoPurgeAuditRecon.js",
            "input" : {
                "mappings" : [
                    "%"
                ],
                "purgeType" : "purgeByExpired",
                "numOfRecons" : 1,
                "intervalUnit" : "days",
                "intervalValue" : 30
            }
        }
    }
}

Le souci étant dans ce cas que, puisqu'on n'a jamais lancé de purge, la requête tombe en time-out...

Dans ce cas, On peut aussi utiliser une méthode plus radicale, consistant à supprimer les lignes dans la table, de la manière suivante :

DELETE FROM auditaccess WHERE activitydate < 'YYYY-MM-DD' ;

On peut générer les instructions avec un script shell, qui va calculer la date (ce qui est plus rapide que d'utiliser les fonctions MySQL) :

#!/bin/bash
# -----------------------------
# Purge OpenIDM audit database
# -----------------------------
#
# Please stop openidm first, and there remove felix-cache content before restarting
#-----------------------------------------------------------------------------------

# Set data retention period here 
LongTimeAgo=$(date -d 'now -6 months' +'%Y-%m-%d')

echo "DELETE FROM auditaccess WHERE activitydate < '$LongTimeAgo' ;" > /tmp/purge.sql
echo "DELETE FROM auditactivity WHERE activitydate < '$LongTimeAgo' ;" >> /tmp/purge.sql
echo "DELETE FROM auditrecon WHERE activitydate < '$LongTimeAgo' ;" >> /tmp/purge.sql
echo "DELETE FROM auditsync WHERE activitydate < '$LongTimeAgo' ;" >> /tmp/purge.sql

# Ou utiliser la commande mysql en ligne 

echo "SELECT table_name, data_length /1024 AS 'Data size KB',  index_length/1024 AS 'Index size KB' , round( (data_length + index_length) / 1024 / 1024,2 ) AS 'Taille MB' FROM information_schema.tables WHERE table_schema = 'openidm' and table_name like 'audit%'; " >> /tmp/purge.sql

echo "exit" >> /tmp/purge.sql

SQLUSER=openidm
SQLPWD=openidm
SQLDB=openidm
DBHOST=db.mydomain.com
DBPORT=3306

mysql -h ${DBHOST} -P ${DBPORT} -u ${SQLUSER} -p${SQLPWD} ${SQLDB} < /tmp/purge.sql

#
# --- End of script
#

Il ne reste plus qu'à lancer le script shell, de manière réguliere (une fois par semaine par exemple), pour limiter le nombre de lignes dans la table, et retrouver un temps de réponse correct lorsqu'on passe par l'écran de gestion des mappings

En conclusion

Si l'accès à la page Mappings de la console d'administration OpenIDM est de plus en plus long, vérifier le nombre de lignes dans la table auditrecon, et lancer régulièrement une purge de cette table, soit via le schedule-autoPurgeAuditRecon, soit directement en SQL.

Catégorie