SeaweedFS ec decode missing shard 的问题及解决
这里记录了使用 SeaweedFS 过程中,对于有时出现的 ec.decode volume
失败的情况,所进行的问题重现、分析和解决办法。
问题
在使用 SeaweedFS 过程中,发现对于一个 erasure coding (ec) 卷,在运行 ec.decode
时,有时会遇到缺少 shard 的情况,类似这样的错误提示:
generateNormalVolume from ec volume 107 on 192.168.1.208:8082
error: generate normal volume 107 on 192.168.1.208:8082: rpc error: code = Unknown desc = ec volume 107 missing shard 1
但奇怪的是,有时候再次运行同样的命令,又可以执行成功。从多次运行的情况来看,问题并不能必然重现,但总体上有相当的规律性。
另外,在走读代码中,看到服务进程里实现了对 signal 的处理,所以在问题重现时,发现如果发送一个 HUP
信号,有时也可以解决问题。
是否已知问题
根据 SeaweedFS 项目的 issue 列表 里的搜索结果来看,确实有人提过相似问题,比如 这里。并且,结合最新版本(当前是 30GB 3.58)也能重现的情况,能确认是个问题并且还未修复。
问题分析及解决
以下是走读代码后发现问题所在的调用关系:
doEcDecode()
collectEcShards()
volumeServerClient.VolumeEcShardsCopy()
generateNormalVolume()
volumeServerClient.VolumeEcShardsToVolume()
vs.store.CollectEcShards()
loop the shardFileNames and check if shard missing
mountVolumeAndDeleteEcShards()
跟踪代码,系统在处理 ec.decode
子命令时,会把所有的 ec shard 文件块从它们各自所在的 volume server,拷贝并归拢到某一台指定的 volume server 上。这些 ec shard 文件块,会被用来恢复成普通卷。定位到的问题是,这些从其他 volume server 上拷贝回来的 ec shard 文件块,并没有在 decode 时被枚举到(具体是在上述 collectEcShards()
函数里面),从而被后续流程发现有 shard 文件块缺失,并进入出错处理流程,最终导致出错。
定位到了原因,后续的修复就容易了。在上述出错的地方,添加 ec shard 文件块的挂载操作,重新编译部署并反复运行后,确认问题解决,ec.decode
可以正常运行完成。
以下是补丁:
diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go
--- a/weed/shell/command_ec_decode.go
+++ b/weed/shell/command_ec_decode.go
@@ -194,12 +194,22 @@ func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[pb.ServerAddr
SourceDataNode: string(loc),
})
if copyErr != nil {
return fmt.Errorf("copy %d.%v %s => %s : %v\n", vid, needToCopyEcIndexBits.ShardIds(), loc, targetNodeLocation, copyErr)
}
+ fmt.Printf("mount %d.%v on %s\n", vid, needToCopyEcIndexBits.ShardIds(), targetNodeLocation)
+ _, mountErr := volumeServerClient.VolumeEcShardsMount(context.Background(), &volume_server_pb.VolumeEcShardsMountRequest{
+ VolumeId: uint32(vid),
+ Collection: collection,
+ ShardIds: needToCopyEcIndexBits.ToUint32Slice(),
+ })
+ if mountErr != nil {
+ return fmt.Errorf("mount %d.%v on %s : %v\n", vid, needToCopyEcIndexBits.ShardIds(), targetNodeLocation, mountErr)
+ }
+
return nil
})
if err != nil {
break
}
后来把整理的补丁,给项目作者发了个 pull request,目前已经被合并。