Domanda

Parte della mia ultima webapp deve scrivere per archiviare un discreto importo come parte della sua registrazione. Un problema che ho notato è che se ci sono alcuni utenti simultanei, le scritture possono si sovrascrivono a vicenda (invece di aggiungere un file). Presumo che ciò sia dovuto al fatto che il file di destinazione può essere aperto in più punti contemporaneamente.

flock (...) è in genere eccezionale ma non sembra funzionare su NFS ... Il che è un grosso problema per me poiché il server di produzione utilizza un array NFS.

La cosa più vicina che ho visto a una soluzione reale riguarda il tentativo di creare una directory di blocco e attendere fino a quando non può essere creata. Dire che manca di eleganza è un eufemismo dell'anno, forse del decennio.

Qualche idea migliore?

Modifica: dovrei aggiungere che non ho root sul server e fare l'archiviazione in un altro modo non è davvero possibile in qualsiasi momento presto, non ultimo entro la mia scadenza.

È stato utile?

Soluzione

Un altro trucco sporco sarebbe flock () a " local " e apri / scrivi sul file NFS solo se tieni il blocco sul file locale.

Modifica: dalla pagina flock () :

  

flock () non funzionerà su NFS e molti   altri file system collegati in rete. Dai un'occhiata   la documentazione del sistema operativo   per maggiori dettagli.

Modifica 2:

Ovviamente si usa sempre il database per sincronizzare l'accesso (suppongo che la tua app usi un db). Questo sarebbe un grande successo se stai registrando molto.

Se è solo per la registrazione, hai davvero bisogno di un file di registro centralizzato? Potresti accedere localmente (e anche combinare i log quando ruotano alla fine della giornata, se necessario)?

Altri suggerimenti

Le operazioni di directory sono NON atomiche in NFSv2 e NFSv3 (fare riferimento al libro "NFS Illustrated" di Brent Callaghan, ISBN 0-201-32570-5; Brent è un veterano di NFS a Sun).

NFSv2 ha due operazioni atomiche:

  • link simbolico
  • rinomina

Con NFSv3 anche la chiamata di creazione è atomica.

Sapendo questo, è possibile implementare spin-lock per file e directory (in shell, non PHP):

blocca dir corrente:

while ! ln -s . lock; do :; done

blocca un file:

while ! ln -s ${f} ${f}.lock; do :; done 

sblocco (presupposto, il processo in esecuzione ha davvero acquisito il blocco):

sblocca dir corrente:

mv lock deleteme && rm deleteme

sblocca un file:

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme
Anche

Rimuovi non è atomico, quindi prima la rinomina (che è atomico) e quindi rimuovi.

Per le chiamate symlink e rename, entrambi i nomi dei file devono risiedere sul file stesso filesystem. La mia proposta: usa solo nomi di file semplici e inserisci file e blocco nella stessa directory.

Un approccio potrebbe essere quello di impostare un'istanza Memcache , condivisa tra ciascuno dei server virtuali . Puoi ape flock () inserendo una voce del nome file nella cache quando avvii le operazioni del file locale e cancellandolo al termine.

Ogni server può accedere a questo pool prima di un'operazione di file e vedere se questo "blocca" è presente, ad esempio

// Check for lock, using $filename as key
$lock = $memcache->get($filename);

if(!$lock) {
    // Set lock in memcache for $filename
    $memcache->set($filename, 1);

    // Do file operations...

    // Blow away "lock"
    $memcache->delete($filename);
}

Non è la soluzione più elegante, ma dovrebbe consentirti di controllare i blocchi da tutti i server nella tua configurazione con relativa facilità.

Puoi anche usare dio_fcntl () per bloccare i file su volumi NFS. Richiede il pacchetto dio, che per impostazione predefinita potrebbe non far parte dell'installazione php.

Anche se non è possibile floccare () i file su NFS e I / O possono essere asincroni, le operazioni di directory su NFS sono atomiche. Ciò significa che in qualsiasi momento, una directory esiste o non esiste.

Per implementare la tua funzione di blocco NFS, controlla o crea una directory quando vuoi che sia bloccata e rimuovila quando hai finito.

Sfortunatamente, probabilmente non è compatibile con qualsiasi altra applicazione che non hai scritto tu.

Dovresti semplicemente usare memcache add ed evitare una race condition.

if ($memcache->add($filename, 1, 1))
{
   $memcache->delete($filename);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top