Linux — 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.
  • Нажмите на файл, прежде чем писать в него в app1.
  • Нажмите на файл, прежде чем проверять, существует ли он в app2.
  • Используя разные методы записи файла из app1 и
    явное закрытие потока записи и снятие блокировки на
    файл.
  • Различные задержки между циклами чтения (например, без задержки или
    с задержкой до 1 секунды).
  • Использование exec для «ls» каталога после записи в него из app1.
  • Используя 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, но команда очистки кеша в 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, является неоптимальным решение.

2

Решение

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

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

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

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

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

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

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]