readable - php file_exists url http
PHP file_exists a veces devuelve false para un archivo en un recurso compartido de CIFS (2)
Puede intentar usar la opción directio para evitar hacer caché de datos de inodo en archivos abiertos en este montaje:
//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
Tengo un recurso compartido CIFS de Windows Server 2012 R2 montado en Ubuntu 14.04.2 LTS (kernel 3.13.0-61-generic) como este
/ etc / 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
El gid=5000
corresponde al grupo www-data
que ejecuta un proceso PHP.
Los archivos se montan correctamente cuando verifico a través de la consola que inició sesión como usuario de www-data
; son legibles y extraíbles (las operaciones que usa el script PHP).
El script PHP está procesando alrededor de 50-70 000 archivos por día. Los archivos se crean en la máquina host de Windows y, algún tiempo después, se notifica al script PHP que se ejecuta en la máquina con Linux sobre un nuevo archivo, comprueba si el archivo existe ( file_exists
), lo lee y lo elimina. Por lo general, todo funciona bien, pero a veces (de unos pocos cientos a 1-2 000 por día), el script PHP genera un error que indica que el archivo no existe. Ese nunca debería ser el caso, ya que solo se notifica de los archivos realmente existentes.
Cuando verifico manualmente los archivos reportados como no existentes, se puede acceder a ellos correctamente en la máquina de Ubuntu y tienen una fecha de creación desde antes de que el script PHP verificara su existencia.
Luego, desencadeno el script PHP manualmente para recoger ese archivo y se recoge sin problemas.
Lo que ya probé
Hay varias preguntas similares, pero parece que he agotado todos los consejos:
-
clearstatcache()
antes de verificarfile_exists($f)
- Los permisos de archivo y directorio son correctos (exactamente el mismo archivo se selecciona correctamente más adelante)
- La ruta utilizada para verificar
file_exists($f)
es una ruta absoluta sin caracteres especiales; las rutas de los archivos siempre tienen el formato/Share/11/222/333.zip
(con varios dígitos) -
noserverino
parámetronoserverino
share mount - Utilicé
cache=none
compartir el parámetro de montaje
/proc/fs/cifs/Stats/
displays como se muestra a continuación, pero no sé si hay algo sospechoso aquí. La acción en cuestión es 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
Un patrón que creo que veo es que el error se genera solo si el archivo en cuestión ya ha sido procesado (leído y eliminado) anteriormente por el script PHP. Hay muchos archivos que se han procesado correctamente y luego se han procesado de nuevo más tarde, pero nunca he visto ese error para un archivo que se procesa por primera vez. El tiempo entre el re-procesamiento varía de 1 a aproximadamente 20 días. Para volver a procesar, el archivo simplemente se vuelve a crear en la misma ruta en el host de Windows con contenido actualizado.
¿Cual puede ser el problema? ¿Cómo puedo investigar mejor? ¿Cómo puedo determinar si el problema está en el lado de PHP o del sistema operativo?
Actualizar
He movido el software que produce los archivos a una máquina virtual de Ubuntu que monta los mismos recursos compartidos de la misma manera. Este componente está codificado en Java. No veo ningún problema al leer / escribir en los archivos.
Actualización - Detalles de PHP
El código PHP exacto es:
$strFile = zipPath($intApplicationNumber);
clearstatcache();
if(!file_exists($strFile)){
return responseInternalError(''ZIP file does not exist'', $strFile);
}
El intApplicationNumber
es un parámetro de solicitud (por ejemplo, 12345678
) que simplemente se transforma en una ruta mediante la función zipPath()
(por ejemplo, /Share/12/345/678.zip
- siempre una ruta completa).
El script puede invocarse simultáneamente con diferentes números de aplicación, pero no se invocará simultáneamente con el mismo número de aplicación.
Si la secuencia de comandos falla (devuelve el error ''ZIP file does not exist''
), se volverá a llamar un minuto después. Si eso falla, será marcado permanentemente como fallido. Luego, generalmente más de una hora después, puedo llamar al script manualmente con la misma invocación (solicitud GET) que se realizó en la producción y funciona bien, el archivo se encuentra y se envía en la respuesta:
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;
}
Después de que el cliente recibe el archivo, notifica al servidor de PHP que el archivo se puede mover a una ubicación de archivo (mediante copy()
y unlink()
). Esa parte funciona bien.
Resultado STRACE
Después de varios días sin errores, el error volvió a aparecer. Corrí strace
y reporta
access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)
para algunos archivos que existen cuando ejecuto ls /Share/11/222/333.zip
desde la línea de comandos. Por lo tanto, el problema está en el nivel del sistema operativo, no se debe culpar a PHP.
Los errores comenzaron a aparecer cuando la carga en el disco en el host aumentó (debido a otros procesos), por lo que la sugerencia de @ risyasin parece más probable: es una cuestión de recursos / tiempos de espera ocupados.
Intentaré el consejo de @miguel-svq de saltarse la prueba de existencia y simplemente ir por fopen()
inmediato y luego manejar el error. Voy a ver si cambia algo.
Esto no es una respuesta definitiva a mi problema, más bien un resumen de lo que descubrí y con lo que resolví.
En la parte inferior del problema se encuentra que es el sistema operativo el que informa que el archivo no existe. Ejecutando strace
muestra de vez en cuando
access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)
para los archivos que existen (y aparecen cuando están listados con ls
).
El host de Windows compartido estaba a veces bajo una gran carga de disco Lo que hice es mover una de las acciones a un host diferente para que la carga se distribuya ahora entre las dos. Además, la carga general en el sistema es un poco más ligera últimamente. Cada vez que recibo el error sobre el archivo que no existe, reintento la solicitud un tiempo después y ya no existe.