使用 CephFS 的同学可能会发现如果在 CephFS 挂载点下去压缩文件的时候有时候会提示 File changed as we read it,有时候还会导致
CRC 失败,本文对 File changed as we read it 问题出现的原因进行了分析并给出了解决办法,希望能对大家有所帮助。
问题现象
首先这个问题可能导致的现象比较多样,最基础的就是把一个压缩文件解压一下然后立马压缩,这时就会提示 File changed as we read it 如下:
1 | [root@An1Uit ceph-fuse]# ./tar.sh |
那除了这个现象以外还可能导致一些应用的 CRC 校验失败,或者 nfs 的 REMOTE I/O ERROR
原因分析
虽然分析问题时候比较令人挠头但是实际问题的原因比较简单,在 client 配置中有一个默认开启的配置项 client_dirsize_rbytes,开启这个选项之后会影响 fill_stat 时传入目录 st->size 的值。
如果对 st->size 不清楚的话,实际上我们执行 ls -lh 看到的文件和目录大小就是 st->size 的值:
1 | ➜ ceph git:(main) ✗ ls -lh |
对于文件这里 st->size 就是 in->size, client_dirsize_rbytes 这个选项对文件也没有什么影响,对于目录的话 st->size 在 xfs 等本地文件系统中填充的同样是 inode 的大小,通常就是一个 block 也就是 4k,而在 CephFS 中默认也就是开启 client_dirsize_rbytes 时 st->size 填充的是 rstat 中递归统计的文件大小总和,如果没有开启的话这里填充的是目录中当前一层的 dentry 个数
1 | [root@An1Annaaq4 fuse]# ls -lhart |
很明显这样对我们平时使用有很大的便利,最直接的就是不用再 dh -su . 了直接就能看总占用,但是关键就在于 rstat 这个东西由 inest 保护,它不是实时更新的,这一点也比较好理解毕竟 rstat 如果稍有改动就要 frozen 目录然后一路更新到根的话那也就没有什么性能可言了
但是这里就有一个问题, tar 这类应用的行为会在压缩开始前拿一把目录的 size 和 mtime,然后在压缩完成后再拿一次并两相比较,如果产生了变化就会像我们遇到的一样提示 File changed as we read it,正常来说在本地文件系统中目录的 size 是不会变的都是 4k,只有 mtime 会因为我们对目录的改动而变化,但是在 CephFS 中反而 mtime 由于受到 filelock 的保护所以导致 getattr 时取到了预期的值,反而 size 也就是 rstat->rbytes 受到 inest 保护,而 getattr 并不会检查 inest 锁的状态,所以有可能第一次拿回一个滞后的值,而在压缩完成后第二次 getattr 时又拿到了一个更新完成的值,这就导致了问题的产生。
解决办法
知道了问题产生的原因的话解决办法也比较简单,将 client_dirsize_rbytes 这项配置设置为 false 即可。不过要注意这样的话我们就没法通过 ls -lh 直接查看目录递归统计值了,只能通过 getfattr 等其他手段来看。
最后小小的吐槽社区 Tracker 上又好几次改 QA 脚本遇到了这个问题,结果最后都没有人看问题的原因,只是回退了脚本。
参与讨论