Якщо ви викликаєте file_exists($path) і $path вказує на бакет Amazon S3, ви звертаєтеся не до локального диска — ви робите мережевий запит. Перевірка в один рядок перетворюється на мережевий цикл (round-trip) до S3, що може додавати десятки мілісекунд до кожного запиту.

PHP приховує віддалене сховище за допомогою обгорток потоків (stream wrappers). Такі функції, як fopen(), file_exists(), is_dir(), unlink() та file_put_contents(), проходять через ці обгортки, які перетворюють виклики на операції S3 API. Мапінг є прямим:

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

Кожен виклик спричиняє таку ж затримку (latency), як і відповідний запит до S3 API. Коли скрипт перевіряє десятки файлів, це створює патерн типу N+1: один мережевий стрибок (network hop) для кожної окремої перевірки.

Чому це важливо саме зараз

У типовому середовищі розробки файлова система знаходиться на тій самій машині, тому перевірка повертається майже миттєво. У продакшені, де активи розміщені на S3, той самий код спричиняє різке падіння продуктивності. AWS SDK кешує деякі результати в пам'яті, завдяки чому повторні перевірки в межах одного PHP-процесу здаються швидкими. Однак більшість розгортань PHP створюють новий процес для кожного вебзапиту, щоразу очищуючи кеш. Результат: повний мережевий цикл (round-trip) для кожного окремого шляху при кожному запиті.

Одного разу рендеринг головної сторінки сайту WordPress займав десять секунд. Запити до бази даних були швидкими, але сторінка ініціювала 73 окремі виклики S3 лише для того, щоб зібрати контент. Кожен виклик був «холодним» запитом, що перетворювало, здавалося б, нешкідливий file_exists() на помітну затримку.

Стратегії пом'якшення наслідків

  • Зберігайте кеш між запитами – перенесіть кеш процесу у спільне сховище, наприклад Redis. Коли один запит виявляє, що ключ існує в S3, наступні запити читатимуть кешований результат замість того, щоб знову звертатися до S3.
  • Використовуйте підхід local-first – записуйте файли на локальний диск, а потім синхронізуйте їх з S3 асинхронно. Основний шлях запиту взаємодіє лише з локальною файловою системою; мережеві витрати переносяться на фонову задачу.
  • Проведіть аудит кодової бази – систематично шукайте функції файлової системи, які можуть звертатися до віддаленої обгортки. Позначайте ті, що використовуються всередині циклів або на критичних для запиту шляхах.
  • Аналізуйте дані APM – якщо час відповіді різко зростає, тоді як метрики бази даних залишаються стабільними, дослідіть час виконання операцій SDK сховища. Раптове зростання затримки SDK часто вказує на приховані виклики S3.

Головний висновок: функція, яка виглядає як мікросекундна локальна перевірка, може приховувати десятки мілісекунд мережевої затримки. Ставтеся до file_exists() на S3 як до зовнішнього виклику, кешуйте його результат і виносьте важкі операції за межі шляху запиту. Інакше прихована затримка продовжуватиме сповільнювати ваш сайт — по одному невидимому мережевому запиту за раз.