En utilisant xhprof, j'ai remarqué que l' file_scan_directory()
exécution de la première page prend plus de 10 secondes. Pourquoi cela prendrait-il autant de temps?
Voici la sortie de xhprofile:
En utilisant xhprof, j'ai remarqué que l' file_scan_directory()
exécution de la première page prend plus de 10 secondes. Pourquoi cela prendrait-il autant de temps?
Voici la sortie de xhprofile:
Réponses:
Il semble que vous soyez affecté par un problème connu dans Drupal 7 .
Très probablement, vous frappez Évitez de ré-analyser le répertoire des modules lorsque plusieurs modules sont manquants . Cela se produit si certains modules manquent dans votre installation. Essayez de vérifier votre table système:
SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename
Et nettoyez tous les modules qui sont toujours activés mais manquants dans le système de fichiers.
Dans l'ensemble, Drupal 7 est bien plus convivial et évolutif que Drupal 6, à part quelques régressions malheureuses comme celle-ci.
En regardant ces fonctions, il semble qu'il manque un module ou peut-être un seul fichier d'un module. Jetez un œil à drupal_get_filename () , il appelle la drupal_system_listing (), qui appelle cette fonction s'il ne trouve pas le fichier demandé. Ajoutez un dpm (func_get_args ()) juste avant d'appeler drupal_system_listing (), qui devrait vous dire quel fichier il ne trouve pas.
Il y a plusieurs raisons pour lesquelles ce problème peut survenir, et à ma grande consternation, je me trouve maintenant un peu informé de ces raisons. Frustrant, si vous venez de remarquer ce problème après la mise à niveau de Drupal core vers 7.33+, cela pourrait être une faute de frappe dans n'importe quel module, même si vous n'avez pas mis à niveau ce module.
Vous voudrez peut-être d'abord vérifier le bogue connu mentionné par @Berdir, surtout si vous avez récemment supprimé des modules "inutilisés" de la base de code. Pour savoir si certains modules sont activés mais ont été supprimés du système de fichiers, vous pouvez exécuter un script tel que celui mentionné ici - ou utiliser le mien, écrit pour une installation multisite sur un système avec drush, à exécuter à partir du répertoire de base Drupal:
find sites -maxdepth 1 -iname '*.*' -type d | sed -rne 's:sites/(.+):echo \1; drush @\1 sqlq "select filename from system where status = 1" | grep "/" | sed -rne "s_(.+)_test -f \\1 || echo \\1_p" | bash:p' | bash
ou ce qui suit:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
Si vous trouvez un module qui a été supprimé de la base de code, suivez les instructions des problèmes mentionnés par @Berdir.
Si ce n'est pas le cas, votre situation est probablement due à une erreur de codage, comme un fichier qui a été supprimé mais qui est toujours ajouté par un appel drupal_add_js (du commentaire 19 dans le numéro # 1082892) ou une faute de frappe malheureuse dans un module ou un thème , par exemple imagecache_actions
(voir https://drupal.org/node/2381357 ).
Dans tous les cas, pour comprendre exactement pourquoi cela se produit, vous devez savoir exactement quel fichier Drupal ne peut pas trouver. Ainsi, selon le commentaire de Berdir, vous pouvez temporairement pirater drupal_get_filename
en bootstrap.inc
ajoutant un journal ou un appel de message juste avant l'appel à drupal_system_listing()
. Si vous avez installé le module Devel, dpm
cela fonctionnera; sinon, vous pouvez utiliser drupal_set_message
ou syslog. Exemples:
dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));
Une fois que vous savez ce que Drupal recherche, c'est un bon pari que vous serez en mesure de savoir où aller à partir de là. Mon problème a été provoqué par un appel pour inclure un fichier du module inexistant imagcache_actions
(notez la faute de frappe). Donc, j'ai cherché imagecache_actions
dans ma base de code (par exemple grep -r imagcache_actions .
), et j'ai trouvé que la version 1.4 de imagecache_canvasactions.module
module_load_include utilise en dehors de tout appel de fonction, dans la portée du fichier, avec une faute de frappe. Encore une fois, cette erreur n'a été exposée qu'après la mise à jour vers Drupal 7.33+. J'ai constaté qu'un problème avait déjà été créé pour imagecache_actions
, ai appliqué le correctif et était de retour dans les affaires.
J'ai eu un problème très similaire: file_scan_directory()
tuer le site. Il s'avère qu'un node_modules
dossier huuge intégré à mon thème personnalisé gulp
était analysé à chaque vidage du cache. Le déplacement de ces fichiers hors du dossier de thème (et la mise à jour de certains chemins dans mon gulpfile) a semblé le réparer pour moi. Alternativement: je pense que vous pouvez pirater file.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
La file_scan_directory()
est une fonction récursive qui regroupe tous les fichiers correspondant à un répertoire donné. Ce sont des utilisations is_dir()
et des opendir()
appels PHP qui peuvent être plus coûteux en termes d'appels système d'E / S. Un simple bootstrap Drupal (par exemple time drush ev ""
) peut appeler file_scan_directory
plusieurs milliers de fois (selon la complexité de votre hiérarchie de dossiers Drupal, par exemple le nombre de modules et ses dossiers).
Dans mon cas , j'avais ~ 1500 appels à file_scan_directory
(24 secondes comprenant un total de 2 appels à partir drupal_system_listing
dans common.inc
, les autres appels ont été divisés par des appels récursifs à file_scan_directory
elle-même.
Afin d'améliorer les performances des appels d'E / S, vous devez implémenter la mise en cache des fichiers. Ceci peut être réalisé en installant et en activant OPCache ( opcache.enable=1
) et en ajustant ses paramètres (voir: Comment utiliser PHP OPCache? ). L'utilisation de la mise en cache basée sur la mémoire telle que memcached / redis est également conseillée.
Lorsque vous utilisez l'interface de ligne de commande (telle que drush
), vous devez également l'activer opcache.enable_cli=1
.
Après la modification, vous pouvez vérifier les appels système les plus consommateurs à l'aide de certains débogueurs disponibles.
Par exemple
Sur Linux en utilisant strace
(appuyez sur Ctrl- Cpour terminer):
sudo strace -c -fp $(pgrep -n php)
Sous Unix utilisant dtrace
(en utilisant les sondes statiques PHP de DTrace ), par exemple
sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'
Vous pouvez également envisager d'optimiser drupal_system_listing()
ou file_scan_directory()
d'implémenter un cache statique, par exemple
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -2104,6 +2104,8 @@ function file_download_access($uri) {
* 'filename', and 'name' members corresponding to the matching files.
*/
function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+ static $dirs = array();
+
// Merge in defaults.
$options += array(
'nomask' => '/(\.\.?|CVS)$/',
@@ -2120,7 +2122,12 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
$uri = "$dir/$filename";
$uri = file_stream_wrapper_uri_normalize($uri);
- if (is_dir($uri) && $options['recurse']) {
+
+ if (empty($dirs[$uri])) {
+ $dirs[$uri] = is_dir($uri);
+ }
+
+ if ($dirs[$uri] && $options['recurse']) {
// Give priority to files in this folder by merging them in after any subdirectory files.
$files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);
Ou pour la mise en cache des file_scan_directory
appels de drupal_system_listing()
, vérifiez le correctif suivant disponible sur: file_scan_directory doit être mis en cache .