watchman 通知后立即读取的文件为空
Files read immediately after watchman notify are empty
我正在通过 JVM 程序中的 socket/bser 接口集成 watchman。
我看到奇怪的时间:
- 构建系统写入一个文件(一个小文本文件)
- 我在 bser 界面收到守望者通知
- 侦听 bser 订阅通知的线程 A 将更新放入单独线程的队列中
- 线程 B 读取队列,读取更改后的文件,然后将文件的数据放到网络上
但是,不知何故,线程 B 正在读取一个空文件。
其中,我假设在某些时候有效地为空,例如IO/syscalls 可能是:
- 清除文件内容
- 写入区块 1
- 写入区块 2
- 关闭文件
而且我假设我的线程 B 正在步骤 1 和 2 之间读取文件。或者可能是 1 和 4,如果 4 是结果被刷新的时候。
我的困惑有两个:
1) 我认为 watchman 的默认 20 毫秒等待会解释这样的事情,我只会在我的线程 A 上看到更新,更不用说当我的线程 B 在第 4 步之后进行读取和数据时已完成写入文件。
2) 即使 watchman 确实告诉我 "too soon" 关于第一个系统调用(比如第 1 步),并且我在它是一个空文件时读取了结果,也应该有另一个 syscall/watchman 通知"btw, the file has some content now".
FWIW/oddly 够了,我在使用 Java WatchService API 时看到了同样的行为,在那里我会得到一个 inotify 事件,但是读取了一个文件 "too soon",因此得到空结果或部分结果,然后在其余数据可用时没有后续 inotify 事件。
我认为这是一个 fluke/nuance 的 WatchService,所以我当时通过检查文件 mod 读取它的时间来解决它,并等待确保 mod在假设文件 "done" 正在写入之前时间 >2 秒。
(请注意,这也处理了 ~100mb+ 正在写入的文件,其中构建过程可能每 100ms+ 写入一大块数据,但使用 WatchService 我看到了 100 条 inotify 通知,这实际上是一次连续写入。)
当我将我的 WatchService 代码移植到 watchman 时,我放弃了这个 "ensureSettled" hack,因为我假设 watchman 的稳定周期为 20 毫秒(这比我使用的 2 秒低很多,但这是默认值)+与有点 beta 的 WatchService 相比,它的总体健壮性意味着它不会成为问题。
但是在使用 watchman-ported 代码的大约一天内,我看到了空文件读取,就像我在 WatchService 中一样。
关于我遗漏的任何想法?
我可以加回 ensureSettled hack,但此时我很好奇发生了什么。
文档对此不是很清楚,抱歉!
订阅通知的发送受解决超时的影响,但由于文件更新是非原子的,因此您可能会在文件内容可见之前启动默认的 20 毫秒;在幕后,内核会为您正在执行的各种更改生成一系列通知,因此如果在您写入(或刷新)数据之前截断需要 20 毫秒,您可能会收到通知 "in the middle".
这东西也依赖于操作系统。这是最近发现和解决的问题的示例:https://github.com/facebook/watchman/commit/bac383c751b248ae742a2a20df3e8272238c0ae2
这听起来不像是你正在经历的事情,它只是为这个讨论增添了一些色彩。
如果您已经有代码来管理客户端中的结算,那么您可能更容易将其添加回去;例如,我们在 watchman-make
中执行此操作。
您可能还希望尝试在您正在观看的目录树根目录中的 .watchmanconfig
文件中设置 https://facebook.github.io/watchman/docs/config.html#settle,并将其留给 watchman 服务器。 If/when您更改此设置,您将需要删除并重启手表。
您选择哪个取决于您希望如何权衡配置的便利性和您要维护的代码量,以及(可能)如果 .watchmanconfig
配置不正确,来自用户群的支持问题量为了他们。
请注意,您可以使用 https://facebook.github.io/watchman/docs/cmd/log-level.html 中的命令调用来实时查看内核通知的调试日志;这可能有助于您准确了解收到哪些通知以及何时收到。
只是好奇,您是否使用 https://github.com/facebook/watchman/tree/master/java 与守望者服务器对话?
我正在通过 JVM 程序中的 socket/bser 接口集成 watchman。
我看到奇怪的时间:
- 构建系统写入一个文件(一个小文本文件)
- 我在 bser 界面收到守望者通知
- 侦听 bser 订阅通知的线程 A 将更新放入单独线程的队列中
- 线程 B 读取队列,读取更改后的文件,然后将文件的数据放到网络上
但是,不知何故,线程 B 正在读取一个空文件。
其中,我假设在某些时候有效地为空,例如IO/syscalls 可能是:
- 清除文件内容
- 写入区块 1
- 写入区块 2
- 关闭文件
而且我假设我的线程 B 正在步骤 1 和 2 之间读取文件。或者可能是 1 和 4,如果 4 是结果被刷新的时候。
我的困惑有两个:
1) 我认为 watchman 的默认 20 毫秒等待会解释这样的事情,我只会在我的线程 A 上看到更新,更不用说当我的线程 B 在第 4 步之后进行读取和数据时已完成写入文件。
2) 即使 watchman 确实告诉我 "too soon" 关于第一个系统调用(比如第 1 步),并且我在它是一个空文件时读取了结果,也应该有另一个 syscall/watchman 通知"btw, the file has some content now".
FWIW/oddly 够了,我在使用 Java WatchService API 时看到了同样的行为,在那里我会得到一个 inotify 事件,但是读取了一个文件 "too soon",因此得到空结果或部分结果,然后在其余数据可用时没有后续 inotify 事件。
我认为这是一个 fluke/nuance 的 WatchService,所以我当时通过检查文件 mod 读取它的时间来解决它,并等待确保 mod在假设文件 "done" 正在写入之前时间 >2 秒。
(请注意,这也处理了 ~100mb+ 正在写入的文件,其中构建过程可能每 100ms+ 写入一大块数据,但使用 WatchService 我看到了 100 条 inotify 通知,这实际上是一次连续写入。)
当我将我的 WatchService 代码移植到 watchman 时,我放弃了这个 "ensureSettled" hack,因为我假设 watchman 的稳定周期为 20 毫秒(这比我使用的 2 秒低很多,但这是默认值)+与有点 beta 的 WatchService 相比,它的总体健壮性意味着它不会成为问题。
但是在使用 watchman-ported 代码的大约一天内,我看到了空文件读取,就像我在 WatchService 中一样。
关于我遗漏的任何想法?
我可以加回 ensureSettled hack,但此时我很好奇发生了什么。
文档对此不是很清楚,抱歉!
订阅通知的发送受解决超时的影响,但由于文件更新是非原子的,因此您可能会在文件内容可见之前启动默认的 20 毫秒;在幕后,内核会为您正在执行的各种更改生成一系列通知,因此如果在您写入(或刷新)数据之前截断需要 20 毫秒,您可能会收到通知 "in the middle".
这东西也依赖于操作系统。这是最近发现和解决的问题的示例:https://github.com/facebook/watchman/commit/bac383c751b248ae742a2a20df3e8272238c0ae2 这听起来不像是你正在经历的事情,它只是为这个讨论增添了一些色彩。
如果您已经有代码来管理客户端中的结算,那么您可能更容易将其添加回去;例如,我们在 watchman-make
中执行此操作。
您可能还希望尝试在您正在观看的目录树根目录中的 .watchmanconfig
文件中设置 https://facebook.github.io/watchman/docs/config.html#settle,并将其留给 watchman 服务器。 If/when您更改此设置,您将需要删除并重启手表。
您选择哪个取决于您希望如何权衡配置的便利性和您要维护的代码量,以及(可能)如果 .watchmanconfig
配置不正确,来自用户群的支持问题量为了他们。
请注意,您可以使用 https://facebook.github.io/watchman/docs/cmd/log-level.html 中的命令调用来实时查看内核通知的调试日志;这可能有助于您准确了解收到哪些通知以及何时收到。
只是好奇,您是否使用 https://github.com/facebook/watchman/tree/master/java 与守望者服务器对话?