Acara inotify tidak ada (dalam direktori .git)

11

Saya menonton file untuk perubahan menggunakan acara inotify (seperti yang terjadi, dari Python, memanggil libc).

Untuk beberapa file selama git clone, saya melihat sesuatu yang aneh: Saya melihat suatu IN_CREATEperistiwa, dan saya melihat lsbahwa file tersebut memiliki konten, namun, saya tidak pernah melihat IN_MODIFYatau IN_CLOSE_WRITE. Ini menyebabkan masalah bagi saya karena saya ingin merespons IN_CLOSE_WRITEpada file: khususnya, untuk memulai pengunggahan konten file.

File yang berperilaku aneh ada di .git/objects/packdirektori, dan berakhir di .packatau .idx. File lain yang dibuat git memiliki rantai IN_CREATE-> IN_MODIFY-> yang lebih teratur IN_CLOSE_WRITE(Saya tidak menonton IN_OPENacara).

Ini ada di dalam docker pada MacOS, tapi saya telah melihat bukti yang sama pada docker di Linux dalam sistem jarak jauh, jadi kecurigaan saya adalah aspek MacOS tidak relevan. Saya melihat ini jika menonton dan git cloneberada di wadah buruh pelabuhan yang sama .

Pertanyaan saya:

  • Mengapa acara ini hilang pada file-file ini?

  • Apa yang bisa dilakukan? Secara khusus, bagaimana saya bisa menanggapi penyelesaian penulisan ke file-file ini? Catatan: idealnya saya ingin menjawab ketika penulisan "selesai" untuk menghindari pengunggahan yang tidak perlu / (salah) mengunggah tulisan "belum selesai".


Sunting: Membaca https://developer.ibm.com/tutorials/l-inotify/ sepertinya yang saya lihat konsisten dengan

  • file sementara yang terpisah, dengan nama seperti tmp_pack_hBV4Alz, sedang dibuat, dimodifikasi dan, ditutup;
  • a keras link dibuat untuk file ini, dengan akhir .packnama;
  • tmp_pack_hBV4Alznama aslinya dihapus.

Saya pikir masalah saya, yang mencoba menggunakan tidak sah sebagai pemicu untuk mengunggah file, kemudian mengurangi untuk memperhatikan bahwa .packfile tersebut merupakan tautan keras ke file lain, dan mengunggah dalam kasus ini?

Michal Charemza
sumber
Jawabannya mungkin ada di suatu tempat di sini ...
choroba
@choroba Anda mungkin benar ... Saya melihat banyak referensi ke mmap, dan tidak memberitahukan tidak melaporkan akses mmap ke file
Michal Charemza
1
BTW apa masalah asli yang Anda coba selesaikan (dengan inotify)? Mungkin ada beberapa solusi yang lebih kuat yang mencoba menebak apa yang proses Git lakukan / lakukan terhadap repositori?
kostix
@kostix Ini adalah bagian dari github.com/uktrade/mobius3 , menyinkronkan folder rumah pengguna dari wadah yang menjalankan JupyterLab atau RStudio di AWS Fargate, ke dan dari S3, dan di folder rumah tersebut terdapat folder .git. Saya tahu solusi tidak akan pernah menjadi "kuat-kuat" ... tapi saya berharap itu bisa "cukup kuat".
Michal Charemza
1
@tink Sepertinya jawaban yang diterima adalah tambalan pada kernel Linux? Ini akan berhasil saya curigai secara umum, tetapi dalam kasus saya di Fargate saya tidak memiliki kendali itu. (Dan saya akui saya sedikit takut konsekuensi tergantung pada kernel yang ditambal dalam jangka panjang bahkan jika saya memiliki kekuatan itu ...)
Michal Charemza

Jawaban:

5

Untuk menjawab pertanyaan Anda secara terpisah untuk git2.24.1 di Linux 4.19.95:

  • Mengapa acara ini hilang pada file-file ini?

Anda tidak melihat IN_MODIFY/ IN_CLOSE_WRITEacara karena git cloneakan selalu mencoba menggunakan tautan keras untuk file di bawah .git/objectsdirektori. Ketika kloning melalui jaringan atau melintasi batas-batas sistem file, peristiwa ini akan muncul lagi.

  • Apa yang bisa dilakukan? Secara khusus, bagaimana saya bisa menanggapi penyelesaian penulisan ke file-file ini? Catatan: idealnya saya ingin menjawab ketika penulisan "selesai" untuk menghindari pengunggahan yang tidak perlu / (salah) mengunggah tulisan "belum selesai".

Untuk mengetahui modifikasi tautan keras, Anda harus mengatur handler untuk CREATEacara inotify yang mengikuti dan melacak tautan tersebut. Harap dicatat bahwa yang sederhana CREATEjuga dapat berarti bahwa file yang kosong dibuat. Kemudian, pada IN_MODIFY/ IN_CLOSE_WRITEke salah satu file Anda harus memicu tindakan yang sama pada semua file yang terhubung juga. Jelas Anda juga harus menghapus hubungan itu di DELETEacara tersebut.

Pendekatan yang lebih sederhana dan lebih kuat mungkin hanya dengan hash semua file secara berkala dan memeriksa apakah konten file telah berubah.


Koreksi

Setelah memeriksa gitkode sumber erat dan berjalan gitdengan strace, saya menemukan bahwa gitmemang menggunakan file yang dipetakan memori, tetapi sebagian besar untuk membaca konten. Lihat penggunaan xmmapyang selalu dipanggil dengan PROT_READsaja. . Karenanya jawaban saya sebelumnya di bawah BUKAN jawaban yang benar. Namun demikian untuk tujuan informasi saya masih ingin menyimpannya di sini:

  • Anda tidak melihat IN_MODIFYacara karena packfile.cdigunakan mmapuntuk akses file dan inotifytidak melaporkan modifikasi untuk mmapfile ed.

    Dari halaman manual inotify :

    API inotify tidak melaporkan akses file dan modifikasi yang mungkin terjadi karena mmap (2), msync (2), dan munmap (2).

Ente
sumber
Mekanisme pendeteksian perubahan saya bergantung pada IN_CLOSE_WRITE, yang menurut saya masih akan terpicu ketika menutup file yang ditulis untuk digunakan mmap, karena file tersebut harus dibuka dalam mode tulis?
Michal Charemza
Saya harus menyelidiki ini, tetapi saya akan curiga bahwa file yang dipetakan dengan memori tidak memicu kejadian yang tidak memberitahukan sama sekali. Sebagian besar peristiwa mempertemukan terhubung ke keadaan deskriptor file, tetapi ketika Anda mmapfile hal-hal bisa mendapatkan sedikit rusak. Misalnya, Anda masih dapat menulis ke deskriptor file tertutup saat file dipetakan ke dalam memori.
Ente
Gores itu, saya baru saja menguji implementasi contoh ini dan saya mendapatkan CLOSE_WRITE_CLOSEbahkan jika saya menghapus closedan munmappada akhirnya. Harus menggali lebih dalam implementasi git yang sebenarnya ..
Ente
Hmm saya sedikit kesulitan untuk mereproduksi masalah Anda. Dalam pengujian saya dengan inotifywaitdan git clone(2.24.1) saya mendapatkan OPEN-> CLOSE_NOWRITE,CLOSEuntuk *.idxfile. Mungkin Anda lupa mengatur handler CLOSE_NOWRITE,CLOSE? Catatan: Anda akan mendapatkan *NOWRITE*karena semua penulisan terjadi melalui memori yang dipetakan.
Ente
Ya, ada CLOSE_NOWRITE: masalahnya adalah saya tidak melihat IN_CLOSE_WRITE, dan saya ingin menanggapi file "perubahan" untuk memicu unggahan, tetapi abaikan file "baca". Catatan, saya benar-benar berpikir saat ini keterbatasan mmap + inotify sedikit herring merah. Saya pikir masalahnya adalah bahwa .pack/ .idxfile pada awalnya dibuat sebagai tautan keras ke file lain, dan hanya memicu IN_CREATE(dan OPEN-> CLOSE_NOWRITEterjadi kemudian ketika git benar-benar membaca file).
Michal Charemza
2

Saya dapat berspekulasi bahwa Git sebagian besar waktu menggunakan pembaruan file atom yang dilakukan seperti ini:

  1. Isi file dibaca ke dalam memori (dan dimodifikasi).
  2. Konten yang dimodifikasi ditulis ke dalam file terpisah (biasanya terletak di direktori yang sama dengan yang asli, dan memiliki nama acak ( mktemp-style).
  3. File baru kemudian rename(2)d-d lebih dari yang asli; operasi ini menjamin bahwa setiap pengamat yang mencoba membuka file menggunakan namanya akan mendapatkan konten lama atau baru.

Pembaruan seperti itu dilihat inotify(7)sebagai moved_toperistiwa — karena file "muncul kembali" dalam direktori.

Kostix
sumber
Ah untuk beberapa file saya pikir ini yang terjadi: Saya melihat berbagai IN_MOVED_FROMdan IN_MOVED_TOacara. Namun, saya tidak melihat ini terjadi untuk .packdan .idxfile
Michal Charemza
File paket mungkin besar (beberapa gigabyte, setidaknya 2GiB, saya percaya); menggunakan mereka menggunakan pembaruan atom mungkin menghambat ruang penyimpanan, sehingga mereka dapat diperbarui menggunakan beberapa strategi lain.
kostix
2

Berdasarkan jawaban yang diterima ini, saya berasumsi mungkin ada beberapa perbedaan dalam peristiwa berdasarkan protokol yang digunakan (yaitu ssh atau https).

Apakah Anda mengamati perilaku yang sama saat memantau kloning dari sistem file lokal dengan --no-hardlinksopsi?

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

Perilaku Anda yang diamati saat menjalankan percobaan pada linux dan host Mac mungkin menghilangkan masalah terbuka ini sebagai penyebab https://github.com/docker/for-mac/issues/896 tetapi menambahkan hanya memetikan.

dwrecked
sumber
2

Ada kemungkinan lain (dari laki-laki tidak tahu):

Perhatikan bahwa antrian acara dapat meluap. Dalam hal ini, peristiwa hilang. Aplikasi yang kuat harus menangani kemungkinan acara yang hilang dengan anggun. Misalnya, mungkin perlu untuk membangun kembali sebagian atau semua cache aplikasi. (Salah satu pendekatan yang sederhana, tetapi mungkin mahal, adalah dengan menutup deskriptor file inotify, mengosongkan cache, membuat deskriptor file inotify baru, dan kemudian membuat kembali entri arloji dan cache untuk objek yang akan dipantau.)

Dan sementara git clonedapat menghasilkan aliran acara besar, ini bisa terjadi.

Bagaimana cara menghindari ini:

  1. Tingkatkan buffer baca, coba fcntl (F_SETPIPE_SZ) (pendekatan ini adalah tebakan, saya belum pernah mencoba).
  2. Baca acara menjadi buffer besar di utas khusus, proseskan peristiwa di utas lain.
Yury Nevinitsin
sumber
2

Mungkin Anda membuat kesalahan yang sama yang saya buat bertahun-tahun yang lalu. Saya hanya menggunakan dua kali inotify. Pertama kali, kode saya hanya berfungsi. Kemudian, saya tidak lagi memiliki sumber itu dan mulai lagi, tetapi kali ini, saya kehilangan acara dan tidak tahu mengapa.

Ternyata ketika saya membaca sebuah acara, saya benar-benar membaca sekumpulan kecil acara. Saya parsing yang saya harapkan, berpikir itu saja, itu saja. Akhirnya, saya menemukan ada lebih banyak data yang diterima, dan ketika saya menambahkan sedikit kode untuk mem-parsing semua peristiwa yang diterima dari satu pembacaan, tidak ada lagi peristiwa yang hilang.

donjuedo
sumber