session.getDatabase 中文件名大小写的问题

最近在项目上遇到了一个奇怪的问题:某个 XPages 页面偶发性报错,对数据库进行签名后问题消失,但过些时间就又会出现。

由于签名后问题消失,最开始排查时就认为是代码签名的问题,可能是服务器上有对应的模板,设计不定时的被 Design 任务刷新回来导致问题。但是通过各种方法找了很多遍,都确认库的设计没有被刷新过。

最终通过检查代码的错误行发现,是一处 session.getDatabase 调用时数据库路径的大小写有问题,修改成与文件系统一致后问题消失。问题虽然解决了,但为什么同样的代码大部分情况下没问题,只有偶发性的报错呢?同样的代码在同为 Linux 平台的产品测试环境上,为什么就一切正常呢?

带着这些疑问继续探索,终于找到了一些端倪。在这个技术文档中提到如下内容:

DB on server names AgentRunner.nsf. Notes the upper/lowercase.

Steps to Reproduce:

After a server restart or a “dbcache flush”:

> lo fixup agentrunner
> 25.06.2002 09:58:00   Database fixup process started
25.06.2002 09:58:00   Unable to fixup database agentrunner: File
does not exist
25.06.2002 09:58:00   Database fixup process shutdown
–> does not work, because db is not in server cache

lo fixup AgentRunner
> 25.06.2002 09:58:07   Database fixup process started
25.06.2002 09:58:07   Performing consistency check on
AgentRunner.nsf…
25.06.2002 09:58:08   Completed consistency check on
AgentRunner.nsf
25.06.2002 09:58:08   Database fixup process shutdown
–> works – it`s exact-case

> lo fixup agentrunner
> 25.06.2002 09:58:12   Database fixup process started
25.06.2002 09:58:12   Database fixup process shutdown
–> works now with lowercase, cause db is in cache

dbcache flush
> lo fixup agentrunner
> 25.06.2002 09:58:21   Database fixup process started
25.06.2002 09:58:21   Unable to fixup database agentrunner: File
does not exist
25.06.2002 09:58:21   Database fixup process shutdown
–> db removed from cache –> doesn`t work

整个过程总结如下:

  • 首先用大小写错误的文件名访问,报错
  • 再用大小写正确的文件名访问,正常
  • 再次用大小写错误的文件名访问,正常(因为上一条命令已经将数据库加入了 dbcache)
  • 清空 dbcache 后,再次用大小写错误的文件名访问,报错

导致这个现象的根本原因就是:

  • 当数据库不在 dbcache 中时,Domino 使用数据库文件名通过文件系统获取数据库对象,此时是区分大小写的
  • 当数据库在 dbcache 中时,Domino 使用数据库文件名在 dbcache 中查找,找到后直接返回数据库对象,此过程无需访问文件系统,所以就不区分大小写

分析到这里,最初的所有现象就能说通了:

  • 错误偶发,是因为大部分情况下此库都在 dbcache 中,可能只有服务器刚刚启动/缓存超限将此库移除时,才会报错
  • 签名后错误消失,是因为签名的过程肯定会把此库加入到 dbcache

吃一堑长一智,既然这个问题让我纠结了这么久,那么如何才能避免此类问题发生呢?建议如下:

  • 数据库名始终使用全小写,代码中获取数据库时也用全小写
  • *nux 测试环境下建议通过 NSF_DbCache_Disable=1 禁用缓存。虽然性能会差一些,但是能尽早的发现问题(千万别在生产服务器上这样做)