Estou observando arquivos em busca de alterações usando eventos inotify (por acaso, do Python, chamando a libc).
Para alguns arquivos durante a git clone
, vejo algo estranho: vejo um IN_CREATE
evento e vejo ls
que o arquivo tem conteúdo, no entanto, nunca vejo IN_MODIFY
ou IN_CLOSE_WRITE
. Isso está me causando problemas, pois eu gostaria de responder IN_CLOSE_WRITE
nos arquivos: especificamente, para iniciar um upload do conteúdo do arquivo.
Os arquivos que se comportam estranhamente estão no .git/objects/pack
diretório e terminam em .pack
ou .idx
. Outros arquivos que o git cria têm uma cadeia IN_CREATE
-> IN_MODIFY
-> mais regular IN_CLOSE_WRITE
(não estou observando IN_OPEN
eventos).
Isso está dentro da janela de encaixe no MacOS, mas vi evidências do mesmo na janela de encaixe no Linux em um sistema remoto, portanto, minha suspeita é que o aspecto do MacOS não é relevante. Estou vendo isso se estiver assistindo e git clone
estiver no mesmo contêiner de docker.
Minhas perguntas:
Por que esses eventos estão ausentes nesses arquivos?
O que pode ser feito sobre isso? Especificamente, como posso responder à conclusão das gravações nesses arquivos? Nota: idealmente, gostaria de responder quando a escrita estiver "finalizada" para evitar o upload desnecessário / (incorreto) de textos "inacabados".
Edit: Reading https://developer.ibm.com/tutorials/l-inotify/ parece que o que estou vendo é consistente com
- um arquivo temporário separado, com nome como
tmp_pack_hBV4Alz
, sendo criado, modificado e fechado; - um duro link é criado para este arquivo, com a final
.pack
nome; - o
tmp_pack_hBV4Alz
nome original é excluído.
Eu acho que o meu problema, que é tentar usar o inotify como um gatilho para fazer upload de arquivos, reduz a perceber que o .pack
arquivo é um link físico para outro arquivo e o upload nesse caso?
Respostas:
Para responder sua pergunta separadamente para
git
2.24.1 no Linux 4.19.95:Você não vê
IN_MODIFY
/IN_CLOSE_WRITE
events porquegit clone
sempre tentará usar links físicos para arquivos no.git/objects
diretório. Ao clonar pela rede ou pelos limites do sistema de arquivos, esses eventos aparecerão novamente.Para capturar a modificação de links físicos, você deve configurar um manipulador para o
CREATE
evento inotify que segue e acompanha esses links. Observe que um simplesCREATE
também pode significar que um arquivo não vazio foi criado. Em seguida, emIN_MODIFY
/IN_CLOSE_WRITE
para qualquer um dos arquivos, você também deve acionar a mesma ação em todos os arquivos vinculados. Obviamente, você também precisa remover esse relacionamento noDELETE
evento.Uma abordagem mais simples e mais robusta provavelmente seria o hash periódico de todos os arquivos e verificar se o conteúdo de um arquivo foi alterado.
Correção
Depois de verificar o
git
código-fonte de perto e funcionandogit
comstrace
, descobri quegit
faz arquivos utilizam memória mapeados, mas principalmente para a leitura de conteúdo. Veja o uso doxmmap
qual é sempre chamadoPROT_READ
apenas. . Portanto, minha resposta anterior abaixo NÃO é a resposta correta. No entanto, para fins informativos, eu ainda gostaria de mantê-lo aqui:Você não vê
IN_MODIFY
eventos porquepackfile.c
usammap
para acessar arquivos einotify
não relata modificações parammap
arquivos ed.Na página de manual inotify :
fonte
IN_CLOSE_WRITE
que eu acho que ainda seria acionado ao fechar um arquivo que foi gravadommap
, porque o arquivo precisaria ter sido aberto no modo de gravação?mmap
um arquivo pode ficar um pouco fora de ordem. Por exemplo, você ainda pode gravar em um descritor de arquivo fechado quando tiver o arquivo mapeado na memória.CLOSE_WRITE_CLOSE
mesmo se eu remover oclose
emunmap
no final. Tem que cavar mais fundo na implementação real do git, então ..inotifywait
egit clone
(2.24.1), recebo umOPEN
->CLOSE_NOWRITE,CLOSE
para os*.idx
arquivos. Talvez você tenha esquecido de configurar um manipulador paraCLOSE_NOWRITE,CLOSE
? Nota: Você receberá um*NOWRITE*
porque todas as gravações ocorridas na memória mapeada são.CLOSE_NOWRITE
: o problema é que eu não vejoIN_CLOSE_WRITE
e gostaria de responder às "alterações" do arquivo para acionar um upload, mas ignore o arquivo "lê". Note que, na verdade, acho que agora a limitação do mmap + inotify é um pouco irritante. Eu acho que o problema é que os arquivos.pack
/.idx
são criados inicialmente como links físicos para outro arquivo e, portanto, apenas acionamIN_CREATE
(e oOPEN
->CLOSE_NOWRITE
acontece mais tarde quando o git está realmente lendo os arquivos).Posso especular que o Git na maioria das vezes use atualizações atômicas de arquivos, assim:
mktemp
-estilo)).rename(2)
d -d sobre o original; essa operação garante que todo observador que tente abrir o arquivo usando seu nome obtenha o conteúdo antigo ou o novo.Essas atualizações são vistas
inotify(7)
comomoved_to
eventos - uma vez que um arquivo "reaparece" em um diretório.fonte
IN_MOVED_FROM
eIN_MOVED_TO
eventos. No entanto, não vejo isso acontecendo nos arquivos.pack
e.idx
Com base nessa resposta aceita, eu suponho que possa haver alguma diferença nos eventos com base no protocolo que está sendo usado (por exemplo, ssh ou https).
Você observa o mesmo comportamento ao monitorar a clonagem do sistema de arquivos local com a
--no-hardlinks
opção?Seu comportamento observado ao executar o experimento em um host Linux e Mac provavelmente elimina esse problema em aberto, sendo a causa https://github.com/docker/for-mac/issues/896, mas adicionando apenas o caso.
fonte
Há outra possibilidade (do homem inotify):
E embora
git clone
possa gerar um fluxo pesado de eventos, isso pode acontecer.Como evitar isso:
fonte
Talvez você tenha cometido o mesmo erro que cometi anos atrás. Eu só usei inotify duas vezes. Na primeira vez, meu código simplesmente funcionou. Mais tarde, eu não tinha mais essa fonte e comecei novamente, mas desta vez, estava perdendo eventos e não sabia o porquê.
Acontece que quando eu estava lendo um evento, estava realmente lendo um pequeno lote de eventos. Eu analisei o que eu esperava, pensando que era isso, isso era tudo. Eventualmente, descobri que há mais dados recebidos e, quando adicionei um pouco de código para analisar todos os eventos recebidos de uma única leitura, nenhum outro evento foi perdido.
fonte