file_scan_directory () tarda unos 10 segundos en ejecutarse

10

Usando xhprof, noté que file_scan_directory()lleva más de 10 segundos ejecutar cuando se carga la primera página. ¿Por qué debería tomar tanto tiempo?

Esta es la salida de xhprofile:

captura de pantalla

hknik
fuente
No puede "file_scan_directory" la "portada", ya que la portada es una entrada en una tabla de base de datos, no una ruta del sistema de archivos.
Letharion
@Letharion Creo que has entendido mal mi pregunta. Me refiero al tiempo que lleva esta función cuando se carga la primera página. Edité la pregunta.
hknik
¿La página principal realmente tiene algo que ver con que la función tome un tiempo específico? ¿Qué directorio estás escaneando realmente? ¿Qué hay en el directorio?
Letharion
¡Ajá! Pensé que llamaba a la función usted mismo y me preguntaba por qué no proporcionó más detalles. La respuesta de Berdir parece muy razonable. :)
Letharion

Respuestas:

14

Parece que te afecta un problema conocido en Drupal 7 .

Lo más probable es que esté presionando Evite volver a escanear el directorio del módulo cuando faltan varios módulos . Ocurre si le faltan algunos módulos en su instalación. Intente verificar su tabla del sistema:

SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename

Y limpie los módulos que todavía están habilitados pero que faltan en el sistema de archivos.

En general, Drupal 7 es mucho más amigable con los recursos y escalable que Drupal 6, aparte de algunas regresiones desafortunadas como esta.

Mirando esas funciones, parece que le falta un módulo o tal vez un solo archivo de un módulo. Eche un vistazo a drupal_get_filename () , llama a drupal_system_listing (), que llama a esta función si no puede encontrar el archivo solicitado. Agregue un dpm (func_get_args ()) justo antes de que llame a drupal_system_listing (), eso debería indicarle qué archivo no está encontrando.

Berdir
fuente
No. Desafortunadamente (!) No falta ningún módulo del sistema de archivos
hknik
Entonces necesita rastrear de dónde proviene la llamada, tal vez un módulo personalizado o contrib está haciendo algo mal. Haga clic en las funciones principales de file_scan_directory () y actualice la publicación inicial con la lista de funciones principales.
Berdir
En cuanto a las funciones, que no parece que le falta un módulo o tal vez un solo archivo de un módulo. Eche un vistazo a drupal_get_filename: api.drupal.org/api/drupal/includes!bootstrap.inc/function/… . Llama a la función si no puede encontrar el archivo solicitado. Agregue un dpm (func_get_args ()) justo antes de que llame a drupal_system_listing (), eso debería decirle qué función no está encontrando.
Berdir
@ Berdir Su último comentario debe estar en su respuesta, ya que es relevante.
kiamlaluno
Los enlaces a "problema conocido en Drupal 7" y "Evitar volver a explorar el directorio del módulo" están rotos. Ambas son respuestas anteriores de stackexchange. ¿Alguien tiene otras referencias?
rfay
4

Hay varias razones por las cuales este problema puede surgir, y para mi gran consternación, ahora me encuentro un poco informado sobre esas razones. Frustrantemente, si acaba de notar este problema después de actualizar Drupal core a 7.33+, esto podría ser un error tipográfico en cualquier módulo, incluso si no ha actualizado ese módulo.

Módulos eliminados de la base de código

Es posible que desee verificar primero el error conocido que @Berdir menciona, especialmente si recientemente ha estado eliminando módulos "no utilizados" de la base del código. Para saber si tiene módulos que están habilitados pero se han eliminado del sistema de archivos, puede ejecutar un script como el mencionado aquí , o usar el mío, escrito para una instalación de múltiples sitios en un sistema con drush, para ejecutarse del directorio base de 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

o lo siguiente:

while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")

Si encuentra un módulo que se ha eliminado de la base del código, siga las instrucciones en los problemas que mencionó @Berdir.

Errores de codificación

Si no es así, es probable que su situación se deba a un error de codificación, como un archivo que se ha eliminado pero que todavía se agrega mediante una llamada a drupal_add_js (del comentario 19 en el número 1082892) o un error tipográfico desafortunado en un módulo o tema , por ejemplo imagecache_actions(ver https://drupal.org/node/2381357 ).

En cualquier caso, para descubrir exactamente por qué sucede esto, debe saber exactamente qué archivo no puede encontrar Drupal. Así, de acuerdo con el comentario de Berdir, se puede cortar temporalmente drupal_get_filenameen bootstrap.incañadiendo una llamada o un mensaje de registro justo antes de la llamada a drupal_system_listing(). Si tiene instalado el módulo Devel dpm, funcionará; si no, puede usar drupal_set_messageo syslog. Ejemplos:

dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));

Una vez que sepa lo que Drupal está buscando, es una buena apuesta que podrá averiguar a dónde ir desde allí. Mi problema fue causado por una llamada para incluir un archivo del módulo inexistente imagcache_actions(tenga en cuenta el error tipográfico). Entonces, busqué imagecache_actionsen mi base de código (por ejemplo grep -r imagcache_actions .), y encontré que la versión 1.4 de imagecache_canvasactions.moduleusa module_load_include fuera de cualquier llamada de función, en el alcance del archivo, con un error tipográfico. De nuevo, este error solo se expuso después de actualizar a Drupal 7.33+. Descubrí que ya se había creado un problema imagecache_actions, apliqué el parche y volví al negocio.

David Hunt
fuente
2

Tuve un problema muy similar: file_scan_directory()estaba matando el sitio. Resulta que una node_modulescarpeta enorme incrustada dentro de mi tema personalizado gulpse estaba escaneando cada descarga de caché. Mover estos archivos fuera de la carpeta de temas (y actualizar algunas rutas en mi archivo) parecía solucionarlo. Alternativamente: creo que puedes hackear file.inc:

'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519

williamsowen
fuente
0

El file_scan_directory()es una función recursiva que todos los archivos que coinciden para un directorio dado. Sus usos is_dir()y opendir()llamadas PHP pueden ser más costosos en términos de llamadas al sistema de E / S. La rutina de arranque simple de Drupal (p time drush ev "". Ej. ) Puede llamar file_scan_directoryunas pocas miles de veces (según la complejidad de la jerarquía de carpetas de Drupal, p. Ej., El número de módulos y sus carpetas).

En mi caso, tenía ~ 1500 llamadas a file_scan_directory(24 segundos en total que consisten en 2 llamadas desde drupal_system_listingadentro common.inc, luego las otras llamadas se dividieron por llamadas recursivas a file_scan_directorysí mismo.

Para mejorar el rendimiento en las llamadas de E / S, debe implementar el almacenamiento en caché de archivos. Esto se puede lograr instalando y habilitando OPCache ( opcache.enable=1) y modificando su configuración (ver: ¿Cómo usar PHP OPCache? ). También se recomienda utilizar el almacenamiento en caché basado en memoria, como memcached / redis.

Cuando utilice la interfaz de línea de comandos (como drush), también debe habilitarla opcache.enable_cli=1.

Después del cambio, puede verificar las llamadas al sistema más consumidoras utilizando algunos depuradores disponibles.

P.ej

  • En Linux usando strace(pulsar Ctrl- Cpara finalizar):

    sudo strace -c -fp $(pgrep -n php)
  • En Unix usando dtrace(usando las sondas estáticas DTrace de PHP ), por ejemplo

    sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'

También puede considerar optimizar drupal_system_listing()o file_scan_directory()implementar caché estática, por ejemplo

--- 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);

O para el almacenamiento en caché de file_scan_directoryllamadas drupal_system_listing(), compruebe el siguiente parche disponible en: file_scan_directory debe almacenarse en caché .

kenorb
fuente