Eventi inotify mancanti (nella directory .git)


11

Sto guardando i file per le modifiche usando gli eventi inotify (come succede, da Python, chiamando in libc).

Per alcuni file durante un git clone, vedo qualcosa di strano: vedo un IN_CREATEevento e vedo via lsche il file ha contenuto, tuttavia, non vedo mai IN_MODIFYo IN_CLOSE_WRITE. Questo mi sta causando problemi poiché vorrei rispondere ai IN_CLOSE_WRITEfile: in particolare, avviare un caricamento del contenuto del file.

I file che si comportano in modo strano si trovano nella .git/objects/packdirectory e terminano in .packo .idx. Altri file creati da Git hanno una catena IN_CREATE-> IN_MODIFY-> più regolare IN_CLOSE_WRITE(non cerco IN_OPENeventi).

Questo è all'interno della finestra mobile su MacOS, ma ho visto prove dello stesso sulla finestra mobile su Linux in un sistema remoto, quindi il mio sospetto è che l'aspetto di MacOS non sia pertinente. Lo vedo se guardo e git clonesono nello stesso contenitore docker.

Le mie domande:

  • Perché questi eventi mancano su questi file?

  • Cosa si può fare al riguardo? In particolare, come posso rispondere al completamento delle scritture su questi file? Nota: idealmente vorrei rispondere quando la scrittura è "finita" per evitare di caricare inutilmente / (erroneamente) la scrittura "incompiuta".


Modifica: leggendo https://developer.ibm.com/tutorials/l-inotify/ sembra che ciò che vedo sia coerente con

  • un file temporaneo separato, con nome simile tmp_pack_hBV4Alz, creato, modificato e chiuso;
  • un difficile collegamento viene creato a questo file, con la finale .packnome;
  • il tmp_pack_hBV4Alznome originale è cancellato.

Penso che il mio problema, che sta cercando di utilizzare inotify come trigger per caricare file, si riduce a notare che il .packfile è un collegamento reale a un altro file e il caricamento in questo caso?


La risposta potrebbe essere da qualche parte qui ...
Choroba,

@choroba Potresti avere ragione ... Vedo molti riferimenti a mmap e inotify non riporta l'accesso mmap ai file
Michal Charemza,

1
A proposito, qual è il problema originale che stai cercando di risolvere (con inotify)? Potrebbe esserci una soluzione più solida che provare a indovinare cosa sta facendo / ha fatto un processo Git in un repository?
kostix il

@kostix Fa parte di github.com/uktrade/mobius3 , sincronizzando le cartelle home degli utenti dai contenitori che eseguono JupyterLab o RStudio in AWS Fargate, da e verso S3, e in quelle cartelle home possono esserci cartelle .git. So che la soluzione di inotify non sarà mai "robusta-robusta" ... ma spero che possa essere "abbastanza robusta".
Michal Charemza,

1
@tink Sembra che la risposta accettata sia una patch sul kernel Linux? Funzionerebbe sospetto in generale, ma nel mio caso su Fargate non ho quel controllo. (E ammetto che temo leggermente le conseguenze di dipendere da un kernel patchato a lungo termine anche se avessi quel potere ...)
Michal Charemza il

Risposte:


5

Per rispondere alla tua domanda separatamente per git2.24.1 su Linux 4.19.95:

  • Perché questi eventi mancano su questi file?

Non vedi IN_MODIFY/ IN_CLOSE_WRITEeventi perché git clonecercheranno sempre di utilizzare i collegamenti reali per i file nella .git/objectsdirectory. Quando si esegue la clonazione in rete o oltre i limiti del file system, questi eventi verranno nuovamente visualizzati.

  • Cosa si può fare al riguardo? In particolare, come posso rispondere al completamento delle scritture su questi file? Nota: idealmente vorrei rispondere quando la scrittura è "finita" per evitare di caricare inutilmente / (erroneamente) la scrittura "incompiuta".

Al fine di rilevare la modifica dei collegamenti fisici, è necessario impostare un gestore per l' CREATEevento inotify che segue e tiene traccia di tali collegamenti. Si noti che un semplice CREATEpuò anche significare che è stato creato un file non vuoto. Quindi, su IN_MODIFY/ IN_CLOSE_WRITEsu uno qualsiasi dei file devi attivare la stessa azione su tutti i file collegati. Ovviamente devi anche rimuovere quella relazione DELETEsull'evento.

Un approccio più semplice e robusto sarebbe probabilmente quello di eseguire periodicamente l'hashing di tutti i file e verificare se il contenuto di un file è cambiato.


Correzione

Dopo aver controllato il gitcodice sorgente da vicino e in esecuzione gitcon strace, ho scoperto che gitfa file di memoria uso mappati, ma soprattutto per la lettura di contenuti. Vedi il cui utilizzo xmmapè sempre chiamato PROT_READsolo. . Pertanto la mia risposta precedente di seguito NON è la risposta corretta. Tuttavia, a scopo informativo, vorrei ancora conservarlo qui:

  • Non vedi IN_MODIFYeventi perché packfile.cutilizza mmapper l'accesso ai file e inotifynon segnala modifiche per i mmapfile ed.

    Dalla manpage inotify :

    L'API inotify non segnala gli accessi ai file e le modifiche che potrebbero verificarsi a causa di mmap (2), msync (2) e munmap (2).


Il mio meccanismo di rilevamento delle modifiche dipende da IN_CLOSE_WRITEquale penso che sarebbe comunque stato attivato quando si chiude un file che è stato scritto usando mmap, perché il file avrebbe dovuto essere aperto in una modalità di scrittura?
Michal Charemza,

Devo indagare su questo, ma sospetto che un file mappato in memoria non inneschi alcun evento inotify. La maggior parte degli eventi intoify sono collegati a uno stato del descrittore di file, ma quando mmapun file le cose possono diventare un po 'fuori servizio. Ad esempio, è ancora possibile scrivere su un descrittore di file chiuso quando il file è mappato in memoria.
Ente,

Scratch that, ho appena testato questa implementazione di esempio e ottengo un CLOSE_WRITE_CLOSEanche se rimuovo closee munmapalla fine. Devo scavare più a fondo nell'attuazione effettiva di git quindi ...
Ente

Hmm, sto lottando un po 'per riprodurre il tuo problema. Nei miei test con inotifywaite git clone(2.24.1) ottengo un OPEN-> CLOSE_NOWRITE,CLOSEper i *.idxfile. Forse hai dimenticato di creare un gestore per CLOSE_NOWRITE,CLOSE? Nota: riceverai un *NOWRITE*perché tutte le scritture avvenute attraverso la memoria mappata sono.
Ente,

Sì, ci sono CLOSE_NOWRITE: il problema è che non vedo IN_CLOSE_WRITEe vorrei rispondere alle "modifiche" del file per attivare un caricamento, ma ignora le "letture" del file. Nota, in realtà penso che in questo momento la limitazione mmap + inotify sia un po 'un'aringa rossa. Penso che il problema sia che i file .pack/ .idxsono inizialmente creati come collegamenti diretti a un altro file, e quindi solo trigger IN_CREATE(e il OPEN-> CLOSE_NOWRITEsuccede più tardi quando git sta effettivamente leggendo i file).
Michal Charemza,

2

Posso ipotizzare che Git utilizzi per lo più aggiornamenti di file atomici che sono fatti in questo modo:

  1. Il contenuto di un file viene letto in memoria (e modificato).
  2. Il contenuto modificato viene scritto in un file separato (generalmente situato nella stessa directory di quello originale e con un nome casuale ( mktempstile).
  3. Il nuovo file è quindi rename(2)d -d su quello originale; questa operazione garantisce che ogni osservatore che tenta di aprire il file usando il suo nome otterrà il vecchio contenuto o quello nuovo.

Tali aggiornamenti sono visti inotify(7)come moved_toeventi, poiché un file "ricompare" in una directory.


Ah per alcuni file penso che lo faccia: vedo i vari IN_MOVED_FROMe gli IN_MOVED_TOeventi. Tuttavia, non vedo che questo accada per i file .packe.idx
Michal Charemza il

I file del pacchetto possono essere enormi (diversi gigabyte, almeno fino a 2GiB, credo); maneggiarli usando gli aggiornamenti atomici potrebbe essere proibitivo sullo spazio di archiviazione, quindi potrebbero essere aggiornati usando qualche altra strategia.
kostix il

2

Sulla base di questa risposta accettata , suppongo che potrebbero esserci delle differenze negli eventi in base al protocollo utilizzato (es. Ssh o https).

Osservate lo stesso comportamento durante il monitoraggio della clonazione dal filesystem locale con l' --no-hardlinksopzione?

$ git clone git@github.com:user/repo.git
# set up watcher for new dir
$ git clone --no-hardlinks repo new-repo

Il tuo comportamento osservato nell'esecuzione dell'esperimento su un host Linux e Mac probabilmente elimina questo problema aperto che è la causa https://github.com/docker/for-mac/issues/896 ma aggiungendo solo in caso.


2

C'è un'altra possibilità (dall'uomo inotify):

Si noti che la coda degli eventi può traboccare. In questo caso, gli eventi vengono persi. Le applicazioni robuste dovrebbero gestire la possibilità di eventi persi con garbo. Ad esempio, potrebbe essere necessario ricostruire una parte o tutta la cache dell'applicazione. (Un approccio semplice, ma forse costoso, è quello di chiudere il descrittore di file inotify, svuotare la cache, creare un nuovo descrittore di file inotify, quindi ricreare le voci di watch e cache per gli oggetti da monitorare.)

E mentre git clonepuò generare un flusso di eventi pesante, ciò può accadere.

Come evitarlo:

  1. Aumenta il buffer di lettura, prova fcntl (F_SETPIPE_SZ) (questo approccio è un'ipotesi, non ho mai provato).
  2. Leggi gli eventi in un grosso buffer in un thread dedicato, elabora gli eventi in un altro thread.

2

Forse hai fatto lo stesso errore che ho fatto anni fa. Ho usato solo inotify due volte. La prima volta, il mio codice ha semplicemente funzionato. Più tardi, non avevo più quella fonte e ricominciai, ma questa volta mi mancavano gli eventi e non sapevo perché.

Si scopre che quando stavo leggendo un evento, stavo davvero leggendo una piccola serie di eventi. Ho analizzato quello che mi aspettavo, pensando che fosse tutto qui. Alla fine, ho scoperto che c'è di più nei dati ricevuti e quando ho aggiunto un piccolo codice per analizzare tutti gli eventi ricevuti da una singola lettura, non sono stati persi altri eventi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.