ho una condivisione CIFS da Windows Server 2012 R2 montata su Ubuntu 14.04.2 LTS (kernel 3.13.0-61-generic) come questofile_exists PHP a volte restituisce false per un file in condivisione CIFS
/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
La gid=5000
corrisponde al gruppo www-data
che esegue un processo PHP.
I file vengono montati correttamente quando controllo tramite la console registrata come l'utente www-data
: sono leggibili e rimovibili (le operazioni utilizzate dallo script PHP).
Lo script PHP elabora circa 50-70.000 file al giorno. I file vengono creati sulla macchina host Windows e qualche tempo dopo lo script PHP in esecuzione sulla macchina Linux viene informato di un nuovo file, controlla se il file esiste (file_exists
), lo legge ed elimina. Di solito tutto funziona bene, ma a volte (da alcune centinaia a 1-2 000 al giorno) lo script PHP solleva un errore che il file non esiste. Questo non dovrebbe mai essere il caso, dal momento che viene notificato solo i file effettivamente esistenti.
Quando controllo manualmente quei file segnalati come non esistenti, sono correttamente accessibili sulla macchina Ubuntu e hanno una data di creazione prima che lo script PHP ne verificasse l'esistenza.
Quindi faccio scattare manualmente lo script PHP per raccogliere quel file e viene raccolto senza problemi.
Quello che ho già provato
Ci sono più domande simili, ma mi sembra di aver esaurito tutti i consigli:
- ho aggiunto
clearstatcache()
prima di controllarefile_exists($f)
- I permessi di file e directory OK (lo stesso file viene prelevato correttamente più avanti)
- Il percorso utilizzato per controllare
file_exists($f)
è un percorso assoluto, senza caratteri speciali - i percorsi dei file sono sempre di formato/Share/11/222/333.zip
(con le varie cifre) - ho usato
noserverino
parti montare parametro - ho usato
cache=none
parti montare parametro
/proc/fs/cifs/Stats/
viene visualizzato come sotto, ma non so se c'è qualcosa di sospetto qui. La quota in questione è 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 modello Penso che vedo è che l'errore viene generato solo se il file in questione è stato già elaborato (leggere e cancellato) in precedenza dallo script PHP. Ci sono molti file che sono stati correttamente elaborati e poi rielaborati più tardi, ma non ho mai visto quell'errore per un file che viene elaborato per la prima volta. Il tempo tra la rielaborazione varia da 1 a circa 20 giorni. Per la rielaborazione, il file viene semplicemente ricreato nello stesso percorso sull'host Windows con contenuto aggiornato.
Quale può essere il problema? Come posso indagare meglio? Come posso determinare se il problema si trova sul lato PHP o OS?
Aggiornamento
Ho spostato il software che produce i file in una macchina virtuale Ubuntu che monta le stesse azioni nello stesso modo. Questo componente è codificato in Java. Non vedo alcun problema durante la lettura/scrittura dei file.
Update - dettagli PHP
Il codice esatto PHP è:
$strFile = zipPath($intApplicationNumber);
clearstatcache();
if(!file_exists($strFile)){
return responseInternalError('ZIP file does not exist', $strFile);
}
Il intApplicationNumber
è un parametro di richiesta (ad es 12345678
.) Che è semplicemente trasformato in un percorso da parte del Funzione zipPath()
(ad esempio \Share\12\345\678.zip
- sempre un percorso completo).
Lo script può essere richiamato in concomitanza con diversi numeri di applicazione, ma non verrà richiamato in concomitanza con lo stesso numero di applicazione.
Se lo script non riesce (restituisce l'errore 'ZIP file does not exist'
), verrà richiamato un minuto dopo. Se fallisce, verrà permanentemente contrassegnato come fallito. Poi, di solito più di un'ora dopo, posso richiamare lo script manualmente con la stessa invocazione (GET richiesta) che è fatto sulla produzione e funziona benissimo, il file viene trovato e inviato nella risposta:
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;
}
Dopo che il client ha ricevuto il file, notifica al server PHP che il file può essere spostato in una posizione di archivio (tramite copy()
e unlink()
). Quella parte funziona bene.
risultato Strace
Dopo diversi giorni di nessun errore, l'errore riapparve. Ho eseguito strace
e rapporti
access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)
per alcuni file che esistono quando corro ls /Share/11/222/333.zip
dalla riga di comando. Pertanto il problema è a livello di sistema operativo, PHP non deve essere biasimato.
Gli errori hanno iniziato a comparire quando il carico sul disco sull'host è aumentato (a causa di altri processi), quindi il suggerimento di @ risyasin di seguito sembra molto probabile - si tratta di risorse impegnate/timeout.
Proverò il consiglio di @ miguel-svq di saltare il test di esistenza e di andare subito a cercare subito l'errore fopen()
. Vedrò se cambierà qualcosa.
Buona domanda. Non è la prima volta che sento parlare di qualcosa di simile inaffidabile. Una soluzione alternativa che ti aiuta un po 'potrebbe essere riprovare il file_exists e non interrompere immediatamente lo script. – DanFromGermany
Grazie @DanFromGermany - sì, è una delle idee sporche che ho avuto - riprova (anche dopo una pausa di N secondi) nel caso si tratti di una sorta di singhiozzo temporaneo. Ma vorrei davvero capire perché succede e correggerlo nella radice. –
Non penso davvero che si tratti di php ma di nfs. possono esserci timeout o risorse impegnate dal momento che si basano sul networking. 'strace' e' tcpdump' su entrambi i lati per vedere cosa sta realmente accadendo può darti degli indizi. provare anche con l'utente di php/webserver durante il test. – risyasin