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]