PHP file_exists или is_file не отвечает правильно в течение 10-20 с на NFS-файлах (EC2)

У нас установлена ​​настройка nginx / php-fpm на EC2, которая получает фрагменты файлов в папку «chunk», установленную NFS (специально для SoftNAS), которая распределяется между несколькими серверами приложений. У нас есть проблема, когда приложение проверяет наличие файла перед загрузкой готового файла на S3, но проверка файла не работает, даже если файл есть.

Приложение имеет clearstatcache () на месте до is_file () или file_exists () (мы пробовали оба), но файл не становится видимым для приложения в течение 10-20.

Это результат некоторых прогонов этого теста:

app1 write timestamp 1484702190.5575 app2 read timestamp 1484702216.0643 25.5068 seconds app1 write timestamp 1484702229.0130 app2 read timestamp 1484702246.0652 17.0522 seconds app1 write timestamp 1484702265.6277 app2 read timestamp 1484702276.0646 10.4369 seconds app1 write timestamp 1484702286.0136 app2 read timestamp 1484702306.0645 20.0509 seconds app1 write timestamp 1484702314.4844 app2 read timestamp 1484702336.0648 21.5804 seconds app1 write timestamp 1484702344.3694 app2 read timestamp 1484702366.0644 21.6950 seconds app1 write timestamp 1484702374.0460 app2 read timestamp 1484702396.0645 22.0185 seconds app1 write timestamp 1484702404.0346 app2 read timestamp 1484702426.0647 22.0301 seconds app1 write timestamp 1484702434.2560 app2 read timestamp 1484702456.1092 21.8532 seconds app1 write timestamp 1484702466.0083 app2 read timestamp 1484702486.1085 20.1002 seconds app1 write timestamp 1484702496.5466 app2 read timestamp 1484702516.1088 19.5622 seconds app1 write timestamp 1484702525.2703 app2 read timestamp 1484702546.1089 20.8386 seconds app1 write timestamp 1484702558.3312 app2 read timestamp 1484702576.1092 17.7780 seconds 

Мы проверили несколько вариантов проверки файла:

  • Использование функций is_file и file_exists при проверке наличия файла на app2.
  • Все варианты использования функции clearstatcache перед проверкой наличия файла на app2.
  • Прикоснитесь к файлу перед записью на него в приложении 1.
  • Коснитесь файла, прежде чем проверять, существует ли он на app2.
  • Используя различные методы записи файла из приложения 1 и явного закрытия потока записи и освобождения блокировки файла.
  • Изменение задержки между считываемыми контурами (например, без задержки или с задержкой в ​​1 секунду).
  • Использование exec для «ls» каталога после записи в него из приложения 1.
  • Использование exec для «ls» в каталоге перед каждым файлом существует проверка на app2.

Ничто из этого не имеет никакого значения. Мы не проводили подробных тестов по каждому варианту, но каждый из них, казалось, занимал слишком много времени, прежде чем файл будет проверен.

Есть одна вещь, которая действительно работала. Выполняя цикл «ls» на app2 в оболочке, файл мгновенно читается скриптом app2.

  app1 write timestamp 1484703581.3749 app2 read timestamp 1484703581.3841 0.0092 seconds app1 write timestamp 1484703638.81 00 app2 read timestamp 1484703638.8139 0.0039 seconds app1 write timestamp 1484703680.8548 app2 read timestamp 1484703680.8576 0.0028 seconds 

Итак, что-то в оболочке правильно очищает кеш-память NFS, но команда clear cache в PHP, похоже, не имеет никакого значения.

(Изменить) код в вопросе:

 public static function get($filepath) { clearstatcache(TRUE, $filepath); if (file_exists($filepath)) { $instance = new static::$_class; $instance->init($filepath); return $instance; } else { // Sometimes a new file is not found with the first is_file() attempt. // Clear the stat cache and try to find the file again. clearstatcache(TRUE, $filepath); if (file_exists($filepath)) { $instance = new static::$_class; $instance->init($filepath); return $instance; } } Log::error("AJRFSFILE " . $_SERVER['PATH_INFO'] . " " . $_SERVER['HTTP_DEVICE'] . " " . $filepath . " " . json_encode(stat($filepath))); return false; } 

(Edit2) Выключает запуск exec () с «ls» в коде, успешно очищает любое кэширование уровня файла на системном уровне, но по очевидным причинам exec () каждый раз, когда мы делаем file_exists, является неоптимальным решение.

Вот что происходит. Кэш статиста PHP использует атрибут atime, доступный из базовой VFS. Когда NFS передает VFS, атрибуты могут кэшировать, чтобы уменьшить количество обращений к серверу. К сожалению, они могут заставить PHP «лгать» о состоянии, потому что на самом деле сервер NFS не предоставил текущую информацию VFS.

Вы можете принудительно установить согласованность с noac mount. Я рекомендую использовать это на любом сервере, где вам абсолютно, в лучшем случае нужна самая последняя информация в кратчайшие сроки:

Используйте параметр noac mount для обеспечения согласованности кеша атрибутов между несколькими клиентами. Почти каждая операция файловой системы проверяет информацию об атрибутах файла. Клиент сохраняет эту информацию в кэше в течение определенного периода времени, чтобы снизить нагрузку на сеть и сервер. Когда noac действует, кеш атрибута файла клиента отключен, поэтому каждая операция, которая должна проверять атрибуты файла, принудительно возвращается на сервер. Это позволяет клиенту просматривать изменения в файле очень быстро, за счет многих дополнительных сетевых операций.

Если noac работает слишком медленно, есть другие опции монтирования, которые могут лучше настроить кеш для ваших нужд. Смотрите: lookupcache и actimeo . Например, уменьшение actimeo уменьшит время, в течение которого NFS локально кэширует информацию: по умолчанию 30 секунд (минимум) до 60 секунд (максимум). Или, как еще один пример, lookupcache=positive обеспечит более быстрый интеллект при появлении новых файлов, но будет долго кэшировать их существование даже после отключения.

Но почему, когда у вас нет этих параметров монтирования, ls в каталоге «исправить» проблему? Оказывается, что opendir и closedir последовательность недействительны кеш атрибутов NFS, что заставляет обратный вызов на сервер.

Поэтому в вашем случае вы используете последовательность opendir()/closedir() для аннулирования кеша. Я не уверен, что system("ls") будет работать, так как я полагаю, что каждый процесс имеет другое представление о кэше базового атрибута, но стоит попробовать.