file_scan_directory () trwa około 10 sekund

10

Za pomocą xhprof zauważyłem, że wykonanie file_scan_directory()zajmuje więcej niż 10 sekund po załadowaniu pierwszej strony. Dlaczego miałoby to trwać tak długo?

To jest wynik działania xhprofile:

zrzut ekranu

hknik
źródło
Nie można „file_scan_directory” „strony głównej”, ponieważ strona główna to pozycja w tabeli bazy danych, a nie ścieżka systemu plików.
Letharion
@Letharion Myślę, że źle zrozumiałeś moje pytanie. Mam na myśli czas potrzebny tej funkcji na załadowanie pierwszej strony. Zredagowałem pytanie.
hknik
Czy strona główna naprawdę ma coś wspólnego z funkcją zajmującą określoną ilość czasu? Jaki katalog faktycznie skanujesz? Co jest w katalogu?
Letharion
Aha! Myślałem, że sam wywołałeś tę funkcję i zastanawiałem się, dlaczego nie podałeś więcej szczegółów. Odpowiedź Berdira wygląda bardzo rozsądnie. :)
Letharion

Odpowiedzi:

14

Wygląda na to, że znany ci problem z Drupal 7 dotyczy Ciebie .

Najprawdopodobniej uderzasz Unikaj ponownego skanowania katalogu modułów, gdy brakuje wielu modułów . Dzieje się tak, jeśli brakuje niektórych modułów w instalacji. Spróbuj sprawdzić tabelę systemową:

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

I oczyść wszystkie moduły, które są nadal włączone, ale brakuje ich w systemie plików.

Ogólnie rzecz biorąc, Drupal 7 jest znacznie bardziej przyjazny dla zasobów i skalowalny niż Drupal 6, poza kilkoma niefortunnymi regresjami takimi jak ten.

Patrząc na te funkcje, wygląda na to, że brakuje modułu lub może pojedynczego pliku modułu. Spójrz na drupal_get_filename () , wywołuje on drupal_system_listing (), który wywołuje tę funkcję, jeśli nie może znaleźć żądanego pliku. Dodaj dpm (func_get_args ()) tuż przed wywołaniem drupal_system_listing (), co powinno ci powiedzieć, którego pliku nie znajduje.

Berdir
źródło
Nie. Niestety (!) W systemie plików nie brakuje modułu
hknik
Następnie potrzebujesz śledzenia, skąd pochodzi połączenie, być może moduł niestandardowy lub contrib robi coś złego. Kliknij funkcje nadrzędne file_scan_directory () i zaktualizuj początkowy post o listę funkcji nadrzędnych.
Berdir
Patrząc na tych funkcji, to nie wyglądało, że brakuje modułu lub może pojedynczy plik modułu. Spójrz na drupal_get_filename: api.drupal.org/api/drupal/include!bootstrap.inc/function/… . Wywołuje funkcję, jeśli nie może znaleźć żądanego pliku. Dodaj dpm (func_get_args ()) tuż przed wywołaniem drupal_system_listing (), co powinno ci powiedzieć, której funkcji nie znajduje.
Berdir
@Berdir Twój ostatni komentarz powinien być w odpowiedzi, ponieważ jest istotny.
kiamlaluno
Linki do „znanego problemu w Drupal 7” i „Unikaj katalogu modułu ponownego skanowania” są zepsute. Oba są poprzednimi odpowiedziami wymiany stosu. Czy ktoś ma inne referencje?
rfay
4

Jest kilka powodów, dla których ten problem może powstać, i ku mojemu wielkiemu przerażeniu, teraz mam dość wiedzy na temat tych przyczyn. Frustrujące jest to, że jeśli właśnie zauważyłeś ten problem po aktualizacji rdzenia Drupala do wersji 7.33+, może to być literówka w dowolnym module, nawet jeśli nie zaktualizowałeś tego modułu.

Moduły usunięte z bazy kodu

Możesz najpierw sprawdzić znany błąd, o którym wspomina @Berdir, szczególnie jeśli ostatnio usuwasz „nieużywane” moduły z bazy kodu. Aby dowiedzieć się, czy masz moduły, które są włączone, ale zostały usunięte z systemu plików, możesz uruchomić skrypt, taki jak wymieniony tutaj - lub użyć mojego, napisanego do instalacji w wielu witrynach w systemie z Drush, aby uruchomić z katalogu podstawowego 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

lub następujące:

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

Jeśli znajdziesz moduł, który został usunięty z bazy kodu, postępuj zgodnie ze wskazówkami w kwestiach wspomnianych przez @Berdir.

Błędy kodowania

Jeśli tak nie jest, Twoja sytuacja jest prawdopodobnie spowodowana błędem kodowania, takim jak plik, który został usunięty, ale nadal jest dodawany przez wywołanie drupal_add_js (od komentarza 19 w numerze # 1082892) lub niefortunną literówkę w module lub motywie , np. imagecache_actions(patrz https://drupal.org/node/2381357 ).

W każdym razie, aby dokładnie zrozumieć, dlaczego tak się dzieje, musisz dokładnie wiedzieć, którego pliku Drupal nie może znaleźć. Tak więc, zgodnie z komentarzem Berdir użytkownika, można tymczasowo włamać drupal_get_filenamesię bootstrap.incprzez dodanie do dziennika lub wiadomość wezwanie tuż przed wywołaniem drupal_system_listing(). Jeśli masz zainstalowany moduł Devel, to dpmbędzie działał; jeśli nie, możesz użyć drupal_set_messagelub syslog. Przykłady:

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

Gdy dowiesz się, czego szuka Drupal, dobrze jest założyć, że będziesz w stanie dowiedzieć się, dokąd się udać. Mój problem spowodowany był wywołaniem dołączenia pliku z nieistniejącego modułu imagcache_actions(zwróć uwagę na literówkę). Tak więc szukałem imagecache_actionsw mojej bazie kodu (np. grep -r imagcache_actions .) I znalazłem, że wersja 1.4 imagecache_canvasactions.moduleużywa modułu moduł_ładuj_włączenie poza jakimkolwiek wywołaniem funkcji, w zakresie plików, z literówką. Ponownie ten błąd został ujawniony dopiero po aktualizacji do Drupal 7.33+. Odkryłem, że problem został już utworzony imagecache_actions, zastosowałem łatkę i wróciłem do pracy.

David Hunt
źródło
2

Miałem bardzo podobny problem - file_scan_directory()zabijałem witrynę. Okazuje się, że ogromny node_modulesfolder osadzony w moim niestandardowym motywie gulpbył skanowany przy każdym opróżnieniu pamięci podręcznej. Przeniesienie tych plików z folderu motywu (i zaktualizowanie niektórych ścieżek w moim pliku gulp) wydawało mi się to naprawić. Alternatywnie: Myślę, że możesz zhakować file.inc:

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

williamsowen
źródło
0

Jest file_scan_directory()to funkcja rekurencyjna, która wszystkie pliki pasujące do danego katalogu. Wykorzystuje is_dir()i opendir()wywołuje PHP, które mogą być najdroższe pod względem wywołań systemowych We / Wy. Prosty bootstrap Drupal (np. time drush ev "") Może wywoływać file_scan_directorykilka tysięcy razy (w zależności od złożoności hierarchii folderów Drupal, np. Liczby modułów i folderów).

W moim przypadku miałem ~ 1500 wywołań file_scan_directory(24 sekund w sumie składających się z 2 połączeń drupal_system_listingw common.inc, a następnie pozostałe połączenia były podzielone przez rekurencyjnych wywołań file_scan_directoryto-ja.

Aby poprawić wydajność połączeń We / Wy, należy zaimplementować buforowanie plików. Można to osiągnąć, instalując i włączając OPCache ( opcache.enable=1) oraz dostosowując jego ustawienia (patrz: Jak korzystać z OPCache PHP? ). Zalecane jest również stosowanie buforowania opartego na pamięci, takiego jak memcached / redis.

Korzystając z interfejsu wiersza polecenia (takiego jak drush), należy również włączyć opcache.enable_cli=1.

Po zmianie możesz sprawdzić bardziej wymagające połączenia systemowe za pomocą dostępnych debuggerów.

Na przykład

  • W systemie Linux przy użyciu strace(naciśnij Ctrl- Caby zakończyć):

    sudo strace -c -fp $(pgrep -n php)
  • W systemie Unix dtrace(za pomocą statycznych sond DTrace PHP ), np

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

Możesz dalej rozważyć optymalizację drupal_system_listing()lub file_scan_directory()wdrożenie statycznej pamięci podręcznej, np

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

Lub w celu buforowania file_scan_directorywywołań z drupal_system_listing(), a następnie sprawdź następującą łatkę dostępną pod adresem: katalog_kanału_pliku powinien być buforowany .

kenorb
źródło