EC 当复制流的硬限制达到阈值时,不在活动副本中考虑存储

HDFS-14699
我们在具有hadoop 3.1.1的80节点集群上尝试了EC功能

  1. 我们定制了一个新的10-2-1024k策略并在路径上使用它,现在我们有12个内部块(12个活动块)
  2. 退役完成后,退役一个DN。现在我们有13个内部区块(12个实时区块和1个停用区块)
  3. 然后关闭一个不具有与1个停用块相同的块ID的DN,现在我们有12个内部块(11个活动块和1个停用块)
  4. 在等待约600秒(在心跳之前)再次调试已退役的DN之后,现在我们有12个内部块(11个活动块和1个重复块)
  5. 则EC不会重建丢失的块

问题的根本原因是当node.getNumberOfBlocksToBeReplicated()> = ReplicationStreamsHardLimit(默认值为4,可以通过propertydfs.namenode.replication.max-streams-hard-limit进行更改)时,它将继续并且不会更新numReplicas在scheduleReconstruction方法中使用来判断它是否具有足够的副本,并且当它认为ec有足够的副本时,它将从requiredReconstruction中删除该块,这使该块再也没有机会重建。
我也发现hdfs监视器可以检查并处理多余的块,但这取决于numReplicas将内部块标记为EXCESS。如下快照所示,dup内部块未标记为EXCESS,监视器无法处理。
对于该问题的快照,具有dup内部块和丢失1个内部块,将无法重建。

DFSStripedInputStream curStripeBuf不能通过unbuffer()释放

HDFS-14308
HDFS缓存的某些用户打开了HDFS文件句柄,以避免重复往返NameNode。例如,默认情况下,Impala最多缓存20,000个HDFS文件句柄。最近对擦除编码文件的测试表明,打开的文件句柄在不使用时会占用大量内存。
例如,当缓存了608个文件句柄时,这是Impala的JMX端点的输出
{
“ name”:“ java.nio:type = BufferPool,name = direct”,
“ modelerType”:“ sun.management.ManagementFactoryHelper $ 1”,
“名称”:“直接”,
“总容量”:1921048960,
“ MemoryUsed”:1921048961,
“计数”:633,
“ ObjectName”:“ java.nio:type = BufferPool,name = direct”
},
这表明每个DFSStripedInputStream的直接缓冲区内存使用量为3MB。附件是Eclipse MAT的输出,显示直接缓冲区来自DFSStripedInputStream对象。当文件句柄被缓存并且可能长时间未使用时,Impala和HBase都调用unbuffer(),但这表明内存仍在使用中。
为了支持在擦除编码文件上缓存文件句柄,DFSStripedInputStream应该避免在unbuffer()调用之后保留缓冲区。看到HDFS-7694。“ unbuffer()”旨在将输入流移至较低的内存状态以支持这些缓存用例。特别是,curStripeBuf似乎是在resetCurStripeBuffer(true)调用中从BUFFER_POOL分配的。直到close()才释放它。
我们发现此问题时,很容易在Apache Impala中进行复制,但这是一个非常复杂的开发环境。最好直接复制它。我已经有一段时间没有转载了,但是我认为该场景的基本步骤是:

  1. 打开一个擦除编码的文件(假设它是128MB或一些合适的大小)
  2. 在中间读取几MB(让我们假设这是一个Parquet文件,并且您读了一个列,或者是一个文本文件,并且在中间读取了一些块(未到达文件末尾))。内容无关紧要。这应该使用一些直接缓冲存储器作为临时存储器。
  3. 调用unbuffer(),它应该释放不需要的内存

#3之后,如果unbuffer()是正确的,则不会使用直接缓冲存储器。重要的是文件保持打开状态。调用close肯定会释放内存。
您可以在调试器中运行此命令并在#3之后测试内存使用情况,也可以循环处理20,000个文件,将文件句柄存储在数组中(不应进行垃圾回收),然后查看是否用完了内存(您可以将一个文件打开20,000次,其行为应与打开20,000个不同文件的行为相同)。
DFSStripedInputStream使用父类DFSInputStream unbuffer()方法,该方法不会释放curStripeBuf,然后将介绍OOM。
使用“ super.unbuffer()”而不是“ closeCurrentBlockReaders()