Thiếu các sự kiện inotify (trong thư mục .git)


11

Tôi đang xem các tệp để thay đổi bằng cách sử dụng các sự kiện inotify (như nó xảy ra, từ Python, gọi vào libc).

Đối với một số tệp trong một git clone, tôi thấy một điều kỳ lạ: Tôi thấy một IN_CREATEsự kiện và tôi thấy qua lsđó tệp có nội dung, tuy nhiên, tôi không bao giờ thấy IN_MODIFYhoặc IN_CLOSE_WRITE. Điều này gây ra sự cố cho tôi vì tôi muốn phản hồi IN_CLOSE_WRITEtrên các tệp: cụ thể, để bắt đầu tải lên nội dung tệp.

Các tập tin hoạt động kỳ lạ nằm trong .git/objects/packthư mục và chúng kết thúc bằng .packhoặc .idx. Các tệp khác mà git tạo có chuỗi thường xuyên hơn IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITE(Tôi không xem các IN_OPENsự kiện).

Đây là bên trong docker trên MacOS, nhưng tôi đã thấy bằng chứng tương tự trên docker trên Linux trong một hệ thống từ xa, vì vậy sự nghi ngờ của tôi là khía cạnh MacOS không liên quan. Tôi đang thấy điều này nếu xem và git cloneở trong cùng một container.

Những câu hỏi của tôi:

  • Tại sao những sự kiện này bị thiếu trên các tập tin này?

  • Có thể làm gì về nó? Cụ thể, làm thế nào tôi có thể đáp ứng với việc hoàn thành ghi vào các tệp này? Lưu ý: lý tưởng là tôi muốn phản hồi khi viết "xong" để tránh không cần thiết / (không chính xác) tải lên văn bản "chưa hoàn thành".


Chỉnh sửa: Đọc https://developer.ibm.com/tutorials/l-inotify/ có vẻ như những gì tôi thấy phù hợp với

  • một tệp tạm thời riêng biệt, với tên như tmp_pack_hBV4Alz, được tạo, sửa đổi và đóng;
  • một liên kết cứng được tạo ra cho tập tin này, với .packtên cuối cùng ;
  • tmp_pack_hBV4Alztên ban đầu bị xóa.

Tôi nghĩ rằng vấn đề của tôi, đó là cố gắng sử dụng inotify như một trình kích hoạt để tải lên các tệp, sau đó giảm để nhận thấy rằng .packtệp là một liên kết cứng đến một tệp khác và tải lên trong trường hợp này?


Câu trả lời có thể ở đâu đó ở đây ...
choroba

@choroba Bạn có thể đúng ... Tôi thấy rất nhiều tài liệu tham khảo về mmap và inotify không báo cáo quyền truy cập mmap vào các tệp
Michal Charemza

1
BTW vấn đề ban đầu bạn đang cố gắng giải quyết (với inotify) là gì? Có thể tồn tại một số giải pháp mạnh mẽ hơn mà cố gắng đoán thứ hai quá trình Git đang làm / đã làm gì với một kho lưu trữ?
kostix

@kostix Đây là một phần của github.com/uktrade/mobius3 , đồng bộ hóa các thư mục nhà của người dùng từ các container chạy JupyterLab hoặc RStudio trong AWS Fargate, đến và từ S3 và trong các thư mục gia đình đó có thể có các thư mục .git. Tôi biết giải pháp inotify sẽ không bao giờ là mạnh mẽ, mạnh mẽ ... nhưng tôi hy vọng nó có thể là mạnh mẽ.
Michal Charemza

1
@tink Có vẻ như câu trả lời được chấp nhận là một bản vá trên nhân Linux? Nói chung, nó sẽ hoạt động, nhưng trong trường hợp của tôi trên Fargate, tôi không có quyền kiểm soát đó. (Và tôi thừa nhận tôi hơi lo sợ hậu quả của việc phụ thuộc vào hạt nhân được vá trong thời gian dài ngay cả khi tôi có sức mạnh đó ...)
Michal Charemza

Câu trả lời:


5

Để trả lời riêng câu hỏi của bạn cho git2.24.1 trên Linux 4.19.95:

  • Tại sao những sự kiện này bị thiếu trên các tập tin này?

Bạn không nhìn thấy IN_MODIFY/ IN_CLOSE_WRITEsự kiện vì git clonesẽ luôn cố gắng sử dụng các liên kết cứng cho các tệp trong .git/objectsthư mục. Khi nhân bản qua mạng hoặc qua ranh giới hệ thống tệp, các sự kiện này sẽ xuất hiện lại.

  • Có thể làm gì về nó? Cụ thể, làm thế nào tôi có thể đáp ứng với việc hoàn thành ghi vào các tệp này? Lưu ý: lý tưởng là tôi muốn phản hồi khi viết "xong" để tránh không cần thiết / (không chính xác) tải lên văn bản "chưa hoàn thành".

Để bắt được sửa đổi các liên kết cứng, bạn phải thiết lập một trình xử lý cho CREATEsự kiện inotify theo dõi và theo dõi các liên kết đó. Xin lưu ý rằng một đơn giản CREATEcũng có thể có nghĩa là một tệp không trống đã được tạo. Sau đó, bật IN_MODIFY/ IN_CLOSE_WRITEđến bất kỳ tệp nào bạn phải kích hoạt cùng một hành động trên tất cả các tệp được liên kết. Rõ ràng bạn cũng phải loại bỏ mối quan hệ đó trong DELETEsự kiện.

Một cách tiếp cận đơn giản và mạnh mẽ hơn có lẽ là chỉ định kỳ băm tất cả các tệp và kiểm tra xem nội dung của tệp có thay đổi hay không.


Điều chỉnh

Sau khi kiểm tra gitmã nguồn chặt chẽ và chạy gitcùng strace, tôi thấy rằng gitnó sử dụng các tệp ánh xạ bộ nhớ, nhưng chủ yếu là để đọc nội dung. Xem cách sử dụng xmmapluôn được gọi với PROT_READchỉ. . Do đó, câu trả lời trước của tôi dưới đây KHÔNG phải là câu trả lời đúng. Tuy nhiên, với mục đích thông tin, tôi vẫn muốn giữ nó ở đây:

  • Bạn không thấy IN_MODIFYcác sự kiện vì packfile.csử dụng mmapđể truy cập tệp và inotifykhông báo cáo sửa đổi cho mmapcác tệp ed.

    Từ trang manotify :

    API inotify không báo cáo các truy cập và sửa đổi tệp có thể xảy ra do mmap (2), msync (2) và munmap (2).


Cơ chế phát hiện thay đổi của tôi phụ thuộc vào IN_CLOSE_WRITE, điều mà tôi nghĩ vẫn sẽ được kích hoạt khi đóng tệp được ghi vào sử dụng mmap, vì tệp có phải đã được mở ở chế độ ghi không?
Michal Charemza

Tôi phải điều tra vấn đề này, nhưng tôi sẽ nghi ngờ rằng một tệp ánh xạ bộ nhớ hoàn toàn không kích hoạt bất kỳ sự kiện inotify nào. Hầu hết các sự kiện inify được liên kết với trạng thái của bộ mô tả tệp, nhưng khi bạn mmapmột tệp, mọi thứ có thể bị lỗi một chút. Ví dụ, bạn vẫn có thể ghi vào bộ mô tả tệp đã đóng khi bạn có tệp được ánh xạ vào bộ nhớ.
Nhập

Không có gì, tôi chỉ thử nghiệm triển khai ví dụ này và tôi nhận được CLOSE_WRITE_CLOSEngay cả khi tôi gỡ bỏ closemunmapcuối cùng. Phải đào sâu hơn vào việc thực hiện git thực tế sau đó ..
Ente

Hmm, tôi đang đấu tranh một chút để tái tạo vấn đề của bạn. Trong các thử nghiệm của tôi với inotifywaitgit clone(2.24.1) tôi nhận được OPEN-> CLOSE_NOWRITE,CLOSEcho các *.idxtệp. Có lẽ bạn quên thiết lập một xử lý cho CLOSE_NOWRITE,CLOSE? Lưu ý: Bạn sẽ nhận được *NOWRITE*vì tất cả các ghi đã xảy ra thông qua bộ nhớ được ánh xạ.
Nhập

Có, có CLOSE_NOWRITE: vấn đề là tôi không thấy IN_CLOSE_WRITEvà tôi muốn trả lời tệp "thay đổi" để kích hoạt tải lên, nhưng bỏ qua tệp "đọc". Lưu ý, tôi thực sự nghĩ rằng ngay bây giờ giới hạn mmap + inotify là một chút cá trích đỏ. Tôi nghĩ vấn đề là các tệp .pack/ .idxban đầu được tạo dưới dạng liên kết cứng đến tệp khác và do đó chỉ kích hoạt IN_CREATE(và OPEN-> CLOSE_NOWRITExảy ra sau đó khi git thực sự đang đọc các tệp).
Michal Charemza

2

Tôi có thể suy đoán rằng Git hầu hết thời gian sử dụng các cập nhật tệp nguyên tử được thực hiện như thế này:

  1. Nội dung của tệp được đọc vào bộ nhớ (và được sửa đổi).
  2. Các nội dung được sửa đổi được ghi vào một tệp riêng biệt (thường nằm trong cùng thư mục với tệp gốc và có mktemptên ( kiểu) ngẫu nhiên .
  3. Các tập tin mới sau đó là rename(2)d -d so với tập tin gốc; Thao tác này đảm bảo rằng mọi người quan sát đang cố mở tệp bằng tên của nó sẽ nhận được nội dung cũ hoặc nội dung mới.

Cập nhật như vậy được nhìn thấy bởi inotify(7)moved_tosự kiện-kể từ khi một tập tin "lại xuất hiện" trong một thư mục.


Ah cho một số tập tin tôi nghĩ rằng nó làm điều này: Tôi thấy sự khác nhau IN_MOVED_FROMIN_MOVED_TOcác sự kiện. Tuy nhiên, tôi không thấy điều này xảy ra cho các tập tin .pack.idx
Michal Charemza

Gói tệp có thể rất lớn (ít nhất là vài gigabyte, tối thiểu là 2GiB, tôi tin); Việc sử dụng chúng bằng các cập nhật nguyên tử có thể bị cấm trên không gian lưu trữ, vì vậy chúng có thể được cập nhật bằng một số chiến lược khác.
kostix

2

Dựa trên câu trả lời được chấp nhận này, tôi cho rằng có thể có một số khác biệt trong các sự kiện dựa trên giao thức được sử dụng (ví dụ: ssh hoặc https).

Bạn có quan sát hành vi tương tự khi giám sát nhân bản từ hệ thống tệp cục bộ với --no-hardlinkstùy chọn không?

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

Hành vi được quan sát của bạn khi chạy thử nghiệm trên cả máy chủ linux và Mac có thể loại bỏ sự cố mở này là nguyên nhân https://github.com/docker/for-mac/issues/896 nhưng chỉ thêm vào.


2

Có một khả năng khác (từ người đàn ông inotify):

Lưu ý rằng hàng đợi sự kiện có thể tràn. Trong trường hợp này, các sự kiện bị mất. Các ứng dụng mạnh mẽ sẽ xử lý khả năng mất các sự kiện một cách duyên dáng. Ví dụ, có thể cần phải xây dựng lại một phần hoặc toàn bộ bộ đệm của ứng dụng. (Một cách tiếp cận đơn giản nhưng có thể tốn kém là đóng bộ mô tả tệp inotify, làm trống bộ đệm, tạo bộ mô tả tệp inotify mới, sau đó tạo lại đồng hồ và các mục nhập bộ đệm cho các đối tượng cần giám sát.)

Và trong khi git clonecó thể tạo ra dòng sự kiện nặng nề, điều này có thể xảy ra.

Làm thế nào để tránh điều này:

  1. Tăng bộ đệm đọc, thử fcntl (F_SETPIPE_SZ) (phương pháp này là một phỏng đoán, tôi chưa bao giờ thử).
  2. Đọc các sự kiện vào một bộ đệm lớn trong một luồng chuyên dụng, xử lý các sự kiện trong một luồng khác.

2

Có thể bạn đã phạm sai lầm tương tự tôi đã làm cách đây nhiều năm. Tôi chỉ sử dụng inotify hai lần. Lần đầu tiên, mã của tôi chỉ đơn giản làm việc. Sau đó, tôi không còn có nguồn đó và bắt đầu lại, nhưng lần này, tôi đã bỏ lỡ các sự kiện và không biết tại sao.

Hóa ra khi tôi đang đọc một sự kiện, tôi thực sự đang đọc một loạt các sự kiện nhỏ. Tôi phân tích cái mà tôi mong đợi, nghĩ rằng đó là tất cả. Cuối cùng, tôi phát hiện ra có nhiều dữ liệu nhận được hơn và khi tôi thêm một ít mã để phân tích tất cả các sự kiện nhận được từ một lần đọc, không có sự kiện nào bị mất nữa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.