file_scan_directory () richiede circa 10 secondi per l'esecuzione


10

Usando xhprof ho notato che ci file_scan_directory()vogliono più di 10 secondi per eseguire il caricamento della prima pagina. Perché dovrebbe impiegare così tanto tempo?

Questo è l'output di xhprofile:

immagine dello schermo


Non è possibile "file_scan_directory" la "prima pagina", poiché la prima pagina è una voce in una tabella del database, non un percorso del file system.
Letharion

@Letharion Penso che tu abbia frainteso la mia domanda. Intendo il tempo impiegato da questa funzione quando si carica la prima pagina. Ho modificato la domanda.
hknik

La prima pagina ha davvero a che fare con la funzione che richiede un determinato periodo di tempo? Quale directory stai effettivamente scansionando? Cosa c'è nella directory?
Letharion

Aha! Pensavo che avessi chiamato tu stesso la funzione e mi chiedevo perché non avessi fornito maggiori dettagli. La risposta di Berdir sembra molto ragionevole. :)
Letharion

Risposte:


14

Sembra che tu sia interessato da un problema noto in Drupal 7 .

Molto probabilmente, stai colpendo Evita di ripetere la scansione della directory dei moduli quando mancano più moduli . Succede se hai alcuni moduli mancanti nella tua installazione. Prova a controllare la tabella di sistema:

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

E ripulisci tutti i moduli che sono ancora abilitati ma mancanti dal filesystem.

Nel complesso, Drupal 7 è molto più intuitivo e scalabile rispetto a Drupal 6, oltre ad alcune sfortunate regressioni come questa.

Guardando quelle funzioni, sembra che manchi un modulo o forse un singolo file di un modulo. Dai un'occhiata a drupal_get_filename () , chiama drupal_system_listing (), che chiama questa funzione se non riesce a trovare il file richiesto. Aggiungi un dpm (func_get_args ()) prima di chiamare drupal_system_listing (), che dovrebbe dirti quale file non sta trovando.


No. Purtroppo (!) Non manca alcun modulo dal filesystem
hknik

Quindi è necessario tracciare l'origine della chiamata, forse un modulo personalizzato o contrib sta facendo qualcosa di sbagliato. Fai clic sulle funzioni principali di file_scan_directory () e aggiorna il post iniziale con l'elenco delle funzioni principali.
Berdir

Guardando quelle funzioni, che fa sembrare che manca un modulo o forse un singolo file di un modulo. Dai un'occhiata a drupal_get_filename: api.drupal.org/api/drupal/includes!bootstrap.inc/function/… . Chiama la funzione se non riesce a trovare il file richiesto. Aggiungi un dpm (func_get_args ()) prima di chiamare drupal_system_listing (), che dovrebbe dirti quale funzione non sta trovando.
Berdir

@Berdir Il tuo ultimo commento dovrebbe essere nella tua risposta, in quanto è pertinente.
kiamlaluno

I collegamenti a "problema noto in Drupal 7" e "Evita di ripetere la scansione della directory del modulo" sono interrotti. Entrambe sono risposte ex stackexchange. Qualcuno ha altri riferimenti?
rfay,

4

Ci sono diverse ragioni per cui questo problema può sorgere e, con mio grande sgomento, ora mi trovo in qualche modo ben informato su tali motivi. Frustrantemente, se hai appena notato questo problema dopo aver aggiornato Drupal core a 7.33+, questo potrebbe essere un errore di battitura in qualsiasi modulo, anche se non hai aggiornato quel modulo.

Moduli rimossi dalla base di codice

Potresti prima verificare il bug noto menzionato da @Berdir, specialmente se hai rimosso di recente moduli "non utilizzati" dalla base di codice. Per scoprire se hai moduli abilitati ma che sono stati rimossi dal file system puoi eseguire uno script come quello menzionato qui - o usare il mio, scritto per un'installazione multi-sito su un sistema con drush, da eseguire dalla directory di 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

o il seguente:

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

Se trovi un modulo che è stato rimosso dalla base di codice, segui le istruzioni nei problemi menzionati da @Berdir.

Errori di codifica

In caso contrario, la tua situazione è probabilmente causata da un errore di codifica, ad esempio un file che è stato rimosso ma che viene ancora aggiunto da una chiamata drupal_add_js (dal commento 19 nel numero # 1082892) o da un errore di battitura sfortunato in un modulo o tema , ad esempio imagecache_actions(vedi https://drupal.org/node/2381357 ).

In ogni caso, per capire esattamente perché ciò sta accadendo, devi sapere esattamente quale file Drupal non riesce a trovare. Così, secondo il commento di Berdir, si può temporaneamente incidere drupal_get_filenamein bootstrap.inccon l'aggiunta di una chiamata o un messaggio di log appena prima della chiamata a drupal_system_listing(). Se hai installato il modulo Develop, allora dpmfunzionerà; in caso contrario, è possibile utilizzare drupal_set_messageo syslog. Esempi:

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

Una volta che sai cosa Drupal sta cercando, è una buona scommessa che sarai in grado di capire dove andare da lì. Il mio problema è stato causato da una chiamata per includere un file dal modulo inesistente imagcache_actions(notare l'errore di battitura). Quindi, ho cercato imagecache_actionsnel mio codebase (ad esempio grep -r imagcache_actions .) e ho scoperto che la versione 1.4 imagecache_canvasactions.moduleutilizza module_load_include al di fuori di qualsiasi chiamata di funzione, nell'ambito del file, con un refuso. Ancora una volta, questo errore è stato esposto solo dopo l'aggiornamento a Drupal 7.33+. Ho scoperto che era già stato creato un problema imagecache_actions, applicato la patch ed era tornato in attività.


2

Ho avuto un problema molto simile: file_scan_directory()stavo uccidendo il sito. Si scopre che una node_modulescartella enorme incorporata nel mio tema personalizzato per cui gulpè stata scansionata ogni cache flush. Spostare questi file fuori dalla cartella del tema (e aggiornare alcuni percorsi nel mio gulpfile) sembrava risolverlo per me. In alternativa: penso che tu possa hackerare file.inc:

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


0

La file_scan_directory()è una funzione ricorsiva che tutti i file che corrispondono a una determinata directory. Sono gli usi is_dir()e le opendir()chiamate PHP che possono essere più costosi in termini di chiamate di sistema I / O. Il bootstrap Drupal semplice (ad es. time drush ev "") Può chiamare file_scan_directoryalcune migliaia di volte (a seconda della complessità della gerarchia delle cartelle di Drupal, ad es. Il numero di moduli e le sue cartelle).

Nel mio caso ho avuto ~ 1500 chiamate a file_scan_directory(24 secondi in totale consistenti in 2 chiamate da drupal_system_listingdentro common.inc, quindi le altre chiamate sono state divise da chiamate ricorsive a file_scan_directoryse stesso.

Per migliorare le prestazioni sulle chiamate I / O, è necessario implementare la memorizzazione nella cache dei file. Ciò può essere ottenuto installando e abilitando OPCache ( opcache.enable=1) e modificandone le impostazioni (vedi: Come usare PHP OPCache? ). Si consiglia inoltre di utilizzare la cache basata sulla memoria come memcached / redis.

Quando si utilizza l'interfaccia della riga di comando (come drush), è necessario abilitare anche opcache.enable_cli=1.

Dopo la modifica è possibile controllare i syscall più consumati utilizzando alcuni debugger disponibili.

Per esempio

  • Su Linux usando strace(premi Ctrl- Cper finire):

    sudo strace -c -fp $(pgrep -n php)
  • Su Unix usando dtrace(usando le sonde statiche DTrace di PHP ), ad es

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

È inoltre possibile prendere in considerazione l'ottimizzazione drupal_system_listing()o l' file_scan_directory()implementazione della cache statica, ad es

--- 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 per la memorizzazione nella cache delle file_scan_directorychiamate da drupal_system_listing(), quindi controllare la seguente patch disponibile su: file_scan_directory deve essere memorizzata nella cache .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.