// Activities
UploadActivity(UserId user_id, Activity a); // need timestamp in activity as upload might not be instant
LoadActivities(UserId user_id); // Lightweight view of all activities
LoadActivity(UserId user_id, ActivityId aid); // Loads a full individual activity and progress against
// goals. Only called if this device does not have the activity cached locally, which is unlikely
// Goals
SetGoal(UserId user_id, Goal goal);
ViewGoals(UserId user_id); // Gets full set of goals. Can load all here as goals will be small
UpdateGoal(UserId user_id, Goal goal);
This is going to be heavier on writes than most workloads. I anticipate that almost all detailed reads of individual activities will be from a cache on the user device.
This data is going to require some notion of interconnectedness for goal tracking (we'll use an aggregation on all previous activities to show progress towards the goal). We definitely don't want a database system that is going to make that incredibly painful unless we're working at a scale that necessitates that, so I'm going to lean towards a standard RDBMS like Postgres.
We're not worried about hot shards/users as there is no activity sharing at this point in time.
Examining a couple of our other goals:
I'm going to assume our users aren't generally jet-setting so we don't need to worry about frequent cross continent replication. We'll do cross continent replication for disaster aversion but it doesn't have to be frequent as not doing it doesn't come with a hit to the performance of the things we care about.
I'll plan to have a replica in us east, us west, and europe central to start.
I'm not concerned about other performance issues yet as we're not allowing users to share activities with others.
As far as actual DB schema goes, we'll have a user table, and a table for each type of activity we support, keyed by userid. Table schemas per activity will vary, and store raw activity data. For example, for running you might upload a set of timestamped coordinates from the run, and then the duration. Each activity needs a start time, duration, and owning user id, but will likely diverge from there.
We want to never, ever lose a user activity (obviously impossible to 100% guarantee but we want to get as close to this as possible). As soon as it is recorded in the app, it needs to get written to a file, so even if the app crashes it is still there. Failure to write it to the file should not crash the app, we should handle that gracefully.
We'll store activities for the app locally in a SQLite database, the standard for mobile apps.
This is very straightforward otherwise, there is no need for a large scale up due to low QPS. In each region, we'll have three active app servers, an active-passive load balancer, and two frontend service replicas to handle TLS termination and communication with app servers. Sophisticated load balancing is not required as traffic is so low. We'll use round robin for now.
Every request will always reach the database. That is totally acceptable with this low of traffic.
The general design is covered in the diagram.
This scales well, easily to 10x-100x users, if we don't add the ability to share activities.
Sharing activities invites a host of new problems: handling hot users, significantly increase read traffic, handling likes and comments, generating feeds for users etc.
Postgres and SQLite are obvious choices given our requirements for relational queries and low traffic.
If a DB dies, we will have backups but it'll take some time to bring one back online. For that scenario, assuming we can reasonably afford it, we'll run an active-passive configuration in each region so that we don't have to failover traffic to a replica in another region except in the case of a region-level failure.
To bring up DBs replicas faster, we'll have regular backups (every hour or so) and keep a write ahead log of each transaction, so that we can just restore the backup and then replay the log in order to get the new DB ready.
User sharing as discussed above. Adding photo capabilities, third party integrations, as well as advanced analytics and the abilities for users to export/delete data.