Java GZIPInputStream OutOfMemoryError

       记录一个集群中用户提交任务失败的情况,用户的任务一直没有问题,但是某天的日志过来后,在集群中报错,报的是GZipInputStream 的OutOfMemory问题。刚开始认为是数据过大,将内存设置大一些就好了,但是设置内存不管作用,在单机给他的日志做测试的时候依然报错,错误堆栈信息为:

2014-11-10 09:52:54,743 FATAL [main] org.apache.hadoop.mapred.YarnChild: Error running child : java.lang.OutOfMemoryError

        at java.util.zip.Inflater.inflateBytes(Native Method)

        at java.util.zip.Inflater.inflate(Inflater.java:238)

        at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:135)

        at java.util.zip.GZIPInputStream.read(GZIPInputStream.java:90)

        at com.sina.suda.mr.util.BinaryDecoder.unGZip(BinaryDecoder.java:21)

        at com.sina.suda.mr.util.BinaryDecoder.decodePostStr(BinaryDecoder.java:67)

       根据堆栈查看了一下jdk source code的路径,发现最后的native 代码报错。Google该问题,发现时jdk6的问题,由于日志传输过程中gzip文件corrrupt掉了,这样导致了OutOfMemory。解决方法比较暴力,在外层catch OutOfMemoryError,然后返回一个空结果。

ThreadLocal造成Hadoop权限混乱问题

一、问题现象

       10月底进行了一次中心机迁移,迁移完成后发现大量文件的权限不正确,导致了用户任务大量出问题。为暂时解决问题,我们对所有的目录增加读和执行权限(a+rx),对所有文件增加了读(a+r)权限,以保证用户能够运行任务。

P1

         如上图所示,用户文件被加入了Acl功能,而管理员并没有为该用户开通任何Acl功能。

P2

        查询Acl后,发现用户加入了一个完全无关的Acl。可以确定是Hadoop的元数据出了问题,导致了权限的混乱。

二、bug 查找

       第二天取消了大部分目录的ACL,同时又设置了一些ACL。在查找问题的过程中发现从新中心机拿出FSImage并重新加载,取消的ACL也有部分设置了新的ACL,可是从新的中心机内存中通过hdfs dfs -ls 查询后权限是完全正常的。初步怀疑是SNN merge Edit和FSImage出问题或者FSImage EditLog写入的时候出问题,最复杂的就是JournalNode内部出问题(最不可能的一种情况)。

       首先查看了一下SNN的merge代码路径,与ANN的savenamespace 路径是完全一致的。写了一个简单的测试程序,从ANN大量随机的建立目录(包含ACL和不含ACL)并记录其EditLog日志,同时从SNN不断的读取journalnode中的Edits,并记录,对比两边Edit日志,最后发现两边的Edit日志是完全一样的,这就排除了是SNN后者Journalnode出的问题。进而去查看记录的EditLog日志,发现在ANN中的Editlog是存在问题的,一些本没有设置ACL的目录被加入了ACL记录,说明这是在ANN log Editlog的时候出现的问题。查看mkdir的代码路径,代码段一直是在sync中的,所以肯定不是由竞争导致的数据不一致,继续查找,在logMkdir部分发现了这段代码,

  public void logMkDir(String path, INode newNode) {

    PermissionStatus permissions = newNode.getPermissionStatus();

    MkdirOp op = MkdirOp.getInstance(cache.get())

      .setInodeId(newNode.getId())

      .setPath(path)

      .setTimestamp(newNode.getModificationTime())

      .setPermissionStatus(permissions);

 

    AclFeature f = newNode.getAclFeature();

    if (f != null) {

      op.setAclEntries(AclStorage.readINodeLogicalAcl(newNode));

    }

    logEdit(op);

  }

           其中cache是一个ThreadLocal的变量,属于线程私有变量,这就存在了一个非常严重的问题,假设之前这个线程mkdir继承了一个ACL,那么就会走到op.setAclEntries,op是从cache中获得的,这次mkdir没有任何问题。下面这个线程又logMkdir,这次newNode是一个普通的INode,不带ACL属性,那么就不会设置任何AclEntries,但是cache中的MkdirOp是存在AclEntries属性的,就是上次设置的属性,这样logEdit以后这个dir的permission就混乱了,不是自己的属性了。导致了重启后属性出现的问题。

       这个bug我已经反馈给了社区,jira号是https://issues.apache.org/jira/browse/HDFS-7385 

 

三、解决方案

          patch非常简单,对于没有AclEntries的op,设置null即可。对于已经存在问题的NameNode,从ANN saveNamespace,获得一个正确的FSImage,SNN先升级代码,读取正确的Image,启动,切换为ANN,再把原来的ANN升级。