Fehlende inotify-Ereignisse (im .git-Verzeichnis)

11

Ich beobachte Dateien mithilfe von inotify-Ereignissen auf Änderungen (wie es passiert, von Python aus, indem ich libc aufrufe).

Für einige Dateien während eines git clonesehe ich etwas Seltsames: Ich sehe ein IN_CREATEEreignis, und ich sehe darüber, lsdass die Datei Inhalt hat, aber ich sehe IN_MODIFYoder nie IN_CLOSE_WRITE. Dies verursacht Probleme, da ich IN_CLOSE_WRITEauf die Dateien antworten möchte : insbesondere, um einen Upload des Dateiinhalts zu initiieren.

Die Dateien, die sich merkwürdig verhalten, befinden sich im .git/objects/packVerzeichnis und enden mit .packoder .idx. Andere Dateien, die git erstellt, haben eine regelmäßigere IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITEKette (ich suche nicht nach IN_OPENEreignissen).

Dies ist in Docker unter MacOS enthalten, aber ich habe Hinweise auf Docker unter Linux in einem Remote-System gesehen, sodass ich den Verdacht habe, dass der MacOS-Aspekt nicht relevant ist. Ich sehe dies, wenn ich zuschaue und mich git cloneim selben Docker-Container befinde.

Meine Fragen:

  • Warum fehlen diese Ereignisse in diesen Dateien?

  • Was kann man dagegen tun? Wie kann ich konkret auf den Abschluss von Schreibvorgängen in diese Dateien reagieren? Hinweis: Idealerweise möchte ich antworten, wenn das Schreiben "beendet" ist, um zu vermeiden, dass unnötig / (fälschlicherweise) "unfertiges" Schreiben hochgeladen wird.


Bearbeiten: Lesen von https://developer.ibm.com/tutorials/l-inotify/ Es sieht so aus, als ob das, was ich sehe, mit dem übereinstimmt, was ich sehe

  • eine separate temporäre Datei mit Namen wie tmp_pack_hBV4Alz, die erstellt, geändert und geschlossen wird;
  • Zu dieser Datei wird ein fester Link mit dem endgültigen .packNamen erstellt.
  • Der ursprüngliche tmp_pack_hBV4AlzName wird gelöscht.

Ich denke, mein Problem, das versucht, inotify als Auslöser zum Hochladen von Dateien zu verwenden, reduziert sich dann darauf, zu bemerken, dass die .packDatei ein fester Link zu einer anderen Datei ist, und in diesem Fall hochzuladen?

Michal Charemza
quelle
Die Antwort könnte irgendwo hier sein ...
Choroba
@choroba Sie könnten Recht haben ... Ich sehe viele Verweise auf mmap, und inotify meldet keinen mmap-Zugriff auf Dateien
Michal Charemza
1
Übrigens, was ist das ursprüngliche Problem, das Sie lösen möchten (mit inotify)? Möglicherweise gibt es eine robustere Lösung, die versucht, zu erraten, was ein Git-Prozess mit einem Repository tut / getan hat?
Kostix
@kostix Dies ist Teil von github.com/uktrade/mobius3 , mit dem die Basisordner der Benutzer von Containern, in denen JupyterLab oder RStudio in AWS Fargate ausgeführt wird, zu und von S3 synchronisiert werden. In diesen Basisordnern können sich .git-Ordner befinden. Ich weiß, dass die inotify-Lösung niemals "robust-robust" sein wird ... aber ich hoffe, dass sie "robust genug" sein kann.
Michal Charemza
1
@tink Es sieht so aus, als wäre die akzeptierte Antwort ein Patch auf dem Linux-Kernel? Es würde funktionieren, vermute ich im Allgemeinen, aber in meinem Fall auf Fargate habe ich diese Kontrolle nicht. (Und ich gebe zu, ich fürchte ein wenig die Konsequenzen, langfristig von einem gepatchten Kernel abhängig zu sein, selbst wenn ich diese Macht hätte ...)
Michal Charemza

Antworten:

5

So beantworten Sie Ihre Frage für git2.24.1 unter Linux 4.19.95 separat:

  • Warum fehlen diese Ereignisse in diesen Dateien?

Sie sehen IN_MODIFY/ IN_CLOSE_WRITEevents nicht, da git cloneimmer versucht wird, feste Links für Dateien im .git/objectsVerzeichnis zu verwenden. Beim Klonen über das Netzwerk oder über Dateisystemgrenzen hinweg werden diese Ereignisse erneut angezeigt.

  • Was kann man dagegen tun? Wie kann ich konkret auf den Abschluss von Schreibvorgängen in diese Dateien reagieren? Hinweis: Idealerweise möchte ich antworten, wenn das Schreiben "beendet" ist, um zu vermeiden, dass unnötig / (fälschlicherweise) "unfertiges" Schreiben hochgeladen wird.

Um Änderungen an Hardlinks abzufangen, müssen Sie einen Handler für das folgende inotify- CREATEEreignis einrichten und diese Links verfolgen. Bitte beachten Sie, dass eine einfache CREATEDatei auch bedeuten kann, dass eine nicht leere Datei erstellt wurde. Dann müssen Sie für IN_MODIFY/ IN_CLOSE_WRITEzu einer der Dateien dieselbe Aktion auch für alle verknüpften Dateien auslösen. Natürlich müssen Sie diese Beziehung auch für das DELETEEreignis entfernen .

Ein einfacherer und robusterer Ansatz wäre wahrscheinlich, alle Dateien regelmäßig zu hashen und zu überprüfen, ob sich der Inhalt einer Datei geändert hat.


Korrektur

Nach Überprüfung der gitQuellcode eng und laufen gitmit strace, fand ich , dass die gitVerwendung Memory - Mapped - Dateien, aber vor allem für das Lesen von Inhalten der Fall ist. Sehen Sie, deren Verwendung xmmapimmer nur mit aufgerufen wird PROT_READ. . Daher ist meine vorherige Antwort unten NICHT die richtige Antwort. Trotzdem möchte ich es zu Informationszwecken hier behalten:

  • Sie sehen nicht , IN_MODIFYEreignisse , weil packfile.cAnwendungen mmapfür Access - Datei und inotifyÄnderungen nicht für berichten mmaped - Dateien.

    Aus der inotify-Manpage :

    Die inotify-API meldet keine Dateizugriffe und Änderungen, die aufgrund von mmap (2), msync (2) und munmap (2) auftreten können.

Ente
quelle
Mein Mechanismus zur Erkennung von Änderungen hängt davon ab IN_CLOSE_WRITE, was meiner Meinung nach beim Schließen einer Datei, in die geschrieben wurde, immer noch ausgelöst wird mmap, da die Datei in einem Schreibmodus hätte geöffnet werden müssen.
Michal Charemza
Ich muss dies untersuchen, aber ich würde vermuten, dass eine Speicherzuordnungsdatei überhaupt keine Inotify-Ereignisse auslöst. Die meisten inify-Ereignisse sind mit einem Status des Dateideskriptors verknüpft, aber wenn Sie mmapeine Datei verwenden, kann es zu Abweichungen kommen . Sie können beispielsweise weiterhin in einen geschlossenen Dateideskriptor schreiben, wenn Sie die Datei im Speicher zugeordnet haben.
Ente
Scratch that, ich habe gerade diese Beispielimplementierung getestet und bekomme eine CLOSE_WRITE_CLOSEgerade, wenn ich das closeund munmapam Ende entferne . Müssen dann tiefer in die eigentliche Git-Implementierung eintauchen ..
Ente
Hmm, ich habe ein bisschen Mühe, Ihr Problem zu reproduzieren. In meinen Tests mit inotifywaitund git clone(2.24.1) bekomme ich ein OPEN-> CLOSE_NOWRITE,CLOSEfür die *.idxDateien. Vielleicht haben Sie vergessen, einen Handler einzurichten CLOSE_NOWRITE,CLOSE? Hinweis: Sie erhalten eine, *NOWRITE*da alle Schreibvorgänge über den zugeordneten Speicher ausgeführt wurden.
Ente
Ja, es gibt CLOSE_NOWRITE: Das Problem ist, dass ich es nicht sehe IN_CLOSE_WRITE, und ich möchte auf "Änderungen" der Datei reagieren, um einen Upload auszulösen, aber "Lesevorgänge" ignorieren. Beachten Sie, ich denke im Moment, dass die Einschränkung von mmap + inotify ein bisschen wie ein roter Hering ist. Ich denke, das Problem ist, dass die .pack/ .idx-Dateien anfänglich als feste Links zu einer anderen Datei erstellt werden und daher nur ausgelöst werden IN_CREATE(und das OPEN-> CLOSE_NOWRITEpassiert später, wenn Git die Dateien tatsächlich liest).
Michal Charemza
2

Ich kann spekulieren, dass Git die meiste Zeit Atomic File Updates verwendet, die wie folgt durchgeführt werden:

  1. Der Inhalt einer Datei wird in den Speicher eingelesen (und geändert).
  2. Der geänderte Inhalt wird in eine separate Datei geschrieben (normalerweise im selben Verzeichnis wie die ursprüngliche) und hat einen zufälligen Namen ( mktemp-style).
  3. Die neue Datei ist dann rename(2)d-d über der ursprünglichen; Diese Operation garantiert, dass jeder Beobachter, der versucht, die Datei unter seinem Namen zu öffnen, entweder den alten oder den neuen Inhalt erhält.

Solche Aktualisierungen werden inotify(7)als moved_toEreignisse angesehen, da eine Datei in einem Verzeichnis "wieder angezeigt" wird.

kostix
quelle
Ah für einige Dateien denke ich, dass es dies tut: Ich sehe die verschiedenen IN_MOVED_FROMund IN_MOVED_TOEreignisse. Ich sehe dies jedoch nicht für die .packund .idxDateien
Michal Charemza
Pack-Dateien können sehr groß sein (mehrere Gigabyte, mindestens bis zu 2 GB, glaube ich); Wenn Sie sie mit atomaren Updates verwenden, ist der Speicherplatz möglicherweise unzulässig, sodass sie möglicherweise mit einer anderen Strategie aktualisiert werden.
Kostix
2

Basierend auf dieser akzeptierten Antwort würde ich annehmen, dass es einen Unterschied in den Ereignissen geben könnte, die auf dem verwendeten Protokoll basieren (dh ssh oder https).

Beobachten Sie dasselbe Verhalten, wenn Sie das Klonen aus dem lokalen Dateisystem mit der --no-hardlinksOption überwachen ?

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

Ihr beobachtetes Verhalten beim Ausführen des Experiments sowohl auf einem Linux- als auch auf einem Mac-Host beseitigt wahrscheinlich dieses offene Problem, das die Ursache https://github.com/docker/for-mac/issues/896 ist, aber nur für den Fall, dass es hinzugefügt wird.

zerstört
quelle
2

Es gibt eine andere Möglichkeit (vom Menschen inotifizieren):

Beachten Sie, dass die Ereigniswarteschlange überlaufen kann. In diesem Fall gehen Ereignisse verloren. Robuste Anwendungen sollten die Möglichkeit verlorener Ereignisse angemessen behandeln. Beispielsweise kann es erforderlich sein, einen Teil oder den gesamten Anwendungscache neu zu erstellen. (Ein einfacher, aber möglicherweise teurer Ansatz besteht darin, den Inotify-Dateideskriptor zu schließen, den Cache zu leeren, einen neuen Inotify-Dateideskriptor zu erstellen und dann Uhren und Cache-Einträge für die zu überwachenden Objekte neu zu erstellen.)

Und während git clonekann schweren Ereignisablauf erzeugt, kann das passieren.

So vermeiden Sie dies:

  1. Erhöhen Sie den Lesepuffer und versuchen Sie es mit fcntl (F_SETPIPE_SZ) (dieser Ansatz ist eine Vermutung, die ich noch nie ausprobiert habe).
  2. Lesen Sie Ereignisse in einem großen Puffer in einem dedizierten Thread und verarbeiten Sie Ereignisse in einem anderen Thread.
Yury Nevinitsin
quelle
2

Vielleicht haben Sie den gleichen Fehler gemacht, den ich vor Jahren gemacht habe. Ich habe inotify nur zweimal verwendet. Beim ersten Mal hat mein Code einfach funktioniert. Später hatte ich diese Quelle nicht mehr und fing wieder an, aber diesmal fehlten mir Ereignisse und ich wusste nicht warum.

Es stellt sich heraus, dass ich beim Lesen eines Ereignisses wirklich eine kleine Menge von Ereignissen gelesen habe. Ich analysierte den, den ich erwartet hatte und dachte, das war es, das war alles. Schließlich stellte ich fest, dass die empfangenen Daten mehr enthalten, und als ich einen kleinen Code hinzufügte, um alle Ereignisse zu analysieren, die von einem einzelnen Lesevorgang empfangen wurden, gingen keine weiteren Ereignisse verloren.

Donjuedo
quelle