Maintenir un site Dnn performant au fil du temps
Auteur : Sébastien Fichot
Date de pulication : 20 October 2010
Article consulté 2170 fois

(5 votes)
Le symptôme le plus significatif d'un site DotNetNuke mis en place depuis plusieurs années et non maintenu est la lenteur : lenteur sur le recherche, lenteur au premier chargement, lenteur à heures fixes sans trop savoir pourquoi, etc. C'est assez rageant quand on voit la rapidité avec laquelle s’exécute une instance DotNetNuke au début de sa vie, et incompréhensible du côté de la DSI qui a employé des méthodes radicales et coûteuses (nouveau serveur Web).
Pour rendre un site un peu vieillissant plus rapide, il existe cependant des méthodes un peu plus compréhensives du budget qui s'appuient sur des constatations simples. Nous les aborderons dans cet article comme une liste de choses à faire pour que votre site continue de bien fonctionner au fil du temps. Bien entendu, un nouveau serveur Web aide aussi ;)
Mettre à jour DotNetNuke
DotNetNuke s'améliore constamment, et son architecture logicielle s'affine un peu plus chaque jour. Pour profiter des améliorations, il faut appliquer régulièrement les mises à jour. Pour découvrir comment réaliser une mise à jour, vous pouvez lire l'article
Mettre à jour DotNetNuke. Vous pouvez aussi préférer certaines solutions automatiques comme
Papayas DNNAutoUpgrade.
Sur la page d'accueil du site, vous pouvez aussi prendre connaissance en temps réel de la publication d'une nouvelle version. Il vous suffit de regarder l'encart de droite qui affiche la dernière version, synchronisé depuis un service CodePlex sur la publication des releases par la Core Team.
Conserver le site en mémoire
Chaque fois qu'un appel est fait sur votre site, DotNetNuke interroge le
cache IIS afin de savoir si il doit générer le contenu ou utiliser celui déjà généré et stocké en mémoire. Ce processus évite la re-génération du contenu similaire à un grand nombre de visiteurs.
Ce cache est cependant vidé à chaque fois que IIS recycle sa mémoire (
Paramètres du pool applicatif).
DotNetNuke ajoute sur à cela un cache basé sur un autre modèle, permettant d'avoir à générer deux fois le même contenu
encore moins souvent.
Le recyclage de la mémoire est bon pour le serveur Web, il libère de la ressource pour les autres services, et si il a swappé une partie de sa mémoire, il libère du disque et permet la défragmentation de zones autrement constamment occupées.
Seulement, le premier utilisateur visitant le site après le recyclage subit un chargement dû au redémarrage de l'application, parfois long de plusieurs secondes (Certains rapportent de 11 à 25 secondes). Pour éviter le désagrément sans perdre les avantages du recyclage mémoire, Anton Bursev a développé
un petit service IIS qui va réveiller l'application après son recyclage (Keep It Warm). Le procédé est assez similaire au
PaceMaker réalisé par Jean-Sylvain Boige pour Dnn 4.
La mémoire est recyclée, et le premier utilisateur ne subi pas de chargement.
Pour utiliser la solution, suivez simplement le How-to présenté sur la page CodePlex du service. Le Service est spécialement conçu pour l'hébergement DotNetNuke, puisqu'il ne va pas simplement réveiller l'hôte du portail, mais aussi chacun des portails pères ou enfants installés sur l'hôte, et les mettre en mémoire :
Keep Alive Service For IIS 6.0/7.5. Notons également que le service réveillera tous les autres types de site web présents sur le serveur Web, quelque soit la technologie utilisée (ASP.NET, PHP, CGI, ...).
Réduire le nombre de tâches planifiées exécutées le jour (au profit de la nuit)
Une tâche planifiée est une routine pouvant s’exécuter à intervalle fixe sur programmation d'un hôte (Pour voir comment en fabriquer vous-même,
cliquez ici) . Les tâches sont insérées les unes après les autres dans un thread séparé du thread de l'hôte DotNetNuke principal (Aka. : Celui qui sert les visiteurs), et s'y exécutent tant que la tâche n'est pas terminée. Cela permet au thread principal de rester disponible pour les visiteurs, et servir les pages même si une indexation ou une autre tâche lourde s’exécutent en fond.
Seulement parfois une tâche planifiée a un objectif si vaste, que son impact sur le serveur SQL est mesurable par les visiteurs. D'autre fois, pour des besoins de sauvegarde, il faut pouvoir déterminer avec une certaine exactitude le laps de temps dans lequel la tâche planifiée s’exécute.
Voici les rudiments qui vous permettront d’
exécuter la nuit ce que le jour ne saurait tolérer.
Tout d'abord, configurez le planificateur de tâches pour que son exécution soit en mode "Automatique" depuis la page
Hôte > Paramètres de l'hôte > Paramètres avancés > Autres paramètres, et enregistrez la configuration. Le mode automatique, ou mode "Timer", permet de définir des tâches exécutées à des heures où aucun visiteur ne visite le site (la nuit). Il existe deux autres modes : Le mode "Utilisateur" ou "Méthode de requête", qui exécute ses actions lorsqu'un visiteur visite le site, et le mode "Désactivé", qui aboli le système.
Rendez-vous ensuite sur le centre de gestion des tâches planifiées :
Hôte > Planificateur.
A partir de cet écran, configurez toutes les tâches planifiées de votre choix afin qu'elles s’exécutent "chaque 1 jour".
Prenons l’exemple de la tâche planifiée nommée "Purge Users Online". Indépendamment de la fonction de cette tâche planifiée (Effacer le cache mémoire comptant les utilisateurs en ligne), voyons quelle micro-configuration adopter pour cette tâche :
- Nom : Le nom de la tâche planifiée. Peut être changé comme bon vous semble.
- Planifiée : Indique si la tâche planifiée est active. Vous pouvez activer / désactiver une tâche sans avoir besoin de l'ajouter / supprimer à chaque fois.
- Récurrence : Cette valeur nous intéresse particulièrement, puisqu'elle permet de définir le laps de temps entre les exécutions. Exemple: "5" et "Minutes" pour lancer la tâche toutes les 5 minutes. Laissez vide pour désactiver la temporisation, et dans notre cas, définissez sur 1 jour.
- Nouvelle tentative : Cette valeur permet de définir le laps de temps à laisser écouler si l’exécution mène à une erreur. Exemple : "5" et "Minutes" pour tenter de relancer la tâche toutes les 5 minutes. Cette valeur dépend évidemment de ce que fait la tâche planifiée. Laissez la valeur par défaut si vous avez un doute.
- Démarrage sur : Cette valeur permet de définir l’évènement sur lequel la tâche lancera son éxécution. Exemple : sélectionnez "APPLICATION_START" pour démarrer cet évènement au moment du lancement de l'application web. Dans notre cas, laissez le choix sur "Aucune".
- Catch Up actif : Si cette option est cochée, en cas d'indisponibilité du serveur, l'évènement sera lancé une seule fois pour toutes les planifications n'ayant pas été lancées durant la période d'indisponibilité. Cela dépend encore de la fonction finale de la tâche planifiée, mais pour avoir une vraie exécution à heure fixe, je vous recommande d'activer le ketchup.
Les autres éléments de configuration ne nous importent pas ou peu pour la programmation des tâches.
Au final, vous devriez obtenir une liste similaire à celle-ci :
Vous pourriez vouloir changer la fréquence d’exécution des "petites" tâches, comme "Purge Users Online", qui remet à zéro le nombre d'utilisateurs connectés, ou encore "Send Log Notifications" qui, si vous avez spécifié un email dans Site > Observateur d’évènement, vous permet d'envoyer les erreurs du site par courriel. Libre à vous d'en décider ainsi.
Et maintenant, le rôle de l'administrateur du site web devient rigolo : A l'heure de la nuit où vous souhaitez que les tâches s’exécutent, levez vous de votre lit, connectez vous, rendez-vous sur Hôte > Planificateur, éditez chacune des tâches planifiées et cliquez sur "Exécuter maintenant". La première exécution fixera automatiquement l'heure d’exécution des tâches suivantes. Vous pourriez aussi vouloir les exécuter dans un ordre spécifique ... Personnellement, j'ai utilisé une Macro avec un plugin Firefox qui a exécuté tout cela pour moi. Ça peut aussi être un WebTest depuis Visual Studio, à vous de voir.
Le lendemain, en accédant à l'historique de la première et de la dernière tâche planifiée lancée la veille, vous devriez pouvoir déterminer le temps d’exécution des tâches planifiées, et donc, ajuster votre Backup automatique, par exemple.
Réduire la taille des images
Saviez-vous que les images que vous chargez peuvent être optimisées pour leur diffusion sur Internet ? Une image JPEG affichée en page d'accueil peut rallonger de plusieurs secondes l'affichage de votre site ! Il existe encore une fois beaucoup de solutions proposant ce service. Je vous propose de découvrir
Smush It qui, à l'aide d'un plugin Firefox, va rendre plus simple une bonne partie des traitements.
Installez l'extension depuis Firefox en recherchant
YSlow dans le menu Extensions. (
Outils > Modules complémentaires > Catalogue)
(Ne cherchez pas l'extension pour Chrome, elle n'existe pas. Utilisez plutôt le service directement depuis le site web :
http://www.smushit.com).
Le service est maintenant encapsulé dans les outils développeurs de Yahoo!,
YSlow.
Redémarrez Firefox, puis affichez votre site. En bas à droite de l'écran, cliquez sur le boutton
YSlow.
Puis cliquez sur l'onglet "Tools", puis "All Smush It".
Après avoir cliqué, un second onglet s'ouvre, et vous pouvez voir le nombre de kilos gagnés sur l'optimisation des images avec une meilleure compression.
Cliquez sur "Download Smushed Images".Vous récupérez un fichier ZIP, que vous décompressez quelque part. Après avoir optimisé toutes les pages (images) de votre site, remplacez les dans leur dossier d'origine sur le serveur Web. Le gain est considérable : plusieurs méga-octets d'économisés sur certains sites, sans perdre en qualité d'image.
Placer les images sur un autre nom de domaine (et modifier RadEditor pour qu'il incorpore la solution)
La plupart des navigateurs autorisent 6 connexions simultanées sur un nom de domaine pour télécharger les ressources en parallèle. Certains ne vont autoriser que 2 connexions simultanées. Si vous placez les images sur un autre nom de domaine, votre site s'affiche plus rapidement. Pour ce faire, vous pouvez créer un sous-domaine de votre nom de domaine (Ex. : images.nomdedomaine.com) et le faire pointer vers votre serveur, puis demander à IIS de rediriger les requêtes vers un dossier spécifique, le root de votre site web.
Modifiez aussi RadEditor pour qu'il accepte de changer le nom de domaine à l'insertion d'une image en suivant la procédure ci-dessous :
Recherchez sur votre server Web le fichier "ImageManager.ascx". Editez le avec Visual Studio par exemple.
A la ligne 233, remplacez
newImage.src = this._currentItem.get_url();
par
newImage.src = "http://images.monsite.com/" + this._currentItem.get_url();
où "images" constitue le sous domaine sur lequel se trouve les images.
Par exemple, si votre domaine est www.monsite.com, et vos images sur mesimages.monsite.com, vous aurez :
newImage.src = "http://mesimages.monsite.com/" + this._currentItem.get_url();
Si votre domaine est www.monsite.com, et vos images sur mesimages.MonAutreNomDeDomaine.com, vous aurez :
newImage.src = "http://mesimages.MonAutreNomDeDomaine.com/" + this._currentItem.get_url();
L'important reste que votre nom de domaine pointe bien vers le bon site.
Enregistrez le fichier, et testez l'insertion d'une image. L'URL insérée devrait utiliser le nom de domaine spécifié.
Note : Si vous utilisez SSL, il vous faudra un nouveau certificat SSL Single-host ou un certificat Wildcard sur le sous domaine.
Identifier les tables lourdes
"La taille des données contenue dans la base de donnée affecte directement sa capacité à traiter rapidement l'appel à ces données."
De fait, il semble important d'identifier rapidement quelles données sont stockées dans la table, et de déterminer leur nécessite.
Le script suivant va vous indiquer
quelles sont les tables contenant le plus de données. Idéalement, ce script est lancé à intervalle régulier et mène à des comparaisons afin de suivre l'évolution de la base et prévoir de nouvelles actions (Merci à
Greg Robidoux pour le script).
BEGIN try
DECLARE @table_name VARCHAR(500) ;
DECLARE @schema_name VARCHAR(500) ;
DECLARE @tab1 TABLE(
tablename VARCHAR (500) collate database_default
, schemaname VARCHAR(500) collate database_default
);
DECLARE @temp_table TABLE (
tablename sysname
, row_count INT
, reserved VARCHAR(50) collate database_default
, data VARCHAR(50) collate database_default
, index_size VARCHAR(50) collate database_default
, unused VARCHAR(50) collate database_default
);
INSERT INTO @tab1
SELECT t1.name
, t2.name
FROM sys.tables t1
INNER JOIN sys.schemas t2 ON ( t1.schema_id = t2.schema_id );
DECLARE c1 CURSOR FOR
SELECT t2.name + '.' + t1.name
FROM sys.tables t1
INNER JOIN sys.schemas t2 ON ( t1.schema_id = t2.schema_id );
OPEN c1;
FETCH NEXT FROM c1 INTO @table_name;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @table_name = REPLACE(@table_name, '[','');
SET @table_name = REPLACE(@table_name, ']','');
-- make sure the object exists before calling sp_spacedused
IF EXISTS(SELECT OBJECT_ID FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(@table_name))
BEGIN
INSERT INTO @temp_table EXEC sp_spaceused @table_name, false ;
END
FETCH NEXT FROM c1 INTO @table_name;
END;
CLOSE c1;
DEALLOCATE c1;
SELECT t1.*
, t2.schemaname
FROM @temp_table t1
INNER JOIN @tab1 t2 ON (t1.tablename = t2.tablename )
ORDER BY schemaname,tablename;
END try
BEGIN catch
SELECT -100 AS l1
, ERROR_NUMBER() AS tablename
, ERROR_SEVERITY() AS row_count
, ERROR_STATE() AS reserved
, ERROR_MESSAGE() AS data
, 1 AS index_size, 1 AS unused, 1 AS schemaname
END catch
Si dans votre modèle métier certaines tables sont conséquemment lourdes, vous devriez
considérer leurs accès avec précaution (Cache, Indexes, Nolock, Types de SELECT, etc.). Pour le modèle Core par défaut, la table la plus lourde reste bien souvent la table EventLogs sur un site à fort traffic, car DotNetNuke, très intelligemment, stocke dans cette table toutes les interactions des visiteurs et les organise par type. D'autres modules stockent tout ce qui se passe et deviennent vite encombrants (DMX de
Bring2Mind par exemple).
Voilà un exemple de résultat retourné par le script dont j'ai masqué le nom des tables :
|
tableName
|
row_count
|
reserved
|
data
|
index_size
|
unused
|
schemaname
|
|
Table 1
|
17
|
48 KB
|
24 KB
|
24 KB
|
0 KB
|
dbo
|
|
Table 2
|
1478462
|
788560 KB
|
788528 KB
|
8 KB
|
24 KB
|
dbo
|
|
Table 3
|
12023
|
1160 KB
|
1096 KB
|
8 KB
|
56 KB
|
dbo
|
|
Table 4
|
12024
|
1224 KB
|
1160 KB
|
8 KB
|
56 KB
|
dbo
|
|
Table 5
|
14314
|
1608 KB
|
1552 KB
|
8 KB
|
48 KB
|
dbo
|
|
Table 6
|
12559
|
1288 KB
|
1256 KB
|
8 KB
|
24 KB
|
dbo
|
|
Table 7
|
12773
|
1288 KB
|
1280 KB
|
8 KB
|
0 KB
|
dbo
|
|
Table 8
|
13067
|
1352 KB
|
1304 KB
|
8 KB
|
40 KB
|
dbo
|
|
Table 9
|
12688
|
1736 KB
|
1696 KB
|
8 KB
|
32 KB
|
dbo
|
|
Table 10
|
463441
|
60744 KB
|
60720 KB
|
8 KB
|
16 KB
|
dbo
|
|
Table 11
|
1812
|
1160 KB
|
1136 KB
|
8 KB
|
16 KB
|
dbo
|
|
Table 12
|
266
|
40 KB
|
16 KB
|
24 KB
|
0 KB
|
dbo
|
|
Table 13
|
19836
|
6600 KB
|
6544 KB
|
8 KB
|
48 KB
|
dbo
|
|
Table 14
|
522786
|
39112 KB
|
39088 KB
|
8 KB
|
16 KB
|
dbo
|
|
Table 15
|
5
|
32 KB
|
8 KB
|
24 KB
|
0 KB
|
dbo
|
|
Table 16
|
479530
|
87240 KB
|
87224 KB
|
8 KB
|
8 KB
|
dbo
|
|
Table 17
|
0
|
0 KB
|
0 KB
|
0 KB
|
0 KB
|
dbo
|
|
Table 18
|
106300
|
21584 KB
|
19184 KB
|
2304 KB
|
96 KB
|
dbo
|
|
Table 19
|
178923
|
33168 KB
|
29088 KB
|
3776 KB
|
304 KB
|
dbo
|
|
Table 20
|
182361
|
31624 KB
|
31600 KB
|
8 KB
|
16 KB
|
dbo
|
|
Table 21
|
1510703
|
409136 KB
|
303696 KB
|
102264 KB
|
3176 KB
|
dbo
|
|
Table 22
|
1517593
|
305736 KB
|
305176 KB
|
8 KB
|
552 KB
|
dbo
|
|
Table 23
|
1270456
|
319176 KB
|
319112 KB
|
8 KB
|
56 KB
|
dbo
|
|
Table 24
|
77424
|
9352 KB
|
9328 KB
|
8 KB
|
16 KB
|
dbo
|
|
Table 25
|
12023
|
1480 KB
|
1424 KB
|
8 KB
|
48 KB
|
dbo
|
|
Table 26
|
0
|
0 KB
|
0 KB
|
0 KB
|
0 KB
|
dbo
|
|
Table 27
|
34188
|
6152 KB
|
6080 KB
|
8 KB
|
64 KB
|
dbo
|
|
Table 28
|
24049
|
4232 KB
|
4168 KB
|
8 KB
|
56 KB
|
dbo
|
|
Table 29
|
1116
|
704 KB
|
240 KB
|
8 KB
|
456 KB
|
dbo
|
|
Table 30
|
21
|
16 KB
|
8 KB
|
8 KB
|
0 KB
|
dbo
|
|
Table 31
|
19
|
16 KB
|
8 KB
|
8 KB
|
0 KB
|
dbo
|
|
Table 32
|
645484
|
99792 KB
|
36048 KB
|
62320 KB
|
1424 KB
|
dbo
|
|
Table 33
|
121
|
32 KB
|
8 KB
|
24 KB
|
0 KB
|
dbo
|
|
Table 34
|
17080
|
2760 KB
|
2272 KB
|
368 KB
|
120 KB
|
dbo
|
|
Table 35
|
451358
|
59400 KB
|
59392 KB
|
8 KB
|
0 KB
|
dbo
|
|
Table 36
|
7563
|
1608 KB
|
1568 KB
|
8 KB
|
32 KB
|
dbo
|
|
Table 37
|
435536
|
57160 KB
|
57088 KB
|
8 KB
|
64 KB
|
dbo
|
|
Table 38
|
4268
|
1040 KB
|
872 KB
|
136 KB
|
32 KB
|
dbo
|
On peut voir que les efforts devront se concentrer sur "Table 2", "Table 21", "Table 22", "Table 23", et les vues, fonctions, indexes et procédures stockées associées.
Une fois votre analyse terminée et avant de supprimer les données, vous pourrez sauvegarder vers des dumps l'ensemble de la base de données via la commande
SQL Management Studio > Tâches > Sauvegarder ou à l'aide du script suivant :
BACKUP DATABASE [Inscrivez ici le nom de la base de données]
TO DISK = N'Inscrivez ici le nom complet du fichier. Ex : C:\Sauvegarde.bak'
WITH NOFORMAT
, NOINIT
, NAME = N'Inscrivez ici le nom interne du jeu de sauvegarde. Ex : MaBase-Backup2010'
, SKIP
, NOREWIND
, NOUNLOAD
, STATS = 30
GO
Effacer les logs du site
Pour effacer les logs, connectez-vous en Admin ou Hôte, et rendez-vous dans
Admin >
Observateur d’évènements, puis cliquez sur
Effacer les logs. Il n'est pas rare de trouver plusieurs centaines de milliers de logs inutiles dans cette zone là.
Certaines sociétés auront cependant l'obligation légale de conserver certains logs, comme les connexions au site. Pour ce faire, le script suivant va vous permettre de supprimer les logs les moins importants et de conserver en base ceux dont vous aurez choisi le type.
DELETE FROM dbo.EventLog
WHERE LogTypeKey NOT IN ('LogTypeKey de votre choix','Deuxième LogTypeKey de votre choix','et ainsi de suite')
Pour composer vous même votre "package" de logs à conserver, voici le détail des LogTypeKey (à gauche) que vous pouvez saisir, ainsi que leur description (à droite) :
|
ADMIN_ALERT
|
Admin Alert
|
|
APPLICATION_END
|
Application Ended
|
|
APPLICATION_SHUTTING_DOWN
|
Application Shutting Down
|
|
APPLICATION_START
|
Application Started
|
|
AUTHENTICATION_CREATED
|
Authentication system created
|
|
AUTHENTICATION_DELETED
|
Authentication system deleted
|
|
AUTHENTICATION_UPDATED
|
Authentication system updated
|
|
AUTHENTICATION_USER_CREATED
|
User authentication added
|
|
AUTHENTICATION_USER_DELETED
|
User authentication deleted
|
|
AUTHENTICATION_USER_UPDATED
|
User authentication updated
|
|
CACHE_DEPENDENCYCHANGED
|
Cache Item Dependency Changed
|
|
CACHE_ERROR
|
Cache Error
|
|
CACHE_EXPIRED
|
Cache Item Expired
|
|
CACHE_OVERFLOW
|
Cache Item Overflow
|
|
CACHE_REFRESH
|
Cache Refresh
|
|
CACHE_REMOVED
|
Cache Item Removed
|
|
CACHE_UNDERUSED
|
Cache Item Underused
|
|
DEBUG
|
Debug Info
|
|
DESKTOPMODULE_CREATED
|
Desktop module created
|
|
DESKTOPMODULE_DELETED
|
Desktop module deleted
|
|
DESKTOPMODULE_UPDATED
|
Desktop module updated
|
|
DESKTOPMODULEPERMISSION_CREATED
|
Desktop module permission created
|
|
DESKTOPMODULEPERMISSION_DELETED
|
Desktop module permission deleted
|
|
DESKTOPMODULEPERMISSION_UPDATED
|
Desktop module permission updated
|
|
FOLDER_CREATED
|
Folder created
|
|
FOLDER_DELETED
|
Folder deleted
|
|
FOLDER_UPDATED
|
Folder updated
|
|
GENERAL_EXCEPTION
|
General Exception
|
|
HOST_ALERT
|
Host Alert
|
|
HOST_SETTING_CREATED
|
Host setting created
|
|
HOST_SETTING_DELETED
|
Host setting deleted
|
|
HOST_SETTING_UPDATED
|
Host setting updated
|
|
LANGUAGE_CREATED
|
Language created
|
|
LANGUAGE_DELETED
|
Language deleted
|
|
LANGUAGE_UPDATED
|
Language updated
|
|
LANGUAGEPACK_CREATED
|
Language pack created
|
|
LANGUAGEPACK_DELETED
|
Language pack deleted
|
|
LANGUAGEPACK_UPDATED
|
Language pack updated
|
|
LANGUAGETOPORTAL_CREATED
|
Language created on portal
|
|
LANGUAGETOPORTAL_DELETED
|
Language deleted on portal
|
|
LANGUAGETOPORTAL_UPDATED
|
Language updated on portal
|
|
LISTENTRY_CREATED
|
List entry created
|
|
LISTENTRY_DELETED
|
List entry deleted
|
|
LISTENTRY_UPDATED
|
List entry updated
|
|
LOG_NOTIFICATION_FAILURE
|
Log Notification Failure
|
|
LOGIN_FAILURE
|
Login Failure
|
|
LOGIN_SUCCESS
|
Login Success
|
|
LOGIN_SUPERUSER
|
Login - Superuser
|
|
LOGIN_USERLOCKEDOUT
|
User Locked Out
|
|
LOGIN_USERNOTAPPROVED
|
User Not Approved
|
|
MODULE_CREATED
|
Module Created
|
|
MODULE_DELETED
|
Module Deleted
|
|
MODULE_LOAD_EXCEPTION
|
Module Load Exception
|
|
MODULE_RESTORED
|
Module Restored
|
|
MODULE_SENT_TO_RECYCLE_BIN
|
Module Sent to Recycle Bin
|
|
MODULE_SETTING_CREATED
|
Module setting created
|
|
MODULE_SETTING_DELETED
|
Module setting deleted
|
|
MODULE_SETTING_UPDATED
|
Module setting updated
|
|
MODULE_UPDATED
|
Module Updated
|
|
PACKAGE_CREATED
|
Package created
|
|
PACKAGE_DELETED
|
Package deleted
|
|
PACKAGE_UPDATED
|
Package updated
|
|
PAGE_LOAD_EXCEPTION
|
Page Load Exception
|
|
PASSWORD_SENT_FAILURE
|
Password Sent Failure
|
|
PASSWORD_SENT_SUCCESS
|
Password Sent Success
|
|
PERMISSION_CREATED
|
Permission created
|
|
PERMISSION_DELETED
|
Permission deleted
|
|
PERMISSION_UPDATED
|
Permission updated
|
|
PORTAL_CREATED
|
Portal Created
|
|
PORTAL_DELETED
|
Portal Deleted
|
|
PORTAL_SETTING_CREATED
|
Portal setting created
|
|
PORTAL_SETTING_DELETED
|
Portal setting deleted
|
|
PORTAL_SETTING_UPDATED
|
Portal setting updated
|
|
PORTALALIAS_CREATED
|
Portal alias created
|
|
PORTALALIAS_DELETED
|
Portal alias deleted
|
|
PORTALALIAS_UPDATED
|
Portal alias updated
|
|
PORTALDESKTOPMODULE_CREATED
|
Portal desktop module created
|
|
PORTALDESKTOPMODULE_DELETED
|
Portal desktop module deleted
|
|
PORTALDESKTOPMODULE_UPDATED
|
Portal desktop module updated
|
|
PORTALINFO_CREATED
|
Portal info created
|
|
PORTALINFO_DELETED
|
Portal info deleted
|
|
PORTALINFO_UPDATED
|
Portal info updated
|
|
PROFILEPROPERTY_CREATED
|
Profile property created
|
|
PROFILEPROPERTY_DELETED
|
Profile property deleted
|
|
PROFILEPROPERTY_UPDATED
|
Profile property updated
|
|
ROLE_CREATED
|
Role Created
|
|
ROLE_DELETED
|
Role Deleted
|
|
ROLE_UPDATED
|
Role Updated
|
|
SCHEDULE_CREATED
|
Schedule created
|
|
SCHEDULE_DELETED
|
Schedule deleted
|
|
SCHEDULE_FIRED_FROM_EVENT
|
Event Schedule Started
|
|
SCHEDULE_UPDATED
|
Schedule updated
|
|
SCHEDULER_EVENT_COMPLETED
|
Scheduler Event Completed
|
|
SCHEDULER_EVENT_FAILURE
|
Scheduler Event Failure
|
|
SCHEDULER_EVENT_PROGRESSING
|
Scheduler Event Progressing
|
|
SCHEDULER_EVENT_STARTED
|
Scheduler Event Started
|
|
SCHEDULER_EXCEPTION
|
Scheduler Exception
|
|
SCHEDULER_SHUTTING_DOWN
|
Scheduler Shutting Down
|
|
SCHEDULER_STARTED
|
Scheduler Started
|
|
SCHEDULER_STOPPED
|
Scheduler Stopped
|
|
SEARCH_INDEXER_EXCEPTION
|
Search Indexer Exception
|
|
SKINCONTROL_CREATED
|
Skin control created
|
|
SKINCONTROL_DELETED
|
Skin control deleted
|
|
SKINCONTROL_UPDATED
|
Skin control updated
|
|
SKINPACKAGE_CREATED
|
Skin package created
|
|
SKINPACKAGE_DELETED
|
Skin package deleted
|
|
SKINPACKAGE_UPDATED
|
Skin package updated
|
|
TAB_CREATED
|
Tab Created
|
|
TAB_DELETED
|
Tab Deleted
|
|
TAB_ORDER_UPDATED
|
Tab order updated
|
|
TAB_RESTORED
|
Tab Restored
|
|
TAB_SENT_TO_RECYCLE_BIN
|
Tab Sent to Recycle Bin
|
|
TAB_UPDATED
|
Tab Updated
|
|
TABMODULE_CREATED
|
Tab module created
|
|
TABMODULE_DELETED
|
Tab module deleted
|
|
TABMODULE_SETTING_CREATED
|
Tab module setting created
|
|
TABMODULE_SETTING_DELETED
|
Tab module setting deleted
|
|
TABMODULE_SETTING_UPDATED
|
Tab module setting updated
|
|
TABMODULE_UPDATED
|
Tab module updated
|
|
TABPERMISSION_CREATED
|
Tab permission created
|
|
TABPERMISSION_DELETED
|
Tab permission deleted
|
|
TABPERMISSION_UPDATED
|
Tab permission updated
|
|
USER_CREATED
|
New User
|
|
USER_DELETED
|
User Deleted
|
|
USER_ROLE_CREATED
|
User Role Created
|
|
USER_ROLE_DELETED
|
User Role Deleted
|
|
USER_ROLE_UPDATED
|
User Role Updated
|
|
USER_UPDATED
|
User updated
|
Ainsi, si vous souhaitez effacer la liste des logs en excluant les logs qui concernent les utilisateurs, vous exécuterez :
DELETE
FROM dbo.EventLog
WHERE LogTypeKey NOT IN (
'LOGIN_FAILURE'
, 'LOGIN_SUCCESS'
, 'LOGIN_SUPERUSER'
, 'LOGIN_USERLOCKEDOUT'
, 'LOGIN_USERNOTAPPROVED'
, 'PASSWORD_SENT_FAILURE'
, 'PASSWORD_SENT_SUCCESS'
, 'USER_CREATED'
, 'USER_DELETED'
, 'USER_UPDATED'
)
Forcer la mise à jour de l'index du moteur de recherche
Je me suis penché sur la question car dans le cadre d'une procédure de maintenance nocturne à heure fixe, nos processus de backup prenaient plus de temps que prévu et parfois notre index de recherche était incomplet sur plusieurs jours. Il s'avère que notre tâche planifiée mettant à jour l'index de recherche du site s'est soudain mise à durer beaucoup plus longtemps que prévu. Censée durer 2 heures maximum, elle nécessitait alors 4 à 5 heures. Cherchant la cause possible d'un tel cafouillage, je me suis aperçu que rien ne vaut une remise à zéro des indexes une fois de temps en temps. L'index est de toute façon régénéré à chaque fois... mais le vider permet d'épargner la comparaison entre l'ancien index et le nouvel index réalisé par DotNetNuke : Si un mot est indexé, alors il le met à jour, sinon, il le créé. Dans notre cas, la création est beaucoup plus rapide que la mise à jour. Voici donc un script SQL qui peut être joué avant chaque mise à jour de l'index.
La mise à jour de l'index de recherche constitue une tâche planifiée (Hôte > Tâches planifiées ou Host > Schedule). Vous trouverez donc dans ce menu plus d'information si vous souhaitez mettre en place un job routinier.
Le modèle de données dédié à la recherche dans DotNetNuke est le suivant (peut être retrouvé sur
le modèle de données en ligne) :
Pour exécuter le script, rendez-vous sur le menu Hôte > SQL, cochez "Lancer un script", puis avant de cliquer sur "Exécuter", collez dans la zone de texte le script suivant :
TRUNCATE TABLE {databaseOwner}{objectQualifier}SearchItemWordPosition
DELETE {databaseOwner}{objectQualifier}SearchItemWord
DELETE {databaseOwner}{objectQualifier}SearchWord
DELETE {databaseOwner}{objectQualifier}SearchItem
Défragmenter les fichiers du site, les fichiers de la base de données et ses indexes
La fragmentation des fichiers d'un disque est un phénomène naturel qui relève l'âge des fichiers. Les séquences des fichiers sont placées puis déplacées et finalement le disque dur doit accéder à plusieurs endroits pour lire un seul et même fichier. Pour y remédier, il existe le défragmenteur de fichiers de Windows, qui fonctionne très bien, ou des utilitaires professionnels comme Diskeeper. Pour un meilleur résultat, prévoyez une défragmentation au cours de laquelle vous aurez préalablement arrêté les services qui occupent les fichiers que vous voulez défragmenter (SQL Server par exemple).
La fragmentation des indexes de la base revêt un caractère plus complexe et crucial. Si vous vous êtes déjà dit : "Avant lorsque j'entrais sur cette page d'édition c'était beaucoup plus rapide", ce paragraphe est fait pour vous. La suite est un petit peu technique et en anglais : ola.hallengren.com mais pour faire bref, téléchargez le script SQL sur CodePlex, puis ouvrez le à l'aide de SQL Server Management Studio. Connectez-vous et éxécutez le script. Attention : Le script, par défaut, va défragmenter tous les indexes de toutes les bases présentes sur le serveur et installer un travail répétitif qui s’exécutera tout seul pour sauvegarder vos bases et les défragmenter. Ci vous ne voulez pas tout cela, mais simplement une défragmentation des indexes, extrayez les scripts de création de la procédure stockée "IndexOptimize" et de la fonction "DatabaseSelect", puis exécutez la procédure stockée "IndexOptimize". Si vous ne savez pas comment faire, vous pouvez exécuter sur votre base DotNetNuke le script suivant : DatabaseSelect_IndexOptimize.sql
... puis exécuter ce script ci (remplacez les deux
NOMDELABASE par le nom de la base de données de votre instance DotNetNuke).
USE [NOMDELABASE]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[IndexOptimize]
@Databases = N'NOMDELABASE',
@FragmentationHigh_LOB = 'INDEX_REBUILD_OFFLINE' ,
@FragmentationHigh_NonLOB = 'INDEX_REBUILD_OFFLINE' ,
@FragmentationMedium_LOB = 'INDEX_REORGANIZE' ,
@FragmentationMedium_NonLOB = 'INDEX_REORGANIZE' ,
@FragmentationLow_LOB = 'NOTHING' ,
@FragmentationLow_NonLOB = 'NOTHING' ,
@FragmentationLevel1 = 5 ,
@FragmentationLevel2 = 30 ,
@PageCountLevel = 1000 ,
@SortInTempdb = 'N' ,
@MaxDOP = NULL ,
@FillFactor = NULL ,
@LOBCompaction = 'Y' ,
@StatisticsSample = NULL ,
@PartitionLevel = 'N' ,
@TimeLimit = NULL ,
@Indexes = NULL ,
@PadIndex = NULL ,
@Execute = 'Y'
SELECT 'Return Value' = @return_value
GO
Le script va retourner une valeur 0, qui indique que les indexes ont été défragmentés. Si vous exécutez le script une nouvelle fois, il s’exécutera plus rapidement (le script a nécessité 3 minutes sur ma base d'exemple, un site DotNetNuke mis à jour depuis DNN 4.1 et visité quotidiennement par plusieurs centaines d'utilisateurs).
Placer la base de données sur un disque plus rapide et indépendant
Si vous disposez de plusieurs disques dur physiques sur votre serveur, il serait judicieux de considérer d'en dédier un à la base de données. Pour ce faire, ouvrez SQL Server Management Studio, et déplacez les fichiers de la base sur ce disque dur.
La procédure est assez simple ; accédez aux
Propriétés de la base de données que vous voulez déplacer afin de connaître son emplacement sur le disque dur. Détachez ensuite la base en accèdant au menu
Tâches >
Détacher. Déplacez ensuite les ficheirs LDF et MDF sur votre nouveau disque dur. Enfin, rattachez la base à son nouvel emplacement en accèdant au menu
Tâches >
Attacher.
Optimiser les paramètres de cache dans la configuration Hôte
La configuration du cache et des performances dans DotNetNuke se résume à peu près à la compréhension de ce screenshot de la section
Performances de la page
Hôte >
Paramètres de l'hôte. J'ai en effet décidé d'inclure dans notre pack de langue une aide beaucoup plus complète que le pack de langue "original" que nous construisions déjà sur dnn.fr. Notre pack de langue peut être téléchargé sur
la page d'accueil.
Utiliser le bon cache dans vos modules personnalisés (Cache session, cache SQL, cookie, etc.)
Lorsque vous développez des modules, n'oubliez pas qu'un certain nombre de contenus peuvent être mis en cache. Cela peut concerner le résultat d'un traitement qui ne variera pas ou peu dans le temps, un contexte utilisateur qui fera office de constante pour toute la durée de la session, etc. Des objets entiers peuvent être "mis en cache", raccourcissant d'autant la génération de la réponse HTTP.
Voici comment utiliser les différents caches et l'usage qui est généralement réservé à chacun.
Le Cache ASP.Net :
Le cache ASP.Net est utilisé pour stocker des données complexes. Stocké sur le serveur, il est réutilisable tout le temps. Il se vide au recyclage du Pool Applicatif. On l'utilise généralement pour stocker le résultat de requêtes volumineuses qu'on ne souhaite pas lancer à tout va, ou encore pour partager des données mises en cache entre plusieurs utilisateurs.
Ajouter / Editer
Cache("CléAccesseurDeLObjet") = objMonObjet
Supprimer
Cache.Remove("CléAccesseurDeLObjet")
Obtenir la valeur
objMonObjet = Cache("CléAccesseurDeLObjet")
Le Cache Session :
Le cache Session est utilisé pour stocker des données propre à chaque utilisateur : un panier eShopping, un profil, etc. Les données sont stockées sur le serveur, et une clé est stockée chez le client dans un cookie permettant d'identifier la session concernée par les appels. Si le client ferme la fenêtre de son navigateur, la session est généralement perdue. La session est également détruite après 20 minutes d'inactivité, par défaut (Cf. pouvant être spécifié dans la configuration de IIS).
Ajouter / Editer
Session("CléAccesseurDeLObjet") = objMonObjet
Supprimer
Session.Contents.Remove("CléAccesseurDeLObjet")
Obtenir la valeur
objMonObjet = Session("CléAccesseurDeLObjet")
Le cache ViewState :
Le cache ViewState est utilisé pour stocker des données d'un PostBack à un autre. Les données sont stockées dans un champ caché accessible au navigateur web. Le ViewState ne peut que stocker des String ou des objets sérialisables, et son usage peut être forcé à false sur certains contrôles ASP.Net grâce à l'attribut "EnableViewState" à false.
Ajouter / Editer
ViewState("CléAccesseurDeLObjet") = objMonObjet
Supprimer
ViewState.Remove("CléAccesseurDeLObjet")
Obtenir la valeur
objMonObjet = ViewState("CléAccesseurDeLObjet")
Vous avez d'autres astuces ? N'hésitez pas à les publier sur le
forum ou à
créer un nouvel article.