0

我正在通过 JVM 程序中的 socket/bser 接口集成 watchman。

我看到奇怪的时机:

  1. 构建系统写入一个文件(一个小文本文件)
  2. 我在 bser 界面上收到守望通知
  3. 线程 A 监听 bser 订阅通知将更新放到一个单独线程的队列中
  4. 线程 B 读取队列,读取更改的文件,然后将文件的数据放到网络上

但是,不知何故,线程 B 正在读取一个空文件。

我假设在某些时候它是有效的空,例如 IO/系统调用可能是:

  1. 清除文件内容
  2. 写入块 1
  3. 写入块 2
  4. 关闭文件

我假设我的线程 B 正在步骤 1 和 2 之间读取文件。或者可能是 1 和 4,如果 4 是刷新结果的时间。

我的困惑有两个方面:

1)我认为守望者的默认 20 毫秒等待会解释这样的事情,而且我只会在我的线程 A 上看到更新,更不用说当我的线程 B 在第 4 步之后进行读取并且数据完成写入时文件。

2)即使守望者确实“过早地”告诉我第一个系统调用(比如步骤 1),并且我在它是一个空文件时读取了结果,应该还有另一个系统调用/守望者通知“顺便说一句,文件有一些现在的内容”。

FWIW/奇怪的是,我在使用 Java WatchService API 时看到了同样的行为,在那里我会得到一个 inotify 事件,但是“太快”读取一个文件,所以得到空的或部分的结果,然后没有跟进当其余数据可用时通知事件。

我认为这是WatchService的侥幸/细微差别,所以我当时通过在读取文件之前检查文件修改时间来解决它,并在假设文件“完成”写入之前等待确保修改时间> 2秒.

(请注意,这也处理了大约 100mb+ 的文件正在写入,其中构建过程可能每 100ms+ 写入一大块数据,但是使用 WatchService 我看到了 100 条 inotify 通知,这些通知基本上是一次连续的写入。)

当我将我的 WatchService 代码移植到 watchman 时,我放弃了这个“ensureSettled”黑客,因为我假设 watchman 的 20 毫秒稳定期(这比我使用的 2 秒低得多,但嘿,这是默认值)+ 与有点beta WatchService 意味着它不会是一个问题。

但是在使用 watchman-ported 代码的大约一天内,我看到了空文件读取,就像我使用 WatchService 一样。

关于我所缺少的任何想法?

我可以添加回 ensureSettled hack,但此时我很好奇发生了什么。

4

1 回答 1

0

文档对此不是很清楚,抱歉!

订阅通知的发送取决于结算超时,但由于文件更新是非原子的,因此可能会在您看到文件内容之前启动默认的 20 毫秒;在幕后,内核会为您正在执行的各种突变生成一系列通知,因此如果截断在您写入(或可能刷新)数据之前需要 20 毫秒,您可能会在“中间”收到通知”。

这些东西也依赖于操作系统。这是最近发现并解决的问题的示例:https://github.com/facebook/watchman/commit/bac383c751b248ae742a2a20df3e8272238c0ae2 听起来与您遇到的情况完全不同,它只是为您添加了一些颜色本次讨论。

如果您已经有代码来管理客户端的结算,那么您可能更容易将其添加回来;例如,我们这样做watchman-make。您可能还希望尝试在您正在观看的目录树根目录中的文件中设置https://facebook.github.io/watchman/docs/config.html#settle并将其留给 watchman 服务器。.watchmanconfig如果/当您更改此设置时,您将需要删除并重新启动手表。

您选择哪一种取决于您希望如何将配置的易用性与您想要维护的代码量以及(可能)来自用户群的支持问题的数量(如果.watchmanconfig没有为他们正确配置)进行交易。

请注意,您可以使用来自https://facebook.github.io/watchman/docs/cmd/log-level.html的命令调用来实时查看内核通知的调试日志;这可能有助于您准确了解收到哪些通知以及何时收到。

只是好奇,您是否使用https://github.com/facebook/watchman/tree/master/java与 watchman 服务器通信?

于 2016-09-29T01:28:37.593 回答