2016-02-18 28 views
27

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 controllare file_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.

+1

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

+0

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. –

+3

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

risposta

1

Si può provare a utilizzare il directio opzione per evitare di fare il caching dei dati inode sul file aperti su questo monte:

//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 
+0

La [pagina man] (http://linux.die.net/man/8/mount.cifs) dice "Questa opzione sarà deprecata in 3.7 Gli utenti dovrebbero usare cache = none invece sui kernel più recenti". Il mio kernel è 3.13 e ho già 'cache = none'. L'uso di 'directio' ha senso allora? –

0

Questo non è certo una risposta definitiva al mio problema, piuttosto una sintesi di ciò che Ho scoperto e con cosa ho sistemato.

Nella parte inferiore del problema si trova che è il sistema operativo che segnala che il file non esiste. Esecuzione strace mostra di tanto in tanto

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

per i file che esistono (e mostrano quando elencato con ls).

L'host di condivisione di Windows era talvolta sottoposto a un carico di disco pesante. Quello che ho fatto è spostare una delle condivisioni su un host diverso in modo che il carico sia ora distribuito tra i due. Inoltre, il carico generale del sistema è un po 'più leggero ultimamente. Ogni volta che ottengo l'errore sul file non esistente, riprovo la richiesta qualche tempo dopo e non è più lì.