Rajan Dhabalia created HDFS-17973:
-------------------------------------

             Summary: HDFS DataNode Write Batching for Efficient Node-Level 
Scaling
                 Key: HDFS-17973
                 URL: https://issues.apache.org/jira/browse/HDFS-17973
             Project: Hadoop HDFS
          Issue Type: Improvement
          Components: hdfs
            Reporter: Rajan Dhabalia


Below is the same content reformatted for direct copy/paste into Jira wiki 
markup. I have not changed the content.
h2. Summary

Introduce an optional in-memory write buffering mechanism in the HDFS DataNode 
to improve per-node I/O efficiency, increase throughput, and reduce read/write 
tail latency.
The optimization batches small DataNode write packets into larger disk writes, 
uses direct I/O to reduce page-cache pressure, and controls flush concurrency 
to reduce disk contention.
The feature is configuration-gated and disabled by default.
h2. Issue Type

Improvement
h2. Component

DataNode
h3. Motivation

HDFS blocks are typically large, but the DataNode processes block data as much 
smaller packets. Multiple threads can concurrently process packets belonging to 
different blocks and files.

While each individual block has a sequential access pattern, packet-level 
interleaving across multiple blocks and files can result in a less sequential 
access pattern at the storage layer.

This is particularly impactful on HDD-based DataNodes, where seek and 
rotational latency make random I/O significantly more expensive than sequential 
I/O.

The current architecture can result in:
 * Frequent small disk writes.
 * Increased write amplification and I/O operations.
 * Randomized physical I/O due to packet interleaving.
 * Dirty write pages consuming OS page-cache memory.
 * Eviction of useful read and read-ahead pages.
 * Increased read/write contention.
 * Higher read latency and write tail latency.
 * Lower effective disk and DataNode throughput.

The goal of this change is to improve {*}vertical efficiency{*}, allowing an 
individual DataNode to achieve higher throughput while providing more 
predictable performance for mixed read/write workloads.
h1. Current Data Plane
h3. Block-Level Packetization

Large HDFS blocks are processed as smaller packets:
{code:java}
HDFS Block
|
v
+------+------+------+------+------+
| P1 | P2 | P3 | P4 | ... |
+------+------+------+------+------+
{code}
Multiple threads can process packets from different blocks/files concurrently:
{code:java}
Thread 1 -> File A -> P1 -> P2 -> P3
Thread 2 -> File B -> P1 -> P2 -> P3
Thread 3 -> File C -> P1 -> P2 -> P3
{code}
This packet-level interleaving can result in non-sequential physical I/O, 
particularly when multiple files are active simultaneously.
h3. Page Cache

With the existing buffered write path:
{code:java}
DataNode
|
v
OS Page Cache
|
v
Kernel Writeback
|
v
Disk
{code}
Writes create dirty pages that compete for memory with:
Read pages.
Read-ahead pages.
Other filesystem cache.
Heavy write workloads can therefore reduce the effectiveness of the page cache 
for reads.
h3. HDD Behavior

HDDs perform best with sequential access. Interleaved requests targeting 
different regions can cause additional head movement and seek overhead.
As a result, small packet writes combined with concurrent reads can 
significantly reduce effective disk throughput and increase read latency.
h1. Proposed Data Plane

The proposed architecture introduces a DataNode-managed write buffer:
{code:java}
DataNode
|
Small packets
|
v
+-------------------+
| In-memory buffer |
+---------+---------+
|
Large batched I/O
|
v
O_DIRECT
|
v
Disk
{code}
h2. Key Changes
h3. 1. Large In-Memory Write Buffers

Accumulate multiple small packets in memory before flushing them to disk.
This converts:
{code:java}
Small writes:
P1 -> P2 -> P3 -> P4 -> P5 -> ...
{code}
into:
{code:java}
Large writes:
+-------------------+
| P1 P2 P3 ... Pn |
+-------------------+
|
v
Disk
{code}
 

Benefits include:
- Fewer disk I/O operations.
- Reduced write amplification.
- Larger sequential writes.
- Better utilization of disk bandwidth.
- Reduced physical I/O fragmentation.
- The buffer size is configurable.
h3. 2. Direct I/O for Buffered Writes

Use
{code:java}
O_DIRECT{code}
when flushing buffered writes to bypass the OS page cache.
This allows the DataNode to explicitly manage write buffering while preserving 
OS page-cache capacity for reads.

Benefits:
- Reduces dirty-page pressure.
- Prevents DataNode writes from unnecessarily consuming read-cache capacity.
- Improves read-ahead effectiveness.
- Reduces read/write memory contention.
h3. 3. Controlled Flush Concurrency

Large writes alone are not sufficient. Excessive concurrent flushes can still 
create random I/O and disk contention. The implementation therefore limits 
concurrent flush bytes per volume.

The goal is to find a balance between:
- Disk parallelism.
- Sequential I/O.
- Disk queue depth.
- Read latency.
h3. 4. Last-Replica-Only Buffering

Provide an option to enable buffering only on the last DataNode in the 
replication pipeline.
This allows the optimization to be introduced selectively without changing the 
write behavior of every replica.
h3. 5. Bounded Memory Usage

The buffering mechanism provides configurable limits for:
- Total DataNode buffer capacity.
- Per-block buffer size.
- Minimum number of volumes.
- Per-volume concurrent flush capacity.
This prevents unbounded memory consumption or excessive disk pressure.
h3. 6. Idle Flush

Partially filled buffers are flushed after a configurable idle timeout.
This prevents small writes from being held indefinitely when the workload does 
not generate enough packets to fill the buffer.
h1. Read Path Optimization

The write buffer is designed to reduce interference with the read path.
With DataNode writes bypassing the OS page cache:
{code:java}
System Memory
|
+------+------+
| |
v v
Read Cache Read-ahead
| |
+------+------+
|
v
Disk
DataNode writes
|
v
Memory Buffer
|
v
O_DIRECT
|
v
Disk
{code}
This allows the page cache to be used more effectively for reads and read-ahead.
Expected benefits include:
- Reduced eviction of useful read pages.
- Better read-ahead effectiveness.
- Lower read latency.
- Reduced read/write contention.
- More predictable mixed-workload performance.
h1. Recommended Kernel tuning with this change

The following kernel settings can complement the DataNode optimization:
{code:java}
Read-ahead and larger block-layer requests
echo 4096 | sudo tee /sys/block/sd${disk}/queue/read_ahead_kb
echo 4096 | sudo tee /sys/block/sd${disk}/queue/max_sectors_kb
Favor sequential I/O while protecting read latency
echo 128 | sudo tee /sys/block/sd${disk}/queue/iosched/fifo_batch
echo 100 | sudo tee /sys/block/sd${disk}/queue/iosched/read_expire
Reduce dirty-page accumulation for remaining buffered writes
echo 500 | sudo tee /proc/sys/vm/dirty_expire_centisecs
echo 2 | sudo tee /proc/sys/vm/dirty_background_ratio
echo 10 | sudo tee /proc/sys/vm/dirty_ratio
echo 1000 | sudo tee /proc/sys/vm/dirty_writeback_centisecs
{code}
h3. Rationale

Read-ahead
Larger read-ahead allows the kernel to fetch larger sequential ranges and 
improves sequential-read efficiency.

Maximum request size
A larger
{code:java}
max_sectors_kb{code}
allows larger requests to be issued to the block layer where supported.

I/O scheduler
Scheduler tuning is intended to improve sequential processing while preventing 
reads from waiting excessively behind writes.

Dirty-page settings
These settings reduce accumulation and prolonged retention of dirty pages for 
remaining buffered filesystem activity. They are supporting host-level tuning 
and are not the primary mechanism behind the DataNode optimization.

Scheduler-specific settings should only be applied when supported by the active 
I/O scheduler.
h1. Configuration
||Property||Default||Description||
|dfs.datanode.write.memory.buffer.enabled|false|Enables DataNode write memory 
buffering.|
|dfs.datanode.write.memory.buffer.last-replica-only|true|Buffers writes only on 
the last replica.|
|dfs.datanode.write.memory.buffer.max.capacity.mb|See docs|Maximum total 
in-memory buffer capacity.|
|dfs.datanode.write.memory.buffer.min.volumes|See docs|Minimum number of 
volumes required.|
|dfs.datanode.write.buffer.size.bytes|See docs|Per-block write buffer size.|
|dfs.datanode.write.buffer.idle.flush.timeout.ms|See docs|Idle timeout for 
partially filled buffers.|
|dfs.datanode.concurrent.flush.mb.per.volume|See docs|Maximum concurrent flush 
bytes per volume.|
|dfs.datanode.read.ahead.cache.bytes.threshold|See docs|Read-ahead cache 
threshold.|
h1. Benchmark Results

Initial benchmarking demonstrates significant improvement in DataNode vertical 
efficiency.
||Metric||Improvement||
|Mixed 80/20 read/write throughput|~30% higher|
|Read-only throughput|Up to ~45% higher|
|Large-block P99 write latency|~80% lower|
|Read latency under concurrent writes|Significant reduction|

The results indicate that larger buffered writes and controlled flushing can 
improve disk utilization while reducing interference between concurrent reads 
and writes.
h1. Expected Impact

For workloads where storage I/O is the primary bottleneck, the expected 
improvements are:
 - 25–40% higher throughput for write-heavy workloads.
 - Up to ~45% higher throughput for read-heavy workloads.
 - ~80% lower large-block write P99 latency.
 - Lower read latency under concurrent writes.
 - Reduced random I/O and disk seek contention.
 - Better utilization of available disk bandwidth.
 - More predictable mixed read/write performance.

The actual improvement will depend on workload characteristics, including 
read/write ratio, I/O concurrency, packet interleaving, disk utilization, and 
underlying storage characteristics.



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