Google Drive System Design Interview: Syncing Billions of Files Without Re-Uploading Them
Google Drive and Google Workspace serve well over 3 billion users across Gmail, Docs, Photos, and Drive, a figure Google has stated publicly. The bytes underneath live in Colossus, Google's exabyte-scale file system that runs across tens of thousands of machines per cluster. A single popular file, say a company onboarding PDF shared with 50,000 employees, must be stored once and made to appear instantly in 50,000 different folder trees. The engineering problem is not storing bytes. It is telling millions of connected clients what changed, and doing it without shipping whole files over the wire.
The core of a Drive-style system is the split between a metadata plane and a data plane. File contents are broken into chunks, hashed, deduplicated, and written to object or block storage that handles replication and durability. A separate metadata database records the folder tree, file versions, chunk lists, permissions, and per-user sync state, and this is the part that must be strongly consistent and low latency. Sync is the hard subsystem: every client keeps a cursor into a per-user change log, and the server tells it only which files changed, not the bytes. When a client does need bytes, delta sync sends only the chunks whose hashes differ from what the client already has. Large uploads are resumable so a dropped connection near the end of a 4 GB file does not restart from zero. Sharing turns one physical file into many logical views through access control lists checked on every read. Concurrent edits from offline clients produce conflicts, which the system resolves by versioning and, for some file types, by forking a conflicted copy rather than silently losing work.
Asked at: Asked at Google, Dropbox, Microsoft, and most companies that run large consumer or enterprise storage products. It is a favorite for senior and staff loops because it forces you to separate metadata from data and to reason about sync as its own distributed problem.
Why this question is asked
Interviewers like this problem because a naive answer (upload file, download file) is trivially wrong at scale, and the real difficulty only appears when you push on it. It tests whether you can separate a strongly consistent metadata store from a durable but eventually consistent blob store, whether you understand content addressing and deduplication, and whether you can design an incremental sync protocol instead of polling for full state. It also probes the messy human parts: two people editing the same file offline, a folder shared with tens of thousands of accounts, and a 10 GB upload on flaky hotel wifi. Strong candidates talk about chunk boundaries, cursors, and conflict semantics. Weak candidates talk only about S3.
Requirements
Always clarify these in the first 5 minutes of the interview. Do not start drawing boxes until both lists are agreed.
Functional requirements
- Upload and download files of any size, from a 2 KB text note to a 50 GB video, with resumable uploads that survive network drops
- Sync files across a user's devices so a change on a laptop appears on the phone within seconds
- Send only changed data on sync, not whole files, using chunk-level delta transfer
- Deduplicate identical content so a file uploaded a million times is stored once
- Keep version history so users can restore a previous revision of a file
- Share files and folders with individuals, groups, or anyone-with-the-link, with viewer, commenter, and editor roles
- Enforce access control on every read and write, including inherited permissions from parent folders
- Detect and resolve conflicts when the same file is edited concurrently on two offline devices
- Support move, rename, trash, and restore operations that propagate to all clients
Non-functional requirements
- Durability of at least eleven nines for stored bytes, so uploaded data is effectively never lost
- Metadata reads and permission checks under roughly 50 ms at the median so the file list feels instant
- Sync latency in the low seconds from a write on one device to a notification on another
- Bandwidth efficiency: never transfer a chunk the receiving side already holds
- Horizontal scalability of both the metadata database and the blob store to exabytes and billions of users
- Strong consistency for the metadata tree and permissions so a revoked share takes effect immediately
- Availability high enough that reads succeed even during regional degradation, via replication and failover
Back-of-envelope scale estimates
Show your math. Pulling numbers from thin air signals you have not thought about the load.
Total users
3 billion+ (stated)
Google has publicly stated more than 3 billion users across Workspace and consumer accounts. Not all are active Drive users, but the storage plane is shared across Gmail, Photos, and Drive.
Chunk size
~4 MB average (design choice)
A common design target for content-defined chunking. Too small means huge metadata and per-chunk overhead, too large means poor dedup and bigger deltas. 4 MB balances metadata count against delta granularity. This is a design choice, not a published Drive number.
Chunks per 1 GB file
~256 chunks
Derived: 1 GB / 4 MB per chunk = 256 chunk records. Each chunk record holds a hash and a storage pointer, so a 1 GB file costs a few kilobytes of metadata beyond the bytes themselves.
Metadata per file
~1-4 KB (estimate)
Estimate: file id, name, parent id, owner, version pointer, timestamps, and a chunk list of a few hundred hashes. For typical office files this lands in the low kilobytes. Metadata volume is therefore a large database in its own right, independent of the petabytes of content.
Resumable upload chunk granularity
256 KB multiples (documented)
Google Drive's resumable upload protocol requires each PUT chunk to be a multiple of 256 KB except the final chunk. This is the transport granularity for a single upload session, distinct from the content-defined chunks used for dedup and delta sync.
Sync fan-out for a hot shared file
10,000s of clients per change
Estimate: an org-wide shared folder can have tens of thousands of members. One edit must surface in every member's changes feed, so the notification path, not the byte path, is the fan-out bottleneck.
High-level architecture
A client (desktop agent, mobile app, or browser) talks to an API gateway that authenticates the request and routes it to the metadata service or the upload/download service. On upload, the client splits the file into content-defined chunks, hashes each one, and asks the metadata service which hashes are new. Only unknown chunks are sent, through a resumable upload session, and land in the blob store, which at Google is Colossus fronted by higher-level object and block abstractions. The blob store handles replication, erasure coding, and durability, and returns storage locations. The client then commits a file version to the metadata database: a record that names the file, its parent folder, its owner, and the ordered list of chunk hashes that make up this revision. The metadata database is the source of truth for the tree and for permissions, and it is strongly consistent so a rename or a revoked share is never ambiguous. Every mutation also appends an entry to a per-user change log. Other devices hold a cursor (a page token) into that log. They either poll changes.list or receive a lightweight push notification that says new changes exist, then pull the change entries, see which file ids moved, and fetch only the chunks whose hashes they lack. Reads go the other way: resolve the file id, check the ACL, read the chunk list, and stream chunks from the blob store, often through a CDN edge for popular content.
In a real interview, sketch this on the whiteboard before diving into any single box.
Core components
Walk through each service. The interviewer wants to hear what each one owns, not just the names.
API gateway and auth
Terminates client connections, verifies OAuth tokens or session cookies, and routes to the metadata, upload, or download services. It enforces per-user rate limits and is the single place that maps an incoming request to an authenticated identity used for every downstream permission check.
Metadata service and database
The source of truth for the folder tree, file records, versions, chunk lists, and permissions. It must be strongly consistent and low latency, since it backs the file list UI and every ACL check. Google's heritage here is Bigtable for large flat keyspaces and Spanner for globally consistent transactional metadata.
Chunking and dedup service
Splits file content into variable-length chunks using a rolling hash so that inserting a byte near the start does not shift every downstream boundary. It hashes each chunk, checks the hash against the store, and only accepts chunks that are new. This is where storage savings and delta efficiency come from.
Blob store (object and block over Colossus)
Durably stores chunk bytes with replication and erasure coding. Content is addressed by hash, so a chunk is written once regardless of how many files or users reference it. At Google this sits on Colossus, whose custodians handle re-replication and RAID reconstruction in the background.
Sync and change-feed service
Maintains a per-user ordered log of changes and hands out page tokens that act as cursors. Clients advance their token to learn exactly which files changed since they last synced. A push channel (changes.watch style) notifies clients that new entries exist without shipping the change data itself.
Upload/download service
Runs resumable upload sessions, tracks received byte ranges, and lets a client query and continue an interrupted transfer. On download it resolves chunk locations, checks permissions, and streams bytes, using edge caching for widely shared files.
Sharing and permissions engine
Evaluates access control lists, including permissions inherited from parent folders and from group memberships. It answers the question 'can this identity do this action on this file' on the hot path of every read and write, and it must reflect a revocation immediately.
Data model
Pick the right store per table. Justify each choice with the access pattern, not by reflex.
filesfile_idnameparent_idowner_idcurrent_version_idis_trashedOne row per logical file or folder. parent_id models the tree. current_version_id points at the head revision. A folder is just a file with no content. Renames and moves mutate this row, not the bytes.
file_versionsversion_idfile_idsize_bytescreated_atauthor_idchunk_manifest_idImmutable revision records. Restoring an old version means pointing files.current_version_id back at an earlier row. Version history and undo fall out of keeping these rows instead of overwriting.
chunk_manifestchunk_manifest_idsequence_nochunk_hashoffsetlengthThe ordered chunk list for a version. Delta sync compares two manifests to find which hashes differ. Multiple versions and multiple files can reference the same chunk_hash, which is where dedup lives.
chunkschunk_hashstorage_locationlengthref_countencodingContent-addressed physical chunks. ref_count supports garbage collection: a chunk is deletable only when no manifest references it. storage_location points into the blob store. Written once per unique hash.
permissionspermission_idfile_idgrantee_idgrantee_typeroleinherited_fromACL entries. grantee_type is user, group, domain, or anyone. role is viewer, commenter, or editor. inherited_from records the ancestor folder a grant flows from, so revoking on a parent can cascade.
changeschange_iduser_idfile_idchange_typesequence_tokenThe per-user change log that drives sync. Clients hold a sequence_token cursor and read forward. change_type covers create, modify, move, trash, and permission change. This table is append-heavy and read by every connected device.
Deep dives
These are the conversations the interviewer is steering you toward. Practice each one until you can talk through it without notes.
Content-defined chunking and deduplication
Fixed-size chunking looks simple but breaks the moment someone inserts a byte at the top of a file, because every subsequent boundary shifts and every chunk hash changes, killing dedup and forcing a full re-upload. The fix is content-defined chunking with a rolling hash such as Rabin fingerprinting. You slide a window over the bytes and cut a boundary whenever the fingerprint matches a pattern, so boundaries are anchored to content, not to absolute offsets. Insert a byte near the start and only the one chunk around the insertion changes, everything after it re-aligns. Each chunk is hashed with a strong function like SHA-256, and that hash is both the identity and the storage key. Before uploading, the client sends the list of hashes and the server replies with which ones it has never seen. Identical content anywhere in the system, the same attachment mailed to a thousand people, resolves to the same hash and is stored exactly once. Dedup can be global across all users or scoped per user for privacy and security reasons, and that scoping choice is a real design decision, since cross-user dedup leaks the fact that a file already exists.
The metadata and data plane split
The single most important structural decision is that file bytes and file metadata live in different systems with different guarantees. Bytes go to a blob store optimized for cheap, durable, high-throughput sequential access, where eventual consistency on some operations is acceptable and durability is enormous. Metadata (the tree, versions, chunk manifests, permissions, and sync cursors) goes to a database that must be strongly consistent and fast for small point and range reads. If you put metadata in the blob store you cannot do a consistent rename or an atomic permission revoke. If you put bytes in the transactional database you cannot afford the storage or the write amplification. Google's own stack mirrors this: Colossus stores the bytes and stores its own file metadata in Bigtable, while product metadata leans on Bigtable and Spanner for transactional, globally consistent records. The interview signal is whether you name the two planes explicitly and give each the guarantee it actually needs, rather than reaching for one database to do everything.
Delta sync: sending only what changed
When a file changes, you never want to move the whole file if you can avoid it. Because content is already chunked and content-addressed, delta sync is a set difference. The sender computes the chunk manifest of the new version, the receiver knows the manifest of the version it holds, and the two sides exchange the list of hashes. The sender ships only the chunks whose hashes the receiver lacks, and the receiver reconstructs the new file by combining chunks it already had with the few new ones. Editing one paragraph of a 200 MB document might move a single 4 MB chunk. This is the same insight rsync popularized, applied at the storage layer instead of at transfer time. The cost is bookkeeping: you must store manifests, keep reference counts so shared chunks are not deleted prematurely, and handle the case where a receiver is many versions behind and needs a larger diff. For tiny files the overhead of chunking is not worth it, so a practical system stores small files whole and only chunks past a size threshold.
Resumable uploads for large files
A 10 GB upload over a mobile connection will be interrupted, and restarting from byte zero every time is unacceptable. Google Drive's resumable upload protocol solves this with a session. The client first makes a request that returns a session URI, then uploads the content in chunks via PUT requests, where each chunk except the last must be a multiple of 256 KB. The server acknowledges the highest contiguous byte range it has stored. If the connection drops, the client sends an empty PUT to the session URI to ask 'how much do you have', gets back a range, and resumes from exactly that offset. Sessions expire after a period of inactivity, and certain 4xx responses mean the session is gone and must be restarted, so the client has to distinguish a transient network error from a dead session. Note that this transport-level chunking at 256 KB boundaries is a different concern from the content-defined chunks used for dedup. One is about surviving a flaky connection during a single upload, the other is about not storing or resending identical content across the whole system.
The changes feed and sync protocol
Clients cannot poll for full state, that would be billions of file listings per minute. Instead each account has an ordered change log and each client holds a cursor into it. Google Drive exposes this as page tokens. A client calls changes.getStartPageToken once to get a baseline, then calls changes.list with that token to receive change entries in chronological order, paging through with nextPageToken until the final page returns a newStartPageToken that it stores for next time. Each entry names a file id and a change type, not the file contents, so the feed stays small even when the files are huge. To avoid constant polling, changes.watch subscribes to push notifications, but crucially those notifications only say 'there are new changes, come look', they carry no change data. The client then pulls the feed and fetches any chunks it now needs. This design keeps the notification path cheap enough to fan out to tens of thousands of clients for a hot shared folder, because the expensive byte transfer only happens for the specific clients that actually open the changed file.
Sharing, ACLs, and permission inheritance
Sharing turns one physical file into many logical views. A file has an access control list of grants: specific users, groups, a whole domain, or anyone with the link, each mapped to a role of viewer, commenter, or editor. The hard parts are inheritance and revocation. A file usually inherits permissions from its parent folder, so moving a file into a shared folder can grant thousands of people access, and moving it out revokes them. You need to evaluate the effective permission as a function of the file's own grants plus every ancestor's grants plus the requester's group memberships, and you need to do it in a few milliseconds on every single read. Two common approaches are to compute effective permissions eagerly and cache them, which makes reads fast but revocation and large moves expensive, or to evaluate lazily by walking ancestors on each check, which makes writes cheap but reads slower. Because a revoked share must take effect immediately for security, permission state lives in the strongly consistent metadata store and permission caches must be invalidated synchronously, never left to expire on a timer.
Conflict resolution on concurrent edits
Two people take a laptop offline, both edit the same spreadsheet, both come back online. You cannot silently pick one and discard the other. For opaque binary files, the standard resolution is versioning plus a conflicted copy: the server accepts both writes as separate versions and, when it detects they descend from the same base version but neither is an ancestor of the other, it keeps one as the head and materializes the other as a clearly labeled conflicted copy so no work is lost. Detecting this needs each version to record its parent version or a vector clock, so the server can tell a genuine fork from a normal linear edit. For structured documents like Google Docs the story is different and much better: those are not synced as blobs at all, they use operational transformation or CRDTs so concurrent edits merge character by character. That is why a Google Doc almost never shows a conflict while a synced .xlsx file can. A strong answer distinguishes these two regimes and explains why block-level file sync is limited to last-writer-plus-fork while collaborative formats can truly merge.
Trade-offs to discuss
Every senior interviewer expects you to surface at least 3 of these. Pick the decisions, state the alternatives, and justify your choice.
Content-defined chunking versus fixed-size blocks
Content-defined boundaries survive insertions and deletions, which preserves dedup and keeps deltas tiny, at the cost of a rolling-hash pass over every byte and variable chunk sizes that complicate storage layout. Fixed blocks are cheaper to compute and align neatly, but a single inserted byte reshuffles every downstream block. For a sync product where small edits are common, content-defined wins despite the CPU cost.
Global dedup versus per-user dedup
Global dedup across all users maximizes storage savings, since one viral file is stored once for everyone. But it leaks existence: an attacker can learn whether a file already exists by observing whether an upload is accepted instantly, and it complicates deletion and encryption. Per-user or per-tenant dedup is safer and simpler to reason about, at the cost of storing popular content many times. Most privacy-conscious designs scope dedup narrowly.
Strongly consistent metadata versus eventually consistent
Strong consistency for the tree and ACLs means a rename or a revoked share is never stale, which is essential for security and for a coherent file list. It costs latency and constrains how far you can scale a single shard. Eventual consistency would scale trivially but would let a revoked user keep reading for seconds, which is unacceptable, so metadata pays for strong consistency while bytes do not.
Push notifications versus client polling for sync
A push channel gives near-real-time sync and avoids millions of empty poll requests, but it requires maintaining long-lived connections and a notification fan-out system. Polling is dead simple and stateless on the server but wastes bandwidth and adds latency. Real systems use push to say 'something changed' and then let the client pull the change feed, getting the best of both.
Storing versions as full copies versus deltas
Because chunks are already deduplicated, keeping full version history is nearly free: a new version only stores the chunks that actually changed and references the rest. Explicit delta chains would save little and would make restore slower, since you would have to replay diffs. Content-addressed chunks give you cheap version history without a separate delta format.
Last-writer-wins with conflict copies versus operational merge
For arbitrary binary files you cannot merge content meaningfully, so keeping both versions as a head and a conflicted copy is the safe default, since it never loses data even if it annoys the user. True operational merge with OT or CRDTs gives a far better experience but only works for formats you understand structurally, like Docs and Sheets. Matching the strategy to the file type is the right call, not forcing one everywhere.
How Google Drive actually does it
Google does not publish a full architecture diagram of Drive, so some of the above is reasoned from general principles and labeled as such. What is public is the substrate. File bytes live in Colossus, Google's cluster-level successor to GFS, which stores its own file metadata in Bigtable and uses curators for the control plane, D file servers for data, and custodians for background durability work like re-replication and RAID reconstruction. Colossus scales to exabytes across tens of thousands of machines per cluster and underpins Cloud Storage, YouTube, Gmail, and Drive alike. The developer-facing sync mechanics are documented directly in the Drive API: the changes feed with getStartPageToken, changes.list, and changes.watch, and the resumable upload protocol with its 256 KB chunk requirement. For transactional, globally consistent product metadata Google has Spanner, and for large flat keyspaces it has Bigtable, which is the lineage behind the metadata plane described here. Where I describe chunk sizes, dedup scoping, or conflict handling as specific numbers or policies, treat those as common design choices rather than confirmed internal details of Drive.
Sources
- A peek behind Colossus, Google's file system (Google Cloud Blog)
- Google Drive API: Retrieve changes (changes feed and page tokens)
- Google Drive API: Upload file data (resumable uploads, 256 KB chunks)
- How Colossus optimizes data placement for performance (Google Cloud Blog)
- changes.list API reference (page tokens, incremental sync)
Lessons to study before this interview
If any of these topics are fuzzy, the interviewer will catch it. Each lesson is 15 to 60 minutes with diagrams, code, and a quiz.
Related system design interview questions
Practice these next. They lean on the same core building blocks as Google Drive.