SD System Design In Depth
0%
design system-designfile-syncdesign

Design: a file sync service

Module 6.3: High-Scale Media & Operations Designs Updated Sep 18, 2026
📄 Cheat Sheet
Design Dropbox or Google Drive w/ a Ex-Meta Staff Engineer System Design Interview
Hello Interview Design Dropbox or Google Drive w/ a Ex-Meta Staff Engineer System Design Interview Watch on YouTube ↗

A direct design of a file-sync service: chunked resumable uploads, presigned URLs, metadata vs blocks, sync notifications and conflict handling. The current video is about generic object storage.

0:00 / --:--

Designing a File Sync Service (Dropbox/Google Drive)

A file-sync service carries changes between a remote folder and devices that may be offline. It has to move the right bytes, distinguish an edit from a retry, and preserve work when two devices change the same file.

The core challenge is minimizing bandwidth and ensuring consistency across multiple devices (laptops, phones) that might go offline and come back online at random times.

Block-Level Sync (Chunking)

If a user changes one line in a 100MB file, uploading the whole file may be inefficient. A client can hash chunks and upload only missing content. Fixed 4MB boundaries work well for aligned replacements, but an insertion near the beginning shifts later boundaries and can make most blocks appear new; content-defined chunking trades more CPU for boundary stability.

This is called block-level sync. The backend object storage doesn't store files as monolithic blobs; it stores these 4MB chunks, named by their cryptographic hash (Content-Addressable Storage). The metadata database maps a file version to an ordered list of chunk hashes.

Deduplication

Content hashes make deduplication possible, but global cross-user deduplication is a security and lifecycle choice rather than an automatic consequence. The service must authorize every reference, avoid revealing whether another account owns a guessed hash, count references safely, and delete a block only after no retained version is entitled to it. Per-tenant deduplication is a simpler isolation boundary.

The Architecture

A typical architecture splits metadata and block storage:

flowchart TD Client["Desktop Client"] BlockServers["Block Servers"] MetadataServers["Metadata Servers"] Notification["Notification Service"] Client -->|Upload Chunks| BlockServers BlockServers --> S3[("Object Storage")] Client -->|Update File Info| MetadataServers MetadataServers --> DB[("Metadata DB")] MetadataServers --> Notification Notification -->|Ping| Client

Synchronization Protocol

Clients need to know when files change on the server. Polling the server every second drains battery and wastes server resources. Instead, clients maintain a long-lived connection (like WebSockets or Long Polling) to a Notification Service.

When Laptop A uploads a change, it updates the Metadata DB. The Metadata Service publishes an event to the Notification Service, which instantly pings Laptop B: "There is a new version of 'document.docx'". Laptop B then requests the new metadata and downloads only the missing chunks from the Block Servers.

Handling Conflicts

If Laptop A and Laptop B both edit the same file while offline, and then both come online, we have a conflict. The system cannot merge the files itself (it doesn't know the business logic of a Word document). The standard approach is to accept both files, creating a "Conflicted Copy" of the second file to arrive, and relying on the user to resolve it.

Worked example: editing a 100 MB file

  • The file is split into 25 chunks of 4 MB. Dropbox has used 4 MB blocks with SHA-256 hashes.
  • The user appends 1 MB to the end. Only the last chunk changes (and possibly one new chunk is added), so the client uploads about 4 to 5 MB instead of 100 MB.
  • The user inserts 1 KB near the start. With fixed chunks every boundary shifts, so all 25 hashes change and about 100 MB is uploaded again. With content-defined chunking (a rolling hash picks boundaries where the hash matches a pattern, averaging about 4 MB), only the chunk containing the insertion changes, so about 4 MB is uploaded.
  • The metadata commit is a small request: file_id, base_rev 17, new block list [h1, h2, ...]. If the server's current revision is not 17, the commit is rejected and the client handles it as a conflict.

Chunking and sync choices

ChoiceOption AOption BGuidance
Chunk boundariesFixed size (simple, cheap)Content-defined (FastCDC, Rabin)Content-defined chunking when edits are often inserts; fixed size for append-only or media files
Change notificationLong pollingWebSocket or pushDropbox historically used long polling for the desktop client; mobile relies on push
Conflict handlingConflicted copyAutomatic merge (OT or CRDT)Opaque binary files get a conflicted copy; Google Docs-style documents merge
Dedup scopePer user or teamGlobalPer user is safer; global saves more storage but leaks information

Sync protocol

sequenceDiagram participant A as Laptop A participant M as Metadata service participant B as Block service participant N as Notifier participant L as Laptop B A->>M: which of h1 to h26 are missing M-->>A: missing h25, h26 A->>B: upload h25, h26 A->>M: commit file 9, base rev 17, blocks h1 to h26 M-->>A: ok, rev 18, cursor 5012 M->>N: namespace changed N-->>L: wake up L->>M: list changes since cursor 5011 M-->>L: file 9 rev 18, blocks h1 to h26 L->>B: download h25, h26 only

Commit the metadata after the blocks are durable, so no device ever sees a version whose blocks are missing. Blocks that are uploaded but never committed are removed later by a garbage collector.

Check yourself Question 1 of 3
Offline edit conflict recall
From: Designing a File Sync Service (Dropbox/Google Drive)

Two laptops edit the same .docx while offline and both come online. What is the standard way a file-sync service handles this?

Key Takeaways

Break files into chunks to only upload/download the parts of a file that actually changed.
Use Content-Addressable Storage (naming chunks by their hash) to achieve automatic deduplication.
Separate the metadata path (database) from the data path (object storage).
Use long-polling or WebSockets to notify clients of changes instantly, avoiding inefficient polling.
Handle offline conflicts gracefully by creating conflicted copies rather than silently overwriting data.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue