Se você chamar file_exists($path) e $path apontar para um bucket do Amazon S3, você não estará acessando um disco local — você estará fazendo uma requisição de rede. Essa verificação de uma única linha torna-se uma ida e volta (round-trip) ao S3 que pode adicionar dezenas de milissegundos a cada requisição.

O PHP esconde o armazenamento remoto por trás de stream wrappers. Funções como fopen(), file_exists(), is_dir(), unlink() e file_put_contents() são roteadas através desses wrappers, que traduzem as chamadas em operações da API do S3. O mapeamento é direto:

  • file_exists()HeadObject
  • is_dir()ListObjects
  • unlink()DeleteObject
  • file_put_contents()PutObject

Cada chamada incorre na mesma latência da respectiva requisição da API do S3. Quando um script verifica dezenas de arquivos, ele cria um padrão do tipo N+1: um salto de rede para cada verificação individual.

Por que isso importa agora

Em um ambiente de desenvolvimento típico, o sistema de arquivos reside na mesma máquina, então a verificação retorna quase instantaneamente. Em produção, onde os assets estão no S3, o mesmo código cria um abismo de performance. O AWS SDK faz o cache de alguns resultados na memória, fazendo com que verificações repetidas em um único processo PHP pareçam rápidas. A maioria das implantações de PHP, no entanto, inicia um novo processo para cada requisição web, limpando o cache a cada vez. O resultado: uma ida e volta de rede completa para cada caminho distinto em cada requisição.

Um site WordPress levou uma vez dez segundos para renderizar sua página inicial. As consultas ao banco de dados eram rápidas, mas a página disparou 73 chamadas individuais ao S3 apenas para montar o conteúdo. Cada chamada era uma requisição "cold", transformando um file_exists() aparentemente inofensivo em um atraso perceptível.

Estratégias de mitigação

  • Persistir o cache entre requisições – Mova o cache do processo para um armazenamento compartilhado, como o Redis. Quando uma requisição descobre que uma chave existe no S3, as requisições subsequentes leem o resultado em cache em vez de contatar o S3 novamente.
  • Adotar um fluxo de trabalho local-first – Escreva arquivos em um disco local e, em seguida, sincronize-os com o S3 de forma assíncrona. O caminho principal da requisição toca apenas o sistema de arquivos local; o custo de rede é transferido para um job em segundo plano.
  • Auditar a base de código – Pesquise sistematicamente por funções de sistema de arquivos que possam ser resolvidas para um wrapper remoto. Sinalize qualquer uma que apareça dentro de loops intensos ou caminhos críticos da requisição.
  • Inspecionar dados de APM – Se o tempo de resposta disparar enquanto as métricas do banco de dados permanecem estáveis, analise detalhadamente os tempos do SDK de armazenamento. Um aumento repentino na latência do SDK geralmente aponta para chamadas ocultas ao S3.

A principal conclusão: uma função que parece uma verificação local de microssegundos pode esconder dezenas de milissegundos de latência de rede. Trate o file_exists() no S3 como uma chamada externa, faça o cache do seu resultado e mova o trabalho pesado para fora do caminho da requisição. Caso contrário, a latência oculta continuará desacelerando seu site, uma requisição de rede invisível por vez.