问题描述
使用 distcp -update 参数同步 hdfs 数据到 cos,发现重复执行的时候,发生的是 overwrite 行为,不是 skip 行为,这点和 hdfs 上的行为不一致。
排查
1.skip 的逻辑如下:大小 和 blkSize 一致的时候, 才可能发生 skip,打印日志重新打包
2.使用新包,再次执行,查看日志发现,hdfs 上的 blkSize 是 64M,cos 是 32M,因此不满足 skip 条件
3.跟踪 cos 文件 blockSize 属性
(1)target 目录不存在的情况,即第一次 distcp,会调用 create 方法创建文件,此时会指定 blockSize
而我们访问 Cos 使用的是 S3AFileSystem,blockSize 参数会被忽略,不会被使用(传了也没用),所以 Cos 上不知道实际的 hdfs 文件的 blockSize 是多少
上图最后会创建一个 S3ABlockOutputStream对象,传入了一个 partSize,看参数意思是该值作为 blockSize
partSize 的描述 和 控制参数如下,默认 100M:
partSize 值最终会设置到 S3ABlockOutputStream 的 blockSize 属性中,而 blockSize 属性的调用位置如下:
查看 blockFactory 的具体实现类, blockSize 参数实际控制的是 buffer。对象存储里的这个 blockSize 参数 和 hdfs 里不是一个概念!
(2)经过(1)的分析可知, cos 上的文件 blockSize 并不是 实际的 hdfs 上的 blockSize,两者不是一个含义,那么,再次执行 distcp 的时候,32M 哪里来的?
获取文件的 blockSize,在 FileSystem 里的流程调用 getFileStatus 获取详情信息,S3AFileSystem 继承了 FileSystem,并重写了该方法
S3AFileSystem 重写了 getDefaultBlockSize, 通过 fs.s3a.block.size 参数控制,默认是 32M
查看 FS_S3A_BLOCK_SIZE 的调用,只有 getDefaultBlockSize 和 initialize(只做参数值检测) 使用到了。
总结
1.S3AFileSystem 里的 fs.s3a.block.size 和 hdfs 中的 dfs.blocksize 代表的含义是一样的,但是 对象存储里该值没实际意义,更像是为了兼容 hdfs,而留下的属性。
2.初步修改思路:Distcp 的时候,针对对象存储,不做 blk 比较。
提交 issues.apache.org/jira/browse…
后来有人回复这个 bug 是因为他的 patch issues.apache.org/jira/browse… 引入的 bug
已经在高版本 roll back 了那个 patch。
3.参照 patch 内容,进行回滚,测试生效
4.回滚 patch 生效的原因是,patch 默认加上了 -pb 参数,roll back 后,默认不带该参数,因此不比较 blockSize,sameBlockSize 返回 true