por que um arquivo às vezes aparece vazio ao ser gravado por outro processo?

1

Temos uma situação em que os arquivos são enviados para uma pasta por FTP e, em seguida, veiculados por nginx. Descobrimos que, se a solicitação GET seguir imediatamente a modificação do arquivo, nginx retornará arquivos com 0 bytes.

Ao tentar depurar este problema, escrevi 2 scripts python para ver se conseguia reproduzir o erro de uma forma simples.

O primeiro escreve em um arquivo

  while True:
      with open('testfile' , 'w') as f:
          f.write("test")

E o segundo lê

  while True:
      with open('testfile' , 'r') as cf:
          print(cf.read())

ao executar esses arquivos em dois processos separados, a saída do leitor é "test" ou "" indicando que, às vezes, o arquivo parece vazio para o leitor. Isso não parece estar relacionado à implementação do python, já que posso reproduzir o efeito com o bash assim:

(writer.sh)

  while true; do
      echo test > testfile
  done

(reader.sh)

  while true; do
      cat testfile
      printf "\n"
  done

O sistema de arquivos é ext4 e o sistema operacional é Ubuntu 16.04.

Então:

Por que o leitor às vezes vê um arquivo vazio (em torno de 50% do tempo)?

Por que nunca vemos uma gravação parcial ("te", "tes" etc)?

Agradecemos antecipadamente por sua ajuda.

    
por Russell 20.04.2018 / 13:27

4 respostas

3

Parabéns, você acabou de descobrir o armazenamento em buffer de arquivos. Ao gravar em disco, você pode usar gravações armazenadas em buffer ou gravações diretas de E / S. Por motivos de desempenho, a maioria dos softwares (incluindo o interpretador Python) usa como padrão as gravações em buffer. Se você precisa realizar E / S direta, existe um bom módulo python apropriadamente chamado de directio que faz exatamente isso.

No entanto, na maioria das vezes, você não precisa de E / S direta, a menos que esteja escrevendo em algum arquivo de log ou em um banco de dados.

    
por 20.04.2018 / 15:52
2

Outros descreveram como esta é a E / S armazenada em buffer, onde você vê o arquivo truncado antes de seu conteúdo ser liberado.

Mais alguns detalhes sobre algumas maneiras de resolver isso:

Carregue os arquivos em um diretório temporário no mesmo sistema de arquivos que o destino e, em seguida, mv no lugar. A renomeação é uma operação atômica, portanto, os leitores verão apenas o arquivo antigo ou o novo, e não algo intermediário. No entanto, o kernel ainda consegue terminar de gravar no disco em sua programação, a menos que o aplicativo chame fsync (). Fechando o arquivo ou aguardando algum tempo arbitrário, não faz com que o arquivo fique no disco.

Ou altere o aplicativo para ser apoiado por um banco de dados. Deixe o banco de dados fornecer uma visão consistente do documento na memória e no armazenamento, é o que eles fazem. Possivelmente não vale a pena o esforço de implementação, se a única razão é livrar-se de uma janela muito pequena de inconsistência.

    
por 22.04.2018 / 03:16
1

Você provavelmente está passando por uma condição de corrida em que:

  • a gravação trunca o arquivo devido ao redirecionamento (">").
  • o arquivo é lido pelo leitor (arquivo vazio).
  • o arquivo é escrito pelo escritor.

Se você colocar um breve sono no loop do gravador, deverá ver isso com muito menos freqüência.

Você pode evitar isso usando uma ação atômica para criar o arquivo, como:

while true do;
    echo test > file.tmp
    mv file.tmp testfile
done

Seu código original irá continuamente truncar e escrever o mesmo arquivo. O loop acima criará continuamente novos arquivos. O comando mv é atômico e o leitor sempre verá um arquivo com dados. Este será o arquivo excluído pelo mv ou o novo arquivo.

    
por 22.04.2018 / 01:38
0

Os arquivos tendem a ser escritos em blocos (subconjuntos dos dados completos), o tamanho desses blocos é determinado por uma combinação da função que está sendo usada e dos recursos do sistema disponíveis. A partir disso, o sistema operacional geralmente tenta otimizar o bloco. Tamanho. O que acontece é que o bloco será gravado na RAM antes de ser gravado no disco e, em seguida, um bloco inteiro será gravado no disco de uma só vez, sem tempo para uma leitura durante a gravação do bloco. Isso leva a uma gravação muito mais rápida do que seria possível.

No seu caso, escrevendo a palavra, "teste" será menor do que qualquer tamanho de bloco escolhido pelo sistema operacional, para que tudo seja escrito de uma só vez. Para o seu teste, você deve escrever um teste muito maior e, provavelmente, definir o tamanho do bloco (embora seja melhor deixar o SO decidir na maior parte do tempo).

Eu suspeito que o que está acontecendo em seu teste é que metade do tempo em que você está pegando o arquivo vazio antes de ele ser gravado e a outra metade ele está capturando-o após o bloco ter sido escrito. Se você tentar escrever uma quantidade de dados maior que o tamanho do seu bloco, eu acho que você verá os arquivos parcialmente escritos.

    
por 20.04.2018 / 14:36