glusterfs7 多次扩容后出现重复文件分析
背景
最近在做glusterfs7版本扩容操作测试的时候,发现偶尔会出现,文件丢失然后再重新创建文件会出现文件重复的现象。具体操作为 从一个节点扩容到10个节点,3副本,添加一个节点操作为probe node–>add-brick–>replace-brick–>rebalance-fix-layout, 添加至10个节点后,删除第1个节点,操作为:replace-brick–>detach node。fuse客户端操作,stat file,若file 不存在 则touch file,就有可能出现 两个重复的file 文件。
原理分析
glusterfs文件分布是同一个一致性hash 算法hash分布的,正常情况下我们查找一个文件,只需要在它的hash 节点上查找即可,如果查找不到就说明文件不存在。但是当glusterfs扩容或者缩减节点的时候hash分布会发生变化会导致查找一个文件可能不在它的hash 节点就需要lookupeverywhere 到所有节点上去查找,当做完rebalance 后修复目录了hash布局,就又可以只在hash节点上查找了,无需lookupeverywhere 。
经原理分析,很可能是扩容后,做了rebalance fix-layout,文件在原来的hash分布节点上,而新的hash 分布节点上没有,而dht 层在做lookup的时候没有去所有节点上查找,所以stat 不到文件,然后就在新的hsah 节点上创建了文件,然后对文件目录做ls 就会出现重复的文件。
源码分析
lookupeverywhere 条件判断
在dht 层dht_lookup_cbk 中如果在hash 节点查找文件返回不存在则通过dht_should_lookup_everywhere 函数判断是否是否要去所有hash 节点做lookup。
if (!conf->defrag && loc->parent) {
ret = dht_inode_ctx_layout_get(loc->parent, this, &parent_layout);
if (!ret && parent_layout &&
//父目录的commit_hash 值和conf的vol_commit_hash 值比较,不相等则lookup everywhere
(parent_layout->commit_hash == conf->vol_commit_hash)) {
lookup_everywhere = _gf_false;
}
}
parent_layout->commit_hash 来源:
在dht_disk_layout_merge 函数体现,即父目录的扩展属性trused.glusterfs.dht 值的高位32位。
conf->vol_commit_hash 来源:
在dht_revalidate_cbk 函数体现,获取来自于brick 目录的扩展属性trusted.glusterfs.dht.commithash 值。
lookupeverywhere条件分析
parent_layout->commit_hash 值变化点:
(1)parent_layout->commit_hash (trused.glusterfs.dht)的高位32位 在目录创建的时候与brick 的conf->vol_commit_hash(trusted.glusterfs.dht.commithash) 值相同。
(2)在集群做reblance 时候会被修复为与brick 的conf->vol_commit_hash(trusted.glusterfs.dht.commithash) 值一样。
conf->vol_commit_hash 值变化点:
(1)做rebalance 的时候,glusterd 进程会生成commit hash 值 启动 rebalance进程会以参数的形式传递,rebalance 进程会设置所有brick 目录的扩展属性(trusted.glusterfs.dht.commithash)为commit hash值。
背景问题原因
在做rebalance fix layout 的时候,glusterd 进程给rebalance进程传递的commit hash 参数值为0,导致 conf->vol_commit_hash 为0与未add-brick 前创建的目录的扩展属性(trusted.glusterfs.dht.commithash)高位32位也为0 相等。导致fuse 客户端访问目录时候就会发现一些文件不存在,因为add-brick hash 范围变了,所以在hash 节点找不到文件,又没有触发 lookupeverywhere 条件,所以文件找不到,然后创建了相同的文件落在了hash 节点上。ls 目录的时候,就把两个文件都读到了,致使出现重复文件。
背景问题解决
解决办法有两种:
1.注释掉dht_should_lookup_everywhere 函数里面的commit hash 值判断,只要在hash 节点looup 返回不存在 就执行lookupeverywhere 。这个解决办法有点暴力,会导致查找判断一个不存在的文件变慢(因为多了一次lookupeverywhere )。
2.找到glusterd 进程给rebalance进程传递的commit hash 参数值为0的bug。这个是最佳解决办法。
总结
glusterfs 分布式文件系统查找文件是在hash 节点上查找,但是扩容和缩减后hash 分布变化后,通过文件父目录的扩展属性(trusted.glusterfs.dht.commithash)高位32位值和brick 目录的扩展属性(trusted.glusterfs.dht.commithash)值来判断如果文件不在hash节点是否需要去所有节点查找文件。这样来优化了文件的查找。这也得出结论扩容和缩减后只做fix-layout,以前的老目录查找文件如果文件不在hash 节点就需要去所有节点查找,做rebalance后所有文件查找就只会在hash 节点查找。