Resumindo: Sim, as gravações simultâneas de vários clientes NFS serão corrompidas.
Os acréscimos simultâneos localmente são bem intercalados, já que o sistema operacional sabe que o arquivo é aberto no modo de acréscimo e atua atomicamente no final atual do arquivo antes de cada chamada de gravação, independentemente de outros processos que possam ter estendido o arquivo.
Mas no NFS, não tem essa sorte. A FAQ do Linux NFS afirma claramente:
A9. Why does opening files with O_APPEND on multiple clients cause the files to become corrupted?
A. The NFS protocol does not support atomic append writes, so append writes are never atomic on NFS for any platform.
Most NFS clients, including the Linux NFS client in kernels newer than 2.4.20, support "close to open" cache consistency,A8. What is close-to-open cache consistency?
A. Perfect cache coherency among disparate NFS clients is very expensive to achieve, so NFS settles for something weaker that satisfies the requirements of most everyday types of file sharing. [...]When the application closes the file, the NFS client writes back any pending changes to the file so that the next opener can view the changes. This also gives the NFS client an opportunity to report any server write errors to the application via the return code from close(). This behavior is referred to as close-to-open cache consistency.
As operações de gravação do NFS contêm apenas uma posição para escrever e os dados a serem gravados. Não há nenhuma disposição para coordenar centralmente onde o final do arquivo é, que é o que seria necessário para garantir que os clientes não sobrescrevessem uns aos outros.
(A página parece um pouco antiga, já que fala principalmente sobre a versão 2.6 do kernel Linux, sem mencionar 3.x. A versão 4 do NFS é mencionada em relação ao suporte do kernel, então pode-se supor que a resposta se aplica para v4 também.)
Um pequeno teste feito em um compartilhamento recente do Linux e do NFS v3:
Escrever números em um loop de shell ( for ((i=0 ; i<9999 ; i++)) ; do printf "$id %06d\n" $i >> testfile ; done
), em dois clientes ao mesmo tempo, resulta em uma saída bem corrompida. Parte disso:
barbar 001031
foo 000010
32
foo 000011
33
foo 000012
Aqui, o primeiro loop em uma máquina escreveu linhas com barbar
, enquanto outro loop em outra máquina escreveu as linhas foo
. A linha que deve dizer barbar 001032
é escrita iniciando na mesma posição que foo 000010
line e somente os números finais da linha mais longa são visíveis. (Observe que, nesse caso, o arquivo é realmente aberto e fechado para cada printf
, já que o redirecionamento está dentro do loop. Mas só ajuda a descobrir qual era o final do arquivo quando o arquivo foi aberto.)
Se o arquivo for mantido aberto o tempo todo, os blocos maiores poderão ser sobrescritos, já que a promessa é apenas de que o sistema cliente grava as alterações no servidor quando o arquivo é fechado. Mesmo truncar o arquivo ao abrir não muda muito, já que o truncamento apenas limpa o arquivo, mas não impede gravações adicionais do outro cliente quando ele fecha o arquivo.