Design Google Drive
Adapted from: Alex Xu, System Design Interview – An Insider's Guide (2020), Chapter 15 — rewritten and condensed for teaching.
Google Drive, Dropbox, Microsoft OneDrive, and Apple iCloud are file storage and synchronization services. You store documents, photos, and videos in the cloud, reach them from any computer, phone, or tablet, and share them with others.
This final design ties the series together. It needs a strongly consistent metadata store, blob storage with cross-region replication, delta sync to save bandwidth, and a notification service to keep every device up to date.
What you'll be able to do after this lesson:
- Grow a single-server file store into a scalable design built on S3, step by step.
- Explain block servers: chunking, compression, encryption, and delta sync.
- Design the metadata schema and the upload and download flows, and justify strong consistency and a relational metadata DB.
- Resolve sync conflicts, choose long polling for notifications, save storage space, and handle failures.
Step 1: Understand the Problem and Establish Scope
| Candidate asks | Interviewer answers |
|---|---|
| Most important features? | Upload/download, file sync, and notifications |
| Mobile, web, or both? | Both |
| Supported file types? | Any |
| Encryption? | Yes, files must be encrypted in storage |
| File size limit? | 10 GB or smaller |
| Users? | 10 M DAU |
In scope:
- Add files, for example by drag and drop
- Download files
- Sync across devices: a file added on one device appears on the others
- See file revisions
- Share files with friends, family, and coworkers
- Notifications when a file is edited, deleted, or shared with you
Out of scope: Google Docs-style real-time collaborative editing.
Non-functional requirements:
- Reliability. Data loss is unacceptable.
- Fast sync. Slow sync makes users give up on the product.
- Low bandwidth use. This matters especially on mobile data plans.
- Scalability to high traffic.
- High availability despite offline or slow servers and network errors.
Back-of-the-envelope estimation
| Quantity | Calculation | Result |
|---|---|---|
| Users | Assumption | 50 M signed up, 10 M DAU |
| Free space | Assumption | 10 GB per user |
| Uploads | 2 files per user per day, 500 KB average | |
| Read:write ratio | Assumption | 1:1 |
| Total space allocated | 50 M × 10 GB | 500 PB |
| Upload QPS | 10 M × 2 ÷ 86,400 s | ≈ 231, call it ~240 |
| Peak upload QPS | 2 × QPS | ~480 |
Step 2: High-Level Design
Rather than presenting the final diagram up front, we'll start with one server and grow it. This is the same approach as Lesson 1.
A single server
- A web server (Apache) for uploads and downloads.
- A database (MySQL) for metadata: users, login info, file info.
- A storage directory,
drive/, with 1 TB of space.
Under drive/, each user has a namespace directory that holds all of their files, stored under their original names. A file or folder is identified uniquely by namespace + relative path:
drive/
├── user1/
│ └── recipes/
│ └── chicken_soup.txt
├── user2/
│ ├── football.mov
│ └── sports.txt
└── user3/
└── best_pic_ever.png
APIs
All APIs require authentication and use HTTPS, so SSL/TLS protects data in transit.
1. Upload a file. There are two types:
- Simple upload for small files.
- Resumable upload for large files or unreliable networks.
POST https://api.example.com/files/upload?uploadType=resumable
data: local file to upload
A resumable upload takes three steps:
- Send an initial request to get a resumable URL.
- Upload the data and track its progress.
- If the upload is interrupted, resume from where it stopped.
2. Download a file.
GET https://api.example.com/files/download
{ "path": "/recipes/soup/best_soup.txt" }
3. List file revisions.
GET https://api.example.com/files/list_revisions
{ "path": "/recipes/soup/best_soup.txt", "limit": 20 }
Moving off the single server
Eventually a "space full" alert arrives: only 10 MB left, and uploads start failing.
First fix: sharding. Spread files across several storage servers by user_id. That works, but losing a storage server would still lose data.
Better fix: Amazon S3. S3 is object storage built for scalability, availability, security, and performance, and it is used by companies such as Netflix and Airbnb. It supports same-region and cross-region replication. A region is a geographic area where AWS runs data centers, and a bucket is roughly a top-level folder. Keeping copies in several regions protects against data loss and keeps files available.
flowchart LR
subgraph Same["Same-region replication"]
B1[(Bucket)] --> B2[(Replica bucket)]
end
subgraph Cross["Cross-region replication"]
R1[(Bucket, region A)] --> R2[(Bucket, region B)]
end
Further improvements:
- Load balancer: spreads traffic evenly and routes around failed web servers.
- Web servers: with a load balancer in place, add or remove them as traffic changes.
- Metadata database: move it off the web server to remove a single point of failure, and add replication and sharding.
- File storage: S3, with files replicated in two geographic regions.
flowchart LR
U[User] --> LB[Load balancer] --> WS[Web servers]
WS --> MDB[(Metadata DB<br/>replicated + sharded)]
WS --> S3A[(S3 region A)]
S3A -- replicate --> S3B[(S3 region B)]
Sync conflicts
When two users change the same file at the same time, the rule is: the first version processed wins. The later one gets a conflict.
sequenceDiagram
participant U1 as User 1
participant S as Server
participant U2 as User 2
U1->>S: update file (based on v1)
S-->>U1: ok → v2
U2->>S: update file (based on v1)
S-->>U2: conflict: server is at v2
For User 2, the system shows both copies: their local version and the latest server version. They can merge the two or overwrite one with the other. (Keeping a document synchronized while many people edit it at once is a much harder problem. See Differential Synchronization in the references.)
Practice: first-processed-wins conflicts
Implement Server.commit(path, base_version, content, user), using optimistic concurrency:
- Each path has a current version. A path that doesn't exist yet is at v0.
- If
base_versionequals the current version, store the content as current + 1 and return"<user>: ok -> v<new>". - Otherwise return
"<user>: conflict (server at v<current>, you based on v<base>)", and change nothing.
Each input line is path base_version user content….
The high-level design
flowchart LR
U["User<br/>browser · mobile"] --> BS[Block servers] --> CS[(Cloud storage)] --> COLD[(Cold storage)]
U --> LB[Load balancer] --> API[API servers]
API --> MC[(Metadata cache)]
API --> MDB[(Metadata DB)]
API --> NS[Notification service]
NS -- long poll --> U
NS --> OBQ[[Offline backup queue]]
BS --> MDB
- Block servers: upload blocks to cloud storage. A file is split into blocks, and each block gets a unique hash stored in the metadata DB. Each block is an independent object in storage (S3). A file is rebuilt by joining its blocks in order. Block size follows Dropbox: at most 4 MB.
- Cloud storage: holds the blocks.
- Cold storage: cheap storage for inactive data that hasn't been touched for a long time.
- Load balancer: spreads requests across API servers.
- API servers: almost everything except uploading file content: authentication, user profiles, file metadata updates, and so on.
- Metadata database: metadata for users, files, blocks, and versions. Only metadata lives here, while the file contents live in cloud storage.
- Metadata cache: hot metadata, for fast reads.
- Notification service: a pub/sub system that tells clients when a file has been added, edited, or removed elsewhere, so they can pull the changes.
- Offline backup queue: stores changes for offline clients, so they can sync when they reconnect.
Check: high-level design
Storage, conflicts, components
1 / 3After sharding files across your own storage servers, what problem remains that S3 with cross-region replication solves?
Pick an answer to see if you are right.
Step 3: Design Deep Dive
Block servers
Re-uploading a large, frequently edited file in full after every change wastes bandwidth. Two optimizations help:
- Delta sync: when a file changes, sync only the changed blocks, using a sync algorithm such as rsync.
- Compression: compress each block, choosing the algorithm by file type. gzip and bzip2 suit text, while images and video need different codecs.
Block servers do the heavy lifting. For a new file:
flowchart LR
F[File] --> SPLIT[Split into blocks] --> COMP[Compress each block] --> ENC[Encrypt each block] --> UP[Upload to cloud storage]
- Split the file into blocks.
- Compress each block.
- Encrypt each block. Encryption is a requirement.
- Upload the blocks to cloud storage.
With delta sync, if only block 2 and block 5 of a file changed, only those two blocks are uploaded.
Practice: delta sync
Implement split_blocks(data, size) (fixed-size blocks, where the last one may be shorter) and changed_blocks(old, new, size). The second function returns the indexes of blocks in new whose hash (the first 8 hex characters of SHA-256) differs from the block at the same index in old. Blocks beyond the end of old count as changed.
Input: line 1 is size, line 2 is the old content, and line 3 is the new content.
The last test shows the weakness of fixed-size blocks: inserting one byte at the front shifts every block boundary, so everything looks changed. Real delta-sync algorithms such as rsync use rolling hashes to find matching blocks at any offset. That way, an insertion costs about one block instead of the whole file.
Strong consistency
Strong consistency is the default here. Two clients must never see the same file differently at the same time. That applies to both the metadata cache and the metadata DB.
Caches are eventually consistent by default: replicas may disagree for a while. To make them strongly consistent:
- Keep the cache replicas consistent with the primary.
- Invalidate the cache on every database write, so the cache and DB hold the same values.
Relational databases provide this naturally through ACID (Atomicity, Consistency, Isolation, Durability). NoSQL stores usually don't, so ACID behavior would have to be built into the sync logic by hand. We choose a relational database for metadata. Contrast this with the AP key-value store of Lesson 6: the requirements here favor consistency.
Metadata database
A simplified schema:
| Table | Key fields | Notes |
|---|---|---|
| user | user_id, user_name, created_at | Basic profile info |
| device | device_id, user_id, push_id, last_logged_in_at | push_id for mobile push. A user can have many devices |
| namespace | id, user_id, is_shared | A user's root directory |
| file | id, namespace_id, relative_path, file_name, is_directory, latest_version, checksum, last_modified | Everything about the latest version |
| file_version | id, file_id, device_id, version_number, last_modified | Version history. Rows are read-only to protect the revision history |
| block | block_id, file_version_id, block_order | Any version is rebuilt by joining its blocks in block_order |
Upload flow
Two requests run in parallel from client 1:
sequenceDiagram
participant C1 as Client 1
participant BS as Block servers
participant CS as Cloud storage
participant API as API servers
participant MDB as Metadata DB
participant NS as Notification service
participant C2 as Client 2
par Add file metadata
C1->>API: 1. add file metadata
API->>MDB: 2. store metadata, status = "pending"
API->>NS: 3. new file being added
NS-->>C2: 4. file is uploading
and Upload file content
C1->>BS: 2.1 upload file content
BS->>CS: 2.2 chunk, compress, encrypt, upload blocks
CS->>API: 2.3 upload-complete callback
API->>MDB: 2.4 status = "uploaded"
API->>NS: 2.5 status changed
NS-->>C2: 2.6 file fully uploaded
end
Add file metadata:
- Client 1 asks to add the new file's metadata.
- The metadata is stored in the DB with status "pending".
- The notification service is told that a new file is being added.
- It tells client 2 that a file is uploading.
Upload the file to cloud storage:
- 2.1. Client 1 uploads the file content to the block servers.
- 2.2. Block servers chunk, compress, and encrypt the file and upload the blocks to cloud storage.
- 2.3. Cloud storage fires an upload-complete callback to the API servers.
- 2.4. The file status changes to "uploaded" in the metadata DB.
- 2.5. The notification service is told about the status change.
- 2.6. It tells client 2 the file is fully uploaded.
Editing a file follows the same flow.
Download flow
How does a client learn that a file changed elsewhere?
- Online: the notification service tells it to pull the latest changes.
- Offline: the changes are saved (in the offline backup queue and cache), and the client pulls them when it reconnects.
sequenceDiagram
participant NS as Notification service
participant C2 as Client 2
participant API as API servers
participant MDB as Metadata DB
participant BS as Block servers
participant CS as Cloud storage
NS-->>C2: 1. a file changed
C2->>API: 2. fetch metadata of changes
API->>MDB: 3. query
MDB-->>API: 4. metadata
API-->>C2: 5. metadata
C2->>BS: 6. download blocks
BS->>CS: 7. fetch blocks
CS-->>BS: 8. blocks
BS-->>C2: 9. blocks → reconstruct file
The client fetches metadata first to learn which blocks it needs, then downloads only those blocks and reassembles the file.
Notification service
Every local change must reach the other clients quickly, to keep conflicts rare. There are two options:
- Long polling, which Dropbox uses.
- WebSocket: persistent and bidirectional.
We choose long polling, because:
- Communication is one-way: the server tells clients about changes, and clients don't push events back over this channel.
- WebSocket suits real-time bidirectional traffic, as in the chat system of Lesson 12. Drive notifications are infrequent and not bursty.
Each client holds a long-poll connection to the notification service. When a change is detected, the connection closes, which tells the client to fetch the latest changes from the metadata servers. After a response or a timeout, the client reconnects immediately.
Saving storage space
Version history plus multi-data-center copies fill storage fast. Three techniques help:
- De-duplicate blocks at the account level: two blocks with the same hash are identical, so store them once.
- Use a smarter backup strategy:
- Set a limit on stored versions. When it's reached, the oldest version is replaced by the new one.
- Keep only valuable versions. A heavily edited document might be saved over 1,000 times in a short period. Cap the saved versions and weight recent ones more. Experiment to find the right number.
- Move cold data to cold storage. Data inactive for months or years goes to something like Amazon S3 Glacier, which is much cheaper than standard S3.
Practice: block-level deduplication
Implement BlockStore.upload(data, size). It splits the data into fixed-size blocks, adds each block's length to self.logical_bytes, stores each block under its hash only if that hash is new, and returns the number of new blocks stored.
The wrapper uploads each input line as a file and then reports the deduplication savings. Line 1 is size, and each remaining line is a file's content.
Failure handling
| Failure | Response |
|---|---|
| Load balancer | A secondary takes over. Load balancers monitor each other with heartbeats, and one that stops sending them is considered failed |
| Block server | Other block servers pick up unfinished or pending jobs |
| Cloud storage | S3 buckets are replicated across regions, so fetch from another region |
| API server | Stateless: the load balancer routes traffic to other API servers |
| Metadata cache | Replicated: read from other nodes and replace the failed one |
| Metadata DB primary | Promote a replica to primary and add a new replica |
| Metadata DB replica | Read from another replica and replace the failed one |
| Notification service | Each server holds a huge number of long-poll connections (Dropbox reported over 1 million per machine in 2012). If one dies, all of its clients must reconnect elsewhere, and reconnecting them all is slow |
| Offline backup queue | Replicated: consumers re-subscribe to the backup queue |
Check: deep dive
Blocks, consistency, notifications
1 / 5A user edits one paragraph in a 2 GB file. With 4 MB blocks and delta sync, roughly how much is uploaded?
Pick an answer to see if you are right.
Step 4: Wrap Up
Strong consistency, low bandwidth, and fast sync make this an interesting design. It has two main flows: managing file metadata and syncing files. The notification service, using long polling, keeps clients up to date.
There's no perfect design. Know the trade-offs. Two alternatives to discuss:
- Upload directly to cloud storage from the client, skipping the block servers.
- Pro: faster, because the file travels to storage only once instead of twice.
- Con: chunking, compression, and encryption must be reimplemented on every platform (iOS, Android, web), which is error-prone and costly. Centralizing them in block servers avoids that.
- Con: clients can be hacked or manipulated, so client-side encryption is risky.
- A separate presence service. Move online/offline tracking out of the notification servers, so that other services can reuse it.
Key Takeaways
- About 500 PB allocated and ~240 upload QPS (480 at peak). Grow from one server to S3 with cross-region replication, a load balancer, stateless web/API servers, and a replicated, sharded metadata DB.
- Block servers split files into blocks (up to 4 MB), then compress, encrypt, and upload them. Delta sync sends only changed blocks.
- Strong consistency for metadata: a relational DB (ACID) plus cache invalidation on write.
- Upload sends metadata ("pending" → "uploaded") and content in parallel, with notifications at each step. Download fetches metadata, then only the needed blocks.
- Conflicts: first processed wins, and the loser sees both copies. Notifications use long polling (one-way, infrequent).
- Save space with block dedupe, version limits that favor recent versions, and cold storage. Plan a failover for every component.
Previous lesson: Design YouTube.
Next lesson: The Learning Continues — curated real-world architecture readings and engineering blogs, mapped back to these lessons.
Reference Materials
- Google Drive
- Upload file data — Google Drive API
- Amazon S3
- Differential Synchronization — Neil Fraser
- Differential Synchronization (talk)
- How We've Scaled Dropbox
- Tridgell, A., & Mackerras, P. (1996). The rsync algorithm.
- librsync
- ACID — Wikipedia
- Dropbox security white paper
- Amazon S3 Glacier FAQs