[Hack] Caching des recherches

Mercredi, 15 Février 2012 Écrit par Fly06

Lu 1145 fois
|
Imprimer
Envoyer
|
0
pixel.gif 0%

Le composant de recherche natif Joomla (com_search) n'inclue aucune gestion du cache.

On pourrait s'en étonner puisque par nature cette fonctionnalité de recherche est très consommatrice de ressources (db/cpu) d'autant plus d'ailleurs que le nombre de type de contenus (plugins de type 'search' activés) et le nombre de contenus par type de contenus (nombre d'enregistrements par table) sont élevés.

L'absence de cache dans com_search tient très probablement à deux propriétés spécifiques des pages (résultats de recherche) générées par com_search :

  • Nombre a priori très élevé des pages possibles
  • Pages crées à l'initiative des visiteurs

Ces deux propriétés ont des implications en terme de performance et de sécurité, nous y reviendront en fin d'article.

Pour le moment regardons quels sont les changements à réaliser sur com_search pour inclure une gestion de cache.

Le cache des sorties html (view)

Le cache des sorties html du composant com_search peut être réalisé très simplement, dans la méthode display() du fichier controller.php, en passant le paramètre optionnel $cachable dans l'appel à la méthode display() de la classe JController de false (valeur par défaut) à true.

En clair, il suffit donc de remplacer cette ligne de code :

parent::display();

par celles-ci :

$user = &JFactory::getUser();
 if ($user->guest){
 parent::display(true); // Cache activé
 } else {
 parent::display(false); // Cache désactivé
 }

C'est un peu plus compliqué que prévu car, en général, on ne souhaite pas activer le cache pour les membres connectés de façon à éviter que des items en accès restreints se retrouvent, via le cache, dans les résultats de recherche des visiteurs ;-)

Le hack pourrait presque s'arrêter ici mais on va ajouter une couche complémentaire au cache des sorties html permettant d'optimiser la pagination des résultats de recherche.

Cache de l'output des plugins de type 'search'

Ceux qui connaissent le fonctionnement de com_search savent que les paramètres de pagination (limit et limitstart) ne sont pas intégrés dans les requêtes SQL. Les résultats de com_search provenants de plusieurs tables via plusieurs requêtes (au moins une requête par plugin de type 'search'), il n'est pas possible de passer ces paramètres à la méthode setQuery() de la classe JDatabase. Le filtrage des résultats retournées par l'éxécution des plugins est réalisé en aval pour ne conserver que les résultats correspondants aux paramètres de pagination courants.

En conséquence, pour une même recherche, les mêmes requêtes sont exécutées et les mêmes résultats sont retournées à chaque changement de page.

On va utiliser le cache pour stocker ces résultats de recherche de façon à les réutiliser au changement de page.

Les modifications à réaliser sont un peu plus complexes car le modèle MVC Joomla ne gère que le cache des sorties html (view) et non celui des résultats intermédiaires de calcul. On va donc utiliser directement la classe JCache et le handler JCacheCallback pour "cacher" le résultat de l'exécution des plugins de type 'search' (méthode getData() de la classe SearchModelSearch dans le fichier models/search.php).

Concrètement, il suffit de remplacer ces lignes de code :

JPluginHelper::importPlugin( 'search');
 $dispatcher =& JDispatcher::getInstance();

 $results = $dispatcher->trigger( 'onSearch', array(
 $this->getState('keyword'),
 $this->getState('match'),
 $this->getState('ordering'),
 $areas['active']) );

 $rows = array();
 foreach($results AS $result) {
 $rows = array_merge( (array) $rows, (array) $result);
 }

par celles-ci :

$user = &JFactory::getUser();
 if ($user->guest){
 // Get a reference to the global cache object.
 $cache = & JFactory::getCache();    

 $rows = $cache->call(    array( 'searchPluginsDispatcher', 'get_data' ), 
 $this->getState('keyword'),
 $this->getState('match'),
 $this->getState('ordering'),
 $areas['active'] );
 }
 else { // On n'active pas le cache si il s'agit d'un utilisateur connecté
 JPluginHelper::importPlugin( 'search');
 $dispatcher =& JDispatcher::getInstance();

 $results = $dispatcher->trigger( 'onSearch', array(
 $this->getState('keyword'),
 $this->getState('match'),
 $this->getState('ordering'),
 $areas['active']) );

 $rows = array();
 foreach($results AS $result) {
 $rows = array_merge( (array) $rows, (array) $result);
 }
 }

On aurait pu simplifier cette partie du hack en passant comme "fonction de callback" l'objet $dispatcher (déjà instancié) et sa méthode trigger() mais cette approche a pour inconvénient d'ajouter toutes les propriétés de l'objet $dispatcher comme paramètres lors de la création de l'id du cache (cf. méthode _makeId() de la classe JCacheCallback).

On utilise donc un appel statique de la méthode 'get_data' sur la classe 'searchPluginsDispatcher'.

Cette classe n'existant pas, il faut donc la créer :

class searchPluginsDispatcher {
function get_data($keyword, $match, $ordering, $active) {

 JPluginHelper::importPlugin( 'search');
 $dispatcher =& JDispatcher::getInstance();

 $results = $dispatcher->trigger( 'onSearch', array($keyword, $match, $ordering, $active) );

 $rows = array();
 foreach($results AS $result) {
 $rows = array_merge( (array) $rows, (array) $result);
 }

 return $rows;

 }
}

Cette classe est à ajouter à la fin du fichier models/search.php.

Performance et sécurité

Le gain en temps de calcul mesuré sur un site de test est d'environ 12-fois pour le cache des views (html) et de 35-fois pour celui des résultats de recherche.

Ces chiffres doivent cependant être relativisés car si le cache n'est jamais utilisé pendant sa durée de vie, le hack précédent est non seulement inutile mais aussi sous-optimal puisque la gestion du cache consomme du temps cpu.

La question qui se pose est quand doit-on appliquer ce hack ?

Cela dépend uniquement de la façon dont les recherches sont distribuées :

  • Si le gros des recherches concerne un petit nombre de mots clés (distribution paretienne) alors l'activation du cache sera bénéfique
  • Si, au contraire, les recherches (mots clés) sont très nombreuses et ont à peu près le mêmes nombre d'occurrences chacune (distribution uniforme) alors ce hack est inutile

Pour le savoir, il suffit d'activer le log des recherches dans le composant com_search et de faire l'analyse après un mois ou plus selon la fréquentation du site.

Un dernier point sur la sécurité.

Un site dynamique est par nature propice aux attaques DoS (flooding).

En activant le cache de com_search, vous ouvrez la voie à des attaques DoS sur le cache de Joomla du fait que les recherches sont l'initiative de l'internaute et que le nombre de recherche possibles est quasi-infini.

Pour se protéger, on pourra utiliser un système anti-flooding ou un captcha en allant farfouiller dans la partie du JED consacrée à la sécurité des sites.

Mise à jour le Samedi, 18 Février 2012 22:16
 
Share
Créer un compte
Calculator
Login

Login



Register

Créer un compte

Question

Calculator

FRF
Fly06.Fr (C) 2009-2011