Charles Connell created HDFS-17916:
--------------------------------------

             Summary: DataStreamer#processDatanodeOrExternalError() fails to 
return byte arrays to ByteArrayManager
                 Key: HDFS-17916
                 URL: https://issues.apache.org/jira/browse/HDFS-17916
             Project: Hadoop HDFS
          Issue Type: Bug
          Components: hdfs-client
    Affects Versions: 3.4.3, 3.5.0, 3.3.6
            Reporter: Charles Connell


A [certain code 
path|https://github.com/apache/hadoop/blob/b322c3ce2c10b45cec2f9acbe6f00fb75c054caa/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DataStreamer.java#L1422]
 in the DFS client DataStreamer appears to discard DFSPacket objects without 
returning their contained byte arrays to the ByteArrayManager. I discovered 
this bug at my company after we had HBase server threads hung for hours at 
{{ByteArrayManager#allocate()}}. I took a heap dump of a high-uptime but 
relatively healthy HBase server, and found evidence it of leaked byte arrays 
there too. In the heap dump, the two {{FixedLengthManager}}s both had 
{{numAllocated = 9}}, but there were zero live {{DFSPacket}} objects. This 
suggests that the byte arrays, and their containing {{DFSPackets}} had been 
garbage collected, unbeknownst to {{FixedLengthManager}}.

DataStreamer.java starting at line 1410, the {{DFSPacket}} that is 
{{remove()}}'d from {{dataQueue}} is allowed to be garbage collected without 
further interaction.
{code}
  if (!streamerClosed && dfsClient.clientRunning) {
    if (stage == BlockConstructionStage.PIPELINE_CLOSE) {

      // If we had an error while closing the pipeline, we go through a 
fast-path
      // where the BlockReceiver does not run. Instead, the DataNode just 
finalizes
      // the block immediately during the 'connect ack' process. So, we want to 
pull
      // the end-of-block packet from the dataQueue, since we don't actually 
have
      // a true pipeline to send it over.
      synchronized (dataQueue) {
        DFSPacket endOfBlockPacket = dataQueue.remove();    // <-- leak: buf 
never released
        Span span = endOfBlockPacket.getSpan();
        if (span != null) {
          span.finish();
          endOfBlockPacket.setSpan(null);
        }
        assert endOfBlockPacket.isLastPacketInBlock();
        assert lastAckedSeqno == endOfBlockPacket.getSeqno() - 1;
        lastAckedSeqno = endOfBlockPacket.getSeqno();
        pipelineRecoveryCount = 0;
        dataQueue.notifyAll();
      }
      endBlock();
    } else {
      initDataStreaming();
    }
  }
{code}

This could be fixed by inserting this line somewhere above:
{code}
endOfBlockPacket.releaseBuffer(byteArrayManager);
{code}

Claude Opus 4.7 was used to assist in finding this bug.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to