Skip to content
Paper distilled · Distributed storage

The Google File System

A cluster file system that makes commodity failure routine, files enormous, and concurrent append a first-class atomic operation.

AuthorsSanjay Ghemawat, Howard Gobioff, and Shun-Tak Leung (Google) VenueSOSP 2003 Year2003
Read the original PDF All papers

In one breath — the whole paper, compressed

GFS stores multi-gigabyte files as fixed 64 MB chunks replicated three times across hundreds of commodity Linux machines, with a single master holding all metadata in memory and chunkservers moving all file data directly to clients. The master never touches file data: it hands out chunk handles and locations, and grants a 60-second lease that makes one replica the primary, and that primary alone picks the serial order in which every replica applies each mutation. On top of this GFS adds record append, which writes a record at least once atomically at an offset GFS chooses, so hundreds of producers can append to one shared file without a distributed lock manager. The price is a deliberately relaxed consistency model, in which concurrent writes leave regions consistent but undefined and appends may leave padding and duplicates, absorbed by applications through checkpointing and self-validating records. Production clusters of 342 and 227 chunkservers held 55 TB and 155 TB while the master carried only 48 MB and 60 MB of metadata and fielded 200 to 500 operations per second.

Before this paper — the world it landed in

The reference points before GFS were AFS, xFS, Frangipani, GPFS and Lustre: systems that cached aggressively at clients, or removed the central server in favour of distributed algorithms for consistency and management, or chased POSIX compliance. They assumed machines were mostly reliable, files were mostly small, and mutation meant overwriting bytes at some offset, so redundancy meant RAID and correctness meant cache coherence. Google's actual workload violated all of those at once: multi-TB datasets made of billions of web documents, thousands of cheap disks that failed constantly enough that silent corruption was a weekly event, and writers that essentially only appended. Client caching was close to worthless because jobs streamed through data sets far larger than any cache, and a POSIX-compliant interface would have cost complexity to solve problems Google did not have. GFS was written by teams that controlled both the file system and every application on top of it, which made co-design a legitimate option rather than a cheat.

The problem — what was actually breaking

  • Component failures are the norm rather than the exception: across hundreds or thousands of commodity machines, application bugs, operating system bugs, human error, and failures of disks, memory, connectors, networking and power supplies guarantee that something is broken at any moment and that some of it will never recover.
  • Files are huge by traditional standards, with multi-GB files the common case and fast-growing data sets of many TB comprising billions of objects, so inherited assumptions about I/O operation size and block size no longer hold and managing billions of KB-sized files would be unwieldy even if it worked.
  • Almost all mutation is appending rather than overwriting, and files once written are read only and usually sequentially, so random-write optimization is wasted effort and caching data blocks at the client loses its appeal.
  • Hundreds of producers, running one per machine, need to append concurrently to a single file used as a producer-consumer queue or a many-way merge target, and doing that with ordinary offset-specified writes would force clients into complicated and expensive synchronization such as a distributed lock manager.
  • High sustained bandwidth to bulk-processing jobs matters more than low latency on any individual read or write, which inverts the optimization target that conventional file systems were built around.
  • A single centralized master is the simplest way to make placement and replication decisions from global knowledge, but it becomes a bottleneck unless its involvement in ordinary reads and writes is aggressively minimized.

Core ideas — the contributions, and why they work

Single master, metadata only

GFS keeps exactly one master owning the namespace, the file-to-chunk mapping and replica locations, all resident in memory, while every byte of file data flows directly between clients and chunkservers. Centralization works because the master's per-operation job is tiny, returning a chunk handle and replica locations that the client then caches, so the master saw only 200 to 500 operations per second on real clusters. It works economically because the master keeps under 64 bytes of metadata per 64 MB chunk and a similar amount per file with prefix compression, so hundreds of TB of storage need only tens of MB of master memory. And it pays off in capability: one master with global knowledge can make sophisticated chunk placement, re-replication and rebalancing decisions that a decentralized metadata scheme would have to coordinate expensively.

64 MB chunks with lazy allocation

Files are cut into fixed 64 MB chunks, orders of magnitude larger than a conventional file system block, each stored as an ordinary Linux file that is extended only as needed. The size shrinks three costs at once: client-master round trips, because one lookup covers 64 MB of sequential I/O; connection overhead, because a client can hold one persistent TCP connection to a chunkserver across many operations on the same chunk; and master memory, because metadata scales with chunk count rather than byte count. Lazy space allocation defuses the obvious objection, internal fragmentation, by never materializing space a chunk has not used. The residual cost is hot spots on small single-chunk files, which the paper hit when a batch-queue executable stored as one chunk was launched on hundreds of machines simultaneously.

Leases that delegate mutation order

Rather than sequencing every write itself, the master grants a chunk lease to one replica, the primary, and that primary assigns consecutive serial numbers to all mutations it receives, possibly from multiple clients. Every replica applies mutations in that serial-number order, so replicas converge on the same content without the master being in the write path at all. The global mutation order is therefore lease-grant order chosen by the master, refined within a lease by serial numbers assigned by the primary, a two-level ordering that needs no consensus protocol among replicas. Leases have an initial 60-second timeout and are extended by piggybacking on HeartBeat messages that were already being exchanged, so even a master that has lost contact with a primary can safely grant a new lease once the old one expires.

Data flow decoupled from control flow

The write protocol separates the expensive part, moving bytes, from the cheap part, deciding order. Clients push data to all replicas before contacting the primary, so the data path can be scheduled purely on network topology while control goes wherever the lease happens to sit. Data travels in a linear pipelined chain rather than a tree, each machine forwarding to the closest machine that has not yet received it and beginning to forward before it has finished receiving, so every machine devotes its full outbound bandwidth to one recipient. The ideal transfer time becomes B/T + RL rather than R times B/T, which is what makes three-way replication affordable on 100 Mbps commodity networking.

Atomic record append

Record append inverts the usual write interface: the client supplies only the data and GFS picks the offset, guaranteeing the record lands as one continuous sequence of bytes at least once and returning where it went. This removes the reason concurrent appenders would need a distributed lock manager, because nobody has to agree on the current end of file when the primary is the only party that ever assigns offsets. Because the guarantee is at-least-once rather than exactly-once, a failed append is simply retried, and the padding or duplicate records left behind become the application's problem rather than a distributed transaction. The payoff is that hundreds of producers on different machines write one shared file at a throughput bounded only by the chunkservers holding the last chunk, essentially independent of the number of writers.

Relaxed consistency, co-designed with applications

GFS calls a file region consistent if all clients always see the same data regardless of which replica they read, and defined if it is consistent and clients additionally see what the mutation wrote in its entirety. Serial successful writes leave regions defined; concurrent successful writes leave them consistent but undefined, because every replica applied the same order yet the result mingles fragments from several mutations; a failed mutation leaves the region inconsistent and therefore undefined. This weaker contract is exactly what lets the master stay out of the data path and lets record append avoid consensus. The paper argues it is affordable only because the applications were co-designed: they append rather than overwrite, checkpoint so readers process only a prefix known to be defined, and embed checksums and unique identifiers so padding and duplicates can be filtered in shared library code.

Failure treated as routine, not exceptional

Because a cluster of thousands of disks always has something broken, GFS builds detection and repair into steady-state operation rather than into exception paths. Chunkservers checksum their own data in 64 KB blocks and verify before returning anything, so corruption never propagates between machines, and idle-time scanning catches rot in chunks nobody reads. The master continuously re-replicates under-goal chunks according to a priority function, rebalances replicas across racks and disk-utilization levels, and reclaims orphaned chunks and deleted files during regular background namespace scans. Servers restore state and start in seconds and are routinely terminated by killing the process, so the recovery path is the normal path and stays exercised.

How it works — the mechanism, concretely

Read path: one master round trip, then cached

The client converts an application byte offset into a chunk index using the fixed 64 MB chunk size, then sends the master the file name and chunk index. The master replies with the chunk handle and the locations of its replicas, and the client caches this keyed by file name and chunk index with a limited timeout. The client then reads directly from the closest replica, specifying chunk handle and byte range, and further reads of the same chunk require no master interaction until the cache entry expires or the file is reopened. In practice the client asks for several chunks at once and the master volunteers information for chunks immediately following those requested, sidestepping future round trips at practically no extra cost.

Write path: seven steps around a lease

The client asks the master which chunkserver holds the current lease and where the other replicas are; if no one holds a lease, the master grants one. The client pushes the bytes to all replicas in any order, and only once every replica acknowledges receipt does it send a write request to the primary, which assigns consecutive serial numbers to all mutations it is receiving, possibly from multiple clients, and applies them locally in that order. The primary forwards the request to each secondary, each applies it in the same serial-number order, the secondaries acknowledge, and the primary replies to the client, reporting any error from any replica. On error the write may have succeeded at the primary and an arbitrary subset of secondaries, leaving the region inconsistent, so client code retries steps three through seven a few times before falling back to restarting the whole write; a write that straddles a chunk boundary is split into several such operations and may interleave with other clients, which is precisely how a region ends up consistent but undefined.

Data flow: linear pipelined chain

Control flows from client to primary to secondaries, but data is pushed linearly along a carefully picked chain of chunkservers so each machine's full outbound bandwidth serves one recipient rather than being divided among several. Each machine forwards to the closest machine in the network topology that has not yet received the data, with distances estimated from IP addresses because Google's topology is simple enough to allow it, which keeps traffic off congested inter-switch links. Chunkservers buffer arriving data in an internal LRU buffer cache until it is used or aged out, and begin forwarding as soon as they receive anything rather than waiting for the whole transfer, which pipelines the pushes over full-duplex TCP so sending does not reduce the receive rate. The ideal elapsed time to move B bytes to R replicas is B/T + RL; with T around 100 Mbps and L far below 1 ms, 1 MB reaches all replicas in roughly 80 ms.

Record append at the primary

The client pushes the record to all replicas of the file's last chunk and then sends its request to the primary, which follows the normal control flow with a little extra logic. The primary checks whether appending would push the chunk past the 64 MB maximum; if so it pads its own replica to the maximum size, tells the secondaries to pad too, and tells the client to retry on the next chunk, which is why a record is restricted to at most one quarter of the chunk size to bound worst-case fragmentation. Otherwise the primary appends at its own offset, instructs every secondary to write at that exact offset, and replies success. If any replica fails, the client retries, so replicas may end up holding duplicates or partial records and are explicitly not guaranteed bytewise identical; the only guarantee is that a successful reply implies the data was written at the same offset on all replicas of some chunk, from which it follows that every future record gets a higher offset or a different chunk even if a different replica later becomes primary.

Master metadata: memory, operation log, checkpoint

The master keeps namespaces, the file-to-chunk mapping, and each chunk's replica locations entirely in memory, which makes master operations fast and makes periodic full scans of its own state cheap enough to drive garbage collection, re-replication and rebalancing. Only namespaces and the file-to-chunk mapping are persistent: they are appended to an operation log on local disk and replicated to remote machines, and no client operation returns until its log record has been flushed both locally and remotely, with records batched to amortize the cost. Replica locations are deliberately not persisted; the master polls every chunkserver at startup and after joins, and keeps current via HeartBeat, because a chunkserver has the final word on what its own disks hold and disks can lose chunks spontaneously. When the log grows past a threshold, the master checkpoints its state into a compact B-tree-like form that maps directly into memory without parsing, built in a separate thread against a new log file so incoming mutations are not delayed, taking about a minute for a few million files; recovery loads the last complete checkpoint and replays only the log records after it, skipping incomplete checkpoints.

Namespace locking and copy-on-write snapshot

GFS has no per-directory data structure and no hard or symbolic links; the namespace is logically a lookup table from full pathname to metadata, prefix-compressed in memory, with a read-write lock per node allocated lazily and freed when unused. An operation on /d1/d2/.../dn/leaf takes read locks on every prefix directory name and a read or write lock on the full leaf name, so many file creations in one directory run concurrently, each taking only a read lock on the directory name, while a snapshot of that directory takes a write lock on it and therefore serializes against them. Locks are acquired in a total order, first by level in the namespace tree and lexicographically within a level, which prevents deadlock. Snapshot first revokes outstanding leases on the covered chunks so any later write must revisit the master, then logs the operation and duplicates the source metadata so the snapshot files point at the same chunks; the first write to a shared chunk C makes the master notice a reference count above one, create C' on the same chunkservers so the copy is local rather than crossing the network, and grant a lease on C' as if nothing unusual happened.

Failure handling: versions, cloning, checksums, garbage

Every chunk carries a version number that the master increments and records persistently, along with the up-to-date replicas, before any client is told about a new lease; a chunkserver that was down reports a stale version on restart, its replica is then treated as not existing when the master answers client requests, and it is removed in regular garbage collection. When available replicas fall below the goal, the master prioritizes cloning by how far the chunk is from its goal, whether it belongs to a live rather than a recently deleted file, and whether it is blocking client progress, while limiting active clone operations cluster-wide and per chunkserver and throttling each clone's read bandwidth. Each chunkserver independently verifies its own data with a 32-bit checksum per 64 KB block, kept in memory and logged persistently, checked before returning data to anyone and updated incrementally for appends, with mismatches returning an error to the requestor, reported to the master, which clones a good replica and then orders the bad one deleted; idle chunkservers scan inactive chunks so cold corruption cannot fool the master into counting a dead replica. Deletion is lazy: the file is renamed to a hidden name carrying a deletion timestamp and is only purged after three days by default, after which orphaned chunks are found in a namespace scan and each chunkserver learns via HeartBeat which of the chunks it reported are no longer known to the master.

What the paper showed — measurements and proofs

  • On a test cluster of one master, two master replicas, 16 chunkservers and 16 clients (dual 1.4 GHz PIII, 2 GB RAM, 100 Mbps NICs, a 1 Gbps inter-switch link), aggregate read rate reached 94 MB/s for 16 clients reading random 4 MB regions from a 320 GB file set, about 75% of the 125 MB/s network limit, while a single client got 10 MB/s, 80% of its 12.5 MB/s per-client limit.
  • Aggregate write rate reached 35 MB/s for 16 clients each writing 1 GB in 1 MB writes to distinct files, about half the 67 MB/s limit imposed by writing every byte to 3 of the 16 chunkservers; a single client managed only 6.3 MB/s, which the authors attribute to their network stack interacting poorly with the replica push pipeline.
  • Record append to a single shared file started at 6.0 MB/s for one client and fell only to 4.8 MB/s at 16 clients, because throughput is limited by the network bandwidth of the chunkservers holding the file's last chunk rather than by the number of appenders.
  • Two production clusters ran 342 and 227 chunkservers with 55 TB and 155 TB used (about 18 TB and 52 TB of unique data at three-way replication) across 992k and 1550k chunks, yet master metadata was only 48 MB and 60 MB, roughly 100 bytes per file, confirming that master memory does not limit system capacity in practice.
  • Master load on those clusters was 200 to 500 operations per second and was not a bottleneck, with FindLocation accounting for 64.3% and 65.8% of master requests; cluster A sustained a 580 MB/s read rate for the preceding week against a network configuration capable of 750 MB/s.
  • Killing a chunkserver in cluster B holding 15,000 chunks and 600 GB restored full replication in 23.2 minutes at an effective rate of 440 MB/s, under a default cap of 91 concurrent clonings at 6.25 MB/s each; a simultaneous double failure of two chunkservers with roughly 16,000 chunks and 660 GB each left 266 chunks at a single replica, all cloned back to at least two replicas within 2 minutes.

Limits and trade-offs — conceded and discovered

  • Conceded by the paper: the single master bounds total capacity by how much metadata fits in its memory, one master process performs all mutations and background work, and a restarted master is hobbled for typically 30 to 60 seconds until it has fetched chunk location information from every chunkserver; shadow masters only serve reads and lag the primary, so metadata such as directory contents can be briefly stale.
  • Conceded by the paper: the consistency model is genuinely weak. Concurrent successful writes leave regions consistent but undefined, failed mutations leave them inconsistent, record append may insert padding and duplicate records, and GFS explicitly does not guarantee that replicas are bytewise identical, so applications must write self-validating, self-identifying records and filter duplicates themselves.
  • Conceded by the paper: single-client write throughput is roughly half the theoretical limit because of the network stack, and 64 MB chunks turn small hot files into hot spots, as happened when a batch-queue executable stored as a single-chunk file was started on hundreds of machines at once and had to be patched with a higher replication factor and staggered start times.
  • Conceded by the paper: three-way replication consumes more raw storage than xFS or Swift, with parity and erasure codes still only under exploration; lazy garbage collection delays reclaiming space, which frustrates users tuning usage when storage is tight; and there is no POSIX API, no client caching, and no hard or symbolic links.
  • Exposed by later work: as Google shifted toward latency-sensitive and interactive workloads, the single master and 64 MB chunk became the scaling ceiling, and its successor Colossus replaced the master with a sharded Bigtable-backed metadata service and swapped replication for Reed-Solomon erasure coding; HDFS inherited the identical single-NameNode bottleneck and required Federation and quorum-journal high availability to work around it, while multi-writer record append proved unpopular enough that HDFS never adopted it.

What it became — the systems that inherited it

GFS became the storage floor of Google's entire data stack: MapReduce read and wrote its inputs and outputs as GFS files, and Bigtable stored its SSTables and commit logs in GFS, so the append-only, at-least-once substrate shaped both of those papers. Outside Google, Hadoop's HDFS is a near-direct reimplementation, taking the single metadata server with in-memory namespace plus edit log and checkpoint, large blocks (64 MB, later 128 MB) replicated three times, the client write pipeline down a chain of storage nodes, and per-block checksums with background verification. Colossus, GFS's in-house successor, kept the chunk-and-lease shape but replaced the single master with a sharded metadata service stored in Bigtable and replaced three-way replication with Reed-Solomon erasure coding, precisely the direction section 5.1.2 named as future work. The structural pattern GFS established, splitting metadata from bulk data, keeping metadata small enough to sit in memory, letting clients talk to storage nodes directly, and treating repair as a routine background job, is now standard in QFS, Ceph, Windows Azure Storage and essentially every cloud object store. Record append was the most contentious inheritance: HDFS never adopted multi-writer append, and the many-producer queue GFS served with it moved instead to dedicated log systems like Kafka, while GFS's relaxed consistency became the canonical teaching example of trading semantics for throughput.

In the paper’s words — verbatim

“First, component failures are the norm rather than the exception.”

§1

“Thus, the global mutation order is defined first by the lease grant order chosen by the master, and within a lease by the serial numbers assigned by the primary.”

§3.1

“GFS does not guarantee that all replicas are bytewise identical. It only guarantees that the data is written at least once as an atomic unit.”

§3.3

Vocabulary — as this paper uses it

Chunk
A fixed-size 64 MB piece of a file, stored as a plain Linux file on a chunkserver and extended only as needed by lazy allocation. Each chunk is replicated on multiple chunkservers, three times by default, with different levels settable per region of the namespace.
Chunk handle
The immutable, globally unique 64-bit identifier the master assigns to a chunk at creation time. Clients and chunkservers name data by handle plus byte range rather than by pathname, which is why data traffic never needs the master.
Lease and primary
A grant from the master to one replica of a chunk, with an initial 60-second timeout extendable via HeartBeat piggybacking, that makes that replica the primary. The primary picks the serial order for all mutations to the chunk, and every other replica follows it.
Record append
A mutation in which the client supplies only the data and GFS appends it atomically at least once at an offset of its own choosing, then returns that offset. It is restricted to at most one quarter of the maximum chunk size so worst-case padding stays acceptable.
Consistent region
A file region where all clients always see the same data regardless of which replica they read from. Concurrent successful writes produce a consistent region without producing a defined one, because all replicas applied the same order but the result mingles fragments.
Defined region
A consistent region in which clients additionally see what a mutation wrote in its entirety. Serial successful writes produce defined regions, as does the region occupied by a successful record append, and applications rely on checkpoints to know which prefix is defined.
Operation log
The master's only persistent record of metadata, flushed to local disk and to remote replicas before any change becomes visible to clients. It doubles as a logical time line that defines a total order over concurrent metadata operations, and files and chunks are identified eternally by their creation times in it.
Chunk version number
A per-chunk counter the master increments and records persistently on every new lease grant, before telling any client. A chunkserver that missed mutations while down reports a lower version on restart, so its replica is treated as nonexistent when the master answers clients and is garbage collected.
Shadow master
A read-only master replica that applies the same growing operation log as the primary and typically lags it by fractions of a second. It preserves metadata read availability while the primary is down; file contents are never stale because they come from chunkservers, only metadata can be.

On the timeline — where this sits in the story

View on the timeline