file_scan_directory () leva cerca de 10 segundos para executar

10

Usando o xhprof, notei que file_scan_directory()leva mais de 10 segundos para executar quando a primeira página é carregada. Por que demoraria tanto tempo?

Esta é a saída do xhprofile:

captura de tela

hknik
fonte
Você não pode "file_scan_directory" a "primeira página", pois a primeira página é uma entrada em uma tabela do banco de dados, não um caminho do sistema de arquivos.
Letharion # 1/12
@ Letharion Acho que você não entendeu minha pergunta. Quero dizer, o tempo que essa função leva quando a página inicial é carregada. Editei a pergunta.
hknik
A primeira página realmente tem algo a ver com a função que leva um tempo específico? Qual diretório você está realmente digitalizando? O que há no diretório?
Letharion
Aha! Pensei que você tivesse chamado a função e me perguntei por que não forneceu mais detalhes. A resposta de Berdir parece muito razoável. :)
Letharion

Respostas:

14

Parece que você foi afetado por um problema conhecido no Drupal 7 .

Provavelmente, você está pressionando Evitar re-varrer o diretório do módulo quando vários módulos estiverem ausentes . Isso acontece se você tiver alguns módulos ausentes na sua instalação. Tente verificar a tabela do sistema:

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

E limpe todos os módulos que ainda estejam ativados, mas ausentes no sistema de arquivos.

No geral, o Drupal 7 é muito mais amigável e escalável que o Drupal 6, além de algumas regressões infelizes como essa.

Observando essas funções, parece que está faltando um módulo ou talvez um único arquivo de um módulo. Dê uma olhada em drupal_get_filename () , ele chama drupal_system_listing (), que chama essa função se não conseguir encontrar o arquivo solicitado. Adicione um dpm (func_get_args ()) logo antes de chamar drupal_system_listing (), que deve informar qual arquivo não está sendo encontrado.

Berdir
fonte
Não. Infelizmente (!) Nenhum módulo está faltando no sistema de arquivos
hknik
Então você precisa rastrear a origem da chamada, talvez um módulo personalizado ou de contribuição esteja fazendo algo errado. Clique nas funções pai de file_scan_directory () e atualize a postagem inicial com a lista de funções pai.
Berdir
Olhando para essas funções, ele não parece que está faltando um módulo ou talvez um único arquivo de um módulo. Dê uma olhada em drupal_get_filename: api.drupal.org/api/drupal/includes!bootstrap.inc/function/… . Ele chama a função se não conseguir encontrar o arquivo solicitado. Adicione um dpm (func_get_args ()) logo antes de chamar drupal_system_listing (), que deve informar qual função ele não está encontrando.
Berdir
@Berdir Seu último comentário deve estar em sua resposta, pois é relevante.
Kiamlaluno
Os links para "problema conhecido no Drupal 7" e "Evitar redigitalizar o diretório do módulo" estão quebrados. Ambos são respostas anteriores de stackexchange. Alguém tem outras referências?
Rfay
4

Há várias razões pelas quais esse problema pode surgir e, para minha grande consternação, agora me encontro um pouco familiarizado com essas razões. De maneira frustrante, se você percebeu esse problema após atualizar o núcleo do Drupal para 7.33 ou mais, isso pode ser um erro de digitação em qualquer módulo, mesmo que você não tenha atualizado esse módulo.

Módulos removidos da base de código

Você pode primeiro verificar o bug conhecido mencionado pelo @Berdir, especialmente se você tiver removido recentemente módulos "não utilizados" da base de código. Para descobrir se você possui módulos ativados, mas que foram removidos do sistema de arquivos, execute um script como o mencionado aqui - ou use o meu, escrito para uma instalação de vários sites em um sistema com drush, a ser executado do diretório base do 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 o seguinte:

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

Se você encontrar um módulo que foi removido da base de código, siga as instruções nos problemas mencionados pelo @Berdir.

Erros de codificação

Caso contrário, é provável que sua situação seja causada por um erro de codificação, como um arquivo que foi removido, mas ainda está sendo adicionado por uma chamada drupal_add_js (do comentário 19 na edição # 1082892) ou por um erro de digitação infeliz em um módulo ou tema , por exemplo imagecache_actions(consulte https://drupal.org/node/2381357 ).

De qualquer forma, para descobrir exatamente por que isso está acontecendo, você precisa saber exatamente qual arquivo o Drupal não pode encontrar. Assim, de acordo com o comentário de Berdir, você pode temporariamente cortar drupal_get_filenameem bootstrap.incadicionando uma chamada de log ou uma mensagem pouco antes da chamada para drupal_system_listing(). Se você tiver o módulo Devel instalado, dpmele funcionará; Caso contrário, você pode usar drupal_set_messageou syslog. Exemplos:

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

Depois de saber o que o Drupal está procurando, é uma boa aposta que você possa descobrir para onde ir a partir daí. Meu problema foi causado por uma chamada para incluir um arquivo do módulo inexistente imagcache_actions(observe o erro de digitação). Então, procurei imagecache_actionsna minha base de código (por exemplo grep -r imagcache_actions .) e descobri que a versão 1.4 do imagecache_canvasactions.moduleusa module_load_include fora de qualquer chamada de função, no escopo do arquivo, com um erro de digitação. Novamente, esse erro foi exposto somente após a atualização para o Drupal 7.33+. Descobri que um problema já havia sido criado imagecache_actions, aplicado o patch e estava de volta aos negócios.

David Hunt
fonte
2

Eu tive um problema muito semelhante - file_scan_directory()estava matando o site. Acontece que uma node_modulespasta enorme incorporada no meu tema personalizado gulpestava sendo examinada a cada descarga de cache. Mover esses arquivos para fora da pasta do tema (e atualizar alguns caminhos no meu arquivo de gulpfile) pareceu corrigi-lo. Como alternativa: acho que você pode hackear file.inc:

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

williamsowen
fonte
0

O file_scan_directory()é uma função recursiva que corresponde a todos os arquivos que correspondem a um determinado diretório. São usos is_dir()e opendir()chamadas PHP, que podem custar mais tempo em termos de chamadas do sistema de E / S. A inicialização simples do Drupal (por exemplo time drush ev "") pode chamar file_scan_directorymilhares de vezes (dependendo da complexidade da hierarquia de pastas do Drupal, por exemplo, número de módulos e suas pastas).

No meu caso, eu tinha ~ 1500 chamadas para file_scan_directory(24 segundos no total, consistindo em 2 chamadas de drupal_system_listingin common.inc, as outras chamadas foram divididas por chamadas recursivas para file_scan_directorysi mesmo.

Para melhorar o desempenho nas chamadas de E / S, você precisa implementar o cache do arquivo. Isso pode ser conseguido instalando e ativando o OPCache ( opcache.enable=1) e ajustando suas configurações (consulte: Como usar o PHP OPCache? ). Também é recomendável usar o cache baseado em memória, como memcached / redis.

Ao usar a interface da linha de comandos (como drush), você também deve habilitar opcache.enable_cli=1.

Após a alteração, você pode verificar os syscalls mais consumíveis usando alguns depuradores disponíveis.

Por exemplo

  • No Linux usando strace(hit Ctrl- Cao fim):

    sudo strace -c -fp $(pgrep -n php)
  • No Unix usando dtrace(usando sondas estáticas DTrace do PHP ), por exemplo

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

Você também pode otimizar drupal_system_listing()ou file_scan_directory()implementar o cache estático, por exemplo,

--- 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 para armazenar file_scan_directorychamadas em cache drupal_system_listing(), verifique o seguinte patch disponível em: file_scan_directory deve ser armazenado em cache .

kenorb
fonte