文章 John Pan · 3 小时 前 4m read

开发 OcuPilot 时我学到的经验:macOS 会悄悄删除保存在 /tmp 目录中的 IRIS 数据库

OcuPilot

在开发 OcuPilot(https://openexchange.intersystems.com/package/OcuPilot)——一款用于 IRIS 管理门户的人工智能副驾驶——的过程中,我为测试运行了大量短暂存在的 IRIS 容器。 这些容器将数据保存在我 Mac 上的 /tmp 目录下的文件夹中,因为那里是临时文件的存放位置。三天后,其中一个容器开始出现莫名其妙的故障:一个仍显示为已挂载的临时数据库背后实际上没有对应的文件,且向其写入数据时会失败。

我起初怀疑是 IRIS 的问题,随后又怀疑是自己的代码出了问题。但事实并非如此。这其实是 macOS 完全按照其设计原理在正常运行。

macOS 的工作原理

每天午夜,macOS 会运行一个小型清理任务,删除 /tmp 目录下超过三天无人触碰的文件。 这是 macOS 自身的一部分,对于临时文件夹来说也是合情合理的。它只检查 /tmp(实际上是 /private/tmp),且不关心文件的具体内容。

为什么 IRIS 数据库特别容易受到影响

IRIS 将您使用的数据保存在内存中,仅在发生变更时才写入数据库文件。我对此进行了测试:读取数据库(即使是全部读取),文件的时间戳也完全保持不变。 因此,对于仅被读取或完全未使用的数据库,清理任务会将其视为未被触及,三天后该文件便会消失。

临时数据库最先被清理,因为 IRIS 几乎从不将它们写入磁盘。但任何数据库只要三天内未发生变更,都会被纳入清理范围。

接下来会发生什么

这一步让我感到惊讶。我特意使用一个全新的容器和一个测试数据库重现了这一现象:

  • 起初,一切看起来都很正常。**IRIS 仍然保持着该文件的打开状态,因此它会持续读取您的数据,并报告数据库已挂载。
  • 当数据库需要扩展时,写入操作开始失败。**一项小规模的更改成功通过;而一项大规模的更改则中途失败。
  • 下次重启时,IRIS 拒绝启动。**它有待写入的更改,但目标文件已不存在,因此它选择停止运行而非进行猜测。该实例一直处于停机状态,直到有人介入处理,而该数据库中的数据已丢失。
  • 容器仍显示为运行中。**Docker 始终报告该容器处于运行状态,尽管其中的 IRIS 并未启动。

如果您是通过搜索来到这里的,这些就是我看到的日志信息。路径是我测试数据库的,您的数据库会有自己的名称。当 IRIS 仍在运行时,messages.log 记录如下:

Error opening volume to write block 652 of /durable/iris/mgr/userdb/

而在下次启动时,IRIS 停止运行并显示:

Failed to open /durable/iris/mgr/userdb/IRIS.DAT: errno=2
We failed to fully restore the WIJ.
** Startup aborted **
Recovery failure. Startup aborted.

要恢复正常,需要从备份中还原数据库。IRIS 确实提供了一种强制启动的方式,但会警告说,如果采用此方法,其他数据库可能会受损。

哪些用户会受到影响

这与容器无关。关键在于文件在您的 Mac 上的存储位置:

  • IRIS 以原生方式安装在常规位置(例如您的用户目录或 /opt 之下) 否
  • 原生安装,或者仅有一个数据库、日志或其他数据文件夹位于 /tmp 之下是
  • 数据位于 /tmp 下的文件夹中的容器(例如 -v /tmp/iris:/durable) 是
  • 使用命名 Docker 卷或位于 /tmp 之外的文件夹的容器 否

/tmp 是进行快速实验的理想位置,而三天的时间足够短,足以让你发现那个本打算在周五删除、却一直忘记直到周一才想起的容器。

如何检查

在 /tmp 目录下查找数据库文件:

find /private/tmp -name IRIS.DAT 2>/dev/null

如果输出内容中有您关心的信息,请在今晚之前将其转移。对于容器,请检查每个容器将数据存储在何处:

docker inspect --format '{{.Name}} {{range .Mounts}}{{.Source}} {{end}}' $(docker ps -q)

任何以 /tmp 或 /private/tmp 开头的源路径都存在风险。

如何避免此问题

  • 请勿将 IRIS 数据存放在 /tmp 中。您可以将其存放在用户主目录下的文件夹中,或者使用命名的 Docker 卷。
  • 若确实需要使用 /tmp 进行快速测试,请将该容器视为一次性容器,并在其运行未满三天时重新创建。
  • 不要依赖容器的“运行中”状态。请确认 IRIS 本身是否已启动,例如在容器内执行 iris qlist 命令。

我的经验总结

  • 当实例出现异常时,在归咎于数据库之前,请先查看其文件所在的位置。
  • 一个运行时间超过计划的测试容器,就不再是“一次性”容器了。
  • “正在运行”不等于“正常工作”。

我正在将 OcuPilot 自身的测试容器从 /tmp 移出。如果你在 Mac 上运行 IRIS,今天请花一分钟检查一下你的情况。

我使用人工智能协助起草和编辑了这篇文章;其中的观点、决策和最终审阅均由我本人负责。