← tonia.ai

Distributed systems · file systems

One namespace, many disks

A distributed file system lets programs open, read, and write files stored on other machines through the same interface they use for local disks. Behind that familiar interface are hard choices about where data is cached, who keeps track of whom, what a reader sees after someone else writes, and what happens when a disk or server dies.

What a DFS provides

The core service is a shared namespace: a tree of directories and files that looks the same from every client, whatever disk actually holds the bytes. A client attaches part of a remote tree into its own by mounting it.

client file server / bin usr home ana raj / srv home ana raj mount /home/ana opens like any local path

Beyond a shared tree, a good DFS aims for the transparency goals of any distributed system. Location transparency means paths don't reveal servers. Access transparency means remote files use the same open/read/write calls. Replication and failure transparency mean users don't see copies or crashes. It also has to scale to many clients and much data without one server becoming the limit.

Design choices

Every distributed file system answers the same few questions. The answers explain most of their differences.

Where do operations run?

Remote access Upload / download client file read · write · read … every operation crosses the network small transfers, server always current client copy file 1 fetch 3 store 2 reads and writes hit the local copy few large transfers, server load drops
QuestionOptionsTrade-off
Access modelRemote access, or upload/downloadFreshness vs. server load and latency
Server stateStateless, or stateful (tracks opens, locks, caches)Simple crash recovery vs. smarter caching and locking
CachingNone, blocks, whole files; memory or diskSpeed vs. keeping copies consistent
ConsistencyFrom UNIX semantics to immutable filesProgrammer convenience vs. scalability
Data placementOne server, partitioned, or replicatedSimplicity vs. capacity and availability

Sharing semantics

When two clients use the same file, what does a reader see after a writer writes? On one machine the answer is simple: the latest write, immediately. Across a network, keeping that promise is expensive, so file systems offer weaker contracts.

SemanticsPromiseTypical of
UNIX (one-copy)Every read sees the most recent write, instantlyLocal file systems; costly when distributed
Close-to-openA client that opens a file sees writes from anyone who closed it before that openNFS
SessionChanges become visible to others only after the writer closes; last close winsAFS
Immutable filesFiles never change; updates create new files or versionsObject stores, many big-data systems
TransactionsGroups of operations are atomic and serializableDatabase-backed storage

Weaker semantics are not a defect. They are what lets caching work. An application that needs stronger guarantees adds them itself, typically with file locks or by writing to a new file and renaming it into place atomically.

NFS: stateless sharing

Sun's Network File System (1985) aimed for one thing above all: easy recovery. In versions 2 and 3, the server keeps no per-client state. Each request carries everything needed to serve it: a file handle identifying the file, an offset, and a length. If the server crashes and reboots, clients just retry and continue. There are no sessions to rebuild.

ClientServer application system call: read() VFS local FS NFS client RPC · XDR VFS local FS disk NFS server RPC · XDR READ(handle, offset, count)
The virtual file system (VFS) layer is what makes remote files transparent: applications call read() and the kernel routes it to the right implementation.

Statelessness pushes NFS toward idempotent operations. "Write these bytes at offset 8192" can safely be retried; "append these bytes" cannot. Clients cache data and attributes for a few seconds (configurable, typically 3 to 60). They flush dirty data on close and revalidate on open, which yields close-to-open consistency. Between those points, two clients can see different contents.

The cost of statelessness is that the server can't help with caching or locking, so those were bolted on as separate protocols. NFSv4 (2000s) gave up pure statelessness. It adds open state, integrated locking, leases for crash recovery, and delegations, which let the server hand a client temporary exclusive rights to cache a file aggressively.

Papers and specsSandberg et al., "Design and Implementation of the Sun Network Filesystem," USENIX Summer 1985 · RFC 7530 (NFSv4) · RFC 8881 (NFSv4.1, pNFS)
TutorialInstall and configure an NFS server (Ubuntu) · Linux NFS wiki

AFS: whole-file caching

The Andrew File System at Carnegie Mellon (mid-1980s) was built for thousands of workstations per campus. Measurements showed most files are small, read far more than written, and rarely shared for writing. So AFS copies the entire file to the client's local disk on open. All reads and writes happen locally, and changed files go back to the server on close.

Instead of clients asking "has this changed?", the server makes a callback promise: it records which clients cache each file and notifies them when another client stores a new version.

client Aserverclient B open: fetch file + callback reads from local disk open: fetch file + callback writes close: store break callback
A keeps reading its cached copy with no server traffic until the callback is broken; its next open fetches the new version.

The result is session semantics, with the server as the record of who caches what. Because clients rarely contact the server, AFS reached roughly ten times more clients per server than NFS in its era. Callback state must be rebuilt after a crash, and clients must refresh callbacks after a network outage, which makes AFS more complex than NFS. AFS also introduced a single global namespace (/afs/cell/…), Kerberos authentication, and volumes that can move between servers without changing paths. Its successor, Coda, added disconnected operation: laptops keep working offline and reconcile changes later.

PapersHoward et al., "Scale and Performance in a Distributed File System," ACM TOCS 6(1), 1988 · Satyanarayanan, "Scalable, Secure, and Highly Available Distributed File Access," IEEE Computer 23(5), 1990 · Kistler & Satyanarayanan, "Disconnected Operation in the Coda File System," ACM TOCS 10(1), 1992
ToolOpenAFS

Keeping caches valid

Caching is what makes a DFS fast. Every cache needs a rule for detecting staleness, and the three main approaches trade server state against network traffic.

ApproachHow it worksServer stateWeakness
Polling (validate)Client asks "changed since T?" on open or after a timeoutNoneTraffic grows with clients; stale for up to the timeout
Callbacks (invalidate)Server notifies holders when a file changesList of cachers per fileLost on crash; a partitioned client can't be reached
LeasesServer promises validity for a fixed time; client renewsBounded, expires on its ownWriter may wait for leases to expire

Leases combine the other two. Like callbacks, they avoid constant polling. Like timeouts, they need no recovery: after a crash the server waits one lease period and every old promise has expired. Caching is not free, though. For write-heavy or widely shared files, invalidation traffic and stale reads can cost more than the cache saves.

PaperGray & Cheriton, "Leases: An Efficient Fault-Tolerant Mechanism for Distributed File Cache Consistency," SOSP 1989

GFS: chunks at scale

The Google File System (2003) started from different assumptions. Components fail constantly. Files are huge, often many gigabytes. Most writes append rather than overwrite. The workload is high-throughput batch reading, not low-latency interactive access. Those assumptions led to a very different design.

  • Large chunks. Files are split into 64 MB chunks, each replicated (by default three times) on chunkservers that store them as ordinary Linux files.
  • Single master. One master holds all metadata in memory: the namespace, the file-to-chunk map, and replica locations. It never handles file data.
  • Separate control and data. Clients ask the master where chunks live, cache the answer, then exchange data directly with chunkservers.
  • Leases for ordering. The master grants one replica a lease, making it primary. The primary chooses the order of mutations for that chunk, and the other replicas apply them in the same order.
masternamespace · chunk mapreplica locations clientcaches locations 1 where is chunk 7? heartbeats, leases chunkserverprimary chunkserver chunkserver 2 push dataalong a chain 3 primary assigns order; replicas apply in that order
Metadata (thin) goes through the master; bulk data (thick) flows directly between machines, pipelined so each link's bandwidth is used once.

A relaxed consistency model

GFS doesn't promise UNIX semantics. It defines what a file region looks like after each kind of mutation. A region is consistent if all replicas hold the same bytes, and defined if it is consistent and reflects one complete write.

OutcomeOverwrite at an offsetRecord append
Serial successDefinedDefined, possibly with padding or duplicates in between
Concurrent successesConsistent but undefined (fragments mixed)Defined, possibly with padding or duplicates in between
FailureInconsistentInconsistent

Record append is the key operation: many clients append to one file at once, and GFS picks the offset, guaranteeing each record lands atomically at least once. Applications cope with duplicates and padding using checksums and record IDs. That suits producer–consumer pipelines and log merging, the workloads GFS was built for.

PapersGhemawat, Gobioff & Leung, "The Google File System," SOSP 2003 · McKusick & Quinlan, "GFS: Evolution on Fast-forward," CACM 53(3), 2010

Surviving failure

At thousands of machines, disks and servers fail every day. A DFS has to detect failures, keep enough copies, and repair itself without operators.

  • Replication with re-replication. When a chunkserver stops sending heartbeats, the master schedules new copies of its chunks elsewhere, prioritizing chunks with the fewest surviving replicas.
  • Placement across failure domains. Replicas go on different racks, so one switch or power failure can't take every copy.
  • Checksums. Each chunkserver verifies data blocks on read, so silent disk corruption is caught and repaired from another replica.
  • Metadata durability. The master logs every metadata change to an operation log replicated on other machines, with periodic checkpoints for fast restart. Read-only shadow masters can serve lookups while it recovers.
  • Stale replica detection. A version number on each chunk lets the master spot replicas that missed updates while their server was down.

Replication vs. erasure coding

Three full copies are simple and fast to read but triple the storage. Erasure coding splits data into k fragments plus m parity fragments, and any k of the k + m fragments can rebuild the data. Large systems keep hot data replicated and convert colder data to erasure codes.

0×1×2×3× raw storage per byte of data 3 replicas 3.0× · survives 2 RS(6, 3) 1.5× · survives 3 RS(10, 4) 1.4× · survives 4
Erasure coding halves the storage and tolerates more losses. The price is CPU for encoding and much more network traffic to rebuild a lost fragment.

PapersFord et al., "Availability in Globally Distributed Storage Systems," OSDI 2010 · Huang et al., "Erasure Coding in Windows Azure Storage," USENIX ATC 2012 · Muralidhar et al., "f4: Facebook's Warm BLOB Storage System," OSDI 2014

Scaling metadata

A single master keeps the design simple, and GFS kept it out of the data path. But its memory caps the number of files, and every open still needs it. As clusters grew to billions of files, that became the limit. Later systems distribute metadata in three main ways:

  • Federation: split the namespace among several independent metadata servers, each owning a subtree (HDFS federation).
  • Metadata in a scalable database: store the namespace in a distributed key-value store or database (Google's Colossus on Bigtable, HopsFS on a NewSQL database, Meta's Tectonic on ZippyDB).
  • Computed placement: compute where data lives from a hash function instead of looking it up. Ceph's CRUSH lets any client calculate object locations, with dynamic subtree partitioning for directories.

PapersWeil et al., "Ceph: A Scalable, High-Performance Distributed File System," OSDI 2006 · Weil et al., "CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data," SC 2006 · Niazi et al., "HopsFS: Scaling Hierarchical File System Metadata Using NewSQL Databases," FAST 2017 · Pan et al., "Facebook's Tectonic Filesystem: Efficiency from Exascale," FAST 2021

Side by side

NFS (v3)AFSGFS / HDFSCeph
Built forShared home dirs on a LANCampus-scale sharingBatch analytics on huge filesGeneral storage at scale
Access modelRemote access, block cachingWhole-file caching on diskLarge streaming reads, appendsObjects, blocks, or POSIX files
Server stateStatelessStateful callbacksMaster holds all metadataMonitors plus distributed metadata servers
ConsistencyClose-to-openSession, last close winsRelaxed; defined appendsStrong (POSIX for CephFS)
Fault toleranceServer is a single point of failureRead-only volume replicas3× replicas, re-replicationReplication or erasure coding, self-healing
Scales byBigger serverCaching; less server contactAdding chunkserversAdding nodes; CRUSH placement

No design wins everywhere. A small team sharing source code wants NFS-like simplicity; a pipeline scanning petabytes wants GFS-like throughput. Start from the workload: file sizes, read/write mix, sharing pattern, and how much inconsistency the application can tolerate.

In practice

Colossus

GFS successorsharded metadata

Google replaced GFS's single master with metadata stored in Bigtable, enabling far more files per cluster. Data moved to Reed-Solomon erasure coding, and a single cluster can serve many workloads at once.

A peek behind Colossus, Google's file system (Google Cloud blog)

Ceph

open sourceunified storage

One cluster serves object (S3-compatible), block, and file interfaces. After ten years of storing data on top of local file systems, the Ceph team built BlueStore, which writes to raw devices directly. Their paper explains why general-purpose file systems make poor backends for distributed storage.

Aghayev et al., "File Systems Unfit as Distributed Storage Backends: Lessons from 10 Years of Ceph Evolution," SOSP 2019

Object storage

flat namespaceimmutable objects

Services like Amazon S3 drop directories, partial overwrites, and POSIX locking in favor of whole-object PUT and GET on a flat key space. Giving up those features is what lets them scale to trillions of objects. S3 moved from eventual to strong read-after-write consistency in 2020. Amazon now uses lightweight formal methods to check its storage-node code.

S3 strong consistency · Bornholt et al., "Using Lightweight Formal Methods to Validate a Key-Value Storage Node in Amazon S3," SOSP 2021

Tools and reading

Try it

  • NFS server and clientExport a directory, mount it on a second machine, and test close-to-open behavior with two writers.
  • HDFS single-node setupUse hdfs fsck -files -blocks -locations to see blocks and replicas.
  • Ceph with cephadmStand up a small cluster, stop a node, and watch it self-heal.
  • MinIOS3-compatible object store with erasure coding, runs on a laptop.
  • fio and IOzoneBenchmark sequential vs. random and small vs. large I/O on local and remote mounts.
  • MIT 6.5840Lecture notes and FAQ on the GFS paper.

Read it