Although the limits may be handled by a single server, we need redundancy in case the main server fails.
Also, many data require processing and may have intermediate outputs, which consumes CPU bandwidth and also reduces disk available bandwidth. We may need multiple servers to handle different data.
Authentication:
Auth authenticate(user, access);
Write and Read accesses are only available after the user is authenticated.
Write:
int write(input, processing_rules, destination);
Read:
int read(&output, source, data_requested);
Depending on the data characteristics, different databases are chosen.
SQL: structured relational data, e.g. user profile
Document DB: e.g. MongoDB, to store documents, such as news feed, etc.
Append-only DB: e.g. Cassandra, to store structured analytical data
Time series DB: e.g. kdb, to store financial data
See diagram
Write:
Read:
request goes to load balancer, then route to read server. Server reads data from cache, or from DB on cache miss.
Archive:
Scheduler kicks off archiving process and move older data to archive storage.
We separate read and write to different servers because the data ingestion throughput is bound by disk I/O and/or DB write throughput. If read and write are put on the same server, we still have enough CPU power to process the requests and data, but read speed might be limited if disk is involved.
We may also need DB replicas in case of DB failure. DB shall have a write-ahead log to avoid possible data duplication or corruption when the DB fails; or some other idempotent mechanism shall be implemented on the processing server side to ensure such data integrity.
For the queue between write processing servers and DB, it may be partitioned based on which DB the data is going to, or partitioned based on server. The former may be cleaner because one single queue contains all same kind of data, and since one kind of data is processed by one server, the order is guaranteed same as data initial arrival, which may be critical in some use cases, such as financial market data.
If a specific kind of data is large, the server (and the queue) which is handling this dataset may become computational bottleneck. We'll need to distribute the traffic to multiple servers. In this case, if the order of data matters, we need to aggregate them per their original order. We may rely on the load balancer, which is initially handling the requests, to put a timestamp or sequence number on each of the incoming request.
We choose consistency over availability, in which case we can retry if the read and/or archiving job fails on the first attempt.
If the data size is much larger than 1 TB daily, then a single dataset may not be handled by a single server. We'll distribute such traffic to multiple servers.