[ 
https://issues.apache.org/jira/browse/HDFS-17863?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18049885#comment-18049885
 ] 

ConfX edited comment on HDFS-17863 at 1/5/26 5:15 PM:
------------------------------------------------------

Think of a case like this:

  Timeline:
  Writer: write(data1) → hflush() → [acked] → write(data2) → [DataNode 
Crash/Restart]
                            ↑                                                   
  ↑
                       data1 is safe                   data2 might be corrupted

 

After restart, getBytesOnDisk() returns len(data1 + data2), but only data1 was 
hflush'd. If we return getBytesOnDisk(), we might expose corrupted data2. So 
the current design is that we return -1 to prevent accessing to ANY OF THE DATA 
(including the hflush'd ones).

The problem: HDFS doesn't track "last known visible length" (the bytes at the 
last successful hflush). So it takes the safe approach: return -1 (nothing) 
rather than risk exposing corrupted data.

However the hflush'd data should remain visible even after datanode restart 
(data1 above). 

So a fix that persist the "visible length" metadata during hflush would be good 
and in getVisibleLength we return the lastPersistedVisibleLength rather than -1.
{code:java}
  public long getVisibleLength() {
    // Return the last known good visible length from before restart
    // This would require persisting bytesAcked to disk during hflush
    return lastPersistedVisibleLength;
  } {code}
I'm happy to send a PR if you think this is reasonable.


was (Author: JIRAUSER296392):
Think of a case like this:


  Timeline:
  Writer: write(data1) → hflush() → [acked] → write(data2) → [DataNode 
Crash/Restart]
                            ↑                                                   
  ↑
                       data1 is safe                   data2 might be corrupted

 

After restart, getBytesOnDisk() returns len(data1 + data2), but only data1 was 
hflush'd. If we return getBytesOnDisk(), we might expose corrupted data2.

The problem: HDFS doesn't track "last known visible length" (the bytes at the 
last successful hflush). So it takes the safe approach: return -1 (nothing) 
rather than risk exposing corrupted data.

However the hflush'd data should remain visible even after datanode restart 
(data1 above). 

So a fix that persist the "visible length" metadata during hflush would be good 
and in getVisibleLength we return the lastPersistedVisibleLength rather than -1.
{code:java}
  public long getVisibleLength() {
    // Return the last known good visible length from before restart
    // This would require persisting bytesAcked to disk during hflush
    return lastPersistedVisibleLength;
  } {code}
I'm happy to send a PR if you think this is reasonable.

> CannotObtainBlockLengthException after DataNode restart
> -------------------------------------------------------
>
>                 Key: HDFS-17863
>                 URL: https://issues.apache.org/jira/browse/HDFS-17863
>             Project: Hadoop HDFS
>          Issue Type: Bug
>          Components: datanode, dfs, hdfs-client
>    Affects Versions: 3.3.5
>         Environment: Hadoop 3.3.5
> Java 8
> Maven 3.6.3
>            Reporter: ConfX
>            Priority: Critical
>         Attachments: reproduce.sh, restart.patch
>
>
> h2. DESCRIPTION:
> After hflush(), HDFS guarantees that written data becomes visible to readers,
> even while the file remains under construction. This guarantee is BROKEN after
> DataNode restart. Under-construction blocks that have been flushed become
> inaccessible (visible length = -1) until explicit lease recovery, causing
> CannotObtainBlockLengthException when clients try to read the file.
> This is a genuine production bug that affects:
>  - HBase WAL recovery after DataNode failures
>  - Streaming applications that write and read simultaneously
>  - Any application relying on hflush() visibility guarantees
> h2. STEPS TO REPRODUCE:
> Download the `reproduce.sh` and `restart.patch`, then
> {code:java}
> $ bash reproduce.sh{code}
> The script:
> 1. Clones Hadoop repository (release 3.3.5 branch)
> 2. Applies test patch (restart.patch) that adds the reproduction test
> 3. Builds the Hadoop HDFS module
> 4. Runs test case: 
> TestBlockToken#testLastLocatedBlockTokenExpiryWithDataNodeRestart
> *EXPECTED RESULT:*
> File should be readable after hflush(), even after DataNode restart
> *ACTUAL RESULT:*
> org.apache.hadoop.hdfs.CannotObtainBlockLengthException: Cannot obtain block
> length for LocatedBlock
> The bug is confirmed if the test fails with CannotObtainBlockLengthException.
> *KEY OBSERVATION:*
>  - Tests with NameNode-only restart (no DataNode restart) DO NOT fail
>  - The bug ONLY occurs when DataNode restarts
> h2. ROOT CAUSE:
> When a DataNode restarts, under-construction block replicas are loaded from
> disk and placed in ReplicaWaitingToBeRecovered (RWR) state:
> File: ReplicaWaitingToBeRecovered.java:75
> @Override
> public long getVisibleLength()
> {   return -1;  // no bytes are visible }
> This state explicitly returns -1 for visible length, meaning "no bytes 
> visible"
> until lease recovery completes.
> When a client tries to open the file:
> 1. DFSInputStream calls readBlockLength() to determine UC block length
> 2. Contacts DataNode via getReplicaVisibleLength()
> 3. Receives -1 (not a valid length)
> 4. Treats this as a failure, tries next DataNode
> 5. All DataNodes return -1
> 6. Throws CannotObtainBlockLengthException
> The problem persists because:
>  - Lease is still held by the original client (output stream still open)
>  - Client is still alive (from HDFS's perspective)
>  - Automatic lease recovery only triggers when lease holder is detected as 
> dead
>  - No mechanism to automatically recover in this scenario
> h2. *DIAGNOSTIC EVIDENCE:*
> BEFORE DataNode Restart:
>  - File under construction: true
>  - Block length: 6 bytes
>  - Block is complete: false
>  - DataNode replica visible length: 6  ✅ READABLE
> AFTER DataNode Restart:
>  - File under construction: true
>  - Block length: 6 bytes
>  - Block is complete: false
>  - DataNode replica visible length: -1  ❌ UNREADABLE!
> AFTER Explicit Lease Recovery:
>  - File under construction: false
>  - Block length: 6 bytes
>  - Block is complete: true
>  - DataNode replica visible length: 6  ✅ READABLE AGAIN!
> h2. WHY THIS IS A BUG (NOT EXPECTED BEHAVIOR):
> HDFS Guarantees:
>  - hflush() ensures data is visible to new readers
>  - Under-construction files should be readable after hflush()
>  - This is the documented contract for hflush()



--
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