POST /documents: Create a new document.GET /documents/{id}: Retrieve a document by its ID.PUT /documents/{id}: Update a document's content.DELETE /documents/{id}: Delete a document.GET /notifications: Retrieve notifications for the user.POST /notifications/mark-read: Mark notifications as read.GET /documents/{id}/versions: Retrieve the version history of a document.POST /documents/{id}/versions/{version_id}/restore: Restore a specific version of a document.The document metadata includes:
The metadata is typicall small and structured, and the typical queries we need to fulfill are:
Based on the above requirements, we should use a relational database to store the document metadata because:
Document content tends to be larger and less structured than metadata, especially for text documents, images, and other rich media. Additionally, the system may need to store multiple snapshots or versions of the content for version history, which increases storage needs over time.
Cloud storage services (e.g., AWS S3, Google Cloud Storage) are designed to handle massive amounts of unstructured data. They are optimized for high throughput, scalability, and durability. They are also cost effective for large amounts of data. Therefore, we should use a cloud storage service to store the document content.
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
Last-Write-Wins
First, let's keep it simple and use the Last-Write-Wins strategy. In this approach, each edit is timestamped, and when conflicts occur, the edit with the latest timestamp is chosen as the winner.
Here's an example:
Pros:
Cons:
This approach is suitable for simple collaborative scenarios where conflicts are rare and losing occasional edits is acceptable. However, for a professional collaborative editing system like Google Docs, we need a more sophisticated approach like Operational Transformation, which we'll discuss next.
Operational Transformation
The primary goal of Operational Transformation (OT) is to ensure that all users see the same final document state after a series of edits, even if those edits are made out of order. It does this by applying transformation functions to modify the conflicting operation so that they can be applied in a way that preserves the intention of each user's changes.
Let's use the same example as above to illustrate how OT works. At step 6, instead of choosing the latest edit, the OT will transform the edit
OT will then transform User B's Operation Based on User A's Edit
User B wanted to insert " amazing" at position 6 in the original document. However, User A's insertion of " beautiful" has shifted the text "World" to the right.
New Position for User B's Edit: After User A's insertion of " beautiful," position 6 becomes position 16 (because " beautiful" is 10 characters long).
Transformed User B's Operation: Insert " amazing" at position 16 in the updated document.
Now we can apply User B's transformed operation to the document.
The document state becomes "Hello beautiful amazing World" and this is the final state.
This is a simple example. In a real-world collaborative editing system, the operations can be much more complex.
Pros:
Cons:
This is one of the reason why collaborative editing systems typically allow only a small number of concurrent users to edit the same document.
CRDTs (Conflict-free Replicated Data Types) are data structures that automatically resolve conflicts in distributed systems.
CRDT vs OT Comparison
Operational Transformation (OT)
CRDTs
How to handle offline editing?
Offline editing presents several challenges that need to be addressed:
Cache document state in IndexedDB/LocalStorage
Store pending operations queue
interface PendingOperation {
id: string;
timestamp: number;
operation: Operation;
status: 'pending' | 'failed' | 'synced';
retryCount: number;
}
2. Conflict Resolution Strategies
Option 1: Optimistic Updates
Apply changes locally immediately
Queue for sync when online
Resolve conflicts during sync
Used by Google Docs
Option 2: Pessimistic Locking
Require online state for edits
Prevent offline modifications
Used by Dropbox Paper
Option 3: Branch-Based
Create local branch for offline changes, similar to how Git works
Merge when online
More complex but flexible
3. Sync Process
Detect online status
Upload pending changes
Fetch remote changes
Resolve conflicts
Update local state
Choice of Conflict Resolution Strategy:
Data Partitioning Strategies:
Choice of Messaging Protocol for Real-Time Updates:
Try to discuss as many failure scenarios/bottlenecks as possible.