Engineering Atlas › System Design Fundamentals

Files spread across machines: distributed file systems

13 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Roles

The metadata server (GFS master, HDFS NameNode) holds the namespace tree, the mapping from each file to its ordered list of chunk ids, and the current location of every chunk replica. It keeps all of this in RAM for speed, and persists namespace changes to an operation log that is replicated before the change is acknowledged. Chunk locations are not persisted; the master rebuilds them from chunk server reports at startup (GFS paper).

Chunk servers (DataNodes) store chunks as ordinary local files, verify checksums on read, and send heartbeats with their chunk lists.

Clients cache metadata, compute which chunk contains a byte offset (offset / chunk_size), ask the master once for that chunk's replicas, and then talk only to chunk servers.

Write path

The master grants a lease on the chunk to one replica, the primary. The client pushes data along a pipeline of replicas, each forwarding to the next, so every link carries the data once. The client then asks the primary to commit; the primary picks a serial order and the secondaries apply in that order. If any replica fails, the client retries, which can leave duplicate records. GFS called this "record append, at least once"; applications deduplicated with unique ids.

Placement and re-replication

Replica placement balances durability against write cost. HDFS puts the first replica on the writer's node, the second on a different rack, and the third on another node in that second rack (HDFS architecture). That survives a full rack loss while only one copy crosses the rack boundary. When a server dies, its heartbeat stops, and the master schedules copies of every chunk that dropped below the target replica count, prioritising chunks with only one copy left. A 12 TB server failure means 12 TB of re-replication spread over the cluster.

Limits of the single master

Every file, directory and block costs about 150 bytes of NameNode heap as a rule of thumb, so 10 million single-block files need about 3 GB (Cloudera). 500 million files of 1 to 2 chunks each needs on the order of 100 to 200 GB of heap, and every open, list, or create goes through one process. This is the small files problem. Fixes include packing small files into containers (HAR, SequenceFiles, Parquet), federating the namespace across several masters, or a distributed metadata layer. Google's GFS successor, Colossus, keeps file system metadata in Bigtable behind a horizontally scalable set of curators, which let it grow more than 100 times past the largest GFS clusters (Google Cloud). The single-master ceiling is real but high: LinkedIn runs a 380 GB NameNode heap holding 1.1 billion namespace objects (LinkedIn).

High availability adds a standby master that tails the edit log from a quorum of JournalNodes and takes over via ZooKeeper, with fencing so the old master cannot keep writing (HDFS HA). Current Hadoop 3 releases also let standbys serve reads as Observer NameNodes: each client carries the last transaction id it saw, and an Observer waits until it has caught up to that id before answering, so a client never reads older state than it wrote (Observer NameNode); at LinkedIn that roughly tripled namespace throughput.

Down the stack

Layer 1The contract

HDFS promises a single-writer, append-only file: one client holds the write lease on a path, others can read concurrently, and there is no random overwrite. Visibility and durability are separate calls. hflush() guarantees that data written so far has reached every DataNode in the pipeline and is visible to new readers, but it may still sit in those machines' memory. hsync() additionally asks each DataNode to fsync. close() completes the last block and makes the file length final at the NameNode. Namespace operations (create, rename, delete) are atomic and durable once acknowledged, because the NameNode logs them to a quorum of JournalNodes first. GFS's contract was weaker: record appends were atomic but at least once, and a region written concurrently could end up consistent yet undefined, holding mingled fragments of several writes (GFS paper).

Layer 2The mechanism

HDFS splits files into blocks of 128 MB by default. On a DataNode each block is two local files: the data, and a .meta file holding a CRC32C for every 512 bytes (dfs.bytes-per-checksum). A writer sends data as packets of about 64 KB, each containing chunks with their checksums. The packet flows down the pipeline of DataNodes, and acknowledgements flow back up; the client keeps unacknowledged packets in an ack queue and, if a DataNode fails, rebuilds the pipeline from the survivors and resends. Readers verify checksums and, on a mismatch, report the bad replica to the NameNode and read another.

The NameNode persists the namespace as an fsimage checkpoint plus an edit log; with high availability, edits go to a quorum of JournalNodes and the standby replays them continuously. Block locations are never persisted. DataNodes send heartbeats every 3 seconds and periodic full block reports, and a DataNode that has been silent for about 10 minutes (twice the 5-minute recheck interval plus ten heartbeats) is declared dead and its blocks re-replicated (hdfs-default.xml). The long timeout is deliberate: it stops a brief network blip from triggering a re-replication storm. GFS used 64 MB chunks, 64 KB checksum blocks with 32-bit checksums, chunk leases of 60 seconds, and under 64 bytes of master metadata per chunk.

Layer 3The machine

Block size comes from disk physics. A hard drive seek costs around 10 ms while sequential transfer runs at 100 to 250 MB/s, so reading a 128 MB block spends about 1% of its time seeking. Small blocks would turn analytics scans into seek-bound workloads and inflate NameNode memory. Checksums every 512 bytes are cheap because modern CPUs compute CRC32C with a dedicated instruction.

The pipeline is a network design. Each byte leaves the client's NIC once and crosses each link once, instead of the client sending three copies over its own uplink. DataNodes serve reads with transferTo, which maps to sendfile on Linux and moves bytes from page cache to socket without copying through user space. When the reader runs on the same machine, short-circuit local reads pass an open file descriptor over a Unix domain socket so the client reads the block file directly. The NameNode is a JVM holding the namespace on heap, so very large namespaces mean heaps of tens to hundreds of gigabytes and garbage collection pauses that stall every client.

Why it's built this way

  1. Commodity disks and servers fail constantly, so data is replicated across machines and racks and verified by checksum on every read.
  2. Disks are fast sequentially and slow to seek, so files are cut into huge blocks and workloads are streaming.
  3. Huge blocks keep the metadata small enough for one machine's RAM, which makes a single master simple and fast.
  4. A single master must not touch data, so clients get locations once and move bytes directly with DataNodes.
  5. Cross-rack bandwidth is scarce, so pipelined writes and rack-aware placement send each byte across the core network as few times as possible.

Diagram

sequenceDiagram
    participant C as Client
    participant M as Master
    participant P as "Primary replica"
    participant S1 as "Secondary 1"
    participant S2 as "Secondary 2"
    C->>M: which servers hold chunk 7 of /logs/a?
    M-->>C: P (lease holder), S1, S2
    C->>P: push data
    P->>S1: forward data
    S1->>S2: forward data
    C->>P: commit write
    P->>S1: apply in serial order
    P->>S2: apply in serial order
    S1-->>P: ack
    S2-->>P: ack
    P-->>C: success

Worked example

An analytics cluster has 400 data nodes, each with 12 × 16 TB disks: 77 PB raw. With 3x replication, usable capacity is about 25 PB, and at 75% target fill, about 19 PB of data.

With 128 MB blocks, 19 PB is about 150 million blocks. At roughly 150 bytes per block object plus file and directory objects, the NameNode needs around 60 to 80 GB of heap, which is workable. If the same data were stored as 1 MB files, the count would be 19 billion objects and no single NameNode could hold it. The team enforces compaction jobs that rewrite small outputs into 512 MB Parquet files.

Recovery: one node holds 192 TB × 75% = 144 TB. When it dies, the other 399 nodes each copy about 360 GB. If re-replication is throttled to 50 MB/s per node to protect production jobs, recovery takes about 2 hours, during which some blocks have only two copies. The probability of losing a second and third replica holder of the same block in that window is low, but it is why HDFS prioritises single-replica blocks first and why larger clusters move cold data to erasure coding. HDFS has supported that natively since Hadoop 3 with policies such as RS-6-3, which stores 6 blocks of data in 9 instead of 18, but erasure-coded files give up part of the contract: append() and truncate() fail, and hflush() and hsync() do nothing, so write-ahead logs and other files that need them must stay replicated (HDFS erasure coding).

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
HDFS-style central metadataSimple, strong namespace consistency, data locality for computeMaster memory and throughput limits, small files problemBatch analytics on large files in your own data centre
Federated or distributed metadata (Colossus, HDFS federation)Scales to billions of filesMore complex, cross-namespace operations harderVery large multi-tenant storage
Ceph (CRUSH placement, no central lookup)No metadata bottleneck for objects and blocks, self-healingOperational complexity, tuningUnified block, object, and file storage on-premises
Cloud object storage insteadNo servers to run, near-infinite capacityNo rename atomicity, no appends, higher latencyMost new cloud analytics
NFS or a single NASPOSIX semantics, simpleSingle-box limitsSmall teams, home directories

Pitfalls in production

Leadership lens

Check yourself

  1. Why does the client send data through a pipeline of replicas rather than to each replica directly?

    Show answer

    A chain uses each server's network link once, so the client's uplink is not divided three ways and total write throughput is higher.

  2. Why does the master not persist chunk locations?

    Show answer

    Chunk servers are the source of truth for what they hold, so the master rebuilds locations from their reports and avoids keeping a second copy in sync.

  3. Why do small files hurt a DFS even when total bytes are modest?

    Show answer

    Every file and chunk is an object in master memory and a separate metadata operation, so file count, not bytes, sets the limit.

  4. What does HDFS rack-aware placement protect against, and at what cost?

    Show answer

    Losing an entire rack, at the cost of sending one replica's worth of data across the rack boundary on each write.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →