Overview
Drawing history can be divided into 2 independent parts:
- Whiteboard background color
- Whiteboard content
Drawing history is sent by the server to the client app per client request. Client requests history once it has connected to the server and may request it during work if needed.
Whiteboard background color
Stores in Redis. Key -- whiteboard ID, value -- color. Default color is specified in the server configuration.
Whiteboard content
It can be represented as log of drawing operations (figures). Each figure has ID and drawing data (color, points, etc.)
When someone draws something on the board server broadcasts it to all other clients of the same board and saves this operation into storage. When client requests history server reads it from storage and sends to the client in batches.
History storing mechanism is write heavy: write on each client action, read when new client joins. Server should only store and broadcast history to clients. It doesn't need to work with history content. As a result NoSQL is a good choice as history storage. Redis and Apache Cassandra are suitable for this use case.
Architecture
Drawing history may contain a lot of data so it may take time to download it in full. Client should be fully functional (broadcast drawings and accepts ones from the server) while history is downloading. Client should download history via separate connection to satisfy this requirement. In such case main connection could be used to process drawing as usual. Connection is closed once history was downloaded.
Contracts
Server:GetHistory(id)
-- request history for board with specified ID
Client:SetBackground(color, version)
-- set background color
Client:Draw(figures)
-- draw figures on the whiteboard
Server
There is separate hub (ex. HistoryHub
) for loading history. GetHistory()
method reads background color and drawing actions from storage and calls SetBackground()
and Draw()
methods of the caller client. It's preferred to read and send drawing actions in batches as history may contain thousands of records.
Client
RemoteWorkspace
component saves board ID into RemoteWorkspaceState
store once connection is established and client joined the board. There is separate component for loading history (ex. RemoteWorkspaceHistory
). It has backgroundColor
store and serverToClientBus
message bus as input params. RemoteWorkspaceHistory
connects to HistoryHub
and invokes its GetHistory
method once board ID appears in the store. SetBackground()
and Draw()
methods set color to backgroundColor
store and push figures to serverToClientBus
message bus accordingly. Connection is closed once history is loaded.
Pitfalls
Current design doesn't allow to handle several use cases effectively
- We can't resume history downloading if connection was lost in the middle. We should start downloading from the very beginning
- We can't download some part of the history if client was offline for some time. We should either download whole history again or ignore missed data
Implementation details
Common
- IDs can be generated by Redis using
INCR
operation
- All operations with storage should be extracted into interface. There are two implementations of the interface
- Redis only
- Redis + Apache Cassandra
- This functionality should be implemented as V3. Both client and server should support V1, V2 and V3 (create separate hubs on server and separate components to work with it on client)
Color
Storing of background color requires 2 values: color and version (more about color version in #10). Setting new color should be followed by updating its version. These 2 actions should be wrapped into Redis transaction to be atomic.
Scope of work
Scope of this issue is storing of whiteboard background color. Storing of whiteboard content should be implemented firstly with Redis and then with Apache Cassandra.
Dependencies