Org Roam 数据库总是全量重建, 可能的修复方法

摘要:Org Roam 的数据库似乎有些任性,每次同步都要重新来过,全量更新。探究其原因,竟然是因为配置路径使用了软链接(symbolic link)。但别担心,我们有办法应对。最直接的解决方式是优化 org-roam-db-sync 的逻辑,但在此之前,我们可以通过设置真实路径或使用 file-truename 函数来暂时避免问题。示例如下:

(setq org-directory (file-truename "~/Notebooks/")
      org-roam-directory (file-truename "~/Notebooks/RoamNotes/")
      )

故事背景:

每次启动 Emacs 或同步数据库,我都得面对那句“Processing modified files…”,它总是不紧不慢地在屏幕上跑。即便在性能强劲的 M2 Mac 上,也得费上那么几秒钟。这个过程原本应该是增量更新,但它却把所有文件都重新搞了一遍。这让 Org 认为所有文件都是新修改过的。

就算一字未动,同步操作也会像全新处理一样。虽然这不会影响使用,但像鞋里的小石子一样,虽小却足以搅扰人心。

昨日实在忍无可忍,我翻开了那段神秘的代码。

这里是 Org Roam 代码

代码的逻辑本身设计得相当周到。首先,它会在 Roam 路径下获取所有文件,然后计算每个文件的 hash 值,并与数据库中的 hash 值进行比对,以此来识别哪些文件发生了变更,需要处理。但问题出在 org-roam-list-files 函数上,它获取的是 symbolic link 的路径,而 org-roam-db--get-current-files 函数获取的则是数据库中存储的绝对路径,这就导致了比对时的不匹配:

(let* ((gc-cons-threshold org-roam-db-gc-threshold)
       (org-agenda-files nil)
       (org-roam-files (org-roam-list-files))
       (current-files (org-roam-db--get-current-files))
       (modified-files nil))
  (dolist (file org-roam-files)
    (let ((contents-hash (org-roam-db--file-hash file)))
      (unless (string= (gethash file current-files)
                       contents-hash)
        (push file modified-files)))
    (remhash file current-files))

解决方案已经在眼前,就看我们何时着手实施了。在此之前,上述临时方案可保你心情舒畅,免受等待之苦。

我发现即使在广阔的网络世界,有关这一问题的信息也寥寥无几。这加强了我分享解决方案的决心,希望我的发现能够帮助到那些面临相似困境的同伴。


在这段技术探索的旅途中,我有几点心得想与大家共勉:

  1. 动手实践比仅仅头脑风暴要来得真切。 使用 debug-on-entry 来追踪 Emacs 的运行情况,就像是为代码的逻辑流动打上了标签,让我能一步步揭开问题的面纱。

  2. 个性化配置往往伴随着独特的挑战。 我的 Org Roam 配置使用了 symbolic link,这种小众做法使我不得不面对额外的挑战。这次经历提醒我,对于这些特殊的设置,我必须保持警觉,准备好解决可能出现的问题。

如果我要给这段经历再加点料,或许是这样:

  • 社区的力量是无穷的。 尽管我自己解决了问题,但我相信将这些发现分享给社区,一定会激发更多的讨论和解决方案,从而帮助更多使用 Org Roam 的用户。毕竟,很多头脑拼在一起,总能碰撞出火花。

  • 记录与反思是成长的阶梯。 这次的问题解决过程,再次提醒我记录自己的发现和思考过程的重要性。每一次回顾,都可能发现新的知识点或更好的处理方法。

希望我的这些小发现和心得,能为大家带来一些启发,也期待与你们在 Org Roam 的道路上相遇。

5 个赞

多次想上手org-roam都败在了数据库的构建速度上😂

我的习惯是大文件,所以用了org-roam之后每次保存都卡顿很久来解析文件。只能说,org-roam还是更适合众多小文件,而非少数大文件。

(use-package org-roam
  :ensure t
  :custom
  (org-roam-directory (file-truename "/path/to/org-files/"))

Note that the file-truename function is only necessary when you use symbolic link to org-roam-directory . Org-roam won’t automatically resolve symbolic link to the directory.

1 个赞

我也是习惯大文件,因为我发现我的工作流中很少有"刚写完就需要立刻链接"的场景,所以我关闭了 org-roam-db-update-on-save ,然后在 emacs 空闲时更新数据库。这样使用下来平时基本感觉不到卡顿。

1 个赞

2024年了在一台顶级笔记本上一个文本编辑器的性能还是如此垃圾😂

还是要对比具体的操作和文件大小。相同的org文件在emac浏览编辑很顺畅,但是导入到logseq导入要很久,浏览也卡顿。所以我不认为emacs的性能差。

感谢建议。我现在想不到有什么场景一定要用org-roam。

插入链接,可以用补全框架搜索之后,写一个自定义函数,把选择的candidate插入链接;

反向链接,不常使用,需要也可以直接搜索。

如无必要我就勿增实体了

那我觉得这种场景更适合用 denote 。无需数据库,文件名由 id、标题、标签 构成,可直接在 dired 里面搜索和批量处理,反向连接直接用 ripgrep 。

多么痛的领悟, 应该多读读文档的…

用了数据库后,继续使用org文件系统,在数据库和文件系统间同步来同步去感觉很复杂, 性能应该也不会好.

我把 org文件放进数据库 后,至今没敢做导出成多个org文件这个功能,因为一旦做了导出,下一步似乎就是多个org文件导入, 再下一步就是计算每个文件的 hash 值,并与数据库中的 hash 值进行比对,识别变更,next……

一开始不做某件事,后面的很多问题也就遇不到了 :joy:

除了兼容非org-mode系统,我暂时没有get到denote这么做的优势。而且这个方案似乎也更倾向多个小文件。

只要org-mode不用大文件来组织,而是每个笔记一个原子笔记,似乎也能做到。

而且现在反向连接用helm加一个自定义的动作也可以实现。

所以评估了下使用的成本和使用的收益,我选择暂时搁置org-roam :joy:

denote 最大的优势在于它的命名的格式,有时间戳,index,title和tag。一看文件名就知道大概的内容。

基于该命名方式可以很方便的作筛查和排序。

搞数据库有啥明显的好处?

方便索引, 内容不多的时候没影响, 内容多到一定程度后搜索和统计速度更快.

感觉就是把org-mode的headline提升到了文件名 :joy:

1 个赞

这个类容多是什么数量级? denote 有人测了下一万笔记没啥性能缺馅。

还是有区别,headline 只是denote 中的title,没有其他信息。

denote 还是文件命名方式,如果使用org roam 也按照这一命名方式执行,应该也可以。

headline行也有tag,我不知道index是什么,但是时间戳也只是命名习惯的问题。

anyway,感谢推荐,听上去这个方案更适合习惯多个小文件的用户。对我来说层级也是笔记管理的重要部分,denote不太适合我的需求。

这个我也没好好测试过,我存放org内容的sqlite数据库文件最多的时候有40多M,后来vacuum等整理了一遍现在有26M

图片

如果把这些都导出成纯文本文件,内容太多我现在也不知道该用什么样的规则来管理这些文件了