Відсутня ініціація подій (у каталозі .git)


11

Я переглядаю файли для змін, використовуючи події inotify (як це трапляється, з Python, викликаючи libc).

Для деяких файлів під час a git cloneя бачу щось дивне: я бачу IN_CREATEподію, і я бачу, lsщо у файлі є вміст, однак я ніколи не бачу IN_MODIFYабо IN_CLOSE_WRITE. Це викликає у мене проблеми, оскільки я хотів би відповісти IN_CLOSE_WRITEна файли: зокрема, ініціювати завантаження вмісту файлу.

Файли, які дивно поводяться, знаходяться в .git/objects/packкаталозі, і вони закінчуються в .packабо .idx. Інші файли, які створює git, мають більш регулярний ланцюг IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITE(я не спостерігаю за IN_OPENподіями).

Це всередині докера на MacOS, але я бачив докази того ж на docker в Linux у віддаленій системі, тому моє підозра в аспекті MacOS не має значення. Я бачу це, якщо дивлюся і git cloneзнаходяться в одному докерному контейнері.

Мої запитання:

  • Чому ці події відсутні у цих файлах?

  • Що з цим можна зробити? Зокрема, як я можу відповісти на завершення запису до цих файлів? Примітка: в ідеалі я хотів би відповісти, коли написання "закінчено", щоб уникнути зайвого / (неправильного) завантаження "незакінченого" написання.


Редагувати: Читання https://developer.ibm.com/tutorials/l-inotify/ виглядає так, що те, що я бачу, відповідає

  • окремий тимчасовий файл із ім'ям tmp_pack_hBV4Alz, яке створюється, змінюється та закривається;
  • важко буде створена посилання на цей файл, з остаточним .packназвою;
  • оригінальне tmp_pack_hBV4Alzім’я видалено.

Я думаю, що моя проблема, яка намагається використовувати inotify як тригер для завантаження файлів, потім зводиться до того, що помічає, що .packфайл є твердим посиланням на інший файл, і завантаження в цьому випадку?


Відповідь може бути десь тут ...
choroba

@choroba Ви можете мати рацію ... Я бачу багато посилань на mmap, а inotify не повідомляє про доступ до файлів mmap
Michal Charemza

1
BTW, яку оригінальну проблему ви намагаєтеся вирішити (за допомогою інотифікації)? Можливо, існує якесь більш надійне рішення, яке намагається вдруге здогадатися, що робить / робить процес Git для сховища?
kostix

@kostix Це частина github.com/uktrade/mobius3 , яка синхронізує домашні папки користувачів із контейнерів, на яких працює JupyterLab або RStudio в AWS Fargate, до та від S3, і в цих домашніх папках можуть бути папки .git. Я знаю, що інотифіковане рішення ніколи не буде "надійним-надійним" ... але я сподіваюся, що воно може бути "досить надійним".
Міхал Шаремза

1
@tink Схоже, прийнята відповідь - це патч на ядрі Linux? Я працюю, я підозрюю в цілому, але в моєму випадку на Fargate я не маю такого контролю. (І я визнаю, я трохи боюся наслідків залежно від виправленого ядра в довгостроковій перспективі, навіть якщо б я мав цю владу ...)
Міхал Чаремза

Відповіді:


5

Щоб відповісти на ваше питання окремо щодо git2.24.1 в Linux 4.19.95:

  • Чому ці події відсутні у цих файлах?

Ви не бачите IN_MODIFY/ IN_CLOSE_WRITEподії, тому що git cloneзавжди будете намагатися використовувати жорсткі посилання для файлів під .git/objectsкаталогом. Під час клонування через мережу або через межі файлової системи ці події з’являться знову.

  • Що з цим можна зробити? Зокрема, як я можу відповісти на завершення запису до цих файлів? Примітка: в ідеалі я хотів би відповісти, коли написання "закінчено", щоб уникнути зайвого / (неправильного) завантаження "незакінченого" написання.

Для того, щоб зафіксувати модифікацію жорстких посилань, вам потрібно встановити обробник для CREATEподії inotify, яка слідкує за цим посиланням і відслідковує їх. Зверніть увагу, що простий CREATEтакож може означати, що створений непорожній файл. Потім, у IN_MODIFY/ IN_CLOSE_WRITEдо будь-якого з файлів, ви повинні запустити однакову дію і на всіх пов'язаних файлах. Очевидно, що ви також повинні зняти ці відносини з DELETEподії.

Більш простим і надійним підходом було б, мабуть, періодично просто хешувати всі файли і перевіряти, чи змінився вміст файлу.


Корекція

Після уважної перевірки gitвихідного коду та роботи gitз ним straceя виявив, що gitвін використовує файли, відображені в пам'яті, але в основному для читання вмісту. Перегляньте, як використання xmmapзавжди викликається PROT_READлише. . Тому моя попередня відповідь нижче НЕ є правильною відповіддю. Тим не менш, з інформаційною метою я все одно хотів би зберігати його тут:

  • Ви не бачите IN_MODIFYподій, оскільки packfile.cвикористовує mmapдля доступу до файлів і inotifyне повідомляє про модифікації mmapфайлів ed.

    З інотифікованої сторінки :

    API inotify не повідомляє про доступ та модифікації файлів, які можуть статися через mmap (2), msync (2) та munmap (2).


Від мого механізму виявлення змін залежить IN_CLOSE_WRITE, що, на мою думку, все-таки буде запущено при закритті файлу, який було записано у користування mmap, тому що цей файл повинен був би бути відкритий у режимі запису?
Міхал Шаремза

Я маю це дослідити, але я б підозрював, що файл, відображений у пам'яті, взагалі не викликає жодних подій. Більшість невмілих подій пов'язані зі станом дескриптора файлів, але коли ви mmapстворюєте файл, речі можуть трохи вийти з ладу. Наприклад, ви все ще можете записати в закритий дескриптор файлів, коли файл буде відображено у пам'яті.
Енте

Подразумете це, я щойно перевірив цю реалізацію прикладу, і я отримаю CLOSE_WRITE_CLOSEрівний, навіть якщо я видаляю closeі munmapв кінці. Тоді доведеться глибше копатись до реальної реалізації git ..
Ente

Хм, я трохи намагаюся відтворити вашу проблему. У своїх тестах з inotifywaitі git clone(2.24.1) я отримую OPEN-> CLOSE_NOWRITE,CLOSEдля *.idxфайлів. Можливо, ви забули налаштувати обробник CLOSE_NOWRITE,CLOSE? Примітка. Ви отримаєте, *NOWRITE*тому що всі записи відбувалися через відображену пам'ять.
Енте

Так, є CLOSE_NOWRITE: проблема в тому, що я не бачу IN_CLOSE_WRITE, і я хотів би відповісти на "зміни" у файлі, щоб викликати завантаження, але ігнорувати файл "читає". Зауважте, я насправді думаю, що зараз обмеження mmap + inotify - це трохи червона оселедець. Я думаю, проблема полягає в тому, що .pack/ .idxфайли спочатку створюються як жорсткі посилання на інший файл, і тому лише тригер IN_CREATEOPEN-> CLOSE_NOWRITEвідбувається пізніше, коли git насправді читає файли).
Michal Charemza

2

Я можу припустити, що Git більшість часу використовує оновлення атомних файлів, які робляться так:

  1. Вміст файлу зачитується в пам'ять (і змінюється).
  2. Змінений вміст записується в окремий файл (як правило, розташований у тому самому каталозі, що і оригінальний, і має рандомізоване ( mktemp-style) ім'я).
  3. Потім новий файл буде rename(2)d -d над початковим; ця операція гарантує, що кожен спостерігач, який намагається відкрити файл, використовуючи його ім'я, отримає або старий вміст, або новий.

Такі оновлення розглядаються inotify(7)як moved_toподії - оскільки файл "з'являється" в каталозі.


Ах, для деяких файлів я думаю, що це робиться так: я бачу різні IN_MOVED_FROMта IN_MOVED_TOподії. Тим НЕ менше, я не бачу це відбувається за .packі .idxфайли
Міхал Charemza

Пакет файлів може бути величезним (кілька гігабайт, до 2GiB принаймні, я вважаю); їх управління за допомогою атомних оновлень може бути захищено на місцях зберігання, тому вони можуть бути оновлені за допомогою іншої стратегії.
кісток

2

Виходячи з цієї прийнятої відповіді, я припускаю, що може бути певна різниця в подіях на основі використовуваного протоколу (тобто ssh або https).

Чи спостерігаєте ви таку саму поведінку під час моніторингу клонування з локальної файлової системи за допомогою --no-hardlinksпараметра?

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

Ваша спостережувана поведінка під час запуску експерименту як на Linux, так і на Mac хості, ймовірно, усуває цю відкриту проблему, яка є причиною https://github.com/docker/for-mac/isissue/896, але додає лише випадкові випадки.


2

Є ще одна можливість (від людини прищеплюють):

Зауважте, що черга подій може переповнюватися. У цьому випадку події втрачаються. Надійні програми повинні витончено обробляти можливість втрачених подій. Наприклад, може знадобитися перебудувати частину або весь кеш програми. (Один простий, але, можливо, дорогий підхід - закрити дескриптор файлу ініціювати, очистити кеш, створити новий дескриптор файлу ініціювати, а потім знову створити записи годинника та кеш-пам'яті для об'єктів, які слід контролювати.)

І поки git clone може генерувати важкі потоки подій, це може статися.

Як цього уникнути:

  1. Збільшити буфер читання, спробуйте fcntl (F_SETPIPE_SZ) (такий підхід - здогадка, я ніколи не пробував).
  2. Прочитайте події у великий буфер у виділеній темі, обробіть події в іншій потоці.

2

Можливо, ви зробили ту саму помилку, яку я зробив років тому. Я тільки два рази використовував inotify. Перший раз мій код просто спрацював. Пізніше я вже не мав цього джерела і почав знову, але цього разу я пропускав події і не знав чому.

Виявляється, коли я читав подію, я справді читав невеличку партію подій. Я розібрав очікуваного, подумавши, що це все, це все. Врешті-решт я виявив, що є більше отриманих даних, і коли я додав трохи коду для розбору всіх подій, отриманих від одного прочитаного, більше подій не втрачено.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.