Brakujące zdarzenia inotify (w katalogu .git)

11

Oglądam pliki pod kątem zmian za pomocą zdarzeń inotify (jak to się dzieje, z Pythona, wywołującego libc).

W przypadku niektórych plików podczas a git clonewidzę coś dziwnego: widzę IN_CREATEzdarzenie i widzę, lsże plik ma treść, jednak nigdy nie widzę IN_MODIFYani IN_CLOSE_WRITE. Powoduje to problemy, ponieważ chciałbym odpowiedzieć IN_CLOSE_WRITEna pliki, a konkretnie zainicjować przesyłanie zawartości pliku.

Pliki, które zachowują się dziwnie, znajdują się w .git/objects/packkatalogu i kończą się na .packlub .idx. Inne pliki, które tworzy git, mają bardziej regularny IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITEłańcuch (nie szukam IN_OPENwydarzeń).

Jest to wewnątrz okna dokowanego w systemie MacOS, ale widziałem dowody tego samego w oknie dokowanym w systemie Linux w zdalnym systemie, więc moje podejrzenie, że aspekt MacOS nie jest istotny. Widzę to, jeśli oglądam i git clonesą w tym samym kontenerze dokera.

Moje pytania:

  • Dlaczego w tych plikach brakuje tych zdarzeń?

  • Co można z tym zrobić? W szczególności, jak mogę zareagować na zakończenie zapisu do tych plików? Uwaga: idealnie chciałbym odpowiedzieć, gdy pisanie jest „zakończone”, aby uniknąć niepotrzebnego / (niepoprawnego) przesyłania „niedokończonego” pisania.


Edycja: Czytanie https://developer.ibm.com/tutorials/l-inotify/ wygląda na to, że to, co widzę, jest zgodne z

  • oddzielny plik tymczasowy o nazwie podobnej tmp_pack_hBV4Alz, tworzony, modyfikowany i zamykany;
  • trudny związek jest tworzony do tego pliku, o ostatecznej .packnazwie;
  • pierwotna tmp_pack_hBV4Alznazwa zostanie usunięta.

Wydaje mi się, że mój problem polega na tym, że próbuję użyć inotify jako wyzwalacza do przesyłania plików, a następnie sprowadza się do zauważenia, że .packplik jest twardym linkiem do innego pliku, i przesłanie w tym przypadku?

Michał Charemza
źródło
Odpowiedź może być gdzieś tutaj ...
choroba
@choroba Być może masz rację ... Widzę wiele odniesień do mmap, a inotify nie zgłasza dostępu mmap do plików
Michał Charemza
1
BTW jaki jest pierwotny problem, który próbujesz rozwiązać (za pomocą inotify)? Czy może istnieć bardziej niezawodne rozwiązanie, które próbuje odgadnąć, co proces Git robi / zrobił w repozytorium?
kostix
@kostix Jest to część github.com/uktrade/mobius3 , synchronizująca foldery domowe użytkowników z kontenerów z JupyterLab lub RStudio w AWS Fargate, do i od S3, aw tych folderach domowych mogą znajdować się foldery .git. Wiem, że rozwiązanie inotify nigdy nie będzie „solidne-solidne” ... ale mam nadzieję, że może być „wystarczająco solidne”.
Michał Charemza
1
@tink Wygląda na to, że zaakceptowana odpowiedź to łatka na jądro Linuksa? Podejrzewam, że działałoby to ogólnie, ale w moim przypadku na Fargate nie mam takiej kontroli. (I przyznaję, że trochę obawiam się konsekwencji uzależnienia się od załatanego jądra w perspektywie długoterminowej, nawet gdybym miał taką moc ...)
Michał Charemza,

Odpowiedzi:

5

Aby odpowiedzieć na pytanie osobno dla git2.24.1 w Linuksie 4.19.95:

  • Dlaczego w tych plikach brakuje tych zdarzeń?

Nie widzisz IN_MODIFY/ IN_CLOSE_WRITEevents, ponieważ git clonezawsze będzie próbował użyć twardych linków do plików w .git/objectskatalogu. Podczas klonowania przez sieć lub przez granice systemu plików zdarzenia te pojawią się ponownie.

  • Co można z tym zrobić? W szczególności, jak mogę zareagować na zakończenie zapisu do tych plików? Uwaga: idealnie chciałbym odpowiedzieć, gdy pisanie jest „zakończone”, aby uniknąć niepotrzebnego / (niepoprawnego) przesyłania „niedokończonego” pisania.

Aby złapać modyfikację twardych linków, musisz skonfigurować moduł obsługi CREATEzdarzenia inotify , które następuje i śledzi te linki. Pamiętaj, że prosty CREATEmoże również oznaczać, że utworzono niepusty plik. Następnie na IN_MODIFY/ IN_CLOSE_WRITEdo dowolnego pliku musisz również wywołać tę samą akcję na wszystkich połączonych plikach. Oczywiście musisz również usunąć tę relację z DELETEwydarzenia.

Prostszym i bardziej niezawodnym podejściem byłoby prawdopodobnie okresowe mieszanie wszystkich plików i sprawdzanie, czy zawartość pliku się zmieniła.


Korekta

Po dokładnym sprawdzeniu gitkodu źródłowego i uruchomieniu gitz nim stracezauważyłem, że gitużywa plików odwzorowanych w pamięci, ale głównie do odczytu zawartości. Zobacz, którego użycie xmmapjest zawsze wywoływane PROT_READtylko z . . Dlatego moja poprzednia odpowiedź poniżej NIE jest poprawną odpowiedzią. Niemniej jednak w celach informacyjnych chciałbym zachować to tutaj:

  • Nie widzisz IN_MODIFYzdarzeń, ponieważ packfile.cużywa mmapdostępu do plików i inotifynie zgłasza zmian dla mmapplików ed.

    Z strony inotify :

    Interfejs API inotify nie zgłasza dostępu do plików i modyfikacji, które mogą wystąpić z powodu mmap (2), msync (2) i munmap (2).

Ente
źródło
Mój mechanizm wykrywania zmian zależy od tego IN_CLOSE_WRITE, co, jak sądzę, nadal byłoby wywoływane podczas zamykania pliku, który został zapisany przy użyciu mmap, ponieważ plik musiałby zostać otwarty w trybie zapisu?
Michał Charemza
Muszę to zbadać, ale podejrzewam, że plik odwzorowany w pamięci w ogóle nie wyzwala żadnych zdarzeń niedotyczących. Większość zdarzeń inify jest powiązanych ze stanem deskryptora pliku, ale kiedy mmapplik jest niepoprawny . Na przykład nadal można zapisywać do zamkniętego deskryptora pliku, gdy plik jest mapowany do pamięci.
Wejdź
Zarysuj to, właśnie przetestowałem tę przykładową implementację i dostaję CLOSE_WRITE_CLOSEnawet, jeśli usunę closei munmapna końcu. Muszę głębiej zagłębić się w rzeczywistą implementację git.
Wejdź
Hmm, staram się trochę odtworzyć twój problem. W moich testach z inotifywaiti git clone(2.24.1) dostaję OPEN-> CLOSE_NOWRITE,CLOSEdla *.idxplików. Może zapomniałeś skonfigurować moduł obsługi CLOSE_NOWRITE,CLOSE? Uwaga: Otrzymasz, *NOWRITE*ponieważ wszystkie zapisy miały miejsce w zmapowanej pamięci.
Wejdź
Tak, są CLOSE_NOWRITE: problem polega na tym, że nie widzę IN_CLOSE_WRITEi chciałbym odpowiedzieć na „zmiany” pliku, aby uruchomić przesyłanie, ale zignoruj ​​„odczyt”. Uwaga, myślę, że obecnie uważam, że ograniczenie mmap + inotify jest trochę czerwone. Wydaje mi się, że problem polega na tym, że pliki .pack/ .idxsą początkowo tworzone jako twarde linki do innego pliku, a zatem wyzwalają tylko IN_CREATE(a OPEN-> CLOSE_NOWRITEdzieje się później, gdy git faktycznie czyta pliki).
Michał Charemza
2

Mogę spekulować, że Git przez większość czasu używa aktualizacji plików atomowych , które są wykonywane w następujący sposób:

  1. Zawartość pliku jest wczytywana do pamięci (i modyfikowana).
  2. Zmodyfikowana zawartość jest zapisywana w osobnym pliku (zwykle znajdującym się w tym samym katalogu co oryginalny) i posiadającym losową ( mktemp-stylową) nazwę.
  3. Nowy plik jest następnie rename(2)d -d nad oryginalnym; ta operacja gwarantuje, że każdy obserwator próbujący otworzyć plik przy użyciu jego nazwy otrzyma starą lub nową zawartość.

Takie aktualizacje są postrzegane inotify(7)jako moved_tozdarzenia - ponieważ plik „pojawia się ponownie” w katalogu.

kostix
źródło
Ach, dla niektórych plików myślę, że to robi: widzę różne IN_MOVED_FROMi IN_MOVED_TOwydarzenia. Nie widzę tego jednak w przypadku plików .packi.idx
Michał Charemza
Pliki paczek mogą być ogromne (kilka gigabajtów, przynajmniej 2GiB, jak sądzę); dzierżenie ich za pomocą aktualizacji atomowych może zabraniać miejsca na dysku, więc mogą być aktualizowane przy użyciu innej strategii.
kostix
2

Na podstawie tej zaakceptowanej odpowiedzi zakładam, że mogą istnieć pewne różnice w zdarzeniach w zależności od używanego protokołu (tj. Ssh lub https).

Czy obserwujesz to samo zachowanie podczas monitorowania klonowania z lokalnego systemu plików z --no-hardlinksopcją?

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

Twoje zaobserwowane zachowanie podczas uruchamiania eksperymentu zarówno na hoście Linux, jak i Mac prawdopodobnie eliminuje ten otwarty problem, który jest przyczyną https://github.com/docker/for-mac/issues/896, ale dodaje się tylko incase.

zaniedbany
źródło
2

Istnieje inna możliwość (od man inotify):

Pamiętaj, że kolejka zdarzeń może się przepełnić. W takim przypadku zdarzenia są tracone. Solidne aplikacje powinny z wdzięcznością poradzić sobie z możliwością utraty zdarzeń. Na przykład może być konieczne przebudowanie części lub całości pamięci podręcznej aplikacji. (Jednym prostym, ale być może drogim podejściem jest zamknięcie deskryptora pliku inotify, opróżnienie pamięci podręcznej, utworzenie nowego deskryptora pliku inotify, a następnie ponowne utworzenie zegarków i wpisów w pamięci podręcznej dla monitorowanych obiektów.)

I chociaż git clonemoże generować duży przepływ zdarzeń, może się to zdarzyć.

Jak tego uniknąć:

  1. Zwiększ bufor odczytu, spróbuj fcntl (F_SETPIPE_SZ) (takie podejście jest przypuszczeniem, nigdy nie próbowałem).
  2. Odczytuj zdarzenia do dużego bufora w dedykowanym wątku, przetwarzaj zdarzenia w innym wątku.
Yury Nevinitsin
źródło
2

Może popełniłeś ten sam błąd, który popełniłem lata temu. Użyłem inotify tylko dwa razy. Za pierwszym razem mój kod po prostu zadziałał. Później nie miałem już tego źródła i zacząłem od nowa, ale tym razem brakowało mi wydarzeń i nie wiedziałem, dlaczego.

Okazuje się, że kiedy czytałem jakieś wydarzenie, naprawdę czytałem małą porcję wydarzeń. Analizowałem ten, którego się spodziewałem, myśląc, że to było to wszystko. W końcu odkryłem, że jest więcej w otrzymywanych danych, a kiedy dodałem mały kod do parsowania wszystkich zdarzeń otrzymanych z jednego odczytu, nie było więcej zdarzeń utraconych.

donjuedo
źródło