缺少inotify事件(在.git目录中)


11

我正在使用inotify事件(恰好发生在Python中,调用libc)来监视文件中的更改。

对于a期间的某些文件git clone,我看到了一些奇怪的事情:我看到一个IN_CREATE事件,并且通过ls它看到该文件包含内容,但是,我从未看到IN_MODIFYIN_CLOSE_WRITE。因为我要IN_CLOSE_WRITE对文件做出响应,这导致了我的问题:具体来说,是启动文件内容的上载。

行为异常的文件位于.git/objects/pack目录中,并以.pack或结尾.idx。git创建的其他文件具有更规则的IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITE链(我不在监视IN_OPEN事件)。

这是在MacOS上的docker内部,但是我已经看到在远程系统上的Linux上的docker上有相同的证据,因此我怀疑MacOS方面是不相关的。如果正在观看并且git clone同一个 Docker容器中,我会看到这个。

我的问题:

  • 为什么这些文件上缺少这些事件?

  • 该怎么办?具体来说,我该如何回应对这些文件的写入完成?注意:理想情况下,我想在“完成”书写时做出响应,以避免不必要地/(不正确地)上传“未完成”的书写。


编辑:阅读https://developer.ibm.com/tutorials/l-inotify/看来我所看到的与

  • 一个单独的临时文件,其名称如tmp_pack_hBV4Alz,正在创建,修改和关闭;
  • 将以最终名称创建到该文件的链接.pack
  • 原始tmp_pack_hBV4Alz名称被删除。

我认为我的问题是尝试使用inotify作为上载文件的触发器,然后简化为注意到该.pack文件是与另一个文件的硬链接,并且在这种情况下上载?


答案可能在这里 ……
choroba

@choroba您可能是对的...我看到很多对mmap的引用,而inotify并未报告对文件的mmap访问
Michal Charemza

1
顺便说一句,您要解决的原始问题是什么(使用inotify)?可能存在一些更健壮的解决方案,试图对Git进程正在/已经对存储库进行了什么猜测。
kostix

@kostix这是github.com/uktrade/mobius3的一部分,用于将用户的主文件夹从在AWS Fargate中运行JupyterLab或RStudio的容器中与S3进行同步,在这些主文件夹中可以存在.git文件夹。我知道inotify解决方案永远不会“健壮” ...但是我希望它可以“足够健壮”。
Michal Charemza

1
@tink看来,可接受的答案是Linux内核上的补丁?一般而言,它会起作用,但就Fargate而言,我没有控制权。(而且我承认,即使我拥有这种能力,从长期来看,我还是会担心依赖修补补丁的后果……)
Michal Charemza

Answers:


5

git在Linux 4.19.95上分别针对2.24.1 回答您的问题:

  • 为什么这些文件上缺少这些事件?

您不会看到IN_MODIFY/ IN_CLOSE_WRITE事件,因为git clone它将始终尝试对目录下的文件使用硬链接.git/objects。通过网络或跨文件系统边界进行克隆时,这些事件将再次出现。

  • 该怎么办?具体来说,我该如何回应对这些文件的写入完成?注意:理想情况下,我想在“完成”书写时做出响应,以避免不必要地/(不正确地)上传“未完成”的书写。

为了捕获硬链接的修改,您必须为inotify CREATE事件设置一个处理程序,该事件紧随其后并跟踪那些链接。请注意,简单CREATE也可以表示创建了一个非空文件。然后,在所有文件的IN_MODIFY/ IN_CLOSE_WRITE上,您还必须对所有链接的文件触发相同的操作。显然,您还必须在DELETE事件上删除该关系。

一个更简单,更可靠的方法可能是定期对所有文件进行哈希处理,并检查文件的内容是否已更改。


更正

检查后git密切的源代码和运行gitstrace,我发现git确实使用内存映射文件,但主要是用于读取内容。请参阅xmmap始终PROT_READ仅与一起使用的用法。。因此,我以前在下面的答案不是正确的答案。尽管如此,出于参考目的,我仍然希望将其保留在此处:

  • 您看不到IN_MODIFY事件,因为packfile.cmmap用于文件访问,inotify并且不会报告对mmaped文件的修改。

    inotify联机帮助页

    inotify API不报告由于mmap(2),msync(2)和munmap(2)可能发生的文件访问和修改。


我的变更检测机制取决于IN_CLOSE_WRITE,我认为在关闭使用写入的文件时仍会触发该机制mmap,因为该文件必须以写入模式打开?
Michal Charemza

我必须对此进行调查,但是我怀疑内存映射文件根本不会触发任何inotify事件。大多数inify事件都链接到文件描述符的状态,但是当您mmap创建文件时,事情可能会有些混乱。例如,当您将文件映射到内存时,仍然可以写入关闭的文件描述符。
恩特

从头开始,我刚刚测试了此示例实现CLOSE_WRITE_CLOSE即使我确实删除了closemunmap最后也确实得到了。必须深入挖掘到实际的Git实现,那么..
恩特

嗯,我在努力重现您的问题。在我的测试中inotifywaitgit clone(2.24.1)我得到一个OPEN- > CLOSE_NOWRITE,CLOSE*.idx文件。也许您忘了为此设置处理程序CLOSE_NOWRITE,CLOSE?注意:您会得到一个a,*NOWRITE*因为所有写入都是通过映射的内存发生的。
恩特

是的,有CLOSE_NOWRITE:问题是我看不到IN_CLOSE_WRITE,我想响应文件“更改”以触发上传,但忽略文件“读取”。请注意,实际上,我现在认为mmap + inotify的限制有点像红鲱鱼。我认为问题在于.pack/ .idx文件最初是作为与另一个文件的硬链接创建的,因此仅触发IN_CREATE(并且OPEN-> CLOSE_NOWRITE稍后在git实际读取文件时发生)。
Michal Charemza

2

我可能会推测,Git大多数时候都使用原子文件更新,如下所示:

  1. 文件内容被读入内存(并被修改)。
  2. 修改后的内容将写入一个单独的文件中(通常与原始文件位于同一目录中,并且具有随机mktemp名称(-style))。
  3. 然后,将新文件的rename(2)d -d替换为原始文件;此操作可确保每个尝试使用其名称打开文件的观察者都将获得旧内容或新内容。

这样的更新被看作inotify(7)moved_to在一个目录中的事件,因为文件“再次出现”。


阿对于某些文件,我认为它这样做:我看到了各种IN_MOVED_FROMIN_MOVED_TO事件。但是,我看不到.packand .idx文件会发生这种情况
Michal Charemza

打包文件可能很大(我相信几个GB,至少2GiB);使用原子更新来使用它们可能会在存储空间上被禁止使用,因此可能会使用其他策略对其进行更新。
kostix

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/issues/896,但只是增加了一些情况。


2

还有另一种可能性(来自人inotify):

请注意,事件队列可能会溢出。在这种情况下,事件将丢失。健壮的应用程序应妥善处理丢失事件的可能性。例如,可能有必要重建部分或全部应用程序缓存。(一种简单但可能昂贵的方法是关闭inotify文件描述符,清空缓存,创建新的inotify文件描述符,然后为要监视的对象重新创建监视和缓存条目。)

尽管这git clone会产生繁重的事件流,但这种情况可能会发生。

如何避免这种情况:

  1. 增加读取缓冲区,尝试fcntl(F_SETPIPE_SZ)(这种方法是一种猜测,我从未尝试过)。
  2. 将事件读到专用线程中的大缓冲区中,在另一个线程中处理事件。

2

也许您犯了我几年前犯的同样错误。我只用了两次inotify。第一次,我的代码很简单。后来,我不再有该来源,而是再次开始,但是这次,我错过了事件,也不知道为什么。

事实证明,当我阅读事件时,实际上是在阅读一小部分事件。我解析了我期望的那个,以为仅此而已。最终,我发现接收到的数据还有更多,并且当我添加一些代码来解析一次读取的所有事件时,不再丢失任何事件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.