Aguarde que o diretório de movimentação seja concluído antes de tentar excluí-lo

4

Eu tenho um processo em execução em um computador que gera simulações escrevendo os dados da simulação no diretório pre/id . Os processos de trabalho copiam uma simulação de pre para um disco local, que pode estar em um computador diferente. pre está em um volume montado com nfs. Esta parte funciona bem.

Quando uma simulação é feita, os resultados são movidos para o diretório result/id , que é o que está causando problemas. O processo de supervisão pode decidir manter esse diretório ou excluí-lo. Ocasionalmente, quando ele tenta excluir result/id , a operação de movimentação parece estar incompleta e a remoção do diretório falha.

Tudo é executado em uma variedade de sabores de linux. Os funcionários movem diretórios usando mv e, em seguida, touch result/id/done para sinalizar ao processo de supervisão que o resultado pode ser lido (e excluído). O processo de supervisão usa boost::filesystem::remove_all para excluir result/id .

Como posso esperar com segurança que a operação de movimentação seja concluída antes de tentar excluí-la?

Adicionado: Este código move o diretório de resultados para onde o processo de supervisão aguarda:

mv $tempDir $finishedCasesDir # copy case to result directory
touch $finishedCasesDir/$caseName/done

Este é o código C ++ que aguarda que done apareça:

if(is_regular_file(resultPath/"done"))
{
  // get relevant result data
  ...
  // remove result directory
  remove_all(resultPath);
}

E o erro:

terminate called after throwing an instance of 'boost::filesystem3::filesystem_error'
what():  boost::filesystem::remove: Directory not empty: "results/711a35ed-818e-4084-ab43-47531fdd8d11"
    
por Christoph 13.02.2014 / 11:26

2 respostas

2

Você se deparou com o comando flock ?

Ele fornece um bloqueio de arquivo dentro do sistema de arquivos para que possa ser usado em scripts de shell.

--- editar

Após minha resposta inicial acima, outras edições foram feitas no post original, ao qual adicionei comentários e uma sugestão final de uma condição de corrida em várias máquinas que estão usando o nfs e planejei um cenário. Este cenário foi desafiado por @alexis, ao que achei que merecia uma resposta.

@alexis você está correto ao trabalhar dentro de um sistema de arquivos, mas a situação se torna mais complicada quando os sistemas de arquivos montados do nfs estão envolvidos.

Não está claro a partir do OP exatamente qual mix de máquinas / servidores / clientes está envolvida nas versões do nfs, mas achei que foi o suficiente para dizer: "Você precisa de um mecanismo de sincronização melhor que o touch-rm". meio que funciona, mas tem uma probabilidade de falha de 1 em 15.000. Por isso, sugeri também que encontre uma maneira melhor de sincronizar ou codificar em torno dele.

Após uma pequena investigação sobre o assunto, encontrei algumas referências que mostram "falhas" em nfs que indicam que a remoção de um arquivo não funciona como esperado em nfs. Mais ainda, existem diferenças entre o nfs v3 e v4, especificamente para resolver essa falha, também o nfs4 poderia funcionar de forma diferente, mas não funciona, ou quebraria a compatibilidade com clientes mais antigos.

Este documento nfs resume a situação que descreve o renomear bobo que foi introduzido no código o problema, e rfc 5661 NFS 4.1 fornece mais detalhes.

- editar 2

Extrato de um parágrafo das referências acima:

Because of the design of the NFS protocol, there is no way for a file to be deleted from the name space but still remain in use by an application. Thus NFS clients have to emulate this using what already exists in the protocol. If an open file is unlinked, an NFS client renames it to a special name that looks like ".nfsXXXXX". This "hides" the file while it remains in use. This is known as a "silly rename." Note that NFS servers have nothing to do with this behavior.

    
por 13.02.2014 / 12:03
1

Idéia # 1 - abordagem alternativa?

Em vez de touch de um arquivo, o que aconteceria se você esperasse o processo mv ser concluído?

$ mv $tempDir $finishedCasesDir & # copy case to result directory
$ wait %1 && touch $finishedCasesDir/$caseName/done

Isso só tocará no arquivo quando o processo mv tiver terminado.

Exemplo

Veja um exemplo usando o comando sleep como substituto do comando mv .

hora de início

$ date
Thu Feb 13 21:23:33 EST 2014

inicie o comando "mv" simulado

$ sleep 10 &
[1] 28561

nós então "esperamos" que ele termine

$ wait %1 && echo 'all done!'
[1]+  Done                    sleep 10
all done!

confirmando que estávamos esperando por ~ 10 segundos.

$ date
Thu Feb 13 21:23:48 EST 2014

continuar

$ ...boost program can then run...

Idéia # 2 - questão NFS?

Com base no feedback de @ Gilles , eu não tinha percebido que você estava trabalhando com esses arquivos através do NFS. Acredito que Gilles esteja 100% correto, eu também encontrei problemas semelhantes ao trabalhar com arquivos via NFS, onde um processo ainda pode ter acesso a um diretório montado pelo NFS que você está tentando excluir. Quando você faz isso, o NFS normalmente cria um arquivo .nfsXXXX no diretório, o que frustrará os aplicativos do Boost que tentam excluir o arquivo, pois ele não está efetivamente vazio.

NOTA: Ter um shell cujo CWD (Current Working Directory) é um subdiretório dentro dessa montagem NFS é suficiente para causar esse problema.

Você pode ler mais sobre esse assunto aqui neste artigo, intitulado: O que é isso? arquivo .nfs e por que não posso removê-lo? .

trecho do artigo acima
% echo test> foo
% tail -f foo
test
^Z
Suspended
% rm foo
% ls -A
.nfsB23D
% rm .nfsB23D
% ls -A
.nfsC23D
% lsof .nfsC23D
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
tail    1257 robh    0r  VREG  176,6        5 3000753 .nfsC23D
%

Aviso: você pode usar a ferramenta lsof para determinar qual processo está mantendo um descritor de arquivo.

Referências

por 14.02.2014 / 03:26