Подтвердить что ты не робот

PHP file_exists иногда возвращает false для файла на общем ресурсе CIFS

У меня есть общий ресурс CIFS от Windows Server 2012 R2, установленный на Ubuntu 14.04.2 LTS (ядро 3.13.0-61-generic), подобное этому

/и т.д. /Fstab

//10.1.2.3/Share /Share cifs credentials=/root/.smbcredentials/share_user,user=share_user,dirmode=0770,filemode=0660,uid=4000,gid=5000,forceuid,forcegid,noserverino,cache=none 0 0

gid=5000 соответствует группе www-data, которая запускает процесс PHP.

Файлы устанавливаются правильно, когда я проверяю через консоль, зарегистрированную как пользователь www-data, - они читаемы и удалены (операции, которые используются PHP скрипт).

PHP скрипт обрабатывает около 50-70 000 файлов в день. Файлы создаются на хост-компьютере Windows, и через некоторое время PHP script, запущенный на машине Linux, уведомляется о новом файле, проверяет, существует ли файл (file_exists), читает его и удаляет. Обычно все работает нормально, но иногда (от нескольких сотен до 1-2 000 в день) PHP скрипт вызывает ошибку, что файл не существует. Это никогда не должно быть так, поскольку оно уведомляется только о фактически существующих файлах.

Когда я вручную проверяю те файлы, которые были указаны как не существующие, они корректно доступны на машине Ubuntu и имеют дату создания до того, как PHP script проверил их существование.

Затем я запускаю PHP script вручную, чтобы забрать этот файл, и он подбирается без проблем.

Что я уже пробовал

Есть несколько похожих вопросов, но я, кажется, исчерпал все советы:

  • Я добавил clearstatcache() перед проверкой file_exists($f)
  • Разрешения для файлов и каталогов в порядке (точно такой же файл будет получен позже)
  • Путь, используемый для проверки file_exists($f), - это абсолютный путь без специальных символов - пути к файлам всегда имеют формат /Share/11/222/333.zip (с различными цифрами)
  • Я использовал параметр
  • Я использовал параметр

/proc/fs/cifs/Stats/ отображается, как показано ниже, но я не знаю, есть ли здесь что-то подозрительное. Эта доля имеет значение 2) \\10.1.2.3\Share

Resources in use
CIFS Session: 1
Share (unique mount targets): 2
SMB Request/Response Buffer: 1 Pool size: 5
SMB Small Req/Resp Buffer: 1 Pool size: 30
Operations (MIDs): 0

6 session 2 share reconnects
Total vfs operations: 133925492 maximum at one time: 11

1) \\10.1.2.3\Share_Archive
SMBs: 53824700 Oplocks breaks: 12
Reads:  699 Bytes: 42507881
Writes: 49175075 Bytes: 801182924574
Flushes: 0
Locks: 12 HardLinks: 0 Symlinks: 0
Opens: 539845 Closes: 539844 Deletes: 156848
Posix Opens: 0 Posix Mkdirs: 0
Mkdirs: 133 Rmdirs: 0
Renames: 0 T2 Renames 0
FindFirst: 21 FNext 28 FClose 0
2) \\10.1.2.3\Share
SMBs: 50466376 Oplocks breaks: 1082284
Reads:  39430299 Bytes: 2255596161939
Writes: 2602 Bytes: 42507782
Flushes: 0
Locks: 1082284 HardLinks: 0 Symlinks: 0
Opens: 2705841 Closes: 2705841 Deletes: 539832
Posix Opens: 0 Posix Mkdirs: 0
Mkdirs: 0 Rmdirs: 0
Renames: 0 T2 Renames 0
FindFirst: 227401 FNext 1422 FClose 0

Один шаблон, я думаю, я вижу, что ошибка возникает только в том случае, если файл, о котором идет речь, уже обрабатывается (читается и удаляется) ранее PHP script. Есть много файлов, которые были правильно обработаны, а затем обработаны позже, но я никогда не видел эту ошибку для файла, который обрабатывается в первый раз. Время между повторной обработкой варьируется от 1 до 20 дней. Для повторной обработки файл просто воссоздается по тому же пути на хосте Windows с обновленным контентом.

В чем может быть проблема? Как лучше исследовать? Как определить, лежит ли проблема на стороне PHP или ОС?


Обновление

Я переместил программное обеспечение, которое создает файлы на виртуальную машину Ubuntu, которая монтирует те же общие ресурсы таким же образом. Этот компонент закодирован в Java. Я не вижу никаких проблем при чтении/записи файлов.


Обновление - подробности PHP

Точный PHP-код:

$strFile = zipPath($intApplicationNumber);

clearstatcache();

if(!file_exists($strFile)){
    return responseInternalError('ZIP file does not exist', $strFile);
}

intApplicationNumber - это параметр запроса (например, 12345678), который просто преобразуется в путь с помощью функции zipPath() (например, \Share\12\345\678.zip - всегда полный путь).

script может быть вызван одновременно с разными номерами приложений, но не будет вызываться одновременно с тем же номером приложения.

Если сбой script (возвращает ошибку 'ZIP file does not exist'), он будет вызываться снова через минуту. Если это не удастся, оно будет постоянно помечено как сбой. Затем, как правило, более часа спустя, я могу вызвать script вручную с тем же вызовом (GET-запрос), который он сделал при создании, и он отлично работает, файл найден и отправлен в ответ:

public static function ResponseRaw($strFile){
    ob_end_clean();
    self::ReadFileChunked($strFile, false);
    exit;
}

protected static function ReadFileChunked($strFile, $blnReturnBytes=true) {
    $intChunkSize = 1048576; // 1M
    $strBuffer = '';
    $intCount = 0;
    $fh = fopen($strFile, 'rb');

    if($fh === false){
        return false;
    }

    while(!feof($fh)){
        $strBuffer = fread($fh, $intChunkSize);
        echo $strBuffer;
        if($blnReturnBytes){
            $intCount += strlen($strBuffer);
        }
    }

    $blnStatus = fclose($fh);

    if($blnReturnBytes && $blnStatus){
        return $intCount;
    }

    return $blnStatus;
}

После того, как клиент получит файл, он сообщает серверу PHP, что файл может быть перемещен в место архива (с помощью copy() и unlink()). Эта часть отлично работает.


Результат STRACE

После нескольких дней отсутствия ошибок ошибка снова появилась. Я запустил strace, и он сообщает

access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)

для некоторых файлов, которые существуют, когда я запускаю ls /Share/11/222/333.zip из командной строки. Поэтому проблема на уровне ОС, PHP не следует обвинять.

Ошибки начали появляться, когда загрузка на диске на хосте увеличилась (из-за других процессов), поэтому предложение @risyasin ниже кажется наиболее вероятным - это вопрос занятых ресурсов/тайм-аутов.

Я попробую @miguel-svq совет пропустить тест на существование и сразу перейти на fopen() и обработать ошибку. Я посмотрю, изменит ли он что-нибудь.

4b9b3361

Ответ 1

Вы можете попробовать использовать параметр directio, чтобы избежать кэширования данных inode в файлах, открытых на этом монтировании:

//10.1.2.3/Share /Share cifs credentials=/root/.smbcredentials/share_user,user=share_user,dirmode=0770,filemode=0660,uid=4000,gid=5000,forceuid,forcegid,noserverino,cache=none,directio 0 0

Ответ 2

Это едва ли окончательный ответ на мою проблему, а краткое изложение того, что я узнал и с чем я согласился.

В основе проблемы лежит то, что OS сообщает, что файл не существует. Время от времени strace показывает

access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)

для файлов, которые существуют (и отображаются, когда они перечислены с помощью ls).

Хост-узел Windows иногда находился под большой нагрузкой на диск. То, что я сделал, - это переместить одну из долей на другой хост, чтобы теперь распределялась загрузка между ними. Кроме того, общая нагрузка на систему в последнее время немного легче. Всякий раз, когда я получаю ошибку о том, что файл не существует, я повторяю запрос через некоторое время, и он больше не существует.