发布于 

Ceph 挂载点压缩提示 File changed as we read it 问题分析

使用 CephFS 的同学可能会发现如果在 CephFS 挂载点下去压缩文件的时候有时候会提示 File changed as we read it,有时候还会导致
CRC 失败,本文对 File changed as we read it 问题出现的原因进行了分析并给出了解决办法,希望能对大家有所帮助。

问题现象

首先这个问题可能导致的现象比较多样,最基础的就是把一个压缩文件解压一下然后立马压缩,这时就会提示 File changed as we read it 如下:

1
2
3
4
[root@An1Uit ceph-fuse]# ./tar.sh
tar: cmake-3.7.2/Tests/LinkFlags: 在我们读入文件时文件发生了变化
tar: cmake-3.7.2/Tests/SimpleCOnly: 在我们读入文件时文件发生了变化
tar: cmake-3.7.2/Tests/ObjC++: 在我们读入文件时文件发生了变化

那除了这个现象以外还可能导致一些应用的 CRC 校验失败,或者 nfs 的 REMOTE I/O ERROR

原因分析

虽然分析问题时候比较令人挠头但是实际问题的原因比较简单,在 client 配置中有一个默认开启的配置项 client_dirsize_rbytes,开启这个选项之后会影响 fill_stat 时传入目录 st->size 的值。

如果对 st->size 不清楚的话,实际上我们执行 ls -lh 看到的文件和目录大小就是 st->size 的值:

1
2
3
4
5
6
7
➜  ceph git:(main) ✗ ls -lh
total 2.8M
drwxr-xr-x 2 root root 4.0K Mar 1 23:55 admin
-rw-r--r-- 1 root root 322 Dec 3 20:34 AUTHORS
drwxr-xr-x 2 root root 4.0K Dec 3 20:34 bin
drwxr-xr-x 19 root root 4.0K Feb 12 12:30 build
-rw-r--r-- 1 root root 80K Mar 1 23:55 ceph.spec.in

对于文件这里 st->size 就是 in->sizeclient_dirsize_rbytes 这个选项对文件也没有什么影响,对于目录的话 st->size 在 xfs 等本地文件系统中填充的同样是 inode 的大小,通常就是一个 block 也就是 4k,而在 CephFS 中默认也就是开启 client_dirsize_rbytesst->size 填充的是 rstat 中递归统计的文件大小总和,如果没有开启的话这里填充的是目录中当前一层的 dentry 个数

1
2
3
4
5
[root@An1Annaaq4 fuse]# ls -lhart
total 5.0G
drwxr-xr-x 28 root root 1.5G Jan 10 18:09 annaDir
drwxr-xr-x 2 root root 110G Jan 13 15:06 ivan
drwxr-xr-x 102 root root 3.9T Jan 25 10:38 fsstress-test-2

很明显这样对我们平时使用有很大的便利,最直接的就是不用再 dh -su . 了直接就能看总占用,但是关键就在于 rstat 这个东西由 inest 保护,它不是实时更新的,这一点也比较好理解毕竟 rstat 如果稍有改动就要 frozen 目录然后一路更新到根的话那也就没有什么性能可言了

但是这里就有一个问题, tar 这类应用的行为会在压缩开始前拿一把目录的 sizemtime,然后在压缩完成后再拿一次并两相比较,如果产生了变化就会像我们遇到的一样提示 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 脚本遇到了这个问题,结果最后都没有人看问题的原因,只是回退了脚本。