Si vous appelez file_exists($path) et que $path pointe vers un bucket Amazon S3, vous n'interrogez pas un disque local — vous effectuez une requête réseau. Cette vérification d'une seule ligne devient un aller-retour vers S3 qui peut ajouter des dizaines de millisecondes à chaque requête.

PHP masque le stockage distant derrière des wrappers de flux. Des fonctions telles que fopen(), file_exists(), is_dir(), unlink() et file_put_contents() passent par ces wrappers, qui traduisent les appels en opérations de l'API S3. La correspondance est directe :

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

Chaque appel entraîne la même latence que la requête API S3 correspondante. Lorsqu'un script vérifie des dizaines de fichiers, il crée un modèle de type N+1 : un saut réseau pour chaque vérification.

Pourquoi c'est important maintenant

Dans un environnement de développement typique, le système de fichiers se trouve sur la même machine, la vérification est donc presque instantanée. En production, là où les ressources sont sur S3, le même code crée une chute brutale de performance. L'AWS SDK met certains résultats en cache en mémoire, ce qui donne l'impression que les vérifications répétées au sein d'un même processus PHP sont rapides. Cependant, la plupart des déploiements PHP lancent un nouveau processus pour chaque requête web, vidant le cache à chaque fois. Résultat : un aller-retour réseau complet pour chaque chemin distinct à chaque requête.

Un site WordPress a un jour mis dix secondes à afficher sa page d'accueil. Les requêtes de base de données étaient rapides, mais la page déclenchait 73 appels S3 individuels juste pour assembler le contenu. Chaque appel était une requête "à froid", transformant un file_exists() apparemment inoffensif en un délai perceptible.

Stratégies d'atténuation

  • Persister le cache entre les requêtes – Déplacez le cache interne au processus vers un magasin partagé tel que Redis. Lorsqu'une requête découvre qu'une clé existe sur S3, les requêtes suivantes lisent le résultat mis en cache au lieu de contacter à nouveau S3.
  • Adopter un flux de travail "local-first" – Écrivez les fichiers sur un disque local, puis synchronisez-les vers S3 de manière asynchrone. Le chemin de requête principal ne touche que le système de fichiers local ; le coût réseau est transféré à une tâche de fond.
  • Auditer la base de code – Recherchez systématiquement les fonctions du système de fichiers susceptibles de se résoudre via un wrapper distant. Signalez celles qui apparaissent dans des boucles serrées ou des chemins critiques pour la requête.
  • Inspecter les données APM – Si le temps de réponse augmente brusquement alors que les métriques de la base de données restent stables, examinez en détail les temps d'exécution du SDK de stockage. Une hausse soudaine de la latence du SDK indique souvent des appels S3 cachés.

L'essentiel à retenir : une fonction qui ressemble à une vérification locale d'une microseconde peut masquer des dizaines de millisecondes de latence réseau. Traitez file_exists() sur S3 comme un appel externe, mettez son résultat en cache et déportez les tâches lourdes hors du chemin de la requête. Sinon, la latence cachée continuera de ralentir votre site, une requête réseau invisible à la fois.